diff --git a/Src/LexText/ParserUI/TryAWordDlg.cs b/Src/LexText/ParserUI/TryAWordDlg.cs index 6cc603ee6b..74338f79fe 100644 --- a/Src/LexText/ParserUI/TryAWordDlg.cs +++ b/Src/LexText/ParserUI/TryAWordDlg.cs @@ -21,6 +21,9 @@ using XCore; using SIL.FieldWorks.Common.FwUtils; using SIL.Utils; +using SIL.FieldWorks.XWorks; +using Gecko; +using static SIL.FieldWorks.XWorks.GeneratedHtmlViewer; namespace SIL.FieldWorks.LexText.Controls { @@ -66,6 +69,8 @@ public class TryAWordDlg : Form, IMediatorProvider, IPropertyTableProvider private WebPageInteractor m_webPageInteractor; private IParserTrace m_trace; + private GeneratedHtmlViewer.FindDialog findDialog; + #endregion Data members /// @@ -153,6 +158,7 @@ private void InitHtmlControl() Size = new Size(m_resultsPanel.Width, m_resultsPanel.Height - (m_resultsLabel.Height + 1)), Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right }; + m_htmlControl.Browser.DomKeyPress += new EventHandler(OnDomKeyPress); // Setting the Dock to fill doesn't work, as we lose the top of the HtmlControl to the // label control at the top of the panel. See LT-7446 for the worst case scenario (120dpi). // So, set the location and size of the HTML control, and anchor it to all four sides of the @@ -359,6 +365,30 @@ private void InitializeComponent() } #endregion + private void OnDomKeyPress(object sender, DomKeyEventArgs e) + { + var ctrl = e.CtrlKey; + if (ctrl && (char)e.KeyChar == 'f') + { + findDialog = new FindDialog(m_htmlControl.Browser); + findDialog.FormClosing += new FormClosingEventHandler(FindDialog_FormClosing); + findDialog.Show(this); + } + else if (e.KeyCode == (uint)Keys.Escape) + { + // we use escape to close the find dialog + findDialog?.Close(); + } + } + private void FindDialog_FormClosing(object sender, FormClosingEventArgs e) + { + using (var executor = new AutoJSContext(m_htmlControl.Browser.Window)) + { + // Javascript query to execute in the browser + var browserJsQuery = "cleanUpHighlights()"; + executor.EvaluateScript(browserJsQuery); + } + } protected override void OnClosed(EventArgs ea) { diff --git a/Src/LexText/ParserUI/WebPageInteractor.cs b/Src/LexText/ParserUI/WebPageInteractor.cs index c448d4dc5d..785e6a0cbb 100644 --- a/Src/LexText/ParserUI/WebPageInteractor.cs +++ b/Src/LexText/ParserUI/WebPageInteractor.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) @@ -9,6 +9,7 @@ using SIL.FieldWorks.FdoUi; using SIL.LCModel; using XCore; +using static SIL.FieldWorks.XWorks.GeneratedHtmlViewer; namespace SIL.FieldWorks.LexText.Controls { @@ -78,6 +79,15 @@ protected void HandleDomClick(object sender, DomMouseEventArgs e) break; } } + if (elem.TagName.Equals("IMG", StringComparison.InvariantCultureIgnoreCase) + && m_htmlControl.ParentForm != null && m_htmlControl.ParentForm.OwnedForms.Length > 0) + { + // Try a Word tracing items: + // close any instance of the find dialog when a section is opened or closed because the matches are + // no longer valid or needed. + FindDialog findDlg = (FindDialog)m_htmlControl.ParentForm.OwnedForms[0]; + findDlg?.Close(); + } } /// diff --git a/Src/Transforms/Presentation/FormatHCTrace.xsl b/Src/Transforms/Presentation/FormatHCTrace.xsl index 5f33277f7d..75fd83b298 100644 --- a/Src/Transforms/Presentation/FormatHCTrace.xsl +++ b/Src/Transforms/Presentation/FormatHCTrace.xsl @@ -2,6 +2,7 @@ + - - - - - - - + + + + + + + + + + + diff --git a/Src/Transforms/Presentation/XLingPap1.xsl b/Src/Transforms/Presentation/XLingPap1.xsl index 0b7717c4c0..843dafaab2 100644 --- a/Src/Transforms/Presentation/XLingPap1.xsl +++ b/Src/Transforms/Presentation/XLingPap1.xsl @@ -1,6 +1,7 @@ + @@ -38,7 +39,10 @@ + diff --git a/Src/xWorks/GeneratedHtmlViewer.cs b/Src/xWorks/GeneratedHtmlViewer.cs index 67ea3d05f2..bad5261b61 100644 --- a/Src/xWorks/GeneratedHtmlViewer.cs +++ b/Src/xWorks/GeneratedHtmlViewer.cs @@ -25,6 +25,8 @@ using SIL.FieldWorks.Resources; using SIL.Utils; using XCore; +using SIL.FieldWorks.FwCoreDlgs; +using Gecko; namespace SIL.FieldWorks.XWorks { @@ -124,6 +126,9 @@ public class GeneratedHtmlViewer : UserControl, IxCoreContentControl private readonly Dictionary m_transforms = new Dictionary(); + private ContextMenu m_ContextMenu; + private FindDialog findDlg = null; + #endregion // Data Members #region Properties @@ -212,10 +217,21 @@ private void InitHtmlControl() { m_htmlControl = new HtmlControl {Dock = DockStyle.Fill}; m_htmlControl.HCBeforeNavigate += OnBeforeNavigate; + m_htmlControl.Browser.DomKeyPress += new EventHandler(OnDomKeyPress); + m_ContextMenu = new ContextMenu(); + m_ContextMenu.MenuItems.Add("Find", new EventHandler(Find_Click)); + m_htmlControl.ContextMenu = this.m_ContextMenu; ResetURLCount(); } + private void Find_Click(object sender, EventArgs e) + { + findDlg = new FindDialog(m_htmlControl.Browser); + findDlg.FormClosing += new FormClosingEventHandler(FindDialog_FormClosing); + findDlg.Show(this); + } + private void ReadParameters() { m_sRegKeyName = XmlUtils.GetMandatoryAttributeValue(m_configurationParameters, "regKeyName"); @@ -997,6 +1013,139 @@ public bool OnDisplayMasterRefresh(object commandObject, ref UIItemDisplayProper display.Enabled = false; return true; // we handled this, no need to ask anyone else. } + private void OnDomKeyPress(object sender, DomKeyEventArgs e) + { + var ctrl = e.CtrlKey; + if (ctrl && (char)e.KeyChar == 'f') + { + Find_Click(sender, e); + } + else if (e.KeyCode == (uint)Keys.Escape) + { + // we use escape to close the find dialog + findDlg?.Close(); + } + } + private void FindDialog_FormClosing(object sender, FormClosingEventArgs e) + { + using (var executor = new AutoJSContext(m_htmlControl.Browser.Window)) + { + // Javascript query to execute in the browser + var browserJsQuery = "cleanUpHighlights()"; + executor.EvaluateScript(browserJsQuery); + } + } + + public class FindDialog : BasicFindDialog + { + //private string results = ""; + private int resultIndex = 0; + private int resultCount = 0; + GeckoWebBrowser geckoBrowser; + internal const string CurrentSelectedEntryClass = "currentSelectedEntry"; + CheckBox matchCase = new CheckBox(); + public FindDialog(GeckoWebBrowser geckoBrowser) + { + this.geckoBrowser = geckoBrowser; + string content = geckoBrowser.Text; + FindNext += FindNextInBrowser; + FindPrev += FindPrevInBrowser; + SearchTextChanged += (sender, args) => + { + InvokeSearch(args.SearchText, matchCase.Checked); + }; + AddMatchCaseCheckBox(); + } + + private void AddMatchCaseCheckBox() + { + matchCase.Checked = false; + matchCase.Text = "Match case"; + Height = Height + 20; + matchCase.Location = new Point(Location.X + 10, Location.Y + 50); + var label = Controls[2]; + label.Location = new Point(label.Location.X, label.Location.Y + 30); + Controls.Add(matchCase); + matchCase.CheckedChanged += new System.EventHandler(matchCase_CheckedChanged); + } + private void matchCase_CheckedChanged(object sender, EventArgs e) + { + InvokeSearch(SearchText, matchCase.Checked); + using (var executor = new AutoJSContext(geckoBrowser.Window)) + { + // Javascript query to execute in the browser + // assume the resultIndex changed + var browserJsQuery = "scrollToStoredPosition(0)"; + executor.EvaluateScript(browserJsQuery); + UpdateSearchCountDisplay(); + } + } + + private void FindPrevInBrowser(object sender, IBasicFindView view) + { + FindInBrowser(false); + } + + private void FindNextInBrowser(object sender, IBasicFindView view) + { + FindInBrowser(true); + } + + private void FindInBrowser(bool forward) + { + if (geckoBrowser == null) + return; + int originalResultIndex = resultIndex; + using (var executor = new AutoJSContext(geckoBrowser.Window)) + { + bool nodeIsVisible = false; + while (!nodeIsVisible) + { + if (forward) + { + resultIndex = resultIndex++ < resultCount - 1 ? resultIndex : 0; + } + else + { + resultIndex = resultIndex-- > 0 ? resultIndex : resultCount - 1; + } + // Javascript query to execute in the browser + var browserJsQuery = "scrollToStoredPosition(" + resultIndex + ", " + forward.ToString().ToLower() + ")"; + var found = executor.EvaluateScript(browserJsQuery); + nodeIsVisible = found.ToBoolean(); + UpdateSearchCountDisplay(); + if (resultIndex == originalResultIndex) + { + MessageBox.Show("No visible match found."); + break; + } + } + } + } + + private void UpdateSearchCountDisplay() + { + if (resultCount > 0) + StatusText = $"{resultIndex + 1} of {resultCount} Results"; + else + StatusText = "0 Results"; + } + private void InvokeSearch(string searchText, bool matchCase) + { + if (geckoBrowser == null) + throw new ApplicationException(); + using (var executor = new AutoJSContext(geckoBrowser.Window)) + { + // Javascript query to execute in the browser + // finds every text element matching the search string and returns the number of occurences + var browserJsQuery = "findAndHighlightText('" + searchText + "'," + matchCase.ToString().ToLower() + ");"; + var matchCount = executor.EvaluateScript(browserJsQuery); + resultCount = (int)matchCount.U32; + } + resultIndex = 0; + UpdateSearchCountDisplay(); + } + } public bool OnDisplayExport(object commandObject, ref UIItemDisplayProperties display) {