From 87d56dd334ce86e38d29389015b546274391a24b Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Fri, 20 Feb 2026 10:44:45 -0800 Subject: [PATCH 1/3] Fix LT-22367: FW crashes after closing a new FW window --- Src/LexText/Interlinear/InterAreaBookmark.cs | 7 ++++++- Src/LexText/Interlinear/InterlinMaster.cs | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Src/LexText/Interlinear/InterAreaBookmark.cs b/Src/LexText/Interlinear/InterAreaBookmark.cs index 330d0dc8c5..b0c72dc774 100644 --- a/Src/LexText/Interlinear/InterAreaBookmark.cs +++ b/Src/LexText/Interlinear/InterAreaBookmark.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2015 SIL International +// Copyright (c) 2015 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) @@ -45,6 +45,11 @@ internal void Init(InterlinMaster interlinMaster, LcmCache cache, PropertyTable m_propertyTable = propertyTable; } + public bool IsPropertyTableDisposed() + { + return m_propertyTable.IsDisposed; + } + /// /// Saves the given AnalysisOccurrence in the InterlinMaster. /// diff --git a/Src/LexText/Interlinear/InterlinMaster.cs b/Src/LexText/Interlinear/InterlinMaster.cs index ceb02d2617..b6d82faa82 100644 --- a/Src/LexText/Interlinear/InterlinMaster.cs +++ b/Src/LexText/Interlinear/InterlinMaster.cs @@ -342,6 +342,7 @@ internal void SaveBookMark() if (!fSaved) { + RemoveStaleBookmarks(); InterAreaBookmark mark; if(m_bookmarks.TryGetValue(new Tuple(CurrentTool, RootStText.Guid), out mark)) { @@ -357,6 +358,24 @@ internal void SaveBookMark() } } + private void RemoveStaleBookmarks() + { + // Remove stale bookmarks that came from a Window > New Window being closed. + IList> staleKeys = new List>(); + foreach (var key in m_bookmarks.Keys) + { + InterAreaBookmark mark = m_bookmarks[key]; + if (mark.IsPropertyTableDisposed()) + { + staleKeys.Add(key); + } + } + foreach (var key in staleKeys) + { + m_bookmarks.Remove(key); + } + } + /// /// Returns true if it already saved a bookmark (from RootBox), false otherwise. /// From 68d043b1a4afc9278be05c6e2b9a60c9bd818539 Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Fri, 20 Feb 2026 11:04:10 -0800 Subject: [PATCH 2/3] Call RemoveStaleBookmarks before accessing bookmarks --- Src/LexText/Discourse/ConstituentChart.cs | 1 + Src/LexText/Interlinear/InterlinMaster.cs | 8 +++++++- Src/LexText/Interlinear/RawTextPane.cs | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Src/LexText/Discourse/ConstituentChart.cs b/Src/LexText/Discourse/ConstituentChart.cs index 6aeaa6287f..30646d420d 100644 --- a/Src/LexText/Discourse/ConstituentChart.cs +++ b/Src/LexText/Discourse/ConstituentChart.cs @@ -1064,6 +1064,7 @@ private static InterAreaBookmark GetAncestorBookmark(Control curLevelControl, IS if (myParent is InterlinMaster) { string tool = (myParent as InterlinMaster).CurrentTool; + InterlinMaster.RemoveStaleBookmarks(); return InterlinMaster.m_bookmarks[new Tuple(tool, basedOnRa.Guid)]; } return GetAncestorBookmark(myParent as Control, basedOnRa); diff --git a/Src/LexText/Interlinear/InterlinMaster.cs b/Src/LexText/Interlinear/InterlinMaster.cs index b6d82faa82..04e1fe9490 100644 --- a/Src/LexText/Interlinear/InterlinMaster.cs +++ b/Src/LexText/Interlinear/InterlinMaster.cs @@ -358,7 +358,7 @@ internal void SaveBookMark() } } - private void RemoveStaleBookmarks() + public static void RemoveStaleBookmarks() { // Remove stale bookmarks that came from a Window > New Window being closed. IList> staleKeys = new List>(); @@ -448,6 +448,7 @@ private bool SaveBookmarkFromRootBox(IVwRootBox rb) { //if there is a bookmark for this text with this tool, then save it, if not some logic error brought us here, //but simply not saving a bookmark which doesn't exist seems better than crashing. naylor 3/2012 + RemoveStaleBookmarks(); var key = new Tuple(CurrentTool, RootStText.Guid); if (m_bookmarks.ContainsKey(key)) { @@ -748,6 +749,7 @@ public override void Init(Mediator mediator, PropertyTable propertyTable, XmlNod } if (m_bookmarks != null && m_bookmarks.Count > 0) { + RemoveStaleBookmarks(); foreach (InterAreaBookmark bookmark in m_bookmarks.Values) { bookmark.Init(this, Cache, propertyTable); @@ -1030,6 +1032,7 @@ private int SetConcordanceBookmarkAndReturnRoot(int hvoRoot) Cache.ServiceLocator.ObjectRepository.TryGetObject(hvoRoot, out text); if (!m_fRefreshOccurred && m_bookmarks != null && text != null) { + RemoveStaleBookmarks(); InterAreaBookmark mark; if (!m_bookmarks.TryGetValue(new Tuple(CurrentTool, text.Guid), out mark)) { @@ -1051,6 +1054,7 @@ private void CreateOrRestoreBookmark(IStText stText) { if (stText != null) { + RemoveStaleBookmarks(); InterAreaBookmark mark; if (m_bookmarks.TryGetValue(new Tuple(CurrentTool, stText.Guid), out mark)) mark.Restore(IndexOfTextRecord); @@ -1088,6 +1092,7 @@ private void SelectAnnotation() if (Clerk.CurrentObjectHvo == 0 || Clerk.SuspendLoadingRecordUntilOnJumpToRecord) return; // Use a bookmark, if we've set one. + RemoveStaleBookmarks(); if ((RootStText != null && ((ICmObject)RootStText).IsValidObject) && m_bookmarks.ContainsKey(new Tuple(CurrentTool, RootStText.Guid)) && m_bookmarks[new Tuple(CurrentTool, RootStText.Guid)].IndexOfParagraph >= 0 && CurrentInterlinearTabControl is IHandleBookmark) { @@ -1468,6 +1473,7 @@ private void m_tabCtrl_Deselecting(object sender, TabControlCancelEventArgs e) { //At this point m_tabCtrl.SelectedIndex is set to the value of the tabPage //we are leaving. + RemoveStaleBookmarks(); if (RootStText != null && m_bookmarks.ContainsKey(new Tuple(CurrentTool, RootStText.Guid))) SaveBookMark(); } diff --git a/Src/LexText/Interlinear/RawTextPane.cs b/Src/LexText/Interlinear/RawTextPane.cs index 6149f00abe..90af3324ad 100644 --- a/Src/LexText/Interlinear/RawTextPane.cs +++ b/Src/LexText/Interlinear/RawTextPane.cs @@ -690,6 +690,7 @@ private void RawTextPane_VisibleChanged(object sender, EventArgs e) { if (CanFocus) { + InterlinMaster.RemoveStaleBookmarks(); var bookmark = InterlinMaster.m_bookmarks[new Tuple(CurrentTool, RootObject.Guid)]; MakeTextSelectionAndScrollToView(bookmark.BeginCharOffset, bookmark.EndCharOffset, 0, bookmark.IndexOfParagraph); From 6b302a716011ca3278fcb2918ee4baf776837100 Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Fri, 20 Feb 2026 13:40:29 -0800 Subject: [PATCH 3/3] Make excellent changes recommended by Hasso --- Src/LexText/Discourse/ConstituentChart.cs | 3 +- Src/LexText/Interlinear/InterAreaBookmark.cs | 5 +- Src/LexText/Interlinear/InterlinMaster.cs | 83 +++++++++++--------- Src/LexText/Interlinear/RawTextPane.cs | 3 +- 4 files changed, 47 insertions(+), 47 deletions(-) diff --git a/Src/LexText/Discourse/ConstituentChart.cs b/Src/LexText/Discourse/ConstituentChart.cs index 30646d420d..483fabc5c9 100644 --- a/Src/LexText/Discourse/ConstituentChart.cs +++ b/Src/LexText/Discourse/ConstituentChart.cs @@ -1064,8 +1064,7 @@ private static InterAreaBookmark GetAncestorBookmark(Control curLevelControl, IS if (myParent is InterlinMaster) { string tool = (myParent as InterlinMaster).CurrentTool; - InterlinMaster.RemoveStaleBookmarks(); - return InterlinMaster.m_bookmarks[new Tuple(tool, basedOnRa.Guid)]; + return InterlinMaster.Bookmarks[new Tuple(tool, basedOnRa.Guid)]; } return GetAncestorBookmark(myParent as Control, basedOnRa); } diff --git a/Src/LexText/Interlinear/InterAreaBookmark.cs b/Src/LexText/Interlinear/InterAreaBookmark.cs index b0c72dc774..6fe07aa02f 100644 --- a/Src/LexText/Interlinear/InterAreaBookmark.cs +++ b/Src/LexText/Interlinear/InterAreaBookmark.cs @@ -45,10 +45,7 @@ internal void Init(InterlinMaster interlinMaster, LcmCache cache, PropertyTable m_propertyTable = propertyTable; } - public bool IsPropertyTableDisposed() - { - return m_propertyTable.IsDisposed; - } + public bool IsPropertyTableDisposed => m_propertyTable.IsDisposed; /// /// Saves the given AnalysisOccurrence in the InterlinMaster. diff --git a/Src/LexText/Interlinear/InterlinMaster.cs b/Src/LexText/Interlinear/InterlinMaster.cs index 04e1fe9490..6c44d978c3 100644 --- a/Src/LexText/Interlinear/InterlinMaster.cs +++ b/Src/LexText/Interlinear/InterlinMaster.cs @@ -62,6 +62,18 @@ public string CurrentTool get { return m_currentTool; } } + public static Dictionary, InterAreaBookmark> Bookmarks + { + get { + if (m_bookmarks == null) + { + m_bookmarks = new Dictionary, InterAreaBookmark>(); + } + RemoveStaleBookmarks(); + return m_bookmarks; + } + } + /// /// Numbers identifying the main tabs in the interlinear text. /// @@ -96,6 +108,24 @@ internal string BookmarkId get { return m_vectorName ?? ""; } } + private static void RemoveStaleBookmarks() + { + // Remove stale bookmarks that came from a Window > New Window being closed. + IList> staleKeys = new List>(); + foreach (var key in m_bookmarks.Keys) + { + InterAreaBookmark mark = m_bookmarks[key]; + if (mark.IsPropertyTableDisposed) + { + staleKeys.Add(key); + } + } + foreach (var key in staleKeys) + { + m_bookmarks.Remove(key); + } + } + /// /// Something sometimes insists on giving the tab control focus when switching tabs. /// This defeats ctrl-tab to move between tabs. @@ -342,9 +372,8 @@ internal void SaveBookMark() if (!fSaved) { - RemoveStaleBookmarks(); InterAreaBookmark mark; - if(m_bookmarks.TryGetValue(new Tuple(CurrentTool, RootStText.Guid), out mark)) + if(Bookmarks.TryGetValue(new Tuple(CurrentTool, RootStText.Guid), out mark)) { //We only want to persist the save if we are in the interlinear edit, not the concordance view mark.Save(curAnalysis, CurrentTool.Equals("interlinearTexts"), IndexOfTextRecord); @@ -353,27 +382,9 @@ internal void SaveBookMark() { mark = new InterAreaBookmark(this, Cache, m_propertyTable); mark.Restore(IndexOfTextRecord); - m_bookmarks.Add(new Tuple(CurrentTool, RootStText.Guid), mark); - } - } - } - - public static void RemoveStaleBookmarks() - { - // Remove stale bookmarks that came from a Window > New Window being closed. - IList> staleKeys = new List>(); - foreach (var key in m_bookmarks.Keys) - { - InterAreaBookmark mark = m_bookmarks[key]; - if (mark.IsPropertyTableDisposed()) - { - staleKeys.Add(key); + Bookmarks.Add(new Tuple(CurrentTool, RootStText.Guid), mark); } } - foreach (var key in staleKeys) - { - m_bookmarks.Remove(key); - } } /// @@ -448,11 +459,10 @@ private bool SaveBookmarkFromRootBox(IVwRootBox rb) { //if there is a bookmark for this text with this tool, then save it, if not some logic error brought us here, //but simply not saving a bookmark which doesn't exist seems better than crashing. naylor 3/2012 - RemoveStaleBookmarks(); var key = new Tuple(CurrentTool, RootStText.Guid); - if (m_bookmarks.ContainsKey(key)) + if (Bookmarks.ContainsKey(key)) { - m_bookmarks[key].Save(IndexOfTextRecord, iPara, Math.Min(ichAnchor, ichEnd), Math.Max(ichAnchor, ichEnd), true); + Bookmarks[key].Save(IndexOfTextRecord, iPara, Math.Min(ichAnchor, ichEnd), Math.Max(ichAnchor, ichEnd), true); } return true; @@ -747,10 +757,9 @@ public override void Init(Mediator mediator, PropertyTable propertyTable, XmlNod m_tcPane.StyleSheet = m_styleSheet; m_tcPane.Visible = true; } - if (m_bookmarks != null && m_bookmarks.Count > 0) + if (Bookmarks.Count > 0) { - RemoveStaleBookmarks(); - foreach (InterAreaBookmark bookmark in m_bookmarks.Values) + foreach (InterAreaBookmark bookmark in Bookmarks.Values) { bookmark.Init(this, Cache, propertyTable); } @@ -1032,12 +1041,11 @@ private int SetConcordanceBookmarkAndReturnRoot(int hvoRoot) Cache.ServiceLocator.ObjectRepository.TryGetObject(hvoRoot, out text); if (!m_fRefreshOccurred && m_bookmarks != null && text != null) { - RemoveStaleBookmarks(); InterAreaBookmark mark; - if (!m_bookmarks.TryGetValue(new Tuple(CurrentTool, text.Guid), out mark)) + if (!Bookmarks.TryGetValue(new Tuple(CurrentTool, text.Guid), out mark)) { mark = new InterAreaBookmark(this, Cache, m_propertyTable); - m_bookmarks.Add(new Tuple(CurrentTool, text.Guid), mark); + Bookmarks.Add(new Tuple(CurrentTool, text.Guid), mark); } mark.Save(point, false, IndexOfTextRecord); @@ -1054,12 +1062,11 @@ private void CreateOrRestoreBookmark(IStText stText) { if (stText != null) { - RemoveStaleBookmarks(); InterAreaBookmark mark; - if (m_bookmarks.TryGetValue(new Tuple(CurrentTool, stText.Guid), out mark)) + if (Bookmarks.TryGetValue(new Tuple(CurrentTool, stText.Guid), out mark)) mark.Restore(IndexOfTextRecord); else - m_bookmarks.Add(new Tuple(CurrentTool, stText.Guid), new InterAreaBookmark(this, Cache, m_propertyTable)); + Bookmarks.Add(new Tuple(CurrentTool, stText.Guid), new InterAreaBookmark(this, Cache, m_propertyTable)); } } @@ -1092,11 +1099,10 @@ private void SelectAnnotation() if (Clerk.CurrentObjectHvo == 0 || Clerk.SuspendLoadingRecordUntilOnJumpToRecord) return; // Use a bookmark, if we've set one. - RemoveStaleBookmarks(); - if ((RootStText != null && ((ICmObject)RootStText).IsValidObject) && m_bookmarks.ContainsKey(new Tuple(CurrentTool, RootStText.Guid)) && - m_bookmarks[new Tuple(CurrentTool, RootStText.Guid)].IndexOfParagraph >= 0 && CurrentInterlinearTabControl is IHandleBookmark) + if ((RootStText != null && ((ICmObject)RootStText).IsValidObject) && Bookmarks.ContainsKey(new Tuple(CurrentTool, RootStText.Guid)) && + Bookmarks[new Tuple(CurrentTool, RootStText.Guid)].IndexOfParagraph >= 0 && CurrentInterlinearTabControl is IHandleBookmark) { - (CurrentInterlinearTabControl as IHandleBookmark).SelectBookmark(m_bookmarks[new Tuple(CurrentTool, RootStText.Guid)]); + (CurrentInterlinearTabControl as IHandleBookmark).SelectBookmark(Bookmarks[new Tuple(CurrentTool, RootStText.Guid)]); } } @@ -1473,8 +1479,7 @@ private void m_tabCtrl_Deselecting(object sender, TabControlCancelEventArgs e) { //At this point m_tabCtrl.SelectedIndex is set to the value of the tabPage //we are leaving. - RemoveStaleBookmarks(); - if (RootStText != null && m_bookmarks.ContainsKey(new Tuple(CurrentTool, RootStText.Guid))) + if (RootStText != null && Bookmarks.ContainsKey(new Tuple(CurrentTool, RootStText.Guid))) SaveBookMark(); } } diff --git a/Src/LexText/Interlinear/RawTextPane.cs b/Src/LexText/Interlinear/RawTextPane.cs index 90af3324ad..5ca65cc9b2 100644 --- a/Src/LexText/Interlinear/RawTextPane.cs +++ b/Src/LexText/Interlinear/RawTextPane.cs @@ -690,8 +690,7 @@ private void RawTextPane_VisibleChanged(object sender, EventArgs e) { if (CanFocus) { - InterlinMaster.RemoveStaleBookmarks(); - var bookmark = InterlinMaster.m_bookmarks[new Tuple(CurrentTool, RootObject.Guid)]; + var bookmark = InterlinMaster.Bookmarks[new Tuple(CurrentTool, RootObject.Guid)]; MakeTextSelectionAndScrollToView(bookmark.BeginCharOffset, bookmark.EndCharOffset, 0, bookmark.IndexOfParagraph); VisibleChanged -= RawTextPane_VisibleChanged;