diff --git a/common/windows/delphi/packages/PackageInfo.pas b/common/windows/delphi/packages/PackageInfo.pas index 81e633fe53..dd4dbcd110 100644 --- a/common/windows/delphi/packages/PackageInfo.pas +++ b/common/windows/delphi/packages/PackageInfo.pas @@ -337,6 +337,8 @@ type procedure SaveJSON(ARoot: TJSONObject); virtual; procedure LoadXML(ARoot: IXMLNode); virtual; procedure SaveXML(ARoot: IXMLNode); virtual; + function ContainsID(const id: string): Boolean; + function IndexOfID(const id: string): Integer; end; TPackageLexicalModel = class(TPackageBaseObject) @@ -2262,6 +2264,21 @@ end; { TPackageKeyboardLanguageList } +function TPackageKeyboardLanguageList.ContainsID(const id: string): Boolean; +begin + Result := IndexOfID(id) >= 0; +end; + +function TPackageKeyboardLanguageList.IndexOfID(const id: string): Integer; +var + i: Integer; +begin + for i := 0 to Count - 1 do + if SameText(Items[i].ID, id) then + Exit(i); + Result := -1; +end; + procedure TPackageKeyboardLanguageList.LoadJSON(ARoot: TJSONObject); var j: Integer; diff --git a/developer/src/common/delphi/compiler/CompilePackage.pas b/developer/src/common/delphi/compiler/CompilePackage.pas index 1e711169dd..c7c5e07123 100644 --- a/developer/src/common/delphi/compiler/CompilePackage.pas +++ b/developer/src/common/delphi/compiler/CompilePackage.pas @@ -27,7 +27,9 @@ unit CompilePackage; interface uses - kpsfile, kmpinffile, PackageInfo, + kpsfile, + kmpinffile, + PackageInfo, Keyman.Developer.System.Project.ProjectLog; function DoCompilePackage(pack: TKPSFile; AMessageEvent: TCompilePackageMessageEvent; ASilent, ACheckFilenameConventions: Boolean; const AOutputFileName: string): Boolean; // I4688 @@ -37,6 +39,7 @@ implementation uses Winapi.Windows, System.Classes, + System.Generics.Collections, System.SysUtils, System.IniFiles, System.Zip, @@ -85,6 +88,7 @@ type procedure CheckKeyboardLanguages; procedure CheckFilenameConventions; function CheckLexicalModels: Boolean; + procedure CheckForDuplicatedLanguages(const resourceType, id: string; languages: TPackageKeyboardLanguageList); end; function DoCompilePackage(pack: TKPSFile; AMessageEvent: TCompilePackageMessageEvent; ASilent, ACheckFilenameConventions: Boolean; const AOutputFileName: string): Boolean; // I4688 @@ -141,6 +145,8 @@ begin end; function TCompilePackage.CheckLexicalModels: Boolean; +var + model: TPackageLexicalModel; begin if pack.LexicalModels.Count > 0 then begin @@ -151,6 +157,12 @@ begin end; end; + for model in pack.LexicalModels do + begin + CheckForDuplicatedLanguages('model', model.id, model.Languages); + end; + + Exit(True); end; @@ -487,6 +499,7 @@ end; const SKKeyboardPackageLanguageNonCanonical = 'The keyboard %0:s has a non-canonical language tag "%1:s" (%2:s), should be "%3:s".'; SKKeyboardShouldHaveAtLeastOneLanguage = 'The keyboard %0:s has no language tags. It should have at least one language tag.'; + SKPackageShouldNotRepeatLanguages = 'The %0:s %1:s has a repeated language "%2:s".'; procedure TCompilePackage.CheckKeyboardLanguages; var @@ -496,6 +509,30 @@ begin begin if k.Languages.Count = 0 then WriteMessage(plsWarning, Format(SKKeyboardShouldHaveAtLeastOneLanguage, [k.ID])); + CheckForDuplicatedLanguages('keyboard', k.ID, k.Languages); + end; +end; + +procedure TCompilePackage.CheckForDuplicatedLanguages(const resourceType, id: string; languages: TPackageKeyboardLanguageList); +var + tags: TDictionary; + lang: TPackageKeyboardLanguage; +begin + tags := TDictionary.Create; + try + for lang in languages do + begin + if tags.ContainsKey(lang.ID.ToLower) then + begin + WriteMessage(plsWarning, Format(SKPackageShouldNotRepeatLanguages, [resourceType, id, lang.ID])); + end + else + begin + tags.Add(lang.ID.ToLower, 0); + end; + end; + finally + tags.Free; end; end; diff --git a/developer/src/tike/child/UfrmPackageEditor.pas b/developer/src/tike/child/UfrmPackageEditor.pas index 22b94a4835..4b36d67170 100644 --- a/developer/src/tike/child/UfrmPackageEditor.pas +++ b/developer/src/tike/child/UfrmPackageEditor.pas @@ -314,8 +314,7 @@ type procedure EnableLexicalModelTabControls; procedure ShowEditLanguageForm(grid: TStringGrid; langs: TPackageKeyboardLanguageList; lang: TPackageKeyboardLanguage); - function ShowAddLanguageForm(grid: TStringGrid; - langs: TPackageKeyboardLanguageList): Boolean; + procedure ShowAddLanguageForm(grid: TStringGrid; langs: TPackageKeyboardLanguageList); procedure RefreshLexicalModelList; procedure UpdateQRCode; @@ -1624,6 +1623,8 @@ procedure TfrmPackageEditor.EnableControls; begin EnableStartMenuControls; EnableDetailsTabControls; + EnableKeyboardTabControls; + EnableLexicalModelTabControls; EnableCompileTabControls; end; @@ -1686,35 +1687,16 @@ end; procedure TfrmPackageEditor.cmdKeyboardAddLanguageClick(Sender: TObject); var k: TPackageKeyboard; - lang: TPackageKeyboardLanguage; - frm: TfrmSelectBCP47Language; begin k := SelectedKeyboard; Assert(Assigned(k)); - - frm := TfrmSelectBCP47Language.Create(Application.MainForm); - try - if frm.ShowModal = mrOk then - begin - lang := TPackageKeyboardLanguage.Create(pack); - lang.ID := frm.LanguageID; - lang.Name := frm.LanguageName; - k.Languages.Add(lang); - RefreshKeyboardLanguageList(k); - gridKeyboardLanguages.Row := gridKeyboardLanguages.RowCount - 1; - gridKeyboardLanguagesClick(gridKeyboardLanguages); - Modified := True; - end; - finally - frm.Free; - end; + ShowAddLanguageForm(gridKeyboardLanguages, k.Languages); end; procedure TfrmPackageEditor.cmdKeyboardEditLanguageClick(Sender: TObject); var k: TPackageKeyboard; lang: TPackageKeyboardLanguage; - frm: TfrmSelectBCP47Language; begin k := SelectedKeyboard; Assert(Assigned(k)); @@ -1722,20 +1704,7 @@ begin lang := SelectedKeyboardLanguage; Assert(Assigned(lang)); - frm := TfrmSelectBCP47Language.Create(Application.MainForm); - try - frm.LanguageID := lang.ID; - frm.LanguageName := lang.Name; - if frm.ShowModal = mrOk then - begin - lang.ID := frm.LanguageID; - lang.Name := frm.LanguageName; - RefreshKeyboardLanguageList(k); - Modified := True; - end; - finally - frm.Free; - end; + ShowEditLanguageForm(gridKeyboardLanguages, k.Languages, lang); end; procedure TfrmPackageEditor.cmdKeyboardRemoveLanguageClick(Sender: TObject); @@ -1819,16 +1788,25 @@ begin end; end; -function TfrmPackageEditor.ShowAddLanguageForm(grid: TStringGrid; langs: TPackageKeyboardLanguageList): Boolean; +procedure TfrmPackageEditor.ShowAddLanguageForm(grid: TStringGrid; langs: TPackageKeyboardLanguageList); var lang: TPackageKeyboardLanguage; frm: TfrmSelectBCP47Language; + n: Integer; begin - Result := False; frm := TfrmSelectBCP47Language.Create(Application.MainForm); try if frm.ShowModal = mrOk then begin + n := langs.IndexOfID(frm.LanguageID); + if n >= 0 then + begin + // Duplicate - we won't re-add the item, just select the existing item + grid.Row := n + 1; + EnableControls; + Exit; + end; + lang := TPackageKeyboardLanguage.Create(pack); lang.ID := frm.LanguageID; lang.Name := frm.LanguageName; @@ -1836,7 +1814,7 @@ begin RefreshLanguageList(grid, langs); grid.Row := grid.RowCount - 1; Modified := True; - Result := True; + EnableControls; end; finally frm.Free; @@ -1846,6 +1824,7 @@ end; procedure TfrmPackageEditor.ShowEditLanguageForm(grid: TStringGrid; langs: TPackageKeyboardLanguageList; lang: TPackageKeyboardLanguage); var frm: TfrmSelectBCP47Language; + n: Integer; begin frm := TfrmSelectBCP47Language.Create(Application.MainForm); try @@ -1853,9 +1832,30 @@ begin frm.LanguageName := lang.Name; if frm.ShowModal = mrOk then begin + if not SameText(frm.LanguageID, lang.ID) then + begin + // If the id has changed, check for duplicates + n := langs.IndexOfID(frm.LanguageID); + if n >= 0 then + begin + // Duplicate - we will delete the edited one and select the existing + // one + langs.Remove(lang); + RefreshLanguageList(grid, langs); + + // The index may have changed, search again + n := langs.IndexOfID(frm.LanguageID); + grid.Row := n + 1; + EnableControls; + Modified := True; + Exit; + end; + end; + lang.ID := frm.LanguageID; lang.Name := frm.LanguageName; RefreshLanguageList(grid, langs); + EnableControls; Modified := True; end; finally @@ -2005,10 +2005,7 @@ var begin lm := SelectedLexicalModel; Assert(Assigned(lm)); - - if ShowAddLanguageForm(gridLexicalModelLanguages, lm.Languages) then - gridLexicalModelLanguagesClick(gridLexicalModelLanguages); - EnableLexicalModelTabControls; + ShowAddLanguageForm(gridLexicalModelLanguages, lm.Languages); end; procedure TfrmPackageEditor.cmdLexicalModelLanguageEditClick(Sender: TObject);