diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSegmentCollection.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSegmentCollection.cs
index ba01c884a6..ed7cd42a6b 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSegmentCollection.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSegmentCollection.cs
@@ -267,7 +267,7 @@ namespace ICSharpCode.AvalonEdit.Document
///
/// Gets the previous segment before the specified segment.
/// Segments are sorted by their start offset.
- /// Returns null if segment is the last segment.
+ /// Returns null if segment is the first segment.
///
public T GetPreviousSegment(T segment)
{
@@ -277,6 +277,26 @@ namespace ICSharpCode.AvalonEdit.Document
}
#endregion
+ #region FirstSegment/LastSegment
+ ///
+ /// Returns the first segment in the collection or null, if the collection is empty.
+ ///
+ public T FirstSegment {
+ get {
+ return root == null ? null : (T)root.LeftMost;
+ }
+ }
+
+ ///
+ /// Returns the last segment in the collection or null, if the collection is empty.
+ ///
+ public T LastSegment {
+ get {
+ return root == null ? null : (T)root.RightMost;
+ }
+ }
+ #endregion
+
#region FindFirstSegmentWithStartAfter
///
/// Gets the first segment with a start offset greater or equal to .
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
index 8e1c2f5505..300b3e50c9 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
@@ -12,9 +12,9 @@ using System.Text;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
-
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
+using ICSharpCode.AvalonEdit.Search;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Editing
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaDefaultInputHandlers.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaDefaultInputHandlers.cs
index 04ba96bf70..e7bb039932 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaDefaultInputHandlers.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaDefaultInputHandlers.cs
@@ -2,8 +2,10 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
+using System.Linq;
using System.Windows;
using System.Windows.Input;
+
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Search;
@@ -41,6 +43,8 @@ namespace ICSharpCode.AvalonEdit.Editing
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, ExecuteUndo, CanExecuteUndo));
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Redo, ExecuteRedo, CanExecuteRedo));
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Find, ExecuteFind));
+ this.CommandBindings.Add(new CommandBinding(SearchCommands.FindNext, ExecuteFindNext));
+ this.CommandBindings.Add(new CommandBinding(SearchCommands.FindPrevious, ExecuteFindPrevious));
}
internal static KeyBinding CreateFrozenKeyBinding(ICommand command, ModifierKeys modifiers, Key key)
@@ -109,7 +113,25 @@ namespace ICSharpCode.AvalonEdit.Editing
void ExecuteFind(object sender, ExecutedRoutedEventArgs e)
{
- new SearchPanel(TextArea);
+ var panel = TextArea.TextView.Layers.OfType().FirstOrDefault();
+ if (panel == null)
+ new SearchPanel(TextArea);
+ else
+ panel.Reactivate();
+ }
+
+ void ExecuteFindNext(object sender, ExecutedRoutedEventArgs e)
+ {
+ var panel = TextArea.TextView.Layers.OfType().FirstOrDefault();
+ if (panel != null)
+ panel.FindNext();
+ }
+
+ void ExecuteFindPrevious(object sender, ExecutedRoutedEventArgs e)
+ {
+ var panel = TextArea.TextView.Layers.OfType().FirstOrDefault();
+ if (panel != null)
+ panel.FindPrevious();
}
}
}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
index 59c9586d27..3d529d71b3 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
@@ -290,7 +290,11 @@
VisualLine.cs
-
+
+
+
+
+
SearchPanel.xaml
Code
@@ -412,6 +416,9 @@
+
+ DropDownButton.cs
+
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/DefaultSearchStrategy.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/DefaultSearchStrategy.cs
new file mode 100644
index 0000000000..47a8d68ba9
--- /dev/null
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/DefaultSearchStrategy.cs
@@ -0,0 +1,52 @@
+// 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.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Threading;
+using ICSharpCode.AvalonEdit.Document;
+using ICSharpCode.AvalonEdit.Editing;
+using ICSharpCode.AvalonEdit.Folding;
+using ICSharpCode.AvalonEdit.Rendering;
+
+namespace ICSharpCode.AvalonEdit.Search
+{
+ class DefaultSearchStrategy : ISearchStrategy
+ {
+ readonly Regex searchPattern;
+
+ public DefaultSearchStrategy(Regex searchPattern)
+ {
+ this.searchPattern = searchPattern;
+ }
+
+ public static ISearchStrategy Create(string searchPattern, bool ignoreCase, bool useRegularExpressions, bool matchWholeWords)
+ {
+ RegexOptions options = RegexOptions.Compiled;
+ if (ignoreCase)
+ options |= RegexOptions.IgnoreCase;
+ if (!useRegularExpressions)
+ searchPattern = Regex.Escape(searchPattern);
+ if (matchWholeWords)
+ searchPattern = "\\b" + searchPattern + "\\b";
+ Regex pattern = new Regex(searchPattern, options);
+ return new DefaultSearchStrategy(pattern);
+ }
+
+ public IEnumerable FindAll(ITextSource document)
+ {
+ foreach (Match result in searchPattern.Matches(document.Text)) {
+ yield return new SearchResult { StartOffset = result.Index, Length = result.Length };
+ }
+ }
+ }
+}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/DropDownButton.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/DropDownButton.cs
new file mode 100644
index 0000000000..af2aac883c
--- /dev/null
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/DropDownButton.cs
@@ -0,0 +1,60 @@
+// 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.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+
+namespace ICSharpCode.AvalonEdit.Search
+{
+ ///
+ /// A button that opens a drop-down menu when clicked.
+ ///
+ class DropDownButton : ButtonBase
+ {
+ public static readonly DependencyProperty DropDownContentProperty
+ = DependencyProperty.Register("DropDownContent", typeof(Popup),
+ typeof(DropDownButton), new FrameworkPropertyMetadata(null));
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
+ protected static readonly DependencyPropertyKey IsDropDownContentOpenPropertyKey
+ = DependencyProperty.RegisterReadOnly("IsDropDownContentOpen", typeof(bool),
+ typeof(DropDownButton), new FrameworkPropertyMetadata(false));
+
+ public static readonly DependencyProperty IsDropDownContentOpenProperty = IsDropDownContentOpenPropertyKey.DependencyProperty;
+
+ static DropDownButton()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(DropDownButton), new FrameworkPropertyMetadata(typeof(DropDownButton)));
+ }
+
+ public Popup DropDownContent {
+ get { return (Popup)GetValue(DropDownContentProperty); }
+ set { SetValue(DropDownContentProperty, value); }
+ }
+
+ public bool IsDropDownContentOpen {
+ get { return (bool)GetValue(IsDropDownContentOpenProperty); }
+ protected set { SetValue(IsDropDownContentOpenPropertyKey, value); }
+ }
+
+ protected override void OnClick()
+ {
+ if (DropDownContent != null && !IsDropDownContentOpen) {
+ DropDownContent.Placement = PlacementMode.Bottom;
+ DropDownContent.PlacementTarget = this;
+ DropDownContent.IsOpen = true;
+ DropDownContent.Closed += DropDownContent_Closed;
+ this.IsDropDownContentOpen = true;
+ }
+ }
+
+ void DropDownContent_Closed(object sender, EventArgs e)
+ {
+ ((Popup)sender).Closed -= DropDownContent_Closed;
+ this.IsDropDownContentOpen = false;
+ }
+ }
+}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/DropDownButton.xaml b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/DropDownButton.xaml
new file mode 100644
index 0000000000..597f2fba4c
--- /dev/null
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/DropDownButton.xaml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs
new file mode 100644
index 0000000000..0dc2722dec
--- /dev/null
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs
@@ -0,0 +1,22 @@
+// 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.Collections.Generic;
+using ICSharpCode.AvalonEdit.Document;
+
+namespace ICSharpCode.AvalonEdit.Search
+{
+ ///
+ /// Description of ISearchStrategy.
+ ///
+ public interface ISearchStrategy
+ {
+ IEnumerable FindAll(ITextSource document);
+ }
+
+ public interface ISearchResult : ISegment
+ {
+
+ }
+}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchCommands.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchCommands.cs
new file mode 100644
index 0000000000..1f8f12979e
--- /dev/null
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchCommands.cs
@@ -0,0 +1,32 @@
+// 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.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+
+using ICSharpCode.AvalonEdit.Document;
+using ICSharpCode.AvalonEdit.Editing;
+using ICSharpCode.AvalonEdit.Rendering;
+
+namespace ICSharpCode.AvalonEdit.Search
+{
+ public static class SearchCommands
+ {
+ public static readonly RoutedCommand FindNext = new RoutedCommand(
+ "FindNext", typeof(SearchPanel),
+ new InputGestureCollection { new KeyGesture(Key.F3) }
+ );
+ public static readonly RoutedCommand FindPrevious = new RoutedCommand(
+ "FindPrevious", typeof(SearchPanel),
+ new InputGestureCollection { new KeyGesture(Key.F3, ModifierKeys.Control) }
+ );
+ }
+
+ class SearchResult : TextSegment, ISearchResult
+ {
+ }
+}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml
index ada9117e83..d12ae27f99 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml
@@ -1,24 +1,37 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
\ No newline at end of file
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml.cs
index 65aba681c3..24131474af 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.xaml.cs
@@ -4,8 +4,10 @@
using System;
using System.Collections.Generic;
using System.Text;
+using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
@@ -13,6 +15,7 @@ using System.Windows.Media;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing;
+using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.AvalonEdit.Rendering;
namespace ICSharpCode.AvalonEdit.Search
@@ -25,6 +28,11 @@ namespace ICSharpCode.AvalonEdit.Search
TextArea textArea;
SearchResultBackgroundRenderer renderer;
SearchResult currentResult;
+ FoldingManager foldingManager;
+
+ public bool UseRegex { get { return useRegex.IsChecked == true; } }
+ public bool MatchCase { get { return matchCase.IsChecked == true; } }
+ public bool WholeWords { get { return wholeWords.IsChecked == true; } }
public SearchPanel(TextArea textArea)
{
@@ -35,32 +43,77 @@ namespace ICSharpCode.AvalonEdit.Search
InitializeComponent();
textArea.TextView.Layers.Add(this);
+ foldingManager = textArea.GetService(typeof(FoldingManager)) as FoldingManager;
renderer = new SearchResultBackgroundRenderer();
textArea.TextView.BackgroundRenderers.Add(renderer);
textArea.Document.TextChanged += delegate { DoSearch(false); };
+ this.Loaded += delegate { searchTextBox.Focus(); };
+
+ useRegex.Checked += delegate { DoSearch(true); };
+ matchCase.Checked += delegate { DoSearch(true); };
+ wholeWords.Checked += delegate { DoSearch(true); };
- Dispatcher.Invoke(DispatcherPriority.Input, (Action)(() => searchTextBox.Focus()));
+ useRegex.Unchecked += delegate { DoSearch(true); };
+ matchCase.Unchecked += delegate { DoSearch(true); };
+ wholeWords.Unchecked += delegate { DoSearch(true); };
}
void SearchTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
DoSearch(true);
}
+
+ ///
+ /// Reactivates the SearchPanel by setting the focus on the search box and selecting all text.
+ ///
+ public void Reactivate()
+ {
+ searchTextBox.Focus();
+ searchTextBox.SelectAll();
+ }
+
+ public void FindNext()
+ {
+ SearchResult result = null;
+ if (currentResult != null)
+ result = renderer.CurrentResults.GetNextSegment(currentResult);
+ if (result == null)
+ result = renderer.CurrentResults.FirstSegment;
+ if (result != null) {
+ currentResult = result;
+ SetResult(result);
+ }
+ }
+
+ public void FindPrevious()
+ {
+ SearchResult result = null;
+ if (currentResult != null)
+ result = renderer.CurrentResults.GetPreviousSegment(currentResult);
+ if (result == null)
+ result = renderer.CurrentResults.LastSegment;
+ if (result != null) {
+ currentResult = result;
+ SetResult(result);
+ }
+ }
void DoSearch(bool changeSelection)
{
renderer.CurrentResults.Clear();
if (!string.IsNullOrEmpty(searchTextBox.Text)) {
-
+ ISearchStrategy strategy = DefaultSearchStrategy.Create(searchTextBox.Text, !MatchCase, UseRegex, WholeWords);
currentResult = null;
int offset = textArea.Caret.Offset;
- foreach (var result in FindAll(searchTextBox.Text, textArea.Document)) {
+ if (changeSelection) {
+ textArea.Selection = SimpleSelection.Empty;
+ }
+ foreach (SearchResult result in strategy.FindAll(textArea.Document)) {
if (currentResult == null && result.StartOffset >= offset) {
currentResult = result;
if (changeSelection) {
- textArea.Caret.Offset = currentResult.StartOffset;
- textArea.Selection = new SimpleSelection(currentResult.StartOffset, currentResult.EndOffset);
+ SetResult(result);
}
}
renderer.CurrentResults.Add(result);
@@ -68,32 +121,17 @@ namespace ICSharpCode.AvalonEdit.Search
}
textArea.TextView.InvalidateLayer(KnownLayer.Selection);
}
-
- IEnumerable FindAll(string search, TextDocument document)
+
+ void SetResult(SearchResult result)
{
- SearchResult lastResult = FindNext(search, 0, document);
- while (lastResult != null) {
- yield return lastResult;
- lastResult = FindNext(search, lastResult.StartOffset + lastResult.Length, document);
+ textArea.Caret.Offset = currentResult.StartOffset;
+ textArea.Selection = new SimpleSelection(currentResult.StartOffset, currentResult.EndOffset);
+ if (foldingManager != null) {
+ foreach (var folding in foldingManager.GetFoldingsContaining(result.StartOffset))
+ folding.IsFolded = false;
}
}
- SearchResult FindNext(string search, int index, TextDocument document)
- {
- int result = document.Text.IndexOf(search, index, StringComparison.OrdinalIgnoreCase);
- if (result > -1)
- return new SearchResult { StartOffset = result, Length = search.Length };
- return null;
- }
-
- SearchResult FindPrev(string search, int index, TextDocument document)
- {
- int result = document.GetText(0, index).LastIndexOf(search, StringComparison.OrdinalIgnoreCase);
- if (result > -1)
- return new SearchResult { StartOffset = result, Length = search.Length };
- return null;
- }
-
void SearchLayerKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape) {
@@ -106,27 +144,5 @@ namespace ICSharpCode.AvalonEdit.Search
textArea.TextView.Layers.Remove(this);
textArea.TextView.BackgroundRenderers.Remove(renderer);
}
-
- void PrevClick(object sender, RoutedEventArgs e)
- {
- if (currentResult != null) {
- var result = FindPrev(searchTextBox.Text, currentResult.StartOffset, textArea.Document);
- if (result != null) {
- currentResult = result;
- textArea.Selection = new SimpleSelection(currentResult.StartOffset, currentResult.EndOffset);
- }
- }
- }
-
- void NextClick(object sender, RoutedEventArgs e)
- {
- if (currentResult != null) {
- var result = FindNext(searchTextBox.Text, currentResult.EndOffset, textArea.Document);
- if (result != null) {
- currentResult = result;
- textArea.Selection = new SimpleSelection(currentResult.StartOffset, currentResult.EndOffset);
- }
- }
- }
}
}
\ No newline at end of file
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchResultBackgroundRenderer.cs
similarity index 97%
rename from src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.cs
rename to src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchResultBackgroundRenderer.cs
index d1d45f68ed..9605b06846 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchPanel.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchResultBackgroundRenderer.cs
@@ -14,10 +14,6 @@ using ICSharpCode.AvalonEdit.Rendering;
namespace ICSharpCode.AvalonEdit.Search
{
- class SearchResult : TextSegment
- {
- }
-
class SearchResultBackgroundRenderer : IBackgroundRenderer
{
TextSegmentCollection currentResults = new TextSegmentCollection();
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/themes/generic.xaml b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/themes/generic.xaml
index ad76a3eea0..f14ce0e98a 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/themes/generic.xaml
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/themes/generic.xaml
@@ -1,9 +1,9 @@
+ xmlns:editing="clr-namespace:ICSharpCode.AvalonEdit.Editing">
+