From f8ce5734231f12b1d2ead3cae2fca056e255505a Mon Sep 17 00:00:00 2001 From: mark-sil <83427558+mark-sil@users.noreply.github.com> Date: Wed, 21 Oct 2026 11:28:33 -0400 Subject: [PATCH 1/4] Delete the context menus and commands that nothing uses FieldWorks no longer defines the 23 context menus that no layout part or code names, or the 20 commands that no remaining menu, toolbar, shortcut or code names. Until now they sat in the area configuration files. The Avalonia menu authority counted 11 of those menus as owned, though no row could show them. The code that only the deleted commands reached is gone too: the rule that offered Insert Stem or Insert Affix Allomorph by the entry's first allomorph. Deleted menus and commands: * mnuDataTree-Allomorphs, with CmdDataTree-Insert-StemAllomorph and CmdDataTree-Insert-AffixAllomorph * mnuDataTree-Allomorphs-Hotlinks * mnuDataTree-DeleteEtymology * mnuDataTree-DeletePronunciation * mnuDataTree-Example-ForNotes * mnuDataTree-MSA, with CmdDataTree-Merge-MSA and CmdDataTree-Delete-MSA * mnuDataTree-MSAs, with CmdDataTree-Insert-StemMsa, CmdDataTree-Insert-InflectionAffixMsa, CmdDataTree-Insert-DerivationAffixMsa and CmdDataTree-Insert-UnclassifiedAffixMsa * mnuDataTree-Senses * mnuDataTree-SubEntryLink, with CmdDataTree-Insert-SubEntryLink * mnuDataTree-Translations * mnuDataTree-Variant * mnuDataTree-Variants, with CmdDataTree-Insert-Variant * mnuDataTree-DeleteCustomItem, with CmdDataTree-Delete-CustomItem * mnuDataTree-SubAnnotationDefn, with CmdDataTree-Insert-AnnotationDefn * mnuDataTree-SubCustomItem * mnuDataTree-SubMorphType, with CmdDataTree-Insert-MorphType * mnuDataTree-CompoundRule-LinkerAffixAllomorph, with CmdDataTree-Create-CompoundRule-LinkerAffixAllomorph and CmdDataTree-Delete-CompoundRule-LinkerAffixAllomorph * mnuInflAffixTemplate-Help * mnuIText-FreeTrans, with CmdDeleteFreeTrans * mnuIText-LitTrans, with CmdDeleteLitTrans * mnuIText-Note, with CmdDeleteNote * PaneBar-ShowHiddenFields-scrNoteTypesEdit (no such tool exists) * PaneBar-ShowHiddenFields-weatherConditionEdit (no such tool exists) * CmdDataTree-Create-LexemeForm-StemAllomorph and CmdDataTree-Create-LexemeForm-AffixAllomorph (in no menu; the allomorph rule was their only gate) Co-Authored-By: Claude Opus 5.5 --- .../Configuration/CommonDataTreeInclude.xml | 6 - .../Configuration/Grammar/DataTreeInclude.xml | 17 --- .../Grammar/InflAffixTemplateInclude.xml | 3 - .../Configuration/Lexicon/DataTreeInclude.xml | 108 ------------------ .../Configuration/Lists/DataTreeInclude.xml | 21 ---- .../Configuration/Words/areaConfiguration.xml | 13 --- .../DetailRules/DetailObjectInsertion.cs | 13 +-- .../DetailRules/DetailObjectInsertionTests.cs | 15 +-- Src/LexText/Lexicon/LexEntryMenuHandler.cs | 6 +- .../Hosting/RecordEditView.LexiconMenus.cs | 2 +- .../Hosting/LexiconMenuAuthorityTests.cs | 25 ++-- 11 files changed, 18 insertions(+), 211 deletions(-) diff --git a/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml b/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml index 73d26ec5a5..354df33fb3 100644 --- a/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml +++ b/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml @@ -163,15 +163,9 @@ - - - - - - diff --git a/DistFiles/Language Explorer/Configuration/Grammar/DataTreeInclude.xml b/DistFiles/Language Explorer/Configuration/Grammar/DataTreeInclude.xml index 9a1fbf89bc..2eae546826 100644 --- a/DistFiles/Language Explorer/Configuration/Grammar/DataTreeInclude.xml +++ b/DistFiles/Language Explorer/Configuration/Grammar/DataTreeInclude.xml @@ -39,15 +39,6 @@ - - - - - - - @@ -184,14 +175,6 @@ - - - - - - diff --git a/DistFiles/Language Explorer/Configuration/Grammar/InflAffixTemplateInclude.xml b/DistFiles/Language Explorer/Configuration/Grammar/InflAffixTemplateInclude.xml index 98b7e25cff..8a2ba6f86f 100644 --- a/DistFiles/Language Explorer/Configuration/Grammar/InflAffixTemplateInclude.xml +++ b/DistFiles/Language Explorer/Configuration/Grammar/InflAffixTemplateInclude.xml @@ -54,9 +54,6 @@ --> - - - diff --git a/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml b/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml index a48e47e20c..65e8b4c453 100644 --- a/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml +++ b/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml @@ -15,12 +15,6 @@ Lexeme form --> - - - - - - @@ -42,12 +36,6 @@ - - - - - - @@ -98,35 +86,6 @@ - - - - - - - - - - - - - - - - - - - - @@ -222,9 +181,6 @@ - - - @@ -265,12 +221,6 @@ - - - - @@ -347,18 +297,10 @@ - - - - - - - - @@ -415,30 +357,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -479,20 +397,6 @@ - - - - - - - - - - - - - - @@ -532,14 +436,6 @@ - - - - - - - - @@ -552,10 +448,6 @@ - - - - diff --git a/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml b/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml index 4b83f387ed..f2a253d930 100644 --- a/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml +++ b/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml @@ -28,18 +28,9 @@ - - - - - - - - - @@ -87,15 +78,6 @@ - - - - - - - - - @@ -108,9 +90,6 @@ - - - diff --git a/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml b/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml index 2e74bf6e61..4bc7d6a5ab 100644 --- a/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml +++ b/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml @@ -79,10 +79,6 @@ - - - - @@ -384,15 +380,6 @@ - - - - - - - - - diff --git a/Src/FdoUi/DetailRules/DetailObjectInsertion.cs b/Src/FdoUi/DetailRules/DetailObjectInsertion.cs index 15e111e134..0109d8eeb7 100644 --- a/Src/FdoUi/DetailRules/DetailObjectInsertion.cs +++ b/Src/FdoUi/DetailRules/DetailObjectInsertion.cs @@ -15,7 +15,7 @@ namespace SIL.FieldWorks.Common.DetailRules /// /// The Insert command of a detail row (message DataTreeInsert): whether a field can take /// a new object now, which object and field the row's command inserts into, and where; - /// which allomorph inserts an entry offers, and which entry a variant insert extends. + /// whether an entry offers Insert Affix Process, and which entry a variant insert extends. /// Creation stays with CmObjectUi.CreateNewUiObject and the variant dialog. /// public static class DetailObjectInsertion @@ -156,9 +156,8 @@ public static int ConcreteClass(ICmObject owner, int classId) } /// - /// Whether an entry offers an allomorph Insert command, chosen by the end of its id: - /// Insert Affix Process only on an affix entry; Insert Stem or Affix Allomorph while the - /// entry has no allomorph or its first is of that kind. It offers any other command. + /// Whether an entry offers an Insert command: Insert Affix Process, chosen by the end + /// of its id, only on an affix entry; any other command always. /// public static bool IsOfferedOnEntry(ILexEntry entry, string commandId) { @@ -166,12 +165,6 @@ public static bool IsOfferedOnEntry(ILexEntry entry, string commandId) return false; if (commandId.EndsWith("AffixProcess", StringComparison.Ordinal)) return entry.PrimaryMorphType != null && entry.PrimaryMorphType.IsAffixType; - if (entry.AlternateFormsOS.Count == 0) - return true; - if (commandId.EndsWith("AffixAllomorph", StringComparison.Ordinal)) - return entry.AlternateFormsOS[0] is IMoAffixAllomorph; - if (commandId.EndsWith("StemAllomorph", StringComparison.Ordinal)) - return entry.AlternateFormsOS[0] is IMoStemAllomorph; return true; } diff --git a/Src/FdoUi/FdoUiTests/DetailRules/DetailObjectInsertionTests.cs b/Src/FdoUi/FdoUiTests/DetailRules/DetailObjectInsertionTests.cs index 13dd9fb03b..d56a1f7f66 100644 --- a/Src/FdoUi/FdoUiTests/DetailRules/DetailObjectInsertionTests.cs +++ b/Src/FdoUi/FdoUiTests/DetailRules/DetailObjectInsertionTests.cs @@ -207,21 +207,16 @@ public void IsOfferedOnEntry_AnAffixProcess_OnlyOnAnAffixEntry() } [Test] - public void IsOfferedOnEntry_AStemOrAffixAllomorph_FollowsTheFirstAllomorph() + public void IsOfferedOnEntry_AnyOtherInsert_OnAnyEntry() { - const string stem = "CmdDataTree-Insert-StemAllomorph"; - const string affix = "CmdDataTree-Insert-AffixAllomorph"; - Assert.That(DetailObjectInsertion.IsOfferedOnEntry(_entry, stem), Is.True, "no allomorph yet: either kind"); - Assert.That(DetailObjectInsertion.IsOfferedOnEntry(_entry, affix), Is.True); + const string allomorph = "CmdDataTree-Insert-AlternateForm"; + Assert.That(DetailObjectInsertion.IsOfferedOnEntry(_entry, allomorph), Is.True, "no allomorph yet"); NonUndoableUnitOfWorkHelper.Do(Cache.ActionHandlerAccessor, () => _entry.AlternateFormsOS.Add(Cache.ServiceLocator.GetInstance().Create())); - Assert.That(DetailObjectInsertion.IsOfferedOnEntry(_entry, stem), Is.False, "the first allomorph is an affix"); - Assert.That(DetailObjectInsertion.IsOfferedOnEntry(_entry, affix), Is.True); - Assert.That(DetailObjectInsertion.IsOfferedOnEntry(_entry, "CmdDataTree-Insert-AlternateForm"), Is.True, - "any other insert"); - Assert.That(DetailObjectInsertion.IsOfferedOnEntry(null, "CmdDataTree-Insert-AlternateForm"), Is.False); + Assert.That(DetailObjectInsertion.IsOfferedOnEntry(_entry, allomorph), Is.True, "the first allomorph is an affix"); + Assert.That(DetailObjectInsertion.IsOfferedOnEntry(null, allomorph), Is.False); } [Test] diff --git a/Src/LexText/Lexicon/LexEntryMenuHandler.cs b/Src/LexText/Lexicon/LexEntryMenuHandler.cs index 4742c6efe6..38533ca4d4 100644 --- a/Src/LexText/Lexicon/LexEntryMenuHandler.cs +++ b/Src/LexText/Lexicon/LexEntryMenuHandler.cs @@ -64,16 +64,12 @@ public override bool OnDisplayDataTreeInsert(object commandObject, ref UIItemDis return true; } XCore.Command command = (XCore.Command)commandObject; - var offered = DetailObjectInsertion.IsOfferedOnEntry(entry, command.Id); if (command.Id.EndsWith("AffixProcess")) { + var offered = DetailObjectInsertion.IsOfferedOnEntry(entry, command.Id); display.Enabled = offered; display.Visible = offered; } - else if (!offered) - { - display.Visible = false; - } return true;//we handled this, no need to ask anyone else. } diff --git a/Src/xWorks/Avalonia/Hosting/RecordEditView.LexiconMenus.cs b/Src/xWorks/Avalonia/Hosting/RecordEditView.LexiconMenus.cs index 620241867e..74d8561b40 100644 --- a/Src/xWorks/Avalonia/Hosting/RecordEditView.LexiconMenus.cs +++ b/Src/xWorks/Avalonia/Hosting/RecordEditView.LexiconMenus.cs @@ -116,7 +116,7 @@ internal bool InsertLeafInScope(DetailMenuLeaf leaf) /// /// The Insert item for the row: absent with no row or no object, and absent when the - /// entry of the row or the shown entry does not offer an allomorph insert; enabled + /// entry of the row or the shown entry does not offer the insert; enabled /// when the command resolves a target from the row's object (a ghost row's included), /// disabled otherwise. With displayNumber the label carries the position the /// shown record's or the row's object's field would give. diff --git a/Src/xWorks/xWorksTests/Avalonia/Hosting/LexiconMenuAuthorityTests.cs b/Src/xWorks/xWorksTests/Avalonia/Hosting/LexiconMenuAuthorityTests.cs index 28278681c0..430d64dee9 100644 --- a/Src/xWorks/xWorksTests/Avalonia/Hosting/LexiconMenuAuthorityTests.cs +++ b/Src/xWorks/xWorksTests/Avalonia/Hosting/LexiconMenuAuthorityTests.cs @@ -50,10 +50,8 @@ public class LexiconMenuAuthorityTests : XWorksAppTestBase // Allomorphs and Variants hotlinks. "mnuDataTree-AlternateForms", "mnuDataTree-VariantForms", "mnuDataTree-Examples", "mnuDataTree-AlternateForms-Hotlinks", "mnuDataTree-VariantForms-Hotlinks", - // Lexicon ids no part reaches (-CmMedia's pronunciation part does not exist). - "mnuDataTree-CmMedia", "mnuDataTree-Variant", "mnuDataTree-DeleteEtymology", - "mnuDataTree-DeletePronunciation", "mnuDataTree-Translations", "mnuDataTree-Senses", "mnuDataTree-MSAs", - "mnuDataTree-Allomorphs", "mnuDataTree-Allomorphs-Hotlinks", "mnuDataTree-Variants", + // Lexicon: an id no part reaches (its pronunciation part does not exist). + "mnuDataTree-CmMedia", // Grammar: posEdit rows (two item rows, five section rows: Affix Templates, Affix // Slots, Inflection Classes, Subclasses, Stem Allomorph Labels), then other tools. "mnuDataTree-POS-InflectionClass", "mnuDataTree-POS-StemName", @@ -63,12 +61,11 @@ public class LexiconMenuAuthorityTests : XWorksAppTestBase "mnuDataTree-MoStemName-Regions", "mnuDataTree-Phoneme-Code", "mnuDataTree-Phoneme-Codes", "mnuDataTree-Delete-Adhoc-Allomorph", "mnuDataTree-Delete-Adhoc-Group", "mnuDataTree-Delete-Adhoc-Morpheme", "mnuDataTree-Adhoc-Group-Members", - "mnuDataTree-CompoundRule-LinkerAffixAllomorph", // Texts: the Info tab's jumps to the interlinear text and its notebook record; no // Avalonia detail view. "mnuDataTree-Text", "mnuTextInfo-Notebook", // Lists and Reversal: no Avalonia detail view. - "mnuDataTree-DeleteCustomItem", "mnuDataTree-DeletePossibility", "mnuDataTree-DeleteQuestion", + "mnuDataTree-DeletePossibility", "mnuDataTree-DeleteQuestion", "mnuDataTree-InsertQuestion", "mnuDataTree-InsertReversalSubentry", "mnuDataTree-InsertReversalSubentry-Hotlinks" }; @@ -438,7 +435,7 @@ public void InsertLeaf_IsOfferedOnTheGhostRow_DisabledWithoutATarget_AndAbsentWi } // Only the inserts that need the whole tree stay with the adapter, so their ids do - // too: SubPossibilities, and an insert into a plain reference field. + // too: SubPossibilities. [Test] public void InsertLeaves_OutsideTheScope_LeaveTheirIdsWithTheAdapter() { @@ -452,31 +449,25 @@ public void InsertLeaves_OutsideTheScope_LeaveTheirIdsWithTheAdapter() Is.True, "a virtual back reference: the dialog adds the variant"); Assert.That(m_view.InsertLeafInScope(Leaf("mnuDataTree-POS-SubPossibilities", "CmdDataTree-Insert-POS-SubPossibilities")), Is.False, "SubPossibilities needs the whole tree"); - Assert.That(m_view.InsertLeafInScope(Leaf("mnuDataTree-SubEntryLink", "CmdDataTree-Insert-SubEntryLink")), - Is.False, "a reference field with no class to create"); Assert.That(authority.Owns("mnuDataTree-POS-SubPossibilities"), Is.False); Assert.That(() => authority.Build("mnuDataTree-POS-SubPossibilities", Leaf("mnuDataTree-POS-SubPossibilities", "CmdDataTree-Insert-POS-SubPossibilities")), Throws.InvalidOperationException, "a leaf outside the scope is not answered"); } - // The allomorph inserts follow the entry: an affix process only on an affix entry, a - // stem or affix allomorph only while the first allomorph is of that kind. + // The allomorph inserts follow the entry: an affix process only on an affix entry, an + // allomorph on any entry. [Test] - public void AllomorphInserts_AreOfferedByTheEntrysMorphTypeAndFirstAllomorph() + public void AllomorphInserts_AreOfferedByTheEntrysMorphType() { var section = Rows().Single(f => f.MenuId == "mnuDataTree-AlternateForms"); var affixProcess = Leaf("mnuDataTree-AlternateForms", "CmdDataTree-Insert-AffixProcess"); var allomorph = Leaf("mnuDataTree-AlternateForms", "CmdDataTree-Insert-AlternateForm"); - var stem = Leaf("mnuDataTree-Allomorphs", "CmdDataTree-Insert-StemAllomorph"); - var affix = Leaf("mnuDataTree-Allomorphs", "CmdDataTree-Insert-AffixAllomorph"); Assert.That(m_view.InsertItem(affixProcess, section), Is.Null, "a stem entry has no affix process"); + Assert.That(m_view.InsertItem(affixProcess, LexemeFormRow()), Is.Null, "a row inside the entry asks the entry"); Assert.That(m_view.InsertItem(allomorph, section)?.IsEnabled, Is.True); Assert.That(m_view.InsertItem(allomorph, section).Label, Is.EqualTo("Insert Allomorph")); - Assert.That(m_view.InsertItem(stem, section)?.IsEnabled, Is.True, "the first allomorph is a stem"); - Assert.That(m_view.InsertItem(affix, section), Is.Null); - Assert.That(m_view.InsertItem(affix, LexemeFormRow()), Is.Null, "a row inside the entry asks the entry"); NonUndoableUnitOfWorkHelper.Do(Cache.ActionHandlerAccessor, () => m_entry.LexemeFormOA.MorphTypeRA = GetMorphTypeOrCreateOne("suffix")); From 2941881589bd04d665d87ffd11f49ccfb6ab765e Mon Sep 17 00:00:00 2001 From: mark-sil <83427558+mark-sil@users.noreply.github.com> Date: Wed, 21 Oct 2026 12:46:06 -0400 Subject: [PATCH 2/4] Delete commented-out commands and layouts that nothing displays FieldWorks no longer keeps command definitions that exist only inside XML comments, or layouts, parts and menus that no screen displays. Until now one live menu item named a commented-out command, which would throw ConfigurationException if its menu were ever built; no shipped layout shows the rows that carry that menu. The commented-out include directives from 2012 and 2013 are gone too. With no such item left, the test that walks every context menu now fails on any item naming a missing command. Deleted: * CmdHelpCheckForUpdates, CmdInsertBoundaryMarker and CmdToggleRawAndInterlinear, with their commented-out menu and toolbar items * CmdWordformSpellingStatusUnknown, CmdWordformSpellingStatusGood and CmdWordformSpellingStatusDisapprove, with the commented-out mnuDataTree-WordformSpellingStatus submenu * CmdDataTree-Insert-FeatureStructure-ClosedFeature, CmdDataTree-Insert-FeatureStructure-ComplexFeature, CmdDataTree-Delete-FeatureStructure-Feature and CmdDataTree-Delete-FeatureStructure-ComplexFeature * mnuDataTree-FeatureStructure-Feature (its one item named the commented-out CmdDataTree-Delete-FeatureStructure-Feature) * mnuDataTree-FeatureStructure-Features (every item commented out) * FsFeatStruc detail layouts FeatureSpecs, MsFeatures, MsEnvFeatures, FromMsFeatures, ToMsFeatures, InflFeats, NestedInflFeats and EditFeatureSpecs * EditFeatureSpec layouts for FsFeatureSpecification, FsClosedValue and FsComplexValue * FsFeatStruc-Detail-FeatureSpecs * FsFeatureSpecification-Detail-Feature, FsFeatureSpecification-Detail-FeatureSpecs and FsFeatureSpecification-Jt-FeatureSummary * FsClosedValue-Detail-Feature, FsClosedValue-Detail-Value, FsClosedValue-Detail-FeatureSpecs and FsClosedValue-Jt-FeatureSummary * FsComplexValue-Detail-Feature, FsComplexValue-Detail-Value, FsComplexValue-Detail-FeatureSpecs and FsComplexValue-Jt-FeatureSummary * LexReference layout ShowSingleReference (its part never existed) * MoInflClass layout SubClasses (nothing used it) * The Role part ref in CmPerson's default layout (CmPerson has no Role field) * The ContextHelp.xml entry for CmdToggleRawAndInterlinear Co-Authored-By: Claude Opus 5.5 --- .../Configuration/ContextHelp.xml | 1 - .../Configuration/Grammar/DataTreeInclude.xml | 27 -------- .../Grammar/Edit/toolConfiguration.xml | 5 -- .../Grammar/InflAffixTemplateInclude.xml | 2 - .../Grammar/areaConfiguration.xml | 13 ---- .../Configuration/Lexicon/DataTreeInclude.xml | 3 - .../Lexicon/areaConfiguration.xml | 1 - .../Configuration/Lists/DataTreeInclude.xml | 1 - .../Language Explorer/Configuration/Main.xml | 3 - .../Parts/CmPossibility.fwlayout | 2 - .../Configuration/Parts/LexSense.fwlayout | 4 -- .../Configuration/Parts/Morphology.fwlayout | 63 ------------------- .../Configuration/Parts/MorphologyParts.xml | 56 ----------------- .../Configuration/Words/areaConfiguration.xml | 11 ---- .../LayoutImportCoverageTests.cs | 29 +++------ .../DetailObjectCommandExecutionTests.cs | 5 +- 16 files changed, 11 insertions(+), 215 deletions(-) diff --git a/DistFiles/Language Explorer/Configuration/ContextHelp.xml b/DistFiles/Language Explorer/Configuration/ContextHelp.xml index aa45f1fdcd..0428ae3585 100644 --- a/DistFiles/Language Explorer/Configuration/ContextHelp.xml +++ b/DistFiles/Language Explorer/Configuration/ContextHelp.xml @@ -91,7 +91,6 @@ Display the dictionary entry for this lexeme above the Lexicon Edit pane. Display all possible fields. - Toggle between the baseline and interlinear text view. Turn off all filters. View the invisible, zero-width spaces in this text. - @@ -48,19 +46,6 @@ - @@ -142,7 +127,6 @@ =========================================================== --> - @@ -175,10 +159,6 @@ - - - - @@ -190,13 +170,6 @@ - - - - diff --git a/DistFiles/Language Explorer/Configuration/Grammar/Edit/toolConfiguration.xml b/DistFiles/Language Explorer/Configuration/Grammar/Edit/toolConfiguration.xml index 3e80dac5a3..a5280dcdbd 100644 --- a/DistFiles/Language Explorer/Configuration/Grammar/Edit/toolConfiguration.xml +++ b/DistFiles/Language Explorer/Configuration/Grammar/Edit/toolConfiguration.xml @@ -152,9 +152,6 @@ - - - @@ -181,8 +178,6 @@ - - diff --git a/DistFiles/Language Explorer/Configuration/Grammar/InflAffixTemplateInclude.xml b/DistFiles/Language Explorer/Configuration/Grammar/InflAffixTemplateInclude.xml index 8a2ba6f86f..7af371d22a 100644 --- a/DistFiles/Language Explorer/Configuration/Grammar/InflAffixTemplateInclude.xml +++ b/DistFiles/Language Explorer/Configuration/Grammar/InflAffixTemplateInclude.xml @@ -1,6 +1,5 @@ - - diff --git a/DistFiles/Language Explorer/Configuration/Grammar/areaConfiguration.xml b/DistFiles/Language Explorer/Configuration/Grammar/areaConfiguration.xml index 36a9dc33aa..219ac94e8c 100644 --- a/DistFiles/Language Explorer/Configuration/Grammar/areaConfiguration.xml +++ b/DistFiles/Language Explorer/Configuration/Grammar/areaConfiguration.xml @@ -31,11 +31,6 @@ - @@ -280,10 +275,6 @@ - @@ -365,10 +356,6 @@ - diff --git a/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml b/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml index 65e8b4c453..8486553a52 100644 --- a/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml +++ b/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml @@ -1,13 +1,11 @@ - - @@ -279,7 +277,6 @@ =========================================================== --> - diff --git a/DistFiles/Language Explorer/Configuration/Lexicon/areaConfiguration.xml b/DistFiles/Language Explorer/Configuration/Lexicon/areaConfiguration.xml index cc0dd533b2..af7ba7173a 100644 --- a/DistFiles/Language Explorer/Configuration/Lexicon/areaConfiguration.xml +++ b/DistFiles/Language Explorer/Configuration/Lexicon/areaConfiguration.xml @@ -272,7 +272,6 @@ - diff --git a/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml b/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml index f2a253d930..7be732ccb8 100644 --- a/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml +++ b/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml @@ -1,6 +1,5 @@ - @@ -616,8 +615,6 @@ - diff --git a/DistFiles/Language Explorer/Configuration/Parts/CmPossibility.fwlayout b/DistFiles/Language Explorer/Configuration/Parts/CmPossibility.fwlayout index f197991118..8b0555c3ce 100644 --- a/DistFiles/Language Explorer/Configuration/Parts/CmPossibility.fwlayout +++ b/DistFiles/Language Explorer/Configuration/Parts/CmPossibility.fwlayout @@ -295,8 +295,6 @@ - - diff --git a/DistFiles/Language Explorer/Configuration/Parts/LexSense.fwlayout b/DistFiles/Language Explorer/Configuration/Parts/LexSense.fwlayout index a23f24f22d..4439f6e69b 100644 --- a/DistFiles/Language Explorer/Configuration/Parts/LexSense.fwlayout +++ b/DistFiles/Language Explorer/Configuration/Parts/LexSense.fwlayout @@ -108,10 +108,6 @@ - - - - diff --git a/DistFiles/Language Explorer/Configuration/Parts/Morphology.fwlayout b/DistFiles/Language Explorer/Configuration/Parts/Morphology.fwlayout index 8ff98db609..355f1cce82 100644 --- a/DistFiles/Language Explorer/Configuration/Parts/Morphology.fwlayout +++ b/DistFiles/Language Explorer/Configuration/Parts/Morphology.fwlayout @@ -231,38 +231,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -337,32 +305,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -490,11 +432,6 @@ - - - - - diff --git a/DistFiles/Language Explorer/Configuration/Parts/MorphologyParts.xml b/DistFiles/Language Explorer/Configuration/Parts/MorphologyParts.xml index 720f4b1681..9eccaf26ed 100644 --- a/DistFiles/Language Explorer/Configuration/Parts/MorphologyParts.xml +++ b/DistFiles/Language Explorer/Configuration/Parts/MorphologyParts.xml @@ -1876,9 +1876,6 @@ - - - @@ -1903,59 +1900,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml b/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml index 4bc7d6a5ab..335df33ac6 100644 --- a/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml +++ b/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml @@ -75,7 +75,6 @@ - @@ -93,9 +92,6 @@ - @@ -318,7 +314,6 @@ - @@ -426,12 +421,6 @@ --> - diff --git a/Src/Common/FwAvalonia/FwAvaloniaTests/LayoutImportCoverageTests.cs b/Src/Common/FwAvalonia/FwAvaloniaTests/LayoutImportCoverageTests.cs index 22b2dd94fa..590613eb26 100644 --- a/Src/Common/FwAvalonia/FwAvaloniaTests/LayoutImportCoverageTests.cs +++ b/Src/Common/FwAvalonia/FwAvaloniaTests/LayoutImportCoverageTests.cs @@ -616,12 +616,10 @@ public void ShippedLayouts_EveryDiagnosticCode_IsInTheKnownTaxonomy() } /// - /// With the metadata-driven base-class walk the importer - /// resolves every shipped part ref that legacy DataTree resolves. What remains is EXACTLY the - /// set of layout refs with no {class-chain}-Detail-{ref} part anywhere in the shipped - /// inventories -- refs legacy DataTree also silently omits ("Just omit the missing part", - /// DataTree.cs:2455-2457; the detail view has no PartGenerator). Asserting the exact set keeps - /// both regressions (new unresolved refs) and silent improvements (parts added) visible. + /// The part refs with no part in Language Explorer/Configuration/Parts, pinned + /// exactly so new unresolved refs and added parts both show. At runtime DataTree + /// also loads DistFiles/Parts (StandardParts.xml and the build's GeneratedParts.xml), + /// which resolves every one of them. /// [Test] public void ShippedLayouts_UnresolvedParts_AreExactlyTheLegacyUnresolvableSet() @@ -630,39 +628,28 @@ public void ShippedLayouts_UnresolvedParts_AreExactlyTheLegacyUnresolvableSet() var (layouts, parts) = LoadShippedFiles(repoRoot); var report = LayoutImportCoverage.Run(layouts, parts, LoadBaseClassMap(repoRoot)); - // Every entry below was verified to have no matching part id (case-insensitive) on the - // class or any base class in the shipped *Parts.xml files; legacy omits them too. Most are - // summary/section header parts that were never shipped, plus the DateCreated/DateModified - // refs Notebook hides (visibility='never') whose parts never existed in the detail view. + // Summary, section and message refs resolve to StandardParts.xml's CmObject parts; + // field refs resolve to the build's GeneratedParts.xml. var legacyUnresolvable = new SortedDictionary(StringComparer.Ordinal) { { "CmAnthroItem-Summary", 1 }, - { "CmPerson-Role", 1 }, { "CmPossibility-Summary", 1 }, { "CmSemanticDomain-Summary", 1 }, - { "FsClosedValue-Summary", 1 }, { "FsComplexFeature-Message", 1 }, - { "FsComplexValue-Summary", 1 }, - { "FsFeatStruc-Blank", 1 }, { "FsFeatStrucType-Message", 1 }, - { "FsFeatureSpecification-Summary", 1 }, { "LexEntry-ImportResidue", 1 }, { "LexEntryInflType-Summary", 1 }, { "LexEntryType-Summary", 2 }, { "LexEtymology-NormalSummary", 1 }, { "LexExtendedNote-NormalSummary", 1 }, { "LexPronunciation-MediaFiles", 1 }, - { "LexReference-ShowSingleReference", 1 }, { "LexSense-HeavySummary", 1 }, { "LexSense-ImportResidue", 1 }, - // LexSense `Pictures` has no LexSense-Detail-Pictures part, so legacy DataTree (and this - // importer) omit sense pictures. { "LexSense-Pictures", 1 }, { "MoAlloAdhocProhib-Message", 1 }, { "MoEndoCompound-HeadLast", 2 }, { "MoExoCompound-ToMsa", 1 }, { "MoInflAffixSlot-Optional", 1 }, - { "MoInflClass-SubclassesAllA", 1 }, { "MoMorphAdhocProhib-Message", 1 }, { "PartOfSpeech-Section", 2 }, { "PhPhoneme-Codes", 1 }, @@ -677,8 +664,8 @@ public void ShippedLayouts_UnresolvedParts_AreExactlyTheLegacyUnresolvableSet() Assert.That(report.UnresolvedPartRefs, Is.EquivalentTo(legacyUnresolvable), "the unresolved-part set changed; if a part was added/renamed update this set, if a " - + "resolution rule regressed fix the resolver (B10 baseline: 63 occurrences, was 259)"); - Assert.That(report.UnresolvedPartRefs.Values.Sum(), Is.EqualTo(63), + + "resolution rule regressed fix the resolver (now 56 occurrences; B10 baseline 63, was 259)"); + Assert.That(report.UnresolvedPartRefs.Values.Sum(), Is.EqualTo(56), "B10 unresolved-part occurrence ceiling"); } diff --git a/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs b/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs index c5410582c1..08507c1b84 100644 --- a/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs +++ b/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs @@ -1358,6 +1358,7 @@ public void UnownedMenu_StillAsksTheMediator_ForSubmenuLeaves() // The XML walk must see what ChoiceGroup.Populate sees. The mediator path drops a // submenu no colleague shows, so nested leaves compare as a subset, top-level exactly. + // No item may name a missing command. [Test] public void ResolveMenu_ReadsWhatChoiceGroupPopulates_ForEveryContextMenuId() { @@ -1386,8 +1387,8 @@ public void ResolveMenu_ReadsWhatChoiceGroupPopulates_ForEveryContextMenuId() undefinedCommands.AddRange(resolved.Unanswerable.Where(u => u.StartsWith("command", StringComparison.Ordinal)) .Select(u => id + ": " + u)); } - TestContext.WriteLine("Context-menu items naming an undefined command (ChoiceGroup fails on them at display time):" - + Environment.NewLine + string.Join(Environment.NewLine, undefinedCommands)); + Assert.That(undefinedCommands, Is.Empty, + "context-menu items naming an undefined command (ChoiceGroup fails on them at display time)"); } // Walks the ChoiceGroup side exactly as the configuration-walk side describes a leaf; From c9fb76f815f0a143341ba6be4d5caea6ef562ffc Mon Sep 17 00:00:00 2001 From: mark-sil <83427558+mark-sil@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:20:30 -0400 Subject: [PATCH 3/4] Delete commands nothing can run, and the code only they reached FieldWorks no longer defines commands that nothing can run, either because no menu, toolbar or code names them or because no handler answers them, and no longer keeps the message handlers and strings that only those commands reached. Until now they sat in the area configuration files; File > Page Setup... showed but was always disabled, and five shortcuts did nothing because no handler existed. The commented-out menu items that named them are gone too. MorphologyListener.OnAnalysisApprove, which only the User Opinion submenu's Approve still calls, becomes the private ApproveAnalysis; mnuDataTree-Pronunciation loses the trailing separator that preceded its commented-out Writing Systems item; and the every-menu test now also fails on a main-menu or toolbar item naming a missing command. Deleted: * CmdBundleUp, CmdBundleDown, CmdBundleUpNc and CmdBundleDownNc (shortcut, no handler) * CmdShowMorphology (Ctrl+M, no handler) * CmdPrevIncompleteBundle (no handler) * CmdParse (no handler; its item in the Interlinear Texts Data menu was hidden and never enabled) * CmdPageSetup (no handler since Translation Editor's was removed in 2015; File > Page Setup... was always disabled), with its ContextHelp.xml entry * CmdMainObjectProperties (no handler), with the commented-out File > Properties submenu and its ContextHelp.xml entry * CmdDataTree-Pronunciation-WritingSystems (no handler) * CmdDataTree-Delete, CmdDataTree-Insert-Entry-Sense, CmdDataTree-Insert-ReversalSubPOS, CmdDataTree-MoveUp-Sense-Symbol and CmdDataTree-MoveDown-Sense-Symbol * CmdAnalysisApprove, with MorphologyListener.OnDisplayAnalysisApprove and the commented-out mnuDataTree-ParserProducedAnalysis-Hotlinks and mnuDataTree-HumanDisapprovedAnalysis-Hotlinks menus * CmdDataTree-Merge-MainWordform, with MorphologyListener.OnMergeWordform, MorphologyListener.OnDisplayMergeWordform and MEStrings ksCannotMergeWordformsYet * CmdWordformEditForm, with MorphologyListener.OnWordformEditForm, MorphologyListener.OnDisplayWordformEditForm and MEStrings ksTodo_WordformEditing * CmdWordformChangeCase, with MorphologyListener.OnWordformChangeCase and MorphologyListener.OnDisplayWordformChangeCase * CmdListColleagues, with Ticker.OnListColleagues * CmdStartLogging, with FwXWindow.OnStartLogging * CmdSynch, with LexTextApp.OnSynchronize * The SpellingStatus handlers under #if NOTYET in MorphologyListener, left from the commands the previous commit removed Co-Authored-By: Claude Opus 5.5 --- .../Configuration/CommonDataTreeInclude.xml | 2 - .../Configuration/ContextHelp.xml | 2 - .../Configuration/Lexicon/DataTreeInclude.xml | 17 -- .../Configuration/Lists/DataTreeInclude.xml | 3 - .../Language Explorer/Configuration/Main.xml | 15 -- .../Configuration/Words/areaConfiguration.xml | 31 ---- Src/LexText/LexTextDll/LexTextApp.cs | 26 ---- Src/LexText/Morphology/MEStrings.Designer.cs | 18 --- Src/LexText/Morphology/MEStrings.resx | 6 - Src/LexText/Morphology/MorphologyListener.cs | 147 +----------------- Src/XCore/Ticker.cs | 11 -- Src/xWorks/FwXWindow.cs | 12 -- .../DetailObjectCommandExecutionTests.cs | 12 +- 13 files changed, 12 insertions(+), 290 deletions(-) diff --git a/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml b/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml index 354df33fb3..d7e19b9933 100644 --- a/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml +++ b/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml @@ -2,7 +2,6 @@ - @@ -11,7 +10,6 @@ - diff --git a/DistFiles/Language Explorer/Configuration/ContextHelp.xml b/DistFiles/Language Explorer/Configuration/ContextHelp.xml index 0428ae3585..4633cef854 100644 --- a/DistFiles/Language Explorer/Configuration/ContextHelp.xml +++ b/DistFiles/Language Explorer/Configuration/ContextHelp.xml @@ -37,7 +37,6 @@ Save Starts RAMP (if it is installed) and prepares an archive package for uploading. Refresh the screen. - Determine page layout options for printing. Print Import an existing lexicon. Export this FieldWorks project to a file. @@ -45,7 +44,6 @@ Restore a FieldWorks project. Delete FieldWorks project. Create a desktop shortcut to this project. - Edit the special properties of this FieldWorks project (such as name and writing systems). Send/Receive this Project (lexicon and texts) diff --git a/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml b/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml index 8486553a52..76ea700dd7 100644 --- a/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml +++ b/DistFiles/Language Explorer/Configuration/Lexicon/DataTreeInclude.xml @@ -92,9 +92,6 @@ - - - @@ -104,12 +101,6 @@ - - - - - - @@ -200,9 +191,6 @@ - - - @@ -429,8 +414,6 @@ - - diff --git a/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml b/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml index 7be732ccb8..5c7ec38ab9 100644 --- a/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml +++ b/DistFiles/Language Explorer/Configuration/Lists/DataTreeInclude.xml @@ -43,9 +43,6 @@ - - - diff --git a/DistFiles/Language Explorer/Configuration/Main.xml b/DistFiles/Language Explorer/Configuration/Main.xml index beb3560484..cea3f62af5 100644 --- a/DistFiles/Language Explorer/Configuration/Main.xml +++ b/DistFiles/Language Explorer/Configuration/Main.xml @@ -50,7 +50,6 @@ - @@ -66,7 +65,6 @@ - @@ -107,7 +105,6 @@ - @@ -127,11 +124,6 @@ - - @@ -171,7 +163,6 @@ - @@ -414,7 +405,6 @@ - @@ -433,12 +423,7 @@ - - - diff --git a/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml b/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml index 335df33ac6..0573ab9d39 100644 --- a/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml +++ b/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml @@ -51,7 +51,6 @@ - @@ -63,19 +62,13 @@ - - - - - - @@ -92,10 +85,7 @@ - - - @@ -107,7 +97,6 @@ - @@ -317,7 +306,6 @@ - @@ -415,23 +403,12 @@ - - - @@ -466,10 +443,6 @@ - @@ -479,10 +452,6 @@ - diff --git a/Src/LexText/LexTextDll/LexTextApp.cs b/Src/LexText/LexTextDll/LexTextApp.cs index 5336aa52bc..8454ab1599 100644 --- a/Src/LexText/LexTextDll/LexTextApp.cs +++ b/Src/LexText/LexTextDll/LexTextApp.cs @@ -497,32 +497,6 @@ public bool OnRestoreDefaultLayouts(object commandObject) return true; } - /// - /// This implements the "Synchronize with LiftShare..." menu command. - /// - /// - /// - /// Until LiftShare is fully implemented, this is irrelevant. - public bool OnSynchronize(object sender) - { -#if WANTPORT // FWR-2845; this was not enabled in 6.0 and may be superseded by Randy's LiftBridge. - Form formActive = ActiveForm; - if (cache != null) - { - FwXWindow wndActive = formActive as FwXWindow; - LiftSynchronizeDlg dlg = new LiftSynchronizeDlg(Cache, wndActive.Mediator); - dlg.ShowDialog(formActive); - return true; - } - else - { - return false; - } -#else - return false; -#endif - } - /// /// On Refresh, we want to reload the XML configuration files. This greatly facilitates developing /// those files, even though it's not as useful for normal use. It might prove useful whenever we diff --git a/Src/LexText/Morphology/MEStrings.Designer.cs b/Src/LexText/Morphology/MEStrings.Designer.cs index 7d18fa9f16..348735476b 100644 --- a/Src/LexText/Morphology/MEStrings.Designer.cs +++ b/Src/LexText/Morphology/MEStrings.Designer.cs @@ -177,15 +177,6 @@ internal static string ksApply { } } - /// - /// Looks up a localized string similar to Merging wordforms needs serious definition, before it can be done. Sorry.. - /// - internal static string ksCannotMergeWordformsYet { - get { - return ResourceManager.GetString("ksCannotMergeWordformsYet", resourceCulture); - } - } - /// /// Looks up a localized string similar to Sorry. We are not able to support respelling the wordform in the required writing system at this time.. /// @@ -951,15 +942,6 @@ internal static string ksTargetFeature { } } - /// - /// Looks up a localized string similar to TODO: Basic wordform editing happens here.. - /// - internal static string ksTodo_WordformEditing { - get { - return ResourceManager.GetString("ksTodo_WordformEditing", resourceCulture); - } - } - /// /// Looks up a localized string similar to Undo Adding Affixes. /// diff --git a/Src/LexText/Morphology/MEStrings.resx b/Src/LexText/Morphology/MEStrings.resx index 98349248d6..e0cca24438 100644 --- a/Src/LexText/Morphology/MEStrings.resx +++ b/Src/LexText/Morphology/MEStrings.resx @@ -133,9 +133,6 @@ Find Wordform - - Merging wordforms needs serious definition, before it can be done. Sorry. - Analysis {0} {0} is a number ("Analysis 1" for first, etc) @@ -143,9 +140,6 @@ no analysis - - TODO: Basic wordform editing happens here. - &Go To diff --git a/Src/LexText/Morphology/MorphologyListener.cs b/Src/LexText/Morphology/MorphologyListener.cs index b6a55d7bdb..f6a50140c2 100644 --- a/Src/LexText/Morphology/MorphologyListener.cs +++ b/Src/LexText/Morphology/MorphologyListener.cs @@ -215,30 +215,6 @@ public int Priority #region XCore Message handlers - /// - /// - /// - /// - /// - /// - public virtual bool OnDisplayMergeWordform(object commandObject, - ref UIItemDisplayProperties display) - { - CheckDisposed(); - - display.Enabled = display.Visible = InFriendlyArea; - return true; //we've handled this - } - public bool OnMergeWordform(object argument) - { - CheckDisposed(); - - // Do something meaningful, - // whenever the definition of merging wordforms gets developed. - MessageBox.Show(MEStrings.ksCannotMergeWordformsYet); - return true; - } - /// /// Enable the spelling tool always. Correct the property value if need be to match whether /// we are actually showing vernacular spelling. @@ -801,14 +777,6 @@ public virtual bool OnJumpToTool(object commandObject) #region Approval Status Message handlers - public virtual bool OnDisplayAnalysisApprove(object commandObject, - ref UIItemDisplayProperties display) - { - display.Enabled = display.Visible = InFriendlyArea; - display.Checked = Analysis != null && Analysis.IsValidObject && Analysis.ApprovalStatusIcon == 1; - return true; //we've handled this - } - /// /// Insert Word Gloss and Approve Analysis /// @@ -819,22 +787,18 @@ public bool OnInsertWordGlossAndApprove(object cmd) // Insert word gloss. OnDataTreeInsert(cmd); } - OnAnalysisApprove(cmd); + ApproveAnalysis(); return true; } /// - /// + /// Marks the current analysis approved. /// - /// The xCore Command object. - /// true - public bool OnAnalysisApprove(object argument) + private void ApproveAnalysis() { IWfiAnalysis anal = Analysis; Debug.Assert(anal != null, "Could not find analysis object."); SetNewStatus(anal, 1); - - return true; } public virtual bool OnDisplayAnalysisUnknown(object commandObject, @@ -883,71 +847,6 @@ public bool OnAnalysisDisapprove(object argument) #endregion Approval Status Message handlers -#if NOTYET - #region SpellingStatus Message handlers - - public virtual bool OnDisplaySpellingStatusUnknown(object commandObject, - ref UIItemDisplayProperties display) - { - display.Enabled = display.Visible = InFriendlyArea; - return true; //we've handled this - } - - /// - /// - /// - /// The xCore Command object. - /// true - public bool OnSpellingStatusUnknown(object argument) - { - MessageBox.Show("TODO: Set spelling status to 'Unknown' goes here, when the integer values that get stored in the database get defined."); - - return true; - } - - public virtual bool OnDisplaySpellingStatusGood(object commandObject, - ref UIItemDisplayProperties display) - { - display.Enabled = display.Visible = InFriendlyArea; - return true; //we've handled this - } - - /// - /// - /// - /// The xCore Command object. - /// true - public bool OnSpellingStatusGood(object argument) - { - MessageBox.Show("TODO: Set spelling status to 'Good' goes here, when the integer values that get stored in the database get defined."); - - return true; - } - - public virtual bool OnDisplaySpellingStatusDisapprove(object commandObject, - ref UIItemDisplayProperties display) - { - display.Enabled = display.Visible = InFriendlyArea; - return true; //we've handled this - } - - /// - /// - /// - /// The xCore Command object. - /// true - public bool OnSpellingStatusDisapprove(object argument) - { - MessageBox.Show("TODO: Set spelling status to 'Disapproved' goes here, when the integer values that get stored in the database get defined."); - - return true; - } - - #endregion SpellingStatus Message handlers -#endif - - #region Wordform edit Message handlers - protected override bool DeleteObject(Command command) { if (base.DeleteObject(command)) @@ -961,46 +860,6 @@ protected override bool DeleteObject(Command command) return false; } - public virtual bool OnDisplayWordformEditForm(object commandObject, - ref UIItemDisplayProperties display) - { - display.Enabled = display.Visible = InFriendlyArea; - return true; //we've handled this - } - - /// - /// - /// - /// The xCore Command object. - /// true - public bool OnWordformEditForm(object argument) - { - MessageBox.Show(MEStrings.ksTodo_WordformEditing); - - return true; - } - - public virtual bool OnDisplayWordformChangeCase(object commandObject, - ref UIItemDisplayProperties display) - { - display.Enabled = display.Visible = InFriendlyArea; - return true; //we've handled this - } - - /// - /// - /// - /// The xCore Command object. - /// true - public bool OnWordformChangeCase(object argument) - { - MessageBox.Show("TODO: Case changing editing happens here."); - - return true; - } - - #endregion Wordform edit Message handlers - #region New analysis message handler // diff --git a/Src/XCore/Ticker.cs b/Src/XCore/Ticker.cs index b6f98b691f..1558d1e327 100644 --- a/Src/XCore/Ticker.cs +++ b/Src/XCore/Ticker.cs @@ -226,17 +226,6 @@ public bool OnListPropertiesTable(object argument) WriteLine(m_propertyTable.GetPropertiesDumpString()); return true;//we handled this, no need to ask anyone else. } - /// - /// Receives the message "ListPropertiesTable" - /// - public bool OnListColleagues(object argument) - { - CheckDisposed(); - - WriteLine ("--------Colleagues"); - WriteLine(m_mediator.GetColleaguesDumpString()); - return true;//we handled this, no need to ask anyone else. - } /// /// enable this command because we are listening for it. diff --git a/Src/xWorks/FwXWindow.cs b/Src/xWorks/FwXWindow.cs index 5b12a37a58..e0d7fad2c2 100644 --- a/Src/xWorks/FwXWindow.cs +++ b/Src/xWorks/FwXWindow.cs @@ -993,18 +993,6 @@ public bool OnNewWindow(object command) return true; } - /// ------------------------------------------------------------------------------------ - /// - /// Start logging events. - /// - /// - /// - /// ------------------------------------------------------------------------------------ - protected bool OnStartLogging(object args) - { - return true; - } - /// ------------------------------------------------------------------------------------ /// /// diff --git a/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs b/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs index 08507c1b84..d35ebe38a5 100644 --- a/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs +++ b/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs @@ -1356,9 +1356,9 @@ public void UnownedMenu_StillAsksTheMediator_ForSubmenuLeaves() Assert.That(asked, Is.True, "the mediator path decides submenu visibility by asking colleagues"); } - // The XML walk must see what ChoiceGroup.Populate sees. The mediator path drops a - // submenu no colleague shows, so nested leaves compare as a subset, top-level exactly. - // No item may name a missing command. + // The XML walk sees what ChoiceGroup.Populate sees: top-level leaves exactly, nested + // ones as a subset (the mediator drops unshown submenus). No menu or toolbar item may + // name a missing command. [Test] public void ResolveMenu_ReadsWhatChoiceGroupPopulates_ForEveryContextMenuId() { @@ -1389,6 +1389,12 @@ public void ResolveMenu_ReadsWhatChoiceGroupPopulates_ForEveryContextMenuId() } Assert.That(undefinedCommands, Is.Empty, "context-menu items naming an undefined command (ChoiceGroup fails on them at display time)"); + + var defined = new HashSet(configuration.SelectNodes("//commands/command[@id]").Cast() + .Select(n => n.Attributes["id"].Value), StringComparer.Ordinal); + var undefinedItems = configuration.SelectNodes("//*[@command]").Cast() + .Select(n => n.Attributes["command"].Value).Where(c => !defined.Contains(c)).Distinct().ToList(); + Assert.That(undefinedItems, Is.Empty, "menu or toolbar items naming an undefined command"); } // Walks the ChoiceGroup side exactly as the configuration-walk side describes a leaf; From dd7ea7f009bbdaca0ee63b6f1e24fdb6e8496c38 Mon Sep 17 00:00:00 2001 From: mark-sil <83427558+mark-sil@users.noreply.github.com> Date: Thu, 22 Oct 2026 12:39:30 -0400 Subject: [PATCH 4/4] Give menu and layout references their own integrity test Shipped menu, toolbar and layout references now have their own test fixture, which also fails when a layout part's menu or hotlinks attribute names a context menu the window configuration does not define. Until now the whole-configuration command check sat inside the Avalonia context-menu parity test, and nothing checked the layouts' menu references. Changed: * EveryCommandReference_NamesADefinedCommand, moved into the new WindowConfigurationIntegrityTests out of ResolveMenu_ReadsWhatChoiceGroupPopulates_ForEveryContextMenuId * EveryLayoutMenuReference_NamesADefinedMenu (new, same fixture) * ShippedLayouts_UnresolvedParts_AreExactlyTheRefsWithNoConfigurationPart, renamed from ShippedLayouts_UnresolvedParts_AreExactlyTheLegacyUnresolvableSet * IsOfferedOnEntry_AnyOtherInsert_OnAnyEntry no longer adds an allomorph the rule ignores * DetailObjectInsertion.IsOfferedOnEntry: its summary now says a null or invalid entry offers nothing * LexEntryMenuHandler.OnDisplayDataTreeInsert compares the AffixProcess suffix ordinally Co-Authored-By: Claude Opus 5.5 --- .../LayoutImportCoverageTests.cs | 6 +- .../DetailRules/DetailObjectInsertion.cs | 2 +- .../DetailRules/DetailObjectInsertionTests.cs | 7 +- Src/LexText/Lexicon/LexEntryMenuHandler.cs | 3 +- .../DetailObjectCommandExecutionTests.cs | 10 +- .../WindowConfigurationIntegrityTests.cs | 107 ++++++++++++++++++ 6 files changed, 116 insertions(+), 19 deletions(-) create mode 100644 Src/xWorks/xWorksTests/WindowConfigurationIntegrityTests.cs diff --git a/Src/Common/FwAvalonia/FwAvaloniaTests/LayoutImportCoverageTests.cs b/Src/Common/FwAvalonia/FwAvaloniaTests/LayoutImportCoverageTests.cs index 590613eb26..d618409d69 100644 --- a/Src/Common/FwAvalonia/FwAvaloniaTests/LayoutImportCoverageTests.cs +++ b/Src/Common/FwAvalonia/FwAvaloniaTests/LayoutImportCoverageTests.cs @@ -622,7 +622,7 @@ public void ShippedLayouts_EveryDiagnosticCode_IsInTheKnownTaxonomy() /// which resolves every one of them. /// [Test] - public void ShippedLayouts_UnresolvedParts_AreExactlyTheLegacyUnresolvableSet() + public void ShippedLayouts_UnresolvedParts_AreExactlyTheRefsWithNoConfigurationPart() { var repoRoot = FindRepoRoot(); var (layouts, parts) = LoadShippedFiles(repoRoot); @@ -630,7 +630,7 @@ public void ShippedLayouts_UnresolvedParts_AreExactlyTheLegacyUnresolvableSet() // Summary, section and message refs resolve to StandardParts.xml's CmObject parts; // field refs resolve to the build's GeneratedParts.xml. - var legacyUnresolvable = new SortedDictionary(StringComparer.Ordinal) + var noConfigurationPart = new SortedDictionary(StringComparer.Ordinal) { { "CmAnthroItem-Summary", 1 }, { "CmPossibility-Summary", 1 }, @@ -662,7 +662,7 @@ public void ShippedLayouts_UnresolvedParts_AreExactlyTheLegacyUnresolvableSet() { "WfiWordform-HeavySummary", 1 } }; - Assert.That(report.UnresolvedPartRefs, Is.EquivalentTo(legacyUnresolvable), + Assert.That(report.UnresolvedPartRefs, Is.EquivalentTo(noConfigurationPart), "the unresolved-part set changed; if a part was added/renamed update this set, if a " + "resolution rule regressed fix the resolver (now 56 occurrences; B10 baseline 63, was 259)"); Assert.That(report.UnresolvedPartRefs.Values.Sum(), Is.EqualTo(56), diff --git a/Src/FdoUi/DetailRules/DetailObjectInsertion.cs b/Src/FdoUi/DetailRules/DetailObjectInsertion.cs index 0109d8eeb7..99471d4597 100644 --- a/Src/FdoUi/DetailRules/DetailObjectInsertion.cs +++ b/Src/FdoUi/DetailRules/DetailObjectInsertion.cs @@ -157,7 +157,7 @@ public static int ConcreteClass(ICmObject owner, int classId) /// /// Whether an entry offers an Insert command: Insert Affix Process, chosen by the end - /// of its id, only on an affix entry; any other command always. + /// of its id, only on an affix entry; any other command on any valid entry. /// public static bool IsOfferedOnEntry(ILexEntry entry, string commandId) { diff --git a/Src/FdoUi/FdoUiTests/DetailRules/DetailObjectInsertionTests.cs b/Src/FdoUi/FdoUiTests/DetailRules/DetailObjectInsertionTests.cs index d56a1f7f66..0e7fbc8fb7 100644 --- a/Src/FdoUi/FdoUiTests/DetailRules/DetailObjectInsertionTests.cs +++ b/Src/FdoUi/FdoUiTests/DetailRules/DetailObjectInsertionTests.cs @@ -210,12 +210,7 @@ public void IsOfferedOnEntry_AnAffixProcess_OnlyOnAnAffixEntry() public void IsOfferedOnEntry_AnyOtherInsert_OnAnyEntry() { const string allomorph = "CmdDataTree-Insert-AlternateForm"; - Assert.That(DetailObjectInsertion.IsOfferedOnEntry(_entry, allomorph), Is.True, "no allomorph yet"); - - NonUndoableUnitOfWorkHelper.Do(Cache.ActionHandlerAccessor, () => - _entry.AlternateFormsOS.Add(Cache.ServiceLocator.GetInstance().Create())); - - Assert.That(DetailObjectInsertion.IsOfferedOnEntry(_entry, allomorph), Is.True, "the first allomorph is an affix"); + Assert.That(DetailObjectInsertion.IsOfferedOnEntry(_entry, allomorph), Is.True); Assert.That(DetailObjectInsertion.IsOfferedOnEntry(null, allomorph), Is.False); } diff --git a/Src/LexText/Lexicon/LexEntryMenuHandler.cs b/Src/LexText/Lexicon/LexEntryMenuHandler.cs index 38533ca4d4..dcdd4ebb2c 100644 --- a/Src/LexText/Lexicon/LexEntryMenuHandler.cs +++ b/Src/LexText/Lexicon/LexEntryMenuHandler.cs @@ -2,6 +2,7 @@ // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) +using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; @@ -64,7 +65,7 @@ public override bool OnDisplayDataTreeInsert(object commandObject, ref UIItemDis return true; } XCore.Command command = (XCore.Command)commandObject; - if (command.Id.EndsWith("AffixProcess")) + if (command.Id.EndsWith("AffixProcess", StringComparison.Ordinal)) { var offered = DetailObjectInsertion.IsOfferedOnEntry(entry, command.Id); display.Enabled = offered; diff --git a/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs b/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs index d35ebe38a5..24256f749e 100644 --- a/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs +++ b/Src/xWorks/xWorksTests/Avalonia/Hosting/DetailObjectCommandExecutionTests.cs @@ -1357,8 +1357,8 @@ public void UnownedMenu_StillAsksTheMediator_ForSubmenuLeaves() } // The XML walk sees what ChoiceGroup.Populate sees: top-level leaves exactly, nested - // ones as a subset (the mediator drops unshown submenus). No menu or toolbar item may - // name a missing command. + // ones as a subset (the mediator drops unshown submenus). No item may name a missing + // command. [Test] public void ResolveMenu_ReadsWhatChoiceGroupPopulates_ForEveryContextMenuId() { @@ -1389,12 +1389,6 @@ public void ResolveMenu_ReadsWhatChoiceGroupPopulates_ForEveryContextMenuId() } Assert.That(undefinedCommands, Is.Empty, "context-menu items naming an undefined command (ChoiceGroup fails on them at display time)"); - - var defined = new HashSet(configuration.SelectNodes("//commands/command[@id]").Cast() - .Select(n => n.Attributes["id"].Value), StringComparer.Ordinal); - var undefinedItems = configuration.SelectNodes("//*[@command]").Cast() - .Select(n => n.Attributes["command"].Value).Where(c => !defined.Contains(c)).Distinct().ToList(); - Assert.That(undefinedItems, Is.Empty, "menu or toolbar items naming an undefined command"); } // Walks the ChoiceGroup side exactly as the configuration-walk side describes a leaf; diff --git a/Src/xWorks/xWorksTests/WindowConfigurationIntegrityTests.cs b/Src/xWorks/xWorksTests/WindowConfigurationIntegrityTests.cs new file mode 100644 index 0000000000..c2fa043dec --- /dev/null +++ b/Src/xWorks/xWorksTests/WindowConfigurationIntegrityTests.cs @@ -0,0 +1,107 @@ +// Copyright (c) 2026 SIL International +// This software is licensed under the LGPL, version 2.1 or later +// (http://www.gnu.org/licenses/lgpl-2.1.html) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml; +using NUnit.Framework; +using SIL.FieldWorks.Common.FwUtils; + +namespace SIL.FieldWorks.XWorks +{ + /// + /// Every command, menu and hotlinks reference in the shipped configuration and layouts + /// names something the configuration defines, so no menu throws when it is built. + /// + [TestFixture] + [Apartment(System.Threading.ApartmentState.STA)] + public class WindowConfigurationIntegrityTests : XWorksAppTestBase + { + private XmlNode m_configuration; + + protected override void Init() + { + m_application = new MockFwXApp(new MockFwManager { Cache = Cache }, null, null); + } + + [SetUp] + public void LoadConfiguration() + { + m_window = new MockFwXWindow(m_application, m_configFilePath); + ((MockFwXWindow)m_window).Init(Cache); + m_window.PropTable.RemoveLocalAndGlobalSettings(); + m_window.LoadUI(m_configFilePath); + m_configuration = m_window.PropTable.GetValue("WindowConfiguration"); + Assert.That(m_configuration, Is.Not.Null); + } + + [TearDown] + public void DisposeWindow() + { + m_configuration = null; + if (m_window != null && !m_window.IsDisposed) + { + m_window.PropTable.RemoveLocalAndGlobalSettings(); + m_window.Dispose(); + } + m_window = null; + } + + [Test] + public void EveryCommandReference_NamesADefinedCommand() + { + var defined = Ids(m_configuration.SelectNodes("//commands/command[@id]")); + var missing = m_configuration.SelectNodes("//*[@command]").Cast() + .Select(n => n.Attributes["command"].Value).Where(id => !defined.Contains(id)) + .Distinct().ToList(); + + Assert.That(missing, Is.Empty, "menu, toolbar or context-menu items naming an undefined command"); + } + + [Test] + public void EveryLayoutMenuReference_NamesADefinedMenu() + { + var defined = Ids(m_configuration.SelectNodes("//menu[@id]")); + var missing = new List(); + var files = ShippedLayoutFiles().ToList(); + Assert.That(files, Is.Not.Empty, "the shipped layout and part files"); + foreach (var path in files) + { + var doc = new XmlDocument(); + doc.Load(path); + foreach (XmlNode node in doc.SelectNodes("//*[@menu or @hotlinks]")) + { + foreach (var attribute in new[] { "menu", "hotlinks" }) + { + var id = node.Attributes[attribute]?.Value; + if (!string.IsNullOrEmpty(id) && !defined.Contains(id)) + missing.Add(Path.GetFileName(path) + ": " + attribute + "=" + id); + } + } + } + + Assert.That(missing, Is.Empty, "layout parts naming a context menu the window configuration lacks"); + } + + // The layout and part files DataTree loads: Language Explorer's and the shared ones. + private static IEnumerable ShippedLayoutFiles() + { + var folders = new[] + { + Path.Combine(FwDirectoryFinder.FlexFolder, "Configuration", "Parts"), + Path.Combine(FwDirectoryFinder.CodeDirectory, "Parts") + }; + return folders.Where(Directory.Exists).SelectMany(folder => + Directory.GetFiles(folder, "*.fwlayout").Concat(Directory.GetFiles(folder, "*Parts.xml"))); + } + + private static HashSet Ids(XmlNodeList nodes) + { + return new HashSet(nodes.Cast().Select(n => n.Attributes["id"].Value), + StringComparer.Ordinal); + } + } +}