From 343ecb92d0bcf371d8deb14b1610bf782606fc95 Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Fri, 15 May 2026 11:43:16 -0700 Subject: [PATCH 1/2] Fix LT-13362: Allow sorting in the Choose Natural Class dialog --- .../Grammar/areaConfiguration.xml | 2 +- Src/Common/Controls/XMLViews/FlatListView.cs | 44 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/DistFiles/Language Explorer/Configuration/Grammar/areaConfiguration.xml b/DistFiles/Language Explorer/Configuration/Grammar/areaConfiguration.xml index 5d5802d030..36a9dc33aa 100644 --- a/DistFiles/Language Explorer/Configuration/Grammar/areaConfiguration.xml +++ b/DistFiles/Language Explorer/Configuration/Grammar/areaConfiguration.xml @@ -177,7 +177,7 @@ - diff --git a/Src/Common/Controls/XMLViews/FlatListView.cs b/Src/Common/Controls/XMLViews/FlatListView.cs index 61272864e7..79b76a69fb 100644 --- a/Src/Common/Controls/XMLViews/FlatListView.cs +++ b/Src/Common/Controls/XMLViews/FlatListView.cs @@ -2,16 +2,18 @@ // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) +using SIL.FieldWorks.Common.FwUtils; +using SIL.FieldWorks.Filters; +using SIL.LCModel; +using SIL.LCModel.Application; +using SIL.LCModel.Core.KernelInterfaces; using System; +using System.Collections; using System.Collections.Generic; using System.Drawing; +using System.Linq; using System.Windows.Forms; using System.Xml; -using System.Linq; -using SIL.LCModel.Core.KernelInterfaces; -using SIL.FieldWorks.Common.FwUtils; -using SIL.LCModel; -using SIL.LCModel.Application; using XCore; namespace SIL.FieldWorks.Common.Controls @@ -41,6 +43,7 @@ public partial class FlatListView : UserControl private XmlNode m_configNode; private BrowseViewer m_bvList; private ObjectListPublisher m_listPublisher; + IEnumerable m_objs; #endregion Data members @@ -79,6 +82,7 @@ public void Initialize(LcmCache cache, IVwStylesheet stylesheet, Mediator mediat m_listPublisher = new ObjectListPublisher(cache.DomainDataByFlid as ISilDataAccessManaged, ObjectListFlid); StoreData(objs); + m_objs = objs; m_bvList = new BrowseViewer(m_configNode, m_cache.LanguageProject.Hvo, ObjectListFlid, m_cache, m_mediator, m_propertyTable, null, m_listPublisher); m_bvList.Location = new Point(0, 0); @@ -90,10 +94,40 @@ public void Initialize(LcmCache cache, IVwStylesheet stylesheet, Mediator mediat m_bvList.StyleSheet = m_stylesheet; m_bvList.Dock = DockStyle.Fill; m_bvList.SelectionChanged += m_bvList_SelectionChanged; + m_bvList.SortersCompatible += m_bvList_AreSortersCompatible; + m_bvList.SorterChanged += m_bvList_SorterChanged; Controls.Add(m_bvList); ResumeLayout(false); } + private bool m_bvList_AreSortersCompatible(RecordSorter first, RecordSorter second) + { + return first.CompatibleSorter(second); + } + + private void m_bvList_SorterChanged(object sender, EventArgs args) + { + using (new WaitCursor(this)) + { + ArrayList itemList = new ArrayList((from obj in m_objs select new ManyOnePathSortItem(obj.Hvo, null, null)).ToArray()); + m_bvList.Sorter.Sort(itemList); + IList objList = (from ManyOnePathSortItem item in itemList select GetObject(item.RootObjectHvo)).ToList(); + StoreData(objList); + } + } + + private ICmObject GetObject(int hvo) + { + foreach (var obj in m_objs) + { + if (obj.Hvo == hvo) + { + return obj; + } + } + return null; + } + /// /// Store the given hvos in the cache as a fake vector property belonging to the /// language project. From 3c8a246edecf510a69b321ddf73b87a57d92d93d Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Mon, 18 May 2026 08:57:52 -0700 Subject: [PATCH 2/2] Add code for filtering columns --- Src/Common/Controls/XMLViews/FlatListView.cs | 88 +++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/Src/Common/Controls/XMLViews/FlatListView.cs b/Src/Common/Controls/XMLViews/FlatListView.cs index 79b76a69fb..fd3a613a7c 100644 --- a/Src/Common/Controls/XMLViews/FlatListView.cs +++ b/Src/Common/Controls/XMLViews/FlatListView.cs @@ -10,6 +10,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.Linq; using System.Windows.Forms; @@ -44,6 +45,7 @@ public partial class FlatListView : UserControl private BrowseViewer m_bvList; private ObjectListPublisher m_listPublisher; IEnumerable m_objs; + RecordFilter m_filter; #endregion Data members @@ -96,6 +98,7 @@ public void Initialize(LcmCache cache, IVwStylesheet stylesheet, Mediator mediat m_bvList.SelectionChanged += m_bvList_SelectionChanged; m_bvList.SortersCompatible += m_bvList_AreSortersCompatible; m_bvList.SorterChanged += m_bvList_SorterChanged; + m_bvList.FilterChanged += m_bvList_FilterChanged; Controls.Add(m_bvList); ResumeLayout(false); } @@ -109,13 +112,18 @@ private void m_bvList_SorterChanged(object sender, EventArgs args) { using (new WaitCursor(this)) { + // Sort m_objs based on the sorter. ArrayList itemList = new ArrayList((from obj in m_objs select new ManyOnePathSortItem(obj.Hvo, null, null)).ToArray()); m_bvList.Sorter.Sort(itemList); - IList objList = (from ManyOnePathSortItem item in itemList select GetObject(item.RootObjectHvo)).ToList(); - StoreData(objList); + m_objs = (from ManyOnePathSortItem item in itemList select GetObject(item.RootObjectHvo)).ToList(); + // Store the filtered data. + StoreData(GetFilteredObjects()); } } + /// + /// Get the object for the given hvo. + /// private ICmObject GetObject(int hvo) { foreach (var obj in m_objs) @@ -128,6 +136,82 @@ private ICmObject GetObject(int hvo) return null; } + private void m_bvList_FilterChanged(object sender, Filters.FilterChangeEventArgs args) + { + // Update the filter. + if (m_filter == null) + { + // Had no filter to begin with + Debug.Assert(args.Removed == null); + m_filter = args.Added is NullFilter ? null : args.Added; + } + else if (m_filter.SameFilter(args.Removed)) + { + // Simplest case: we had just one filter, the one being removed. + // Change filter to whatever (if anything) replaces it. + m_filter = args.Added is NullFilter ? null : args.Added; + } + else if (m_filter is AndFilter) + { + AndFilter af = m_filter as AndFilter; + if (args.Removed != null) + { + af.Remove(args.Removed); + } + if (args.Added != null) + { + //When the user chooses "all records/no filter", the RecordClerk will remove + //its previous filter and add a NullFilter. In that case, we don't really need to add + // that filter. Instead, we can just add nothing. + if (!(args.Added is NullFilter)) + af.Add(args.Added); + } + // Remove AndFilter if we get down to one. + // This is not just an optimization, it allows the last filter to be removed + // leaving empty, so the status bar can show that there is then no filter. + if (af.Filters.Count == 1) + m_filter = af.Filters[0] as RecordFilter; + } + else + { + // m_filter is not an AndFilter, so can't contain the one we're removing, nor IS it the one + // we're removing...so we have no way to remove, and it's an error if we're trying to. + Debug.Assert(args.Removed == null || args.Removed is NullFilter); + if (args.Added != null && !(args.Added is NullFilter)) // presumably true or nothing changed, but for paranoia.. + { + // We already checked for m_filter being null, so we now have two filters, + // and need to make an AndFilter. + AndFilter addFilter = new AndFilter(); + addFilter.Add(m_filter); + addFilter.Add(args.Added); + m_filter = addFilter; + } + } + // Store the filtered data. + StoreData(GetFilteredObjects()); + } + + /// + /// Get the filtered objects from m_objs. + /// + private IEnumerable GetFilteredObjects() + { + if (m_filter == null) + { + return m_objs; + } + ArrayList itemList = new ArrayList((from obj in m_objs select new ManyOnePathSortItem(obj.Hvo, null, null)).ToArray()); + IList objList = new List(); + foreach (ManyOnePathSortItem item in itemList) + { + if (m_filter.Accept(item)) + { + objList.Add(GetObject(item.RootObjectHvo)); + } + } + return objList; + } + /// /// Store the given hvos in the cache as a fake vector property belonging to the /// language project.