diff --git a/windows/src/engine/kmcomapi/kmcomapi.dpr b/windows/src/engine/kmcomapi/kmcomapi.dpr index 2a60ffbbc8..29ca83ce39 100644 --- a/windows/src/engine/kmcomapi/kmcomapi.dpr +++ b/windows/src/engine/kmcomapi/kmcomapi.dpr @@ -145,7 +145,8 @@ uses VKeys in '..\..\global\delphi\general\VKeys.pas', Windows8LanguageList in '..\..\global\delphi\general\Windows8LanguageList.pas', BCP47SuppressScriptRegistry in '..\..\global\delphi\general\BCP47SuppressScriptRegistry.pas', - BCP47Tag in '..\..\global\delphi\general\BCP47Tag.pas'; + BCP47Tag in '..\..\global\delphi\general\BCP47Tag.pas', + JsonUtil in '..\..\global\delphi\general\JsonUtil.pas'; {$R *.TLB} diff --git a/windows/src/engine/kmcomapi/kmcomapi.dproj b/windows/src/engine/kmcomapi/kmcomapi.dproj index 1cc46008ed..9a2b083879 100644 --- a/windows/src/engine/kmcomapi/kmcomapi.dproj +++ b/windows/src/engine/kmcomapi/kmcomapi.dproj @@ -252,6 +252,7 @@ + Cfg_2 diff --git a/windows/src/global/delphi/general/JsonUtil.pas b/windows/src/global/delphi/general/JsonUtil.pas index 9282c12342..0391858a01 100644 --- a/windows/src/global/delphi/general/JsonUtil.pas +++ b/windows/src/global/delphi/general/JsonUtil.pas @@ -84,7 +84,7 @@ begin if i < JSONValue.Count - 1 then OutputStrings[OutputStrings.Count-1] := OutputStrings[OutputStrings.Count-1] + ','; // I4440 end; - OutputStrings.add(StringOfChar(' ', indent * INDENT_SIZE) + ']'); + OutputStrings.add(StringOfChar(' ', (indent-1) * INDENT_SIZE) + ']'); end; procedure PrettyPrintJSON(JSONValue: TJSONValue; OutputStrings: TStrings; indent: integer = 0); diff --git a/windows/src/global/delphi/general/PackageInfo.pas b/windows/src/global/delphi/general/PackageInfo.pas index af9c0ec76c..597cba9330 100644 --- a/windows/src/global/delphi/general/PackageInfo.pas +++ b/windows/src/global/delphi/general/PackageInfo.pas @@ -40,6 +40,7 @@ uses System.Classes, System.Generics.Collections, System.IniFiles, + System.JSON, System.Sysutils, Winapi.Windows, Xml.XMLDoc, @@ -50,6 +51,8 @@ uses { Package Information Classes } +function GetJsonValueString(o: TJSONObject; const n: string): string; + type TPackageInfoEntryType = (pietName, pietVersion, pietCopyright, pietAuthor, pietWebsite, pietOther); @@ -150,6 +153,8 @@ type procedure SaveIni(AIni: TIniFile); virtual; procedure LoadXML(ARoot: IXMLNode); virtual; procedure SaveXML(ARoot: IXMLNode); virtual; + procedure LoadJSON(ARoot: TJSONObject); virtual; + procedure SaveJSON(ARoot: TJSONObject); virtual; end; { Package Options } @@ -178,6 +183,8 @@ type procedure SaveIni(AIni: TIniFile); virtual; procedure LoadXML(ARoot: IXMLNode); virtual; procedure SaveXML(ARoot: IXMLNode); virtual; + procedure LoadJSON(ARoot: TJSONObject); virtual; + procedure SaveJSON(ARoot: TJSONObject); virtual; property LoadLegacy: Boolean read FLoadLegacy write FLoadLegacy; property FileVersion: WideString read FFileVersion write SetFileVersion; property ExecuteProgram: WideString read FExecuteProgram write SetExecuteProgram; @@ -218,6 +225,8 @@ type procedure SaveIni(AIni: TIniFile); virtual; procedure LoadXML(ARoot: IXMLNode); virtual; procedure SaveXML(ARoot: IXMLNode); virtual; + procedure LoadJSON(ARoot: TJSONObject); virtual; + procedure SaveJSON(ARoot: TJSONObject); virtual; function IndexOf(Name: WideString): Integer; overload; @@ -246,6 +255,8 @@ type procedure SaveIni(AIni: TIniFile); virtual; procedure LoadXML(ARoot: IXMLNode); virtual; procedure SaveXML(ARoot: IXMLNode); virtual; + procedure LoadJSON(ARoot: TJSONObject); virtual; + procedure SaveJSON(ARoot: TJSONObject); virtual; end; TPackageStartMenu = class(TPackageBaseObject) @@ -261,6 +272,8 @@ type procedure SaveIni(AIni: TIniFile); virtual; procedure LoadXML(ARoot: IXMLNode); virtual; procedure SaveXML(ARoot: IXMLNode); virtual; + procedure LoadJSON(ARoot: TJSONObject); virtual; + procedure SaveJSON(ARoot: TJSONObject); virtual; end; { Package contained files } @@ -291,6 +304,8 @@ type procedure SaveIni(AIni: TIniFile); virtual; procedure LoadXML(ARoot: IXMLNode); virtual; procedure SaveXML(ARoot: IXMLNode); virtual; + procedure LoadJSON(ARoot: TJSONObject); virtual; + procedure SaveJSON(ARoot: TJSONObject); virtual; function IndexOfFileType(FFileType: TKMFileType): Integer; function FromFileName(Filename: WideString): TPackageContentFile; @@ -338,6 +353,8 @@ type procedure SaveIni(AIni: TIniFile); virtual; procedure LoadXML(ARoot: IXMLNode); virtual; procedure SaveXML(ARoot: IXMLNode); virtual; + procedure LoadJSON(ARoot: TJSONObject); virtual; + procedure SaveJSON(ARoot: TJSONObject); virtual; end; TPackageKeyboardLanguage = class(TPackageBaseObject) @@ -359,6 +376,8 @@ type function XMLRootNode: WideString; virtual; procedure DoLoadXML(ARoot: IXMLNode); virtual; procedure DoSaveXML(ARoot: IXMLNode); virtual; + procedure DoLoadJSON(ARoot: TJSONObject); virtual; + procedure DoSaveJSON(ARoot: TJSONObject); virtual; public Options: TPackageOptions; StartMenu: TPackageStartMenu; @@ -376,6 +395,9 @@ type procedure LoadXML; //virtual; procedure SaveXML; //virtual; + procedure LoadJSON; + procedure SaveJSON; + function SaveXMLToText: WideString; procedure LoadXMLFromText(Text: WideString); @@ -393,6 +415,7 @@ uses System.TypInfo, System.Variants, + JsonUtil, KeymanVersion, utildir, utilsystem, @@ -406,6 +429,78 @@ const SDisplayFontNotOwnedCorrectly = 'The display font file ''%s'' referred to is not part of the package.'; SOSKFontNotOwnedCorrectly = 'The OSK font file ''%s'' referred to is not part of the package.'; + + + +const + SXML_PackageKeyboards = 'Keyboards'; + SXML_PackageKeyboard_Name = 'Name'; + SXML_PackageKeyboard_ID = 'ID'; + SXML_PackageKeyboard_Version = 'Version'; + SXML_PackageKeyboard_OSKFont = 'OSKFont'; + SXML_PackageKeyboard_DisplayFont = 'DisplayFont'; + SXML_PackageKeyboard_Languages = 'Languages'; + + SXML_PackageKeyboard_Language = 'Language'; + SXML_PackageKeyboard_Language_ID = 'ID'; + SXML_PackageKeyboard_Language_Name = 'Name'; + +const + SJSON_System = 'system'; + SJSON_System_KeymanDeveloperVersion = 'keymanDeveloperVersion'; + SJSON_System_FileVersion = 'fileVersion'; + + SJSON_Options = 'options'; + SJSON_Options_ExecuteProgram = 'executeProgram'; + SJSON_Options_ReadMeFile = 'readmeFile'; + SJSON_Options_GraphicFile = 'graphicFile'; + + SJSON_Registry = 'registry'; + SJSON_Registry_Root = 'root'; + SJSON_Registry_Key = 'key'; + SJSON_Registry_Name = 'name'; + SJSON_Registry_ValueType = 'valueType'; + SJSON_Registry_Value = 'value'; + + SJSON_StartMenu = 'startMenu'; + SJSON_StartMenu_Folder = 'folder'; + SJSON_StartMenu_AddUninstallEntry = 'addUninstallEntry'; + SJSON_StartMenu_Items = 'items'; + SJSON_StartMenu_Items_Name = 'name'; + SJSON_StartMenu_Items_Filename = 'filename'; + SJSON_StartMenu_Items_Arguments = 'arguments'; + SJSON_StartMenu_Items_Icon = 'icon'; + SJSON_StartMenu_Items_Location = 'location'; + + SJSON_Info = 'info'; + SJSON_Info__Description = 'description'; + SJSON_Info__URL = 'url'; + + SJSON_Info_Website = 'website'; + SJSON_Info_Version = 'version'; + SJSON_Info_Name = 'name'; + SJSON_Info_Copyright = 'copyright'; + SJSON_Info_Author = 'author'; + + SJSON_PackageInfoEntryTypeNames: array[TPackageInfoEntryType] of string = + (SJSON_Info_Name, SJSON_Info_Version, SJSON_Info_Copyright, SJSON_Info_Author, SJSON_Info_Website, ''); + + SJSON_Files = 'files'; + SJSON_Files_Name = 'name'; + SJSON_Files_Description = 'description'; + SJSON_Files_CopyLocation = 'copyLocation'; + + SJSON_Keyboards = 'keyboards'; + SJSON_Keyboard_Name = 'name'; + SJSON_Keyboard_ID = 'id'; + SJSON_Keyboard_Version = 'version'; + SJSON_Keyboard_OSKFont = 'oskFont'; + SJSON_Keyboard_DisplayFont = 'displayFont'; + + SJSON_Keyboard_Languages = 'languages'; + SJSON_Keyboard_Language_ID = 'id'; + SJSON_Keyboard_Language_Name = 'name'; + {------------------------------------------------------------------------------- - TPackageOptions - ------------------------------------------------------------------------------} @@ -482,6 +577,22 @@ begin FRegistryKeys.LoadIni(AIni); end; +procedure TPackageOptions.LoadJSON(ARoot: TJSONObject); +var + FSystem, FOptions: TJSONObject; +begin + FSystem := ARoot.Values[SJSON_System] as TJSONObject; + FOptions := ARoot.Values[SJSON_Options] as TJSONObject; + + FileVersion := GetJsonValueString(FSystem, SJSON_System_FileVersion); + ExecuteProgram := GetJsonValueString(FOptions, SJSON_Options_ExecuteProgram); + ReadmeFile := Package.Files.FromFileName(GetJsonValueString(FOptions, SJSON_Options_ReadMeFile)); + GraphicFile := Package.Files.FromFileName(GetJsonValueString(FOptions, SJSON_Options_GraphicFile)); + if Assigned(ReadmeFile) then ReadmeFile.AddNotifyObject(ReadmeRemoved); + if Assigned(GraphicFile) then GraphicFile.AddNotifyObject(GraphicRemoved); + FRegistryKeys.LoadJSON(ARoot); +end; + procedure TPackageOptions.SaveIni(AIni: TIniFile); begin AIni.WriteString('Package', 'Version', FileVersion); @@ -493,6 +604,26 @@ begin FRegistryKeys.SaveIni(AIni); end; +procedure TPackageOptions.SaveJSON(ARoot: TJSONObject); +var + FOptions, FSystem: TJSONObject; +begin + FSystem := TJSONObject.Create; + ARoot.AddPair(SJSON_System, FSystem); + FSystem.AddPair(SJSON_System_KeymanDeveloperVersion, GetVersionString); + FSystem.AddPair(SJSON_System_FileVersion, FileVersion); + + FOptions := TJSONObject.Create; + ARoot.AddPair(SJSON_Options, FOptions); + if ExecuteProgram <> '' then + FOptions.AddPair(SJSON_Options_ExecuteProgram, ExecuteProgram); + if Assigned(ReadmeFile) then + FOptions.AddPair(SJSON_Options_ReadMeFile, ReadmeFile.RelativeFileName); + if Assigned(GraphicFile) then + FOptions.AddPair(SJSON_Options_GraphicFile, GraphicFile.RelativeFileName); + FRegistryKeys.SaveJSON(ARoot); +end; + procedure TPackageOptions.SetExecuteProgram(Value: WideString); begin FExecuteProgram := Value; @@ -556,7 +687,7 @@ begin FInfoType := pietOther; for piet := Low(PackageInfoEntryTypeNames) to High(PackageInfoEntryTypeNames) do - if PackageInfoEntryTypeNames[piet] = LowerCase(FName) then + if SameText(PackageInfoEntryTypeNames[piet], FName) then FInfoType := piet; end; @@ -629,6 +760,34 @@ begin end; end; +procedure TPackageInfoEntryList.LoadJSON(ARoot: TJSONObject); +var + name: string; + i: Integer; + inf: TPackageInfoEntry; + FItems: TJSONObject; + FNode: TJSONObject; + v: TPackageInfoEntryType; +begin + Clear; + FItems := ARoot.Values[SJSON_Info] as TJSONObject; + for i := 0 to FItems.Count - 1 do + begin + name := FItems.Pairs[i].JsonString.Value; + for v := Low(TPackageInfoEntryType) to High(TPackageInfoEntryType) do + if SJSON_PackageInfoEntryTypeNames[v] = name then + begin + FNode := FItems.Pairs[i].JsonValue as TJSONObject; + inf := TPackageInfoEntry.Create(Package); + inf.Name := name; + inf.Description := GetJsonValueString(FNode, SJSON_Info__Description); + inf.URL := GetJsonValueString(FNode, SJSON_Info__URL); + Add(inf); + Break; + end; + end; +end; + procedure TPackageInfoEntryList.SaveIni(AIni: TIniFile); var i: Integer; @@ -637,6 +796,26 @@ begin AIni.WriteString('Info', Items[i].Name, Format('"%s","%s"', [Items[i].Description, Items[i].URL])); end; +procedure TPackageInfoEntryList.SaveJSON(ARoot: TJSONObject); +var + i: Integer; + AInfoRoot, ANode: TJSONObject; +begin + AInfoRoot := TJSONObject.Create; + ARoot.AddPair(SJSON_Info, AInfoRoot); + for i := 0 to Count - 1 do + begin + if SJSON_PackageInfoEntryTypeNames[Items[i].InfoType] <> '' then + begin + ANode := TJSONObject.Create; + AInfoRoot.AddPair(SJSON_PackageInfoEntryTypeNames[Items[i].InfoType], ANode); + ANode.AddPair(SJSON_Info__Description, Items[i].Description); + if Items[i].URL <> '' then + ANode.AddPair(SJSON_Info__URL, Items[i].URL); + end; + end; +end; + function TPackageInfoEntryList.DescIndexOf(Name: WideString): WideString; var i: Integer; @@ -729,16 +908,16 @@ end; - TPackageStartMenuEntryList - ------------------------------------------------------------------------------} -procedure TPackageStartMenuEntryList.LoadXML(ARoot: IXMLNode); +function StrToStartMenuEntryLocation(s: WideString): TPackageStartMenuEntryLocation; +var + v: Integer; +begin + v := System.TypInfo.GetEnumValue(TypeInfo(TPackageStartMenuEntryLocation), s); + if v = -1 then Result := psmelStartMenu + else Result := TPackageStartMenuEntryLocation(v); +end; - function StrToStartMenuEntryLocation(s: WideString): TPackageStartMenuEntryLocation; - var - v: Integer; - begin - v := System.TypInfo.GetEnumValue(TypeInfo(TPackageStartMenuEntryLocation), s); - if v = -1 then Result := psmelStartMenu - else Result := TPackageStartMenuEntryLocation(v); - end; +procedure TPackageStartMenuEntryList.LoadXML(ARoot: IXMLNode); var sme: TPackageStartMenuEntry; i: Integer; @@ -805,6 +984,31 @@ begin end; end; +procedure TPackageStartMenuEntryList.LoadJSON(ARoot: TJSONObject); +var + i: Integer; + AItems: TJSONArray; + AItem: TJSONObject; + sme: TPackageStartMenuEntry; +begin + AItems := ARoot.Values[SJSON_StartMenu_Items] as TJSONArray; + if not Assigned(AItems) then + Exit; + + for i := 0 to AItems.Count - 1 do + begin + AItem := AItems.Items[i] as TJSONObject; + + sme := TPackageStartMenuEntry.Create(Package); + sme.Name := GetJsonValueString(AItem, SJSON_StartMenu_Items_Name); + sme.Prog := GetJsonValueString(AItem, SJSON_StartMenu_Items_Filename); + sme.Params := GetJsonValueString(AItem, SJSON_StartMenu_Items_Arguments); + sme.Icon := GetJsonValueString(AItem, SJSON_StartMenu_Items_Icon); + sme.Location := StrToStartMenuEntryLocation(GetJsonValueString(AItem, SJSON_StartMenu_Items_Location)); + Self.Add(sme); + end; +end; + procedure TPackageStartMenuEntryList.SaveIni(AIni: TIniFile); var i: Integer; @@ -813,6 +1017,30 @@ begin AIni.WriteString('StartMenuEntries', Items[i].Name, Format('"%s","%s"', [Items[i].Prog, Items[i].Params])); end; +procedure TPackageStartMenuEntryList.SaveJSON(ARoot: TJSONObject); +var + i: Integer; + AItems: TJSONArray; + AItem: TJSONObject; +begin + AItems := TJSONArray.Create; + ARoot.AddPair(SJSON_StartMenu_Items, AItems); + for i := 0 to Count - 1 do + begin + AItem := TJSONObject.Create; + AItems.Add(AItem); + AItem.AddPair(SJSON_StartMenu_Items_Name, Items[i].Name); + AItem.AddPair(SJSON_StartMenu_Items_Filename, Items[i].Prog); + if Items[i].Params <> '' then + AItem.AddPair(SJSON_StartMenu_Items_Arguments, Items[i].Params); + if Items[i].Icon <> '' then + AItem.AddPair(SJSON_StartMenu_Items_Icon, Items[i].Icon); + if Items[i].Location <> psmelStartMenu then + AItem.AddPair(SJSON_StartMenu_Items_Location, + System.TypInfo.GetEnumName(TypeInfo(TPackageStartMenuEntryLocation), Ord(Items[i].Location))); + end; +end; + procedure TPackageStartMenuEntryList.Assign(Source: TPackageStartMenuEntryList); var i: Integer; @@ -880,6 +1108,21 @@ begin Entries.LoadIni(AIni); end; +procedure TPackageStartMenu.LoadJSON(ARoot: TJSONObject); +var + ANode: TJSONObject; +begin + ANode := ARoot.Values[SJSON_StartMenu] as TJSONObject; + if not Assigned(ANode) then + Exit; + + Path := GetJsonValueString(ANode, SJSON_StartMenu_Folder); + DoCreate := Path <> ''; + AddUninstallEntry := Assigned(ANode.Values[SJSON_StartMenu_AddUninstallEntry]) and + (ANode.Values[SJSON_StartMenu_AddUninstallEntry] is TJSONTrue); + Entries.LoadJSON(ANode); +end; + procedure TPackageStartMenu.SaveIni(AIni: TIniFile); begin AIni.WriteString('StartMenu', 'Path', Path); @@ -888,6 +1131,22 @@ begin Entries.SaveIni(AIni); end; +procedure TPackageStartMenu.SaveJSON(ARoot: TJSONObject); +var + ANode: TJSONObject; +begin + if (Path = '') and (Entries.Count = 0) then + Exit; + + ANode := TJSONObject.Create; + ARoot.AddPair(SJSON_StartMenu, ANode); + if Path <> '' then + ANode.AddPair(SJSON_StartMenu_Folder, Path); + if AddUninstallEntry then + ANode.AddPair(SJSON_StartMenu_AddUninstallEntry, TJSONBool.Create(True)); + Entries.SaveJSON(ANode); +end; + {------------------------------------------------------------------------------- - TPackageSubFile - ------------------------------------------------------------------------------} @@ -983,7 +1242,7 @@ begin ChildNodes['Name'].NodeValue := Items[i].FileName; ChildNodes['Description'].NodeValue := Items[i].Description; ChildNodes['CopyLocation'].NodeValue := Ord(Items[i].CopyLocation); - ChildNodes['FileType'].NodeValue := ExtractFileExt(Items[i].FFileName); + ChildNodes['FileType'].NodeValue := ExtractFileExt(Items[i].FFileName); end; end; end; @@ -1017,6 +1276,28 @@ begin end; end; +procedure TPackageContentFileList.LoadJSON(ARoot: TJSONObject); +var + subfile: TPackageContentFile; + i: Integer; + FItems: TJSONArray; + FItem: TJSONObject; +begin + Clear; + FItems := ARoot.Values[SJSON_Files] as TJSONArray; + for i := 0 to FItems.Count - 1 do + begin + FItem := FItems.Items[i] as TJSONObject; + + subfile := TPackageContentFile.Create(Package); + subfile.FileName := GetJsonValueString(FItem, SJSON_Files_Name); + subfile.Description := GetJsonValueString(FItem, SJSON_Files_Description); + if Assigned(FItem.Values[SJSON_Files_CopyLocation]) then + subfile.FCopyLocation := TPackageFileCopyLocation((FItem.Values[SJSON_Files_CopyLocation] as TJSONNumber).AsInt); + Add(subfile); + end; +end; + procedure TPackageContentFileList.SaveIni(AIni: TIniFile); var i: Integer; @@ -1026,6 +1307,28 @@ begin Items[i].FileName, Ord(Items[i].CopyLocation)])); end; +procedure TPackageContentFileList.SaveJSON(ARoot: TJSONObject); +var + i: Integer; + AItems: TJSONArray; + AItem: TJSONObject; +begin + if Count = 0 then + Exit; + + AItems := TJSONArray.Create; + ARoot.AddPair(SJSON_Files, AItems); + for i := 0 to Count - 1 do + begin + AItem := TJSONObject.Create; + AItems.Add(AItem); + AItem.AddPair(SJSON_Files_Name, Items[i].FileName); + AItem.AddPair(SJSON_Files_Description, Items[i].Description); + if Items[i].CopyLocation <> pfclPackage then + AItem.AddPair(SJSON_Files_CopyLocation, TJSONNumber.Create(Ord(Items[i].CopyLocation))); + end; +end; + procedure TPackageContentFileList.Assign(Source: TPackageContentFileList); var i: Integer; @@ -1130,6 +1433,21 @@ begin end; end; +procedure TPackage.LoadJSON; +var + FJSON: TJSONObject; +begin + with TStringStream.Create('', TEncoding.UTF8) do + try + LoadFromFile(FileName); + FJSON := TJSONObject.ParseJSONValue(DataString) as TJSONObject; + finally + Free; + end; + + DoLoadJSON(FJSON); +end; + procedure PackageLoadError(Message: WideString); begin raise EPackageInfo.Create(Message); @@ -1209,13 +1527,21 @@ begin end; end; +procedure TPackage.DoLoadJSON(ARoot: TJSONObject); +begin + StartMenu.LoadJSON(ARoot); + Info.LoadJSON(ARoot); + Files.LoadJSON(ARoot); + Options.LoadJSON(ARoot); + Keyboards.LoadJSON(ARoot); +end; + procedure TPackage.DoLoadXML(ARoot: IXMLNode); var FVersion: WideString; begin - FVersion := VarToWideStr(ARoot.ChildNodes['System'].ChildNodes['FileVersion'].NodeValue); - if (FVersion <> SKeymanVersion70) and (FVersion <> SKeymanVersion80) and (FVersion <> SKeymanVersion) then + if FVersion <> SKeymanVersion70 then PackageLoadError('Package file version '+FVersion+' is not recognised.'); StartMenu.LoadXML(ARoot); @@ -1225,6 +1551,15 @@ begin Keyboards.LoadXML(ARoot); end; +procedure TPackage.DoSaveJSON(ARoot: TJSONObject); +begin + Options.SaveJSON(ARoot); + StartMenu.SaveJSON(ARoot); + Info.SaveJSON(ARoot); + Files.SaveJSON(ARoot); + Keyboards.SaveJSON(ARoot); +end; + procedure TPackage.DoSaveXML(ARoot: IXMLNode); begin ARoot.ChildNodes['System'].ChildNodes['KeymanDeveloperVersion'].NodeValue := GetVersionString; @@ -1254,6 +1589,34 @@ begin end; end; +procedure TPackage.SaveJSON; +var + FJSON: TJSONObject; + s: TStringList; +begin + if FileExists(FileName) then DeleteFileCleanAttr(FileName); // I4574 + + FJSON := TJSONObject.Create; + try + DoSaveJSON(FJSON); + s := TStringList.Create; + try + PrettyPrintJSON(FJSON, s); + with TStringStream.Create(s.Text, TEncoding.UTF8) do + try + // Use TStringStream to avoid BOM from TStringList + SaveToFile(FileName); + finally + Free; + end; + finally + s.Free; + end; + finally + FJSON.Free; + end; +end; + procedure TPackage.SaveXML; var doc: IXMLDocument; @@ -1418,6 +1781,48 @@ begin end; end; +procedure TPackageRegistryKeyList.LoadJSON(ARoot: TJSONObject); +var + i: Integer; + ANode: TJSONArray; + AKeyNode: TJSONObject; + pkr: TPackageRegistryKey; + name, value, root, key, valuetype: WideString; +begin + Clear; + + ANode := ARoot.Values[SJSON_Registry] as TJSONArray; + if not Assigned(ANode) then Exit; + + for i := 0 to ANode.Count - 1 do + begin + AKeyNode := ANode.Items[i] as TJSONObject; + pkr := TPackageRegistryKey.Create(Package); + + name := GetJsonValueString(AKeyNode, SJSON_Registry_Name); + value := GetJsonValueString(AKeyNode, SJSON_Registry_Value); + root := GetJsonValueString(AKeyNode, SJSON_Registry_Root); + key := GetJsonValueString(AKeyNode, SJSON_Registry_Key); + + if root = 'HKCU' then pkr.Root := HKEY_CURRENT_USER + else if root = 'HKLM' then pkr.Root := HKEY_LOCAL_MACHINE + else if root = 'HKCR' then pkr.Root := HKEY_CLASSES_ROOT + else pkr.Root := HKEY_CURRENT_USER; + + pkr.Name := name; + pkr.Key := key; + + valuetype := GetJsonValueString(AKeyNode, SJSON_Registry_ValueType); + if valuetype = 'D' + then pkr.ValueType := rkvtDWord + else pkr.ValueType := rkvtString; + + pkr.Value := value; + + Add(pkr); + end; +end; + procedure TPackageRegistryKeyList.SaveIni(AIni: TIniFile); var pkr: TPackageRegistryKey; @@ -1439,6 +1844,38 @@ begin end; end; +procedure TPackageRegistryKeyList.SaveJSON(ARoot: TJSONObject); +var + i: Integer; + AKeyNode: TJSONObject; + ANode: TJSONArray; + pkr: TPackageRegistryKey; + root, valuetype: WideString; +begin + if Count = 0 then + Exit; + + ANode := TJSONArray.Create; + ARoot.AddPair(SJSON_Registry, ANode); + for i := 0 to Count - 1 do + begin + pkr := Items[i]; + if pkr.Root = HKEY_CURRENT_USER then root := 'HKCU' + else if pkr.Root = HKEY_LOCAL_MACHINE then root := 'HKLM' + else if pkr.Root = HKEY_CLASSES_ROOT then root := 'HKCR' + else root := 'HKCU'; + + if pkr.ValueType = rkvtString then valuetype := 'S' else valuetype := 'D'; + AKeyNode := TJSONObject.Create; + ANode.Add(AKeyNode); + AKeyNode.AddPair(SJSON_Registry_Root, root); + AKeyNode.AddPair(SJSON_Registry_Key, pkr.Key); + AKeyNode.AddPair(SJSON_Registry_Name, pkr.Name); + AKeyNode.AddPair(SJSON_Registry_ValueType, valuetype); + AKeyNode.AddPair(SJSON_Registry_Value, pkr.Value); + end; +end; + procedure TPackageRegistryKeyList.LoadXML(ARoot: IXMLNode); var i: Integer; @@ -1630,15 +2067,6 @@ begin end; end; -const - SPackageKeyboard_Name = 'Name'; - SPackageKeyboard_ID = 'ID'; - SPackageKeyboard_Version = 'Version'; - SPackageKeyboard_OSKFont = 'OSKFont'; - SPackageKeyboard_DisplayFont = 'DisplayFont'; - SPackageKeyboard_Language = 'Language'; - SPackageKeyboard_Language_ID = 'ID'; - procedure TPackageKeyboardList.LoadIni(AIni: TIniFile); var s: TStringList; @@ -1659,15 +2087,15 @@ begin Break; keyboard := TPackageKeyboard.Create(Package); - keyboard.Name := AIni.ReadString(FSectionName, SPackageKeyboard_Name, ''); - keyboard.ID := AIni.ReadString(FSectionName, SPackageKeyboard_ID, ''); - keyboard.Version := AIni.ReadString(FSectionName, SPackageKeyboard_Version, '1.0'); - keyboard.OSKFont := Package.Files.FromFileNameEx(AIni.ReadString(FSectionName, SPackageKeyboard_OSKFont, '')); - keyboard.DisplayFont := Package.Files.FromFileNameEx(AIni.ReadString(FSectionName, SPackageKeyboard_DisplayFont, '')); + keyboard.Name := AIni.ReadString(FSectionName, SXML_PackageKeyboard_Name, ''); + keyboard.ID := AIni.ReadString(FSectionName, SXML_PackageKeyboard_ID, ''); + keyboard.Version := AIni.ReadString(FSectionName, SXML_PackageKeyboard_Version, '1.0'); + keyboard.OSKFont := Package.Files.FromFileNameEx(AIni.ReadString(FSectionName, SXML_PackageKeyboard_OSKFont, '')); + keyboard.DisplayFont := Package.Files.FromFileNameEx(AIni.ReadString(FSectionName, SXML_PackageKeyboard_DisplayFont, '')); for j := 0 to MaxInt do begin - ln := AIni.ReadString(FSectionName, SPackageKeyboard_Language+IntToStr(j), ''); + ln := AIni.ReadString(FSectionName, SXML_PackageKeyboard_Language+IntToStr(j), ''); if ln = '' then Break; FLanguage := TPackageKeyboardLanguage.Create(Package); @@ -1683,6 +2111,49 @@ begin end; end; +procedure TPackageKeyboardList.LoadJSON(ARoot: TJSONObject); +var + keyboard: TPackageKeyboard; + i, j: Integer; + ALanguages, ANode: TJSONArray; + AKeyboard, ALanguage: TJSONObject; + FLanguage: TPackageKeyboardLanguage; +begin + Clear; + + ANode := ARoot.Values[SJSON_Keyboards] as TJSONArray; + if not Assigned(ANode) then + Exit; + + for i := 0 to ANode.Count - 1 do + begin + AKeyboard := ANode.Items[i] as TJSONObject; + + keyboard := TPackageKeyboard.Create(Package); + keyboard.Name := GetJsonValueString(AKeyboard, SJSON_Keyboard_Name); + keyboard.ID := GetJsonValueString(AKeyboard,SJSON_Keyboard_ID); + keyboard.Version := GetJsonValueString(AKeyboard, SJSON_Keyboard_Version); + keyboard.OSKFont := Package.Files.FromFileNameEx(GetJsonValueString(AKeyboard, SJSON_Keyboard_OSKFont)); + keyboard.DisplayFont := Package.Files.FromFileNameEx(GetJsonValueString(AKeyboard, SJSON_Keyboard_DisplayFont)); + + ALanguages := AKeyboard.Values[SJSON_Keyboard_Languages] as TJSONArray; + if Assigned(ALanguages) then + begin + for j := 0 to ALanguages.Count - 1 do + begin + ALanguage := ALanguages.Items[j] as TJSONObject; + + FLanguage := TPackageKeyboardLanguage.Create(Package); + FLanguage.ID := GetJsonValueString(ALanguage, SJSON_Keyboard_Language_ID); + FLanguage.Name := GetJsonValueString(ALanguage, SJSON_Keyboard_Language_Name); + keyboard.Languages.Add(FLanguage); + end; + end; + + Add(keyboard); + end; +end; + procedure TPackageKeyboardList.LoadXML(ARoot: IXMLNode); var keyboard: TPackageKeyboard; @@ -1698,11 +2169,11 @@ begin AKeyboard := ANode.ChildNodes[i]; keyboard := TPackageKeyboard.Create(Package); - keyboard.Name := VarToStr(AKeyboard.ChildValues[SPackageKeyboard_Name]); - keyboard.ID := VarToStr(AKeyboard.ChildValues[SPackageKeyboard_ID]); - keyboard.Version := VarToStr(AKeyboard.ChildValues[SPackageKeyboard_Version]); - keyboard.OSKFont := Package.Files.FromFileNameEx(VarToStr(AKeyboard.ChildValues[SPackageKeyboard_OSKFont])); - keyboard.DisplayFont := Package.Files.FromFileNameEx(VarToStr(AKeyboard.ChildValues[SPackageKeyboard_DisplayFont])); + keyboard.Name := VarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_Name]); + keyboard.ID := VarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_ID]); + keyboard.Version := VarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_Version]); + keyboard.OSKFont := Package.Files.FromFileNameEx(VarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_OSKFont])); + keyboard.DisplayFont := Package.Files.FromFileNameEx(VarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_DisplayFont])); ALanguages := AKeyboard.ChildNodes['Languages']; if Assigned(ALanguages) then @@ -1712,7 +2183,7 @@ begin ALanguage := ALanguages.ChildNodes[j]; FLanguage := TPackageKeyboardLanguage.Create(Package); - FLanguage.ID := ALanguage.Attributes[SPackageKeyboard_Language_ID]; + FLanguage.ID := ALanguage.Attributes[SXML_PackageKeyboard_Language_ID]; FLanguage.Name := ALanguage.NodeValue; keyboard.Languages.Add(FLanguage); end; @@ -1730,17 +2201,54 @@ begin for i := 0 to Count-1 do begin FSectionName := 'Keyboard'+IntToStr(i); - AIni.WriteString(FSectionName, SPackageKeyboard_Name, Items[i].Name); - AIni.WriteString(FSectionName, SPackageKeyboard_ID, Items[i].ID); - AIni.WriteString(FSectionName, SPackageKeyboard_Version, Items[i].Version); + AIni.WriteString(FSectionName, SXML_PackageKeyboard_Name, Items[i].Name); + AIni.WriteString(FSectionName, SXML_PackageKeyboard_ID, Items[i].ID); + AIni.WriteString(FSectionName, SXML_PackageKeyboard_Version, Items[i].Version); if Assigned(Items[i].OSKFont) then - AIni.WriteString(FSectionName, SPackageKeyboard_OSKFont, ExtractFileName(Items[i].OSKFont.FileName)); + AIni.WriteString(FSectionName, SXML_PackageKeyboard_OSKFont, Items[i].OSKFont.RelativeFileName); if Assigned(Items[i].DisplayFont) then - AIni.WriteString(FSectionName, SPackageKeyboard_DisplayFont, ExtractFileName(Items[i].DisplayFont.FileName)); + AIni.WriteString(FSectionName, SXML_PackageKeyboard_DisplayFont, Items[i].DisplayFont.RelativeFileName); for j := 0 to Items[i].Languages.Count-1 do begin - AIni.WriteString(FSectionName, SPackageKeyboard_Language+IntToStr(j), Items[i].Languages[j].ID+','+Items[i].Languages[j].Name); + AIni.WriteString(FSectionName, SXML_PackageKeyboard_Language+IntToStr(j), Items[i].Languages[j].ID+','+Items[i].Languages[j].Name); + end; + end; +end; + +procedure TPackageKeyboardList.SaveJSON(ARoot: TJSONObject); +var + i, j: Integer; + AKeyboard, ALanguage: TJSONObject; + AKeyboards, ALanguages: TJSONArray; +begin + if Count = 0 then + Exit; + + AKeyboards := TJSONArray.Create; + ARoot.AddPair(SJSON_Keyboards, AKeyboards); + + for i := 0 to Count - 1 do + begin + AKeyboard := TJSONObject.Create; + AKeyboards.Add(AKeyboard); + + AKeyboard.AddPair(SJSON_Keyboard_Name, Items[i].Name); + AKeyboard.AddPair(SJSON_Keyboard_ID, Items[i].ID); + AKeyboard.AddPair(SJSON_Keyboard_Version, Items[i].Version); + if Assigned(Items[i].OSKFont) then + AKeyboard.AddPair(SJSON_Keyboard_OSKFont, Items[i].OSKFont.RelativeFileName); + if Assigned(Items[i].DisplayFont) then + AKeyboard.AddPair(SJSON_Keyboard_DisplayFont, Items[i].DisplayFont.RelativeFileName); + + ALanguages := TJSONArray.Create; + AKeyboard.AddPair(SJSON_Keyboard_Languages, ALanguages); + for j := 0 to Items[i].Languages.Count - 1 do + begin + ALanguage := TJSONObject.Create; + ALanguages.Add(ALanguage); + ALanguage.AddPair(SJSON_Keyboard_Language_Name, Items[i].Languages[j].Name); + ALanguage.AddPair(SJSON_Keyboard_Language_ID, Items[i].Languages[j].ID); end; end; end; @@ -1756,22 +2264,32 @@ begin begin AKeyboard := ANode.AddChild('Keyboard'); - AKeyboard.ChildNodes[SPackageKeyboard_Name].NodeValue := Items[i].Name; - AKeyboard.ChildNodes[SPackageKeyboard_ID].NodeValue := Items[i].ID; - AKeyboard.ChildNodes[SPackageKeyboard_Version].NodeValue := Items[i].Version; + AKeyboard.ChildNodes[SXML_PackageKeyboard_Name].NodeValue := Items[i].Name; + AKeyboard.ChildNodes[SXML_PackageKeyboard_ID].NodeValue := Items[i].ID; + AKeyboard.ChildNodes[SXML_PackageKeyboard_Version].NodeValue := Items[i].Version; if Assigned(Items[i].OSKFont) then - AKeyboard.ChildNodes[SPackageKeyboard_OSKFont].NodeValue := ExtractFileName(Items[i].OSKFont.FileName); + AKeyboard.ChildNodes[SXML_PackageKeyboard_OSKFont].NodeValue := Items[i].OSKFont.RelativeFileName; if Assigned(Items[i].DisplayFont) then - AKeyboard.ChildNodes[SPackageKeyboard_DisplayFont].NodeValue := ExtractFileName(Items[i].DisplayFont.FileName); + AKeyboard.ChildNodes[SXML_PackageKeyboard_DisplayFont].NodeValue := Items[i].DisplayFont.RelativeFileName; ALanguages := AKeyboard.AddChild('Languages'); for j := 0 to Items[i].Languages.Count - 1 do begin ALanguage := ALanguages.AddChild('Language'); ALanguage.NodeValue := Items[i].Languages[j].Name; - ALanguage.Attributes[SPackageKeyboard_Language_ID] := Items[i].Languages[j].ID; + ALanguage.Attributes[SXML_PackageKeyboard_Language_ID] := Items[i].Languages[j].ID; end; end; end; +function GetJsonValueString(o: TJSONObject; const n: string): string; +var + v: TJSONValue; +begin + v := o.Values[n]; + if not Assigned(v) + then Result := '' + else Result := v.Value; +end; + end. diff --git a/windows/src/global/delphi/general/kmpinffile.pas b/windows/src/global/delphi/general/kmpinffile.pas index de506a42b4..4159a7afaf 100644 --- a/windows/src/global/delphi/general/kmpinffile.pas +++ b/windows/src/global/delphi/general/kmpinffile.pas @@ -59,7 +59,7 @@ begin end; {------------------------------------------------------------------------------- - kmp.inf file format - + kmp.inf 5.0 file format - =================== - - [Install] - diff --git a/windows/src/global/delphi/general/kpsfile.pas b/windows/src/global/delphi/general/kpsfile.pas index 30d3b49176..f3a798cb39 100644 --- a/windows/src/global/delphi/general/kpsfile.pas +++ b/windows/src/global/delphi/general/kpsfile.pas @@ -29,6 +29,7 @@ interface uses System.Classes, System.IniFiles, + System.JSON, System.SysUtils, Winapi.Windows, Xml.XMLIntf, @@ -47,6 +48,8 @@ type procedure Assign(Source: TPackageOptions); override; procedure LoadXML(ARoot: IXMLNode); override; procedure SaveXML(ARoot: IXMLNode); override; + procedure LoadJSON(ARoot: TJSONObject); override; + procedure SaveJSON(ARoot: TJSONObject); override; property MSIFileName: WideString read FMSIFileName write FMSIFileName; property MSIOptions: WideString read FMSIOptions write FMSIOptions; // I3126 end; @@ -58,6 +61,8 @@ type procedure Import(AIni: TIniFile); override; procedure DoLoadXML(ARoot: IXMLNode); override; procedure DoSaveXML(ARoot: IXMLNode); override; + procedure DoLoadJSON(ARoot: TJSONObject); override; + procedure DoSaveJSON(ARoot: TJSONObject); override; public function KPSOptions: TKPSOptions; constructor Create; @@ -79,6 +84,12 @@ uses utilfiletypes, utilsystem; +const + SJSON_Options = 'options'; + SJSON_Options_MSIFileName = 'msiFilename'; + SJSON_Options_MSIOptions = 'msiOptions'; + SJSON_Strings = 'strings'; + { TKPSFile } constructor TKPSFile.Create; @@ -102,6 +113,24 @@ begin Files[i].FileName := ExpandFileNameEx(FileName, Files[i].FileName); end; +procedure TKPSFile.DoLoadJSON(ARoot: TJSONObject); +var + i: Integer; + ANode: TJSONObject; +begin + inherited; + + if FileName <> '' then + for i := 0 to Files.Count - 1 do + Files[i].FileName := ExpandFileNameEx(FileName, Files[i].FileName); + + FStrings.Clear; + ANode := ARoot.Values[SJSON_Strings] as TJSONObject; + if ANode <> nil then + for i := 0 to ANode.Count - 1 do + FStrings.Add(ANode.Pairs[i].JsonString.Value + '=' + ANode.Pairs[i].Value); +end; + procedure TKPSFile.DoLoadXML(ARoot: IXMLNode); var i: Integer; @@ -131,6 +160,32 @@ begin Files[i].FileName := ExpandFileNameEx(FileName, Files[i].FileName); end; +procedure TKPSFile.DoSaveJSON; +var + i: Integer; + ANode: TJSONObject; +begin + Options.FileVersion := SKeymanVersion70; + + if FileName <> '' then + for i := 0 to Files.Count - 1 do + Files[i].FileName := ExtractRelativePath(FileName, Files[i].FileName); + + inherited; + + if FileName <> '' then + for i := 0 to Files.Count - 1 do + Files[i].FileName := ExpandFileNameEx(FileName, Files[i].FileName); + + if FStrings.Count > 0 then + begin + ANode := TJSONObject.Create; + ARoot.AddPair(SJSON_Strings, ANode); + for i := 0 to FStrings.Count - 1 do + ANode.AddPair(FStrings.Names[i], FStrings.ValueFromIndex[i]); + end; +end; + procedure TKPSFile.DoSaveXML(ARoot: IXMLNode); var i: Integer; @@ -290,6 +345,16 @@ begin FMSIOptions := (Source as TKPSOptions).MSIOptions; // I3126 end; +procedure TKPSOptions.LoadJSON(ARoot: TJSONObject); +var + FOptions: TJSONObject; +begin + inherited; + FOptions := ARoot.Values[SJSON_Options] as TJSONObject; + FMSIFileName := GetJsonValueString(FOptions, SJSON_Options_MSIFileName); + FMSIOptions := GetJsonValueString(FOptions, SJSON_Options_MSIOptions); +end; + procedure TKPSOptions.LoadXML(ARoot: IXMLNode); begin inherited; @@ -297,6 +362,18 @@ begin FMSIOptions := VarToWideStr(ARoot.ChildNodes['Options'].ChildNodes['MSIOptions'].NodeValue); // I3126 end; +procedure TKPSOptions.SaveJSON(ARoot: TJSONObject); +var + FOptions: TJSONObject; +begin + inherited; + FOptions := ARoot.Values[SJSON_Options] as TJSONObject; + if FMSIFileName <> '' then + FOptions.AddPair(SJSON_Options_MSIFileName, FMSIFileName); + if FMSIOptions <> '' then + FOptions.AddPair(SJSON_Options_MSIOptions, FMSIOptions); +end; + procedure TKPSOptions.SaveXML(ARoot: IXMLNode); begin inherited; diff --git a/windows/src/unit-tests/package-info/PackageInfoTest.pas b/windows/src/unit-tests/package-info/PackageInfoTest.pas index caf939eabb..c5798e7fd5 100644 --- a/windows/src/unit-tests/package-info/PackageInfoTest.pas +++ b/windows/src/unit-tests/package-info/PackageInfoTest.pas @@ -26,6 +26,9 @@ type [Test] procedure TestXMLInOut; + [Test] + procedure TestJSONInOut; + [Test] procedure TestRoundTrip; @@ -116,6 +119,16 @@ begin try p1.FileName := DataPath + 'test2.out.inf'; p1.LoadIni; + p1.FileName := DataPath + 'test2.out.json'; + p1.SaveJSON; + finally + p1.Free; + end; + + p1 := TKpsFile.Create; + try + p1.FileName := DataPath + 'test2.out.json'; + p1.LoadJSON; p1.FileName := DataPath + 'test2.out.xml'; p1.SaveXML; finally @@ -143,6 +156,16 @@ begin try p1.FileName := DataPath + 'test3.out.xml'; p1.LoadXML; + p1.FileName := DataPath + 'test3.out.json'; + p1.SaveJSON; + finally + p1.Free; + end; + + p1 := TKpsFile.Create; + try + p1.FileName := DataPath + 'test3.out.json'; + p1.LoadJSON; p1.FileName := DataPath + 'test3.out.inf'; p1.SaveIni; finally @@ -169,6 +192,23 @@ begin DoCompare(DataPath + 'test.xml', DataPath + 'test4.out.xml'); end; +procedure TPackageInfoTest.TestJSONInOut; +var + p1: TPackage; +begin + p1 := TKpsFile.Create; + try + p1.FileName := DataPath + 'test.json'; + p1.LoadJSON; + p1.FileName := DataPath + 'test5.out.json'; + p1.SaveJSON; + finally + p1.Free; + end; + + DoCompare(DataPath + 'test.json', DataPath + 'test5.out.json'); +end; + procedure TPackageInfoTest.DoCompare(f1, f2: string); var k1, k2: TKpsFile; @@ -177,14 +217,14 @@ begin k2 := TKpsFile.Create; try k1.FileName := f1; - if SameText(ExtractFileExt(f1), '.xml') - then k1.LoadXml - else k1.LoadIni; + if SameText(ExtractFileExt(f1), '.xml') then k1.LoadXml + else if SameText(ExtractFileExt(f1), '.json') then k1.LoadJSON + else k1.LoadIni; k2.FileName := f2; - if SameText(ExtractFileExt(f2), '.xml') - then k2.LoadXml - else k2.LoadIni; + if SameText(ExtractFileExt(f2), '.xml') then k2.LoadXml + else if SameText(ExtractFileExt(f2), '.json') then k2.LoadJSON + else k2.LoadIni; DoCompareKpsFiles(f1, f2, k1, k2); finally diff --git a/windows/src/unit-tests/package-info/PackageInfoTestSuite.dpr b/windows/src/unit-tests/package-info/PackageInfoTestSuite.dpr index a02499a8e0..5565088043 100644 --- a/windows/src/unit-tests/package-info/PackageInfoTestSuite.dpr +++ b/windows/src/unit-tests/package-info/PackageInfoTestSuite.dpr @@ -8,7 +8,6 @@ uses {$IFDEF TESTINSIGHT} TestInsight.DUnitX, {$ENDIF } - PackageInfo in '..\..\global\delphi\general\PackageInfo.pas', utilfiletypes in '..\..\global\delphi\general\utilfiletypes.pas', utilstr in '..\..\global\delphi\general\utilstr.pas', @@ -23,11 +22,11 @@ uses GetOsVersion in '..\..\global\delphi\general\GetOsVersion.pas', kpsfile in '..\..\global\delphi\general\kpsfile.pas', PackageFileFormats in '..\..\global\delphi\general\PackageFileFormats.pas', - DUnitX.Loggers.Console, DUnitX.Loggers.Xml.NUnit, DUnitX.TestFramework, - PackageInfoTest in 'PackageInfoTest.pas'; + PackageInfoTest in 'PackageInfoTest.pas', + JsonUtil in '..\..\global\delphi\general\JsonUtil.pas'; var runner : ITestRunner; diff --git a/windows/src/unit-tests/package-info/PackageInfoTestSuite.dproj b/windows/src/unit-tests/package-info/PackageInfoTestSuite.dproj index 8cb444285f..ab6a27c15e 100644 --- a/windows/src/unit-tests/package-info/PackageInfoTestSuite.dproj +++ b/windows/src/unit-tests/package-info/PackageInfoTestSuite.dproj @@ -89,21 +89,22 @@ MainSource - - - - - - - - - - - - - - + + + + + + + + + + + + + + + Cfg_2 Base @@ -515,6 +516,7 @@ + False True False diff --git a/windows/src/unit-tests/package-info/test/test.json b/windows/src/unit-tests/package-info/test/test.json new file mode 100644 index 0000000000..6437c8acd9 --- /dev/null +++ b/windows/src/unit-tests/package-info/test/test.json @@ -0,0 +1,187 @@ +{ + "system" : { + "keymanDeveloperVersion" : "0.0.0.0", + "fileVersion" : "7.0" + }, + "options" : { + "readmeFile" : "readme.htm", + "graphicFile" : "sideimage.bmp" + }, + "startMenu" : { + "folder" : "GFF Amharic Keyboard", + "addUninstallEntry" : true, + "items" : [ + { + "name" : "Welcome", + "filename" : "..\\..\\..\\gff\\gff_amh_7\\source\\welcome.htm" + }, + { + "name" : "Amharic Keyboard Manual (Amharic)", + "filename" : "AmharicTyping-Amharic.pdf" + }, + { + "name" : "Amharic Keyboard Manual (English)", + "filename" : "AmharicTyping-English.pdf" + }, + { + "name" : "OFL (Abyssinica SIL Licence)", + "filename" : "OFL.txt" + }, + { + "name" : "GPL (WashRa + Free Serif Licence)", + "filename" : "GNU-GPLv2.txt" + } + ] + }, + "info" : { + "website" : { + "description" : "http://keyboards.ethiopic.org", + "url" : "http://keyboards.ethiopic.org" + }, + "version" : { + "description" : "8.0.0" + }, + "name" : { + "description" : "GFF Amharic Keyboard" + }, + "copyright" : { + "description" : "Creative Commons Attribution 3.0" + }, + "author" : { + "description" : "The Ge'ez Frontier Foundation", + "url" : "mailto:keyboards@ethiopic.org" + } + }, + "files" : [ + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\readme.htm", + "description" : "File readme.htm" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\sideimage.bmp", + "description" : "File sideimage.bmp" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_amh_7\\source\\AmharicTyping-Amharic.pdf", + "description" : "File AmharicTyping-Amharic.pdf" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_amh_7\\source\\AmharicTyping-English.pdf", + "description" : "File AmharicTyping-English.pdf" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\OFL.txt", + "description" : "File OFL.txt" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\GNU-GPLv2.txt", + "description" : "File GNU-GPLv2.txt" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_amh_7\\source\\welcome.htm", + "description" : "File Welcome.htm" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\AbyssinicaSIL-R.ttf", + "description" : "Font Abyssinica SIL" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\fantuwua.ttf", + "description" : "Font Ethiopic Fantuwua" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\FreeSerif.otf", + "description" : "Font Free Serif" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\goffer.ttf", + "description" : "Font Ethiopic Yigezu Bisrat Goffer" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\hiwua.ttf", + "description" : "Font Ethiopic Hiwua" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\jiret.ttf", + "description" : "Font Ethiopia Jiret" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\jiretsl.ttf", + "description" : "Font Ethiopia Jiret Slant" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\tint.ttf", + "description" : "Font Ethiopic Tint" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\washrab.ttf", + "description" : "Font Ethiopic WashRa Bold" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\washrabsl.ttf", + "description" : "Font Ethiopic WashRa Bold Slant" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\washrasb.ttf", + "description" : "Font Ethiopic WashRa SemiBold" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\washrasbsl.ttf", + "description" : "Font Ethiopic WashRa SemiBold Slant" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\wookianos.ttf", + "description" : "Font Ethiopic Wookianos" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\shared\\fonts\\geez\\yebse.ttf", + "description" : "Font Ethiopic Yebse" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_amh_7\\build\\gff_amh_7.kvk", + "description" : "File gff_amh_7.kvk" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_amh_7\\build\\gff_amh_7.kmx", + "description" : "Keyboard Amharic" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_ethiopic_7\\build\\gff_ethiopic_7.kvk", + "description" : "File gff_ethiopic_7.kvk" + }, + { + "name" : "C:\\Projects\\keyman\\open\\windows\\src\\unit-tests\\package-info\\Win32\\Debug\\..\\..\\test\\..\\..\\..\\gff\\gff_ethiopic_7\\build\\gff_ethiopic_7.kmx", + "description" : "Keyboard Ethiopic - (Language Neutral)" + } + ], + "keyboards" : [ + { + "name" : "Amharic", + "id" : "gff_amh_7", + "version" : "8.0", + "oskFont" : "..\\..\\..\\shared\\fonts\\geez\\wookianos.ttf", + "displayFont" : "..\\..\\..\\shared\\fonts\\geez\\fantuwua.ttf", + "languages" : [ + { + "name" : "Amharic", + "id" : "amh" + }, + { + "name" : "Ge'ez", + "id" : "gez" + } + ] + }, + { + "name" : "Ethiopic Language Neutral", + "id" : "gff_ethiopic_7", + "version" : "8.0", + "languages" : [ + { + "name" : "Ge'ez", + "id" : "gez" + } + ] + } + ] +}