5 changed files with 4 additions and 322 deletions
@ -1,317 +0,0 @@
@@ -1,317 +0,0 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Windows; |
||||
using System.Windows.Documents; |
||||
using System.Windows.Input; |
||||
|
||||
using ICSharpCode.AvalonEdit.Document; |
||||
using ICSharpCode.AvalonEdit.Editing; |
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.SharpDevelop; |
||||
using ICSharpCode.SharpDevelop.Gui; |
||||
|
||||
namespace ICSharpCode.AvalonEdit.AddIn |
||||
{ |
||||
/// <summary>
|
||||
/// IncrementalSearch handler for AvalonEdit.
|
||||
/// </summary>
|
||||
public sealed class IncrementalSearch : ITextAreaInputHandler |
||||
{ |
||||
readonly LogicalDirection direction; |
||||
readonly TextArea textArea; |
||||
|
||||
StringBuilder searchText = new StringBuilder(); |
||||
string text; |
||||
int startIndex; |
||||
int originalStartIndex; |
||||
bool passedEndOfDocument; |
||||
|
||||
public IncrementalSearch(TextArea textArea, LogicalDirection direction) |
||||
{ |
||||
if (textArea == null) |
||||
throw new ArgumentNullException("textArea"); |
||||
this.textArea = textArea; |
||||
this.direction = direction; |
||||
} |
||||
|
||||
#region Attach + Detach
|
||||
TextArea ITextAreaInputHandler.TextArea { |
||||
get { return textArea; } |
||||
} |
||||
|
||||
void ITextAreaInputHandler.Attach() |
||||
{ |
||||
textArea.PreviewKeyDown += textArea_PreviewKeyDown; |
||||
textArea.TextEntering += textArea_TextEntering; |
||||
textArea.LostFocus += textArea_LostFocus; |
||||
textArea.PreviewMouseDown += textArea_PreviewMouseDown; |
||||
|
||||
EnableIncrementalSearchCursor(); |
||||
|
||||
// Get text to search and initial search position.
|
||||
text = textArea.Document.Text; |
||||
startIndex = textArea.Caret.Offset; |
||||
originalStartIndex = startIndex; |
||||
|
||||
GetInitialSearchText(); |
||||
ShowTextFound(searchText.ToString()); |
||||
} |
||||
|
||||
void ITextAreaInputHandler.Detach() |
||||
{ |
||||
textArea.PreviewKeyDown -= textArea_PreviewKeyDown; |
||||
textArea.TextEntering -= textArea_TextEntering; |
||||
textArea.LostFocus -= textArea_LostFocus; |
||||
textArea.PreviewMouseDown -= textArea_PreviewMouseDown; |
||||
|
||||
DisableIncrementalSearchCursor(); |
||||
ClearStatusBarMessage(); |
||||
} |
||||
|
||||
public void StopIncrementalSearch() |
||||
{ |
||||
if (textArea.ActiveInputHandler == this) { |
||||
textArea.ActiveInputHandler = textArea.DefaultInputHandler; |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets the initial text to include in the incremental search.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If multiple lines are selected then no initial search text
|
||||
/// is set.
|
||||
/// </remarks>
|
||||
void GetInitialSearchText() |
||||
{ |
||||
if (!textArea.Selection.IsEmpty) { |
||||
startIndex = textArea.Selection.SurroundingSegment.Offset; |
||||
if (!textArea.Selection.IsMultiline(textArea.Document)) { |
||||
searchText.Append(textArea.Selection.GetText(textArea.Document)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Changes the text editor's cursor so the user knows we are in
|
||||
/// incremental search mode.
|
||||
/// </summary>
|
||||
void EnableIncrementalSearchCursor() |
||||
{ |
||||
string resourceName = "ICSharpCode.AvalonEdit.AddIn.Resources.IncrementalSearchCursor.cur"; |
||||
if (direction == LogicalDirection.Backward) { |
||||
resourceName = "ICSharpCode.AvalonEdit.AddIn.Resources.ReverseIncrementalSearchCursor.cur"; |
||||
} |
||||
textArea.Cursor = new Cursor(GetType().Assembly.GetManifestResourceStream(resourceName)); |
||||
} |
||||
|
||||
void DisableIncrementalSearchCursor() |
||||
{ |
||||
textArea.ClearValue(TextArea.CursorProperty); |
||||
} |
||||
#endregion
|
||||
|
||||
#region Status bar functions
|
||||
/// <summary>
|
||||
/// Shows the status bar message. All messages are prefixed
|
||||
/// with the standard Incremental Search string.
|
||||
/// </summary>
|
||||
void ShowTextFound(string find) |
||||
{ |
||||
if (passedEndOfDocument) { |
||||
ShowMessage(String.Concat(find, StringParser.Parse(" ${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.PassedEndOfDocumentStatusBarMessage}")), true); |
||||
} else { |
||||
ShowMessage(find, false); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Shows a highlighed status bar message.
|
||||
/// </summary>
|
||||
void ShowMessage(string message, bool highlight) |
||||
{ |
||||
string incrementalSearchStartMessage; |
||||
if (direction == LogicalDirection.Forward) { |
||||
incrementalSearchStartMessage = StringParser.Parse("${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.ForwardsSearchStatusBarMessage} "); |
||||
} else { |
||||
incrementalSearchStartMessage = StringParser.Parse("${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.ReverseSearchStatusBarMessage} "); |
||||
} |
||||
|
||||
string fullMessage = incrementalSearchStartMessage + message; |
||||
WorkbenchSingleton.StatusBar.SetMessage(fullMessage, highlight); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Shows a status bar message indicating that the specified text
|
||||
/// was not found.
|
||||
/// </summary>
|
||||
void ShowTextNotFound(string find) |
||||
{ |
||||
ShowMessage(find + StringParser.Parse(" ${res:ICSharpCode.SharpDevelop.DefaultEditor.IncrementalSearch.NotFoundStatusBarMessage}"), true); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Clears the status bar message.
|
||||
/// </summary>
|
||||
void ClearStatusBarMessage() |
||||
{ |
||||
WorkbenchSingleton.StatusBar.SetMessage(String.Empty); |
||||
} |
||||
#endregion
|
||||
|
||||
#region Running the search
|
||||
void HighlightText(int offset, int length) |
||||
{ |
||||
int endOffset = offset + length; |
||||
textArea.Caret.Offset = endOffset; |
||||
textArea.Selection = new SimpleSelection(offset, endOffset); |
||||
textArea.Caret.BringCaretToView(); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Runs the incremental search either forwards or backwards.
|
||||
/// </summary>
|
||||
void RunSearch() |
||||
{ |
||||
string find = searchText.ToString(); |
||||
int index = FindText(find, startIndex); |
||||
if (index == -1) { |
||||
index = FindText(find, GetWrapAroundStartIndex()); |
||||
passedEndOfDocument = true; |
||||
} |
||||
|
||||
// Highlight found text and show status bar message.
|
||||
if (index >= 0) { |
||||
startIndex = index; |
||||
HighlightText(index, find.Length); |
||||
ShowTextFound(find); |
||||
} else { |
||||
ShowTextNotFound(find); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets the index the search should start from when we wrap around. This
|
||||
/// is either the start of the string or the very end depending on which
|
||||
/// way we are searching.
|
||||
/// </summary>
|
||||
int GetWrapAroundStartIndex() |
||||
{ |
||||
int wrapAroundIndex = 0; |
||||
if (direction == LogicalDirection.Backward) { |
||||
wrapAroundIndex = text.Length - 1; |
||||
} |
||||
return wrapAroundIndex; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Looks for the string from the last position we searched from. The
|
||||
/// search is case insensitive if all the characters of the search
|
||||
/// string are lower case. If one of the search characters is upper case
|
||||
/// then the search is case sensitive. The search can be either forwards
|
||||
/// or backwards.
|
||||
/// </summary>
|
||||
int FindText(string find, int startIndex) |
||||
{ |
||||
StringComparison stringComparison = GetStringComparisonType(find); |
||||
if (direction == LogicalDirection.Forward) { |
||||
return text.IndexOf(find, startIndex, stringComparison); |
||||
} else { |
||||
// Reverse search.
|
||||
string searchText = GetReverseSearchText(startIndex + find.Length); |
||||
return searchText.LastIndexOf(find, stringComparison); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets whether the search string comparision should be case
|
||||
/// sensitive. If all the characters of the find string are lower case
|
||||
/// then the search is case insensitive. If any character is upper case
|
||||
/// then the search is case sensitive.
|
||||
/// </summary>
|
||||
StringComparison GetStringComparisonType(string find) |
||||
{ |
||||
foreach (char c in find) { |
||||
if (Char.IsUpper(c)) { |
||||
return StringComparison.InvariantCulture; |
||||
} |
||||
} |
||||
return StringComparison.InvariantCultureIgnoreCase; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets the text to search when doing a reverse search.
|
||||
/// </summary>
|
||||
string GetReverseSearchText(int endIndex) |
||||
{ |
||||
if (endIndex < text.Length) { |
||||
return text.Substring(0, endIndex); |
||||
} |
||||
endIndex = text.Length - 1; |
||||
if (endIndex >= 0) { |
||||
return text; |
||||
} |
||||
return String.Empty; |
||||
} |
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
void textArea_PreviewKeyDown(object sender, KeyEventArgs e) |
||||
{ |
||||
switch (e.Key) { |
||||
case Key.Enter: |
||||
case Key.Escape: |
||||
e.Handled = true; |
||||
StopIncrementalSearch(); |
||||
break; |
||||
case Key.Back: |
||||
// Remove last search char and try search again.
|
||||
int length = searchText.Length; |
||||
if (length > 0) { |
||||
searchText.Remove(length - 1, 1); |
||||
// Run search back at original starting point.
|
||||
startIndex = originalStartIndex; |
||||
passedEndOfDocument = false; |
||||
RunSearch(); |
||||
e.Handled = true; |
||||
} else { |
||||
StopIncrementalSearch(); |
||||
} |
||||
break; |
||||
default: |
||||
TextAreaInputHandler[] rootHandlers = { textArea.DefaultInputHandler }; |
||||
var allHandlers = rootHandlers.Flatten(h => h.NestedInputHandlers.OfType<TextAreaInputHandler>()); |
||||
var keyGestures = allHandlers.SelectMany(h => h.InputBindings).Select(h => h.Gesture).OfType<KeyGesture>(); |
||||
foreach (KeyGesture gesture in keyGestures) { |
||||
if (gesture.Key == e.Key) { |
||||
StopIncrementalSearch(); |
||||
break; |
||||
} |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
|
||||
void textArea_TextEntering(object sender, TextCompositionEventArgs e) |
||||
{ |
||||
searchText.Append(e.Text); |
||||
e.Handled = true; |
||||
RunSearch(); |
||||
} |
||||
|
||||
void textArea_LostFocus(object sender, RoutedEventArgs e) |
||||
{ |
||||
StopIncrementalSearch(); |
||||
} |
||||
|
||||
void textArea_PreviewMouseDown(object sender, MouseButtonEventArgs e) |
||||
{ |
||||
StopIncrementalSearch(); |
||||
} |
||||
#endregion
|
||||
} |
||||
} |
||||
Loading…
Reference in new issue