/// <summary> /// Applies one step of the async highlighting against the main scheme. /// </summary> /// <returns>Whether another step should be called.</returns> protected bool StepHiliteMain(AsyncHighlightState state) { if (!state.Enum.MoveNext()) { state.ActualSearchHits = state.Words; // As the main scheme has succeeded, the original word list is the same as the final one return(false); // Over! } // Get the current word WordPtr word = (WordPtr)state.Enum.Current; if (word.StartOffset < 0) { return(StartHiliteBackup(state)); // Fallback to the backup scheme } // Choose a color for highlighting this word state.PickNextColor(word.Original); // Select the supposed search hit location Select(word.StartOffset, word.Text.Length); // Check whether we've selected the proper thing if (String.Compare(SelectedText, word.Text, true, CultureInfo.InvariantCulture) != 0) { Trace.WriteLine(String.Format("Main highlighting expected to find \"{0}\" but got \"{1}\", aborting.", word.Text, SelectedText), "[JRTB]"); return(StartHiliteBackup(state)); // Fallback to the backup scheme } // Apply the coloring!! Win32Declarations.SendMessage(Handle, EditMessage.SETCHARFORMAT, SCF.SELECTION, ref state.Fmt); return(true); // Call more }
/// <summary> /// Initiates the async highlighting of the search hits. /// </summary> /// <param name="words">Words to be highlighted. MUST belong to a single document section.</param> public void HighlightWords(WordPtr[] words) { #region Preconditions if (words == null) { return; } // Validness check WordPtr.AssertValid(words, true); #endregion Preconditions _wordsSearchHits = null; // Here the real search hits will be stored; in case of main highlighting they correspond to the ones passed in _statuswriter = Core.UIManager.GetStatusWriter(this, StatusPane.UI); _statuswriter.ShowStatus("Highlighting search hits in the document…"); // Initiate the async hilite process _stateHilite = new AsyncHighlightState(words); if (StartHiliteMain(_stateHilite)) { Core.UserInterfaceAP.QueueJobAt(DateTime.Now.AddMilliseconds(500), "Highlight the search hits.", new MethodInvoker(StepHilite)); // Succeeded — queue execution } else { // Failed — deinitialize _stateHilite = null; _statuswriter.ClearStatus(); _statuswriter = null; Trace.WriteLine("Failed to initiate the main highlighting scheme.", "[JRTB]"); } }
/// <summary> /// Does the asynchronous highlighting step. /// </summary> private void StepHilite() { if (_stateHilite == null) { return; // Has been shut down } uint dwStart = Win32Declarations.GetTickCount(); uint dwLimit = 222; // Allow running for this much milliseconds continuously // Freeze the control Win32Declarations.SendMessage(Handle, Win32Declarations.WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero); try { int nIterations; for (nIterations = 0; Win32Declarations.GetTickCount() - dwStart < dwLimit; nIterations++) // Work for some limited time { if (!_stateHilite.StepHiliteDelegate(_stateHilite)) // Invoke the individual highlighting step { // Highlighting Completed! // Reset the status bar dials _statuswriter.ClearStatus(); _statuswriter = null; // Retrieve the values _wordsSearchHits = _stateHilite.ActualSearchHits; _nCurrentSearchHit = -1; // Deinitialize the hilite search _stateHilite = null; // Jump to the next search hit GotoNextSearchHit(true, false); // Invalidate Win32Declarations.SendMessage(Handle, Win32Declarations.WM_SETREDRAW, (IntPtr)1, IntPtr.Zero); Invalidate(); // Done! Trace.WriteLine(String.Format("The JetRichTextBox has completed the async highlighting with {0} hits total.", (_wordsSearchHits != null ? _wordsSearchHits.Length.ToString() : "#ERROR#")), "[JRTB]"); return; } } Trace.WriteLine(String.Format("The JetRichTextBox async highlighting has done {0} highlightings on this step.", nIterations), "[JRTB]"); } finally { // Unfreeze the events and repaint Win32Declarations.SendMessage(Handle, Win32Declarations.WM_SETREDRAW, (IntPtr)1, IntPtr.Zero); if ((_stateHilite != null) && (Win32Declarations.GetTickCount() - _stateHilite.LastRepaintTime > 2000)) // Repaint rarely { Invalidate(); _stateHilite.LastRepaintTime = Win32Declarations.GetTickCount(); } } // Requeue the rest of execution Application.DoEvents(); // Without this, the painting events won't occur Core.UserInterfaceAP.QueueJob("Highlight the search hits.", new MethodInvoker(StepHilite)); }
/// <summary> /// Starts applying the main highlighting scheme that uses the provided offsets for highlighting the text. /// In case the offsets do not hit the expected words, aborts the highlighting and returns <c>Null</c>, /// indicating that the backup scheme should be used. /// </summary> /// <returns>Success flag.</returns> protected bool StartHiliteMain(AsyncHighlightState state) { state.StepHiliteDelegate = new AsyncHighlightState.StepHiliteAny(StepHiliteMain); // Assign the entries for highlighting state.Enum = state.Words.GetEnumerator(); return(true); }
protected override void Dispose(bool disposing) { base.Dispose(disposing); _stateHilite = null; // Stop the currently-running highlighting if (_statuswriter != null) { _statuswriter.ClearStatus(); _statuswriter = null; } _wordsSearchHits = null; _nCurrentSearchHit = -1; }
/// <summary> /// Applies one step of the async highlighting against the backup scheme. /// </summary> /// <returns>Whether another step should be called.</returns> protected bool StepHiliteBackup(AsyncHighlightState state) { if (state.CurPos < 0) // Should we pick a new word form for searching it? { if (!state.Enum.MoveNext()) { // Completed!! // Sort the search hits in order of appearance and supply to the storage state.ActualSearchHitsCache.Sort(new WordPtrOffsetComparer()); state.ActualSearchHits = (WordPtr[])state.ActualSearchHitsCache.ToArray(typeof(WordPtr)); // Take the search hits return(false); // Finish it } state.CurPos = 0; // Start looking for it from the beginning } string sOriginal = (string)state.Enum.Current; WordPtr wordSearchHit = (WordPtr)state.HashWordForms[sOriginal]; // Choose a color for highlighting the hits of this text state.PickNextColor(wordSearchHit.Original); // Look for the next entry, starting from the very place we left it the prev time int nOldPos = state.CurPos; state.CurPos = Find(wordSearchHit.Text, state.CurPos, RichTextBoxFinds.NoHighlight | RichTextBoxFinds.WholeWord); if (state.CurPos < 0) // If not found, will be negative { return(true); // Switch to looking for the next entry, or complete the process if there are no more } if (state.CurPos <= nOldPos) // Sometimes the Find function will return a result BEFORE the search start :) { // Switch to looking for the next entry, or complete the process if there are no more state.CurPos = -1; return(true); } // Add the search hit data WordPtr hit = wordSearchHit; // Make a copy of the hit hit.StartOffset = state.CurPos; // The actual starting offset state.ActualSearchHitsCache.Add(hit); // Select the supposed search hit location Select(state.CurPos, wordSearchHit.Text.Length); state.CurPos += wordSearchHit.Text.Length; // Skip the already-found entry (otherwise we'll keep finding it again and again, eternally) // Apply the coloring!! Win32Declarations.SendMessage(Handle, EditMessage.SETCHARFORMAT, SCF.SELECTION, ref state.Fmt); return(true); // Try looking for the next entry }
/// <summary> /// Starts applying the backup hilite scheme that applies when the main scheme fails. /// It searches for the entries in the text and ignores the offsets given as they're assumed to be incorrect. /// </summary> /// <returns>Success flag.</returns> protected bool StartHiliteBackup(AsyncHighlightState state) { state.StepHiliteDelegate = new AsyncHighlightState.StepHiliteAny(StepHiliteBackup); // Make a hash of the words to highlight (the particular word forms) state.HashWordForms = new Hashtable(state.Words.Length); foreach (WordPtr word in state.Words) { state.HashWordForms[word.Text] = word; } Trace.WriteLine(String.Format("{0} unique forms were picked out of {1} original word-ptrs.", state.HashWordForms.Count, state.Words.Length), "[JRTB]"); // Seed the process state.Enum = state.HashWordForms.Keys.GetEnumerator(); state.ActualSearchHitsCache = new ArrayList(state.HashWordForms.Count); state.CurPos = -1; // Go on return(true); }