Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion Src/Common/Controls/XMLViews/BrowseViewer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Security;
using System.Text;
using System.Windows.Forms;
using System.Xml;
Expand Down Expand Up @@ -2968,7 +2969,7 @@ protected void InsertColumn(XmlNode colSpec, int position)

// Note: often we also want to update LayoutCache.LayoutVersionNumber.
// (last updated by Ariel Rorabaugh, Oct 28, 2025, for adding PictureLicense column to Browse view)
internal const int kBrowseViewVersion = 19;
internal const int kBrowseViewVersion = 20;

/// <summary>
/// Column has been added or removed, update all child windows.
Expand Down Expand Up @@ -3042,6 +3043,24 @@ protected void UpdateColumnList()
colList.Append(node.OuterXml);
}
}
// Save labels of hidden columns so we can distinguish "removed by user" from "new column" on load.
// Use SortedSet with Ordinal comparison for deterministic output (stable for Send/Receive).
var activeLabels = new HashSet<string>();
foreach (XmlNode node in ColumnSpecs)
{
var label = XmlUtils.GetOptionalAttributeValue(node, "label", "");
if (!string.IsNullOrEmpty(label))
activeLabels.Add(label);
}
var hiddenLabels = new SortedSet<string>(StringComparer.Ordinal);
foreach (XmlNode node in m_xbv.Vc.PossibleColumnSpecs)
{
var label = XmlUtils.GetOptionalAttributeValue(node, "label", "");
if (!string.IsNullOrEmpty(label) && !activeLabels.Contains(label))
hiddenLabels.Add(label);
}
foreach (var label in hiddenLabels)
colList.Append("<hidden label=\"" + SecurityElement.Escape(label) + "\"/>");
colList.Append("</root>");
m_propertyTable.SetProperty(m_xbv.Vc.ColListId, colList.ToString(), PropertyTable.SettingsGroup.LocalSettings, true);
}
Expand Down
41 changes: 41 additions & 0 deletions Src/Common/Controls/XMLViews/XMLViewsTests/TestXmlBrowseView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,47 @@ public void MigrateBrowseColumns()
}
}

[Test]
public void MigrateBrowseColumns_Version19ToVersion20()
{
var input =
"<root version=\"19\">" +
"<column layout=\"EntryHeadwordForFindEntry\" label=\"Headword\" sortmethod=\"FullSortKey\" ws=\"$ws=vernacular\" editable=\"false\" width=\"96000\"/>" +
"<column layout=\"Unknown Test\"/>" +
"</root>";
using (var mediator = new Mediator())
using (var propertyTable = new PropertyTable(mediator))
{
var output = XmlBrowseViewBaseVc.GetSavedColumns(input, mediator, propertyTable, "myKey");
Assert.That(XmlUtils.GetOptionalAttributeValue(output.DocumentElement, "version"), Is.EqualTo(BrowseViewer.kBrowseViewVersion.ToString()));
Assert.That(XmlUtils.GetOptionalAttributeValue(output.DocumentElement, "version"), Is.EqualTo("20"));
// Columns should be preserved as-is
var headwordNode = output.SelectSingleNode("//column[@label='Headword']");
Assert.That(headwordNode, Is.Not.Null);
Assert.That(XmlUtils.GetOptionalAttributeValue(headwordNode, "layout"), Is.EqualTo("EntryHeadwordForFindEntry"));
}
}

[Test]
public void MigrateBrowseColumns_HiddenElementsPreservedThroughMigration()
{
// Version 19 with hidden elements (simulating a future scenario where hidden elements exist)
var input =
"<root version=\"19\">" +
"<column layout=\"EntryHeadwordForFindEntry\" label=\"Headword\"/>" +
"<hidden label=\"Lexeme Form\"/>" +
"</root>";
using (var mediator = new Mediator())
using (var propertyTable = new PropertyTable(mediator))
{
var output = XmlBrowseViewBaseVc.GetSavedColumns(input, mediator, propertyTable, "myKey");
Assert.That(XmlUtils.GetOptionalAttributeValue(output.DocumentElement, "version"), Is.EqualTo("20"));
// Hidden elements should be preserved
var hiddenNode = output.SelectSingleNode("//hidden[@label='Lexeme Form']");
Assert.That(hiddenNode, Is.Not.Null, "Hidden column labels should be preserved through migration");
}
}

void VerifyColumn(XmlNode output, string layoutName, string attrName, string attrVal)
{
var node = output.SelectSingleNode("//column[@layout='" + layoutName + "']");
Expand Down
31 changes: 29 additions & 2 deletions Src/Common/Controls/XMLViews/XmlBrowseViewBaseVc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,34 @@ public XmlBrowseViewBaseVc(XmlNode xnSpec, int fakeFlid, XmlBrowseViewBase xbv)
ColumnSpecs.Add(node);
}

// Collect labels of columns the user deliberately removed (hidden).
var hiddenNodes = doc.DocumentElement.SelectNodes("//hidden");
var hiddenLabels = new HashSet<string>();
bool hasHiddenTracking = hiddenNodes.Count > 0;
if (hasHiddenTracking)
{
foreach (XmlNode hidden in hiddenNodes)
{
var label = XmlUtils.GetOptionalAttributeValue(hidden, "label", "");
if (!string.IsNullOrEmpty(label))
hiddenLabels.Add(label);
}
}

foreach (var node in newPossibleColumns)
{
// add any possible columns that were not in the saved list and are common (and not custom)
if (!IsCustomField(node, out _) && XmlUtils.GetOptionalAttributeValue(node, "common", "false") == "true")
if (!hasHiddenTracking)
{
// Bootstrap: no hidden tracking yet (pre-upgrade save).
// Don't auto-add anything — all missing columns are presumed
// deliberately removed. They'll be properly tracked on next save.
continue;
}
// Add common non-custom columns that were not in the saved list and not deliberately hidden.
var label = XmlUtils.GetOptionalAttributeValue(node, "label", "");
if (!hiddenLabels.Contains(label)
&& !IsCustomField(node, out _)
&& XmlUtils.GetOptionalAttributeValue(node, "common", "false") == "true")
{
ColumnSpecs.Add(node);
}
Expand Down Expand Up @@ -299,6 +323,9 @@ internal static XmlDocument GetSavedColumns(string savedCols, Mediator mediator,
case 18:
savedCols = FixVersion19Columns(savedCols);
savedCols = savedCols.Replace("root version=\"18\"", "root version=\"19\"");
goto case 19;
case 19:
savedCols = savedCols.Replace("root version=\"19\"", "root version=\"20\"");
propertyTable.SetProperty(colListId, savedCols, true);
doc.LoadXml(savedCols);
break;
Expand Down
Loading