MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Diagnostics;5using System.Windows.Documents;6using System.Windows.Media;7 8namespace Microsoft.Management.UI.Internal9{10 /// <summary>11 /// Moves through search highlights built in a ParagraphBuilder12 /// changing the color of the current highlight.13 /// </summary>14 internal class ParagraphSearcher15 {16 /// <summary>17 /// Highlight for all matches except the current.18 /// </summary>19 internal static readonly Brush HighlightBrush = Brushes.Yellow;20 21 /// <summary>22 /// Highlight for the current match.23 /// </summary>24 private static readonly Brush CurrentHighlightBrush = Brushes.Cyan;25 26 /// <summary>27 /// Current match being highlighted in search.28 /// </summary>29 private Run currentHighlightedMatch;30 31 /// <summary>32 /// Initializes a new instance of the ParagraphSearcher class.33 /// </summary>34 internal ParagraphSearcher()35 {36 }37 38 /// <summary>39 /// Move to the next highlight starting at the <paramref name="caretPosition"/>.40 /// </summary>41 /// <param name="forward">True for next false for previous.</param>42 /// <param name="caretPosition">Caret position.</param>43 /// <returns>The next highlight starting at the <paramref name="caretPosition"/>.</returns>44 internal Run MoveAndHighlightNextNextMatch(bool forward, TextPointer caretPosition)45 {46 Debug.Assert(caretPosition != null, "a caret position is always valid");47 Debug.Assert(caretPosition.Parent != null && caretPosition.Parent is Run, "a caret Parent is always a valid Run");48 Run caretRun = (Run)caretPosition.Parent;49 50 Run currentRun;51 52 if (this.currentHighlightedMatch != null)53 {54 // restore the curent highlighted background to plain highlighted55 this.currentHighlightedMatch.Background = ParagraphSearcher.HighlightBrush;56 }57 58 // If the caret is in the end of a highlight we move to the adjacent run59 // It has to be in the end because if there is a match at the beginning of the file60 // and the caret has not been touched (so it is in the beginning of the file too)61 // we want to highlight this first match.62 // Considering the caller always set the caret to the end of the highlight63 // The condition below works well for successive searchs64 // We also need to move to the adjacent run if the caret is at the first run and we65 // are moving backwards so that a search backwards when the first run is highlighted66 // and the caret is at the beginning will wrap to the end67 if ((!forward && IsFirstRun(caretRun)) ||68 ((caretPosition.GetOffsetToPosition(caretRun.ContentEnd) == 0) && ParagraphSearcher.Ishighlighted(caretRun)))69 {70 currentRun = ParagraphSearcher.GetNextRun(caretRun, forward);71 }72 else73 {74 currentRun = caretRun;75 }76 77 currentRun = ParagraphSearcher.GetNextMatch(currentRun, forward);78 79 if (currentRun == null)80 {81 // if we could not find a next highlight wraparound82 currentRun = ParagraphSearcher.GetFirstOrLastRun(caretRun, forward);83 currentRun = ParagraphSearcher.GetNextMatch(currentRun, forward);84 }85 86 this.currentHighlightedMatch = currentRun;87 if (this.currentHighlightedMatch != null)88 {89 // restore the curent highlighted background to current highlighted90 this.currentHighlightedMatch.Background = ParagraphSearcher.CurrentHighlightBrush;91 }92 93 return currentRun;94 }95 96 /// <summary>97 /// Resets the search for fresh calls to MoveAndHighlightNextNextMatch.98 /// </summary>99 internal void ResetSearch()100 {101 this.currentHighlightedMatch = null;102 }103 104 /// <summary>105 /// Returns true if <paramref name="run"/> is highlighted.106 /// </summary>107 /// <param name="run">Run to check if is highlighted.</param>108 /// <returns>True if <paramref name="run"/> is highlighted.</returns>109 private static bool Ishighlighted(Run run)110 {111 if (run == null)112 {113 return false;114 }115 116 SolidColorBrush background = run.Background as SolidColorBrush;117 if (background != null && background == ParagraphSearcher.HighlightBrush)118 {119 return true;120 }121 122 return false;123 }124 125 /// <summary>126 /// Get the next or previous run according to <paramref name="forward"/>.127 /// </summary>128 /// <param name="currentRun">The current run.</param>129 /// <param name="forward">True for next false for previous.</param>130 /// <returns>The next or previous run according to <paramref name="forward"/>.</returns>131 private static Run GetNextRun(Run currentRun, bool forward)132 {133 Bold parentBold = currentRun.Parent as Bold;134 135 Inline nextInline;136 137 if (forward)138 {139 nextInline = parentBold != null ? ((Inline)parentBold).NextInline : currentRun.NextInline;140 }141 else142 {143 nextInline = parentBold != null ? ((Inline)parentBold).PreviousInline : currentRun.PreviousInline;144 }145 146 return GetRun(nextInline);147 }148 149 /// <summary>150 /// Gets the run of an inline. Inlines in a ParagrahBuilder are either a Run or a Bold151 /// which contains a Run.152 /// </summary>153 /// <param name="inline">Inline to get the run from.</param>154 /// <returns>The run of the inline.</returns>155 private static Run GetRun(Inline inline)156 {157 Bold inlineBold = inline as Bold;158 if (inlineBold != null)159 {160 return (Run)inlineBold.Inlines.FirstInline;161 }162 163 return (Run)inline;164 }165 166 /// <summary>167 /// Gets the next highlighted run starting and including <paramref name="currentRun"/>168 /// according to the direction specified in <paramref name="forward"/>.169 /// </summary>170 /// <param name="currentRun">The current run.</param>171 /// <param name="forward">True for next false for previous.</param>172 /// <returns>173 /// the next highlighted run starting and including <paramref name="currentRun"/>174 /// according to the direction specified in <paramref name="forward"/>.175 /// </returns>176 private static Run GetNextMatch(Run currentRun, bool forward)177 {178 while (currentRun != null)179 {180 if (ParagraphSearcher.Ishighlighted(currentRun))181 {182 return currentRun;183 }184 185 currentRun = ParagraphSearcher.GetNextRun(currentRun, forward);186 }187 188 return currentRun;189 }190 191 /// <summary>192 /// Gets the run's paragraph.193 /// </summary>194 /// <param name="run">Run to get the paragraph from.</param>195 /// <returns>The run's paragraph.</returns>196 private static Paragraph GetParagraph(Run run)197 {198 Bold parentBold = run.Parent as Bold;199 Paragraph parentParagraph = (parentBold != null ? parentBold.Parent : run.Parent) as Paragraph;200 Debug.Assert(parentParagraph != null, "the documents we are saerching are built with ParagraphBuilder, which builds the document like this");201 return parentParagraph;202 }203 204 /// <summary>205 /// Returns true if the run is the first run of the paragraph.206 /// </summary>207 /// <param name="run">Run to check.</param>208 /// <returns>True if the run is the first run of the paragraph.</returns>209 private static bool IsFirstRun(Run run)210 {211 Paragraph paragraph = GetParagraph(run);212 Run firstRun = ParagraphSearcher.GetRun(paragraph.Inlines.FirstInline);213 return run == firstRun;214 }215 216 /// <summary>217 /// Gets the first or lasr run in the paragraph containing <paramref name="caretRun"/>.218 /// </summary>219 /// <param name="caretRun">Run containing the caret.</param>220 /// <param name="forward">True for first false for last.</param>221 /// <returns>The first or last run in the paragraph containing <paramref name="caretRun"/>.</returns>222 private static Run GetFirstOrLastRun(Run caretRun, bool forward)223 {224 Debug.Assert(caretRun != null, "a caret run is always valid");225 226 Paragraph paragraph = GetParagraph(caretRun);227 228 Inline firstOrLastInline;229 if (forward)230 {231 firstOrLastInline = paragraph.Inlines.FirstInline;232 }233 else234 {235 firstOrLastInline = paragraph.Inlines.LastInline;236 }237 238 return GetRun(firstOrLastInline);239 }240 }241}242 