From 42663f09f66d6bdeff3a947d2c3ad1dc5dd6304f Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Tue, 27 Aug 2024 09:21:26 -0700 Subject: [PATCH 1/2] LT-21852: Change the file types part (#144) --- .../Export Templates/Phonology.xml | 2 +- Src/FwResources/FwStrings.Designer.cs | 13 +++++++++++-- Src/FwResources/FwStrings.resx | 4 ++++ Src/FwResources/ResourceHelper.cs | 3 +++ Src/xWorks/FwXWindow.cs | 4 ++-- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/DistFiles/Language Explorer/Export Templates/Phonology.xml b/DistFiles/Language Explorer/Export Templates/Phonology.xml index e762f03275..e8f386105a 100644 --- a/DistFiles/Language Explorer/Export Templates/Phonology.xml +++ b/DistFiles/Language Explorer/Export Templates/Phonology.xml @@ -1,4 +1,4 @@ \ No newline at end of file diff --git a/Src/FwResources/FwStrings.Designer.cs b/Src/FwResources/FwStrings.Designer.cs index 056a8cbff4..55c8c2fd70 100644 --- a/Src/FwResources/FwStrings.Designer.cs +++ b/Src/FwResources/FwStrings.Designer.cs @@ -19,7 +19,7 @@ namespace SIL.FieldWorks.Resources { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class FwStrings { @@ -322,7 +322,7 @@ internal static string kstidChangeHomographNumberWs { } /// - /// Looks up a localized string similar to Change Vernacular Writing System. + /// Looks up a localized string similar to Default Vernacular Writing System Has Changed. /// internal static string kstidChangeHomographNumberWsTitle { get { @@ -2080,6 +2080,15 @@ internal static string kstidPersonalNotes { } } + /// + /// Looks up a localized string similar to Phonology XML Files. + /// + internal static string kstidPhonologyXML { + get { + return ResourceManager.GetString("kstidPhonologyXML", resourceCulture); + } + } + /// /// Looks up a localized string similar to PhraseTags. /// diff --git a/Src/FwResources/FwStrings.resx b/Src/FwResources/FwStrings.resx index 1fc8553c3b..fe1d129694 100644 --- a/Src/FwResources/FwStrings.resx +++ b/Src/FwResources/FwStrings.resx @@ -1421,4 +1421,8 @@ FieldWorks ReadMe for help in this area. The writing systems {0} are missing the default collation. The standard icu collation will be used. {0} is a list of one or more writing systems + + Phonology XML Files + Used in the list of file types in file open/save dialogs + \ No newline at end of file diff --git a/Src/FwResources/ResourceHelper.cs b/Src/FwResources/ResourceHelper.cs index d423f979c7..693b09e6c1 100644 --- a/Src/FwResources/ResourceHelper.cs +++ b/Src/FwResources/ResourceHelper.cs @@ -34,6 +34,8 @@ public enum FileFilterType AllScriptureStandardFormat, /// *.xml XML, + /// Phonology XML (*.xml) + PhonologyXML, /// *.rtf RichTextFormat, /// *.pdf @@ -133,6 +135,7 @@ static ResourceHelper() s_fileFilterExtensions[FileFilterType.DefaultStandardFormat] = "*.sf"; s_fileFilterExtensions[FileFilterType.AllScriptureStandardFormat] = "*.db; *.sf; *.sfm; *.txt"; s_fileFilterExtensions[FileFilterType.XML] = "*.xml"; + s_fileFilterExtensions[FileFilterType.PhonologyXML] = "*.xml"; s_fileFilterExtensions[FileFilterType.RichTextFormat] = "*.rtf"; s_fileFilterExtensions[FileFilterType.PDF] = "*.pdf"; s_fileFilterExtensions[FileFilterType.OXES] = "*" + FwFileExtensions.ksOpenXmlForEditingScripture; diff --git a/Src/xWorks/FwXWindow.cs b/Src/xWorks/FwXWindow.cs index 7a3307595d..3558d208bf 100644 --- a/Src/xWorks/FwXWindow.cs +++ b/Src/xWorks/FwXWindow.cs @@ -1938,10 +1938,10 @@ public bool OnImportPhonology(object commandObject) { dlg.CheckFileExists = true; dlg.RestoreDirectory = true; - dlg.Title = ResourceHelper.GetResourceString("kstidXML"); + dlg.Title = ResourceHelper.GetResourceString("kstidPhonologyXML"); dlg.ValidateNames = true; dlg.Multiselect = false; - dlg.Filter = ResourceHelper.FileFilter(FileFilterType.XML); + dlg.Filter = ResourceHelper.FileFilter(FileFilterType.PhonologyXML); if (dlg.ShowDialog(form) != DialogResult.OK) return true; filename = dlg.FileName; From b055914ae0cafc6fe4d4cff925abf5968470d458 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Tue, 27 Aug 2024 10:24:55 -0700 Subject: [PATCH 2/2] Add menu items for moving fields up and down in the interface (#127) * Add menu items and handlers for move up/down for each field slice * Also clean up some obsolete code Co-authored-by: Jake Oliver --- .../Configuration/CommonDataTreeInclude.xml | 11 ++ .../Controls/DetailControls/ButtonLauncher.cs | 7 +- .../Controls/DetailControls/DataTree.cs | 13 +- Src/Common/Controls/DetailControls/Slice.cs | 182 +++++++++++++++--- .../Controls/DetailControls/SliceTreeNode.cs | 13 +- 5 files changed, 174 insertions(+), 52 deletions(-) diff --git a/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml b/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml index e412f6c952..85d7b0c2b6 100644 --- a/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml +++ b/DistFiles/Language Explorer/Configuration/CommonDataTreeInclude.xml @@ -5,6 +5,9 @@ + + + @@ -15,6 +18,10 @@ + + + + @@ -31,6 +38,10 @@ behavior="singlePropertySequenceValue" property="SelectedWritingSystemHvosForCurrentContextMenu" defaultPropertyValue=""/> + + + + diff --git a/Src/Common/Controls/DetailControls/ButtonLauncher.cs b/Src/Common/Controls/DetailControls/ButtonLauncher.cs index b8eed5faa8..5d4ee46f45 100644 --- a/Src/Common/Controls/DetailControls/ButtonLauncher.cs +++ b/Src/Common/Controls/DetailControls/ButtonLauncher.cs @@ -47,12 +47,7 @@ protected Slice Slice { get { - // Depending on compile switch for SLICE_IS_SPLITCONTAINER, - // grandParent will be both a Slice and a SplitContainer - // (Slice is a subclass of SplitContainer), - // or just a SplitContainer (SplitContainer is the only child Control of a Slice). - // If grandParent is not a Slice, then we have to move up to the great-grandparent - // to find the Slice. + // Return the Slice parent of this button, even if the button buried in other controls Control parent = Parent; while (!(parent is Slice)) parent = parent.Parent; diff --git a/Src/Common/Controls/DetailControls/DataTree.cs b/Src/Common/Controls/DetailControls/DataTree.cs index ffa37eceb0..23c034831f 100644 --- a/Src/Common/Controls/DetailControls/DataTree.cs +++ b/Src/Common/Controls/DetailControls/DataTree.cs @@ -54,6 +54,11 @@ namespace SIL.FieldWorks.Common.Framework.DetailControls /// System.Windows.Forms.UserControl public class DataTree : UserControl, IVwNotifyChange, IxCoreColleague, IRefreshableRoot { + /// + /// Part refs that don't represent actual data slices + /// + public static string[] SpecialPartRefs = { "ChangeHandler", "_CustomFieldPlaceholder" }; + /// /// Occurs when the current slice changes /// @@ -294,13 +299,9 @@ void slice_SplitterMoved(object sender, SplitterEventArgs e) if (m_currentSlice == null) return; // Too early to do much; - // Depending on compile switch for SLICE_IS_SPLITCONTAINER, - // the sender will be both a Slice and a SplitContainer - // (Slice is a subclass of SplitContainer), - // or just a SplitContainer (SplitContainer is the only child Control of a Slice). - Slice movedSlice = sender is Slice ? (Slice) sender + var movedSlice = sender is Slice slice ? slice // sender is also a SplitContainer. - : (Slice) ((SplitContainer) sender).Parent; // Have to move up one parent notch to get to teh Slice. + : (Slice) ((SplitContainer) sender).Parent; // Review: This branch is probably obsolete. if (m_currentSlice != movedSlice) return; // Too early to do much; diff --git a/Src/Common/Controls/DetailControls/Slice.cs b/Src/Common/Controls/DetailControls/Slice.cs index 27cf955e4a..a5a1c73198 100644 --- a/Src/Common/Controls/DetailControls/Slice.cs +++ b/Src/Common/Controls/DetailControls/Slice.cs @@ -28,6 +28,11 @@ namespace SIL.FieldWorks.Common.Framework.DetailControls { + enum Direction + { + Up, Down + } + /// /// A Slice is essentially one row of a tree. /// It contains both a SliceTreeNode on the left of the splitter line, and a @@ -37,22 +42,8 @@ namespace SIL.FieldWorks.Common.Framework.DetailControls /// within the tree for this item, knowing whether the item can be expanded, /// and optionally drawing the part of the tree that is opposite the item, and /// many other things.} -#if SLICE_IS_SPLITCONTAINER - /// The problem I (RandyR) ran into with this is when the DataTree scrolled and reset the Top of the slice, - /// the internal SplitterRectangle ended up being non-0 in many cases, - /// which resulted in the splitter not be in the right place (visible) - /// The MS docs say in a vertical orientation like this, the 'Y" - /// value of SplitterRectangle will always be 0. - /// I don't know if it is a bug in the MS code or in our code that lets it be non-0, - /// but I worked with it quite a while without finding the true problem. - /// So, I went back to a Slice having a SplitContainer, - /// rather than the better option of it being a SplitContainer. - /// - public class Slice : SplitContainer, IxCoreColleague -#else /// public class Slice : UserControl, IxCoreColleague -#endif { #region Constants @@ -225,11 +216,7 @@ protected internal SplitContainer SplitCont { CheckDisposed(); -#if SLICE_IS_SPLITCONTAINER - return this; -#else return Controls[0] as SplitContainer; -#endif } } @@ -468,9 +455,6 @@ public ImageCollection SmallImages /// public Slice() { -#if SLICE_IS_SPLITCONTAINER - TabStop = false; -#else // Create a SplitContainer to hold the two (or one control. m_splitter = new SplitContainer {TabStop = false, AccessibleName = "Slice.SplitContainer"}; // Do this once right away, mainly so child controls like check box that don't control @@ -478,7 +462,6 @@ public Slice() // until our own size is definitely established by SetWidthForDataTreeLayout. m_splitter.Size = Size; Controls.Add(m_splitter); -#endif // This is really important. Since some slices are invisible, all must be, // or Show() will reorder them. Visible = false; @@ -528,7 +511,7 @@ public virtual void RegisterWithContextHelper() { CheckDisposed(); - if (Control != null)//grouping nodes do not have a control + if (Control != null) //grouping nodes do not have a control { //It's OK to send null as an id if (m_mediator != null) // helpful for robustness and testing. @@ -2801,6 +2784,70 @@ internal protected virtual bool UpdateDisplayIfNeeded(int hvo, int tag) return false; } + private void MoveField(Direction dir) + { + CheckDisposed(); + if (ContainingDataTree.ShowingAllFields) + { + XmlNode swapWith; + XmlNode fieldRef = FieldReferenceForSlice(); + + if (fieldRef == null) + { + Debug.Fail("Could not identify field to move on slice."); + return; + } + + if (dir == Direction.Up) + { + swapWith = PrevPartSibling(fieldRef); + } + else + { + swapWith = NextPartSibling(fieldRef.NextSibling); + } + + var parent = fieldRef.ParentNode; + // Reorder in the parent node in the xml + if (parent != null) + { + parent.RemoveChild(fieldRef); + if (dir == Direction.Up) + parent.InsertBefore(fieldRef, swapWith); + else + parent.InsertAfter(fieldRef, swapWith); + } + + // Persist in the parent part (might not be the immediate parent node) + Inventory.GetInventory("layouts", m_cache.ProjectId.Name) + .PersistOverrideElement(PartParent(fieldRef)); + ContainingDataTree.RefreshList(true); + } + } + + /// + /// Find the last part ref in the Key which represents the part of the data and configuration for this slice. + /// This is built up in DataTree with the path to the part in the combined layout and parts configuration files. + /// There may be other part refs in the path if this slice represents a subfield. + /// + private XmlNode FieldReferenceForSlice() + { + XmlNode fieldRef = null; + foreach (object obj in Key) + { + var node = obj as XmlNode; + if (node == null || node.Name != "part" || + XmlUtils.GetOptionalAttributeValue(node, "ref", null) == null) + { + continue; + } + + fieldRef = node; + } + + return fieldRef; + } + protected void SetFieldVisibility(string visibility) { CheckDisposed(); @@ -2907,15 +2954,90 @@ protected bool IsVisibilityItemChecked(string visibility) { CheckDisposed(); - XmlNode lastPartRef = null; - foreach (object obj in Key) + var lastPartRef = FieldReferenceForSlice(); + + return lastPartRef != null && + XmlUtils.GetOptionalAttributeValue(lastPartRef, "visibility", "always") == + visibility; + } + + private bool CheckValidMove(UIItemDisplayProperties display, Direction dir) + { + XmlNode lastPartRef = FieldReferenceForSlice(); + + if (lastPartRef == null) + return false; + return dir == Direction.Up + ? PrevPartSibling(lastPartRef) != null + : NextPartSibling(lastPartRef) != null; + } + + private XmlNode PrevPartSibling(XmlNode partRef) + { + XmlNode prev = partRef.PreviousSibling; + while (prev != null && (prev.NodeType != XmlNodeType.Element || prev.Name != "part" || + XmlUtils.GetOptionalAttributeValue(prev, "ref", null) == null || + DataTree.SpecialPartRefs.Contains(XmlUtils.GetOptionalAttributeValue(prev, "ref")))) { - var node = obj as XmlNode; - if (node == null || node.Name != "part" || XmlUtils.GetOptionalAttributeValue(node, "ref", null) == null) - continue; - lastPartRef = node; + prev = prev.PreviousSibling; } - return lastPartRef != null && XmlUtils.GetOptionalAttributeValue(lastPartRef, "visibility", "always") == visibility; + return prev; + } + + private XmlNode NextPartSibling(XmlNode partRef) + { + XmlNode next = partRef.NextSibling; + while (next != null && (next.NodeType != XmlNodeType.Element || next.Name != "part" || + XmlUtils.GetOptionalAttributeValue(next, "ref", null) == null)) + { + next = next.NextSibling; + } + return next; + } + + private XmlNode PartParent(XmlNode partRef) + { + XmlNode parent = partRef.ParentNode; + while (parent != null && (parent.NodeType != XmlNodeType.Element || (parent.Name != "part" && parent.Name != "layout"))) + { + parent = parent.ParentNode; + } + if(parent == null) + throw new ConfigurationException("Could not find parent part node", m_configurationNode); + return parent; + } + + /// + public bool OnDisplayMoveFieldUp(object args, ref UIItemDisplayProperties display) + { + CheckDisposed(); + display.Enabled = ContainingDataTree.ShowingAllFields && CheckValidMove(display, Direction.Up); + + return true; + } + + /// + public bool OnDisplayMoveFieldDown(object args, ref UIItemDisplayProperties display) + { + CheckDisposed(); + display.Enabled = ContainingDataTree.ShowingAllFields && CheckValidMove(display, Direction.Down); + return true; + } + + /// + public bool OnMoveFieldUp(object args) + { + CheckDisposed(); + MoveField(Direction.Up); + return true; + } + + /// + public bool OnMoveFieldDown(object args) + { + CheckDisposed(); + MoveField(Direction.Down); + return true; } /// diff --git a/Src/Common/Controls/DetailControls/SliceTreeNode.cs b/Src/Common/Controls/DetailControls/SliceTreeNode.cs index 5d9e6efbf9..f614bf9d5e 100644 --- a/Src/Common/Controls/DetailControls/SliceTreeNode.cs +++ b/Src/Common/Controls/DetailControls/SliceTreeNode.cs @@ -42,9 +42,7 @@ public class SliceTreeNode : UserControl internal const int kdxpLeftMargin = 2; // Gap at the far left of everything. #endregion - protected bool m_inMenuButton = false; - - private bool m_fShowPlusMinus = false; + private bool m_fShowPlusMinus; /// /// Required designer variable. /// @@ -57,13 +55,8 @@ public Slice Slice { CheckDisposed(); - // Depending on compile switch for SLICE_IS_SPLITCONTAINER, - // grandParent will be both a Slice and a SplitContainer - // (Slice is a subclass of SplitContainer), - // or just a SplitContainer (SplitContainer is the only child Control of a Slice). - // If grandParent is not a Slice, then we have to move up to the great-grandparent - // to find the Slice. - Control parent = Parent; + // Return the Slice parent of this button, even if the button buried in other controls + var parent = Parent; while (!(parent is Slice)) parent = parent.Parent;