Browse Source

Added incremental search.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@1779 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Matt Ward 19 years ago
parent
commit
5de660a1f5
  1. 7
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/AbstractMargin.cs
  2. 7
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControlBase.cs
  3. 7
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextView.cs
  4. 3
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  5. BIN
      src/Main/Base/Project/Resources/IncrementalSearchCursor.cur
  6. BIN
      src/Main/Base/Project/Resources/ReverseIncrementalSearchCursor.cur
  7. 13
      src/Main/Base/Project/Src/Gui/Components/StatusBar/SdStatusBar.cs
  8. 5
      src/Main/Base/Project/Src/Services/StatusBar/StatusBarService.cs
  9. 34
      src/Main/Base/Project/Src/TextEditor/Commands/SearchCommands.cs
  10. 389
      src/Main/Base/Project/Src/TextEditor/Gui/Editor/IncrementalSearch.cs

7
src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/AbstractMargin.cs

@ -24,6 +24,8 @@ namespace ICSharpCode.TextEditor @@ -24,6 +24,8 @@ namespace ICSharpCode.TextEditor
/// </summary>
public abstract class AbstractMargin
{
Cursor cursor = Cursors.Default;
[CLSCompliant(false)]
protected Rectangle drawingPosition = new Rectangle(0, 0, 0, 0);
[CLSCompliant(false)]
@ -58,7 +60,10 @@ namespace ICSharpCode.TextEditor @@ -58,7 +60,10 @@ namespace ICSharpCode.TextEditor
public virtual Cursor Cursor {
get {
return Cursors.Default;
return cursor;
}
set {
cursor = value;
}
}

7
src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControlBase.cs

@ -512,9 +512,14 @@ namespace ICSharpCode.TextEditor @@ -512,9 +512,14 @@ namespace ICSharpCode.TextEditor
}
}
public bool IsEditAction(Keys keyData)
{
return editactions.ContainsKey(keyData);
}
internal IEditAction GetEditAction(Keys keyData)
{
if (!editactions.ContainsKey(keyData)) {
if (!IsEditAction(keyData)) {
return null;
}
return (IEditAction)editactions[keyData];

7
src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextView.cs

@ -52,12 +52,6 @@ namespace ICSharpCode.TextEditor @@ -52,12 +52,6 @@ namespace ICSharpCode.TextEditor
}
}
public override Cursor Cursor {
get {
return Cursors.IBeam;
}
}
public int FirstPhysicalLine {
get {
return textArea.VirtualTop.Y / fontHeight;
@ -113,6 +107,7 @@ namespace ICSharpCode.TextEditor @@ -113,6 +107,7 @@ namespace ICSharpCode.TextEditor
StringFormatFlags.NoWrap |
StringFormatFlags.NoClip;
base.Cursor = Cursors.IBeam;
OptionsChanged();
}

3
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -704,6 +704,9 @@ @@ -704,6 +704,9 @@
<Compile Include="Src\Internal\ConditionEvaluators\CompareProjectPropertyConditionEvaluator.cs" />
<Compile Include="Src\Services\ParserService\Net1xProjectContentRegistry.cs" />
<Compile Include="Src\Services\ParserService\NetCFProjectContentRegistry.cs" />
<Compile Include="Src\TextEditor\Gui\Editor\IncrementalSearch.cs" />
<EmbeddedResource Include="Resources\IncrementalSearchCursor.cur" />
<EmbeddedResource Include="Resources\ReverseIncrementalSearchCursor.cur" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Libraries\DockPanel_Src\WinFormsUI\WinFormsUI.csproj">

BIN
src/Main/Base/Project/Resources/IncrementalSearchCursor.cur

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

BIN
src/Main/Base/Project/Resources/ReverseIncrementalSearchCursor.cur

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

13
src/Main/Base/Project/Src/Gui/Components/StatusBar/SdStatusBar.cs

@ -101,6 +101,19 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -101,6 +101,19 @@ namespace ICSharpCode.SharpDevelop.Gui
public void SetMessage(string message)
{
SetMessage(message, false);
}
public void SetMessage(string message, bool highlighted)
{
if (highlighted) {
txtStatusBarPanel.BackColor = SystemColors.Highlight;
txtStatusBarPanel.ForeColor = Color.White;
} else if (txtStatusBarPanel.BackColor == SystemColors.Highlight) {
txtStatusBarPanel.BackColor = SystemColors.Control;
txtStatusBarPanel.ForeColor = SystemColors.ControlText;
}
currentMessage = message;
if (this.IsHandleCreated)
BeginInvoke(new MethodInvoker(UpdateText));

5
src/Main/Base/Project/Src/Services/StatusBar/StatusBarService.cs

@ -90,6 +90,11 @@ namespace ICSharpCode.Core @@ -90,6 +90,11 @@ namespace ICSharpCode.Core
statusBar.SetMessage(image, StringParser.Parse(message));
}
public static void SetMessage(string message, bool highlighted)
{
statusBar.SetMessage(message, highlighted);
}
static bool wasError = false;
static string lastMessage = "";

34
src/Main/Base/Project/Src/TextEditor/Commands/SearchCommands.cs

@ -42,4 +42,38 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands @@ -42,4 +42,38 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands
}
}
}
public class RunIncrementalSearch : AbstractMenuCommand
{
static IncrementalSearch incrementalSearch;
public override void Run()
{
IWorkbenchWindow window = WorkbenchSingleton.Workbench.ActiveWorkbenchWindow;
if (window != null) {
ITextEditorControlProvider textEditorControlProvider = window.ActiveViewContent as ITextEditorControlProvider;
if (textEditorControlProvider != null) {
if (incrementalSearch != null) {
incrementalSearch.Dispose();
}
incrementalSearch = new IncrementalSearch(textEditorControlProvider.TextEditorControl, Forwards);
}
}
}
protected virtual bool Forwards {
get {
return true;
}
}
}
public class RunReverseIncrementalSearch : RunIncrementalSearch
{
protected override bool Forwards {
get {
return false;
}
}
}
}

389
src/Main/Base/Project/Src/TextEditor/Gui/Editor/IncrementalSearch.cs

@ -0,0 +1,389 @@ @@ -0,0 +1,389 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version>
// </file>
using ICSharpCode.Core;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor.Gui;
using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
{
public class IncrementalSearch : IDisposable
{
bool disposed;
TextEditorControl textEditor;
Cursor previousCursor;
IFormattingStrategy previousFormattingStrategy;
string forwardsIncrementalSearchStartMessage = "Incremental Search: ";
string reverseIncrementalSearchStartMessage = "Reverse Incremental Search: ";
string incrementalSearchStartMessage;
StringBuilder searchText = new StringBuilder();
string text;
int startIndex;
int originalStartIndex;
Cursor cursor;
bool passedEndOfDocument;
// Indicates whether this is a forward search or a reverse search.
bool forwards = true;
/// <summary>
/// Dummy formatting strategy that stops the FormatLine method
/// from automatically adding things like xml comment tags.
/// </summary>
class IncrementalSearchFormattingStrategy : DefaultFormattingStrategy
{
public override int FormatLine(TextArea textArea, int line, int cursorOffset, char ch)
{
return 0;
}
}
/// <summary>
/// Creates a incremental search that goes forwards.
/// </summary>
public IncrementalSearch(TextEditorControl textEditor)
: this(textEditor, true)
{
}
/// <summary>
/// Creates an incremental search for the specified text editor.
/// </summary>
/// <param name="forwards">Indicates whether the search goes
/// forward from the cursor or backwards.</param>
public IncrementalSearch(TextEditorControl textEditor, bool forwards)
{
this.forwards = forwards;
if (forwards) {
incrementalSearchStartMessage = forwardsIncrementalSearchStartMessage;
} else {
incrementalSearchStartMessage = reverseIncrementalSearchStartMessage;
}
this.textEditor = textEditor;
AddFormattingStrategy();
TextArea.KeyEventHandler += TextAreaKeyPress;
TextArea.DoProcessDialogKey += TextAreaProcessDialogKey;
TextArea.LostFocus += TextAreaLostFocus;
TextArea.MouseClick += TextAreaMouseClick;
EnableIncrementalSearchCursor();
// Get text to search and initial search position.
text = textEditor.Document.TextContent.ToLowerInvariant();
startIndex = TextArea.Caret.Offset;
originalStartIndex = startIndex;
GetInitialSearchText();
ShowTextFound(searchText.ToString());
}
public void Dispose()
{
if (!disposed) {
disposed = true;
TextArea.KeyEventHandler -= TextAreaKeyPress;
TextArea.DoProcessDialogKey -= TextAreaProcessDialogKey;
TextArea.LostFocus -= TextAreaLostFocus;
TextArea.MouseClick -= TextAreaMouseClick;
DisableIncrementalSearchCursor();
RemoveFormattingStrategy();
if (cursor != null) {
cursor.Dispose();
}
ClearStatusBarMessage();
}
}
TextArea TextArea {
get {
return textEditor.ActiveTextAreaControl.TextArea;
}
}
void TextAreaLostFocus(object source, EventArgs e)
{
StopIncrementalSearch();
}
/// <summary>
/// Stop the incremental search if the user clicks.
/// </summary>
void TextAreaMouseClick(object source, MouseEventArgs e)
{
StopIncrementalSearch();
}
void StopIncrementalSearch()
{
Dispose();
}
/// <summary>
/// Searches the text incrementally on each key press.
/// </summary>
bool TextAreaKeyPress(char ch)
{
// Search for text.
searchText.Append(ch);
RunSearch();
return true;
}
void HighlightText(int offset, int length)
{
int endOffset = offset + length;
TextArea.Caret.Position = TextArea.Document.OffsetToPosition(endOffset);
TextArea.SelectionManager.ClearSelection();
IDocument document = TextArea.Document;
DefaultSelection selection = new DefaultSelection(document, document.OffsetToPosition(offset), document.OffsetToPosition(endOffset));
TextArea.SelectionManager.SetSelection(selection);
textEditor.Refresh();
}
/// <summary>
/// Runs the incremental search either forwards or backwards.
/// </summary>
void RunSearch()
{
string find = searchText.ToString();
int index = FindText(find, startIndex, forwards);
if (index == -1 && find.Length == 1) {
// First character so allow wrap around.
index = FindText(find, GetWrapAroundStartIndex(), forwards);
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 (!forwards) {
wrapAroundIndex = text.Length - 1;
}
return wrapAroundIndex;
}
/// <summary>
/// Looks for the string from the last position we searched from. The
/// search is case insensitive. The search can be either forwards
/// or backwards.
/// </summary>
int FindText(string s, int startIndex, bool forwards)
{
string find = s.ToLowerInvariant();
if (forwards) {
return text.IndexOf(find, startIndex);
}
// Reverse search.
string searchText = GetReverseSearchText(startIndex + find.Length);
return searchText.LastIndexOf(find);
}
/// <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 + 1);
}
endIndex = text.Length - 1;
if (endIndex >= 0) {
return text;
}
return String.Empty;
}
/// <summary>
/// Checks the dialog key to see if the incremental search
/// should be cancelled.
/// </summary>
/// <remarks>
/// If the user presses the escape or enter key then the
/// incremental search is aborted. If the user executes any
/// edit action via the keyboard the incremental search is aborted
/// and the edit action is allowed to execute.
/// </remarks>
bool TextAreaProcessDialogKey(Keys keys)
{
if (keys == Keys.Escape ||
keys == Keys.Enter) {
StopIncrementalSearch();
return true;
} else if (keys == Keys.Back) {
// Remove last search char and try search again.
int length = searchText.ToString().Length;
if (length > 0) {
searchText.Remove(length - 1, 1);
// Run search back at original starting point.
startIndex = originalStartIndex;
if (length == 1) {
passedEndOfDocument = false;
}
RunSearch();
return true;
} else {
StopIncrementalSearch();
return false;
}
} else if (textEditor.IsEditAction(keys)) {
StopIncrementalSearch();
return false;
}
return false;
}
static bool IsGreaterThanKey(Keys keys)
{
return (int)(keys & Keys.KeyCode) == '>';
}
/// <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, " (passed end of document)"), true);
} else {
ShowMessage(find, false);
}
}
/// <summary>
/// Shows a highlighed status bar message.
/// </summary>
void ShowMessage(string message, bool highlight)
{
string fullMessage = String.Concat(incrementalSearchStartMessage, message);
StatusBarService.SetMessage(fullMessage, highlight);
}
/// <summary>
/// Shows a status bar message indicating that the specified text
/// was not found.
/// </summary>
void ShowTextNotFound(string find)
{
ShowMessage(String.Concat(find, " (not found)"), true);
}
/// <summary>
/// Clears the status bar message.
/// </summary>
void ClearStatusBarMessage()
{
StatusBarService.SetMessage(String.Empty);
}
/// <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.SelectionManager.HasSomethingSelected) {
ISelection selection = TextArea.SelectionManager.SelectionCollection[0];
startIndex = selection.Offset;
if (!IsMultilineSelection(selection)) {
searchText.Append(selection.SelectedText);
}
}
}
bool IsMultilineSelection(ISelection selection)
{
return selection.StartPosition.Y != selection.EndPosition.Y;
}
/// <summary>
/// Gets the cursor to be displayed in the text editor whilst doing
/// an incremental search.
/// </summary>
Cursor GetCursor()
{
if (cursor == null) {
string resourceName = "Resources.IncrementalSearchCursor.cur";
if (!forwards) {
resourceName = "Resources.ReverseIncrementalSearchCursor.cur";
}
cursor = new Cursor(GetType().Assembly.GetManifestResourceStream(resourceName));
}
return cursor;
}
/// <summary>
/// Changes the text editor's cursor so the user knows we are in
/// incremental search mode.
/// </summary>
void EnableIncrementalSearchCursor()
{
previousCursor = TextArea.Cursor;
Cursor cursor = GetCursor();
TextArea.Cursor = cursor;
TextArea.TextView.Cursor = cursor;
}
void DisableIncrementalSearchCursor()
{
TextArea.Cursor = previousCursor;
TextArea.TextView.Cursor = previousCursor;
}
/// <summary>
/// Replace the existing formatting strategy with our dummy one.
/// </summary>
/// <remarks>
/// Special case. We need to replace the formatting strategy to
/// prevent the text editor from autocompletiong xml elements and
/// xml comment tags. The text editor always calls
/// IFormattingStrategy.FormatLine regardless of whether any text was
/// actually inserted or replaced.
/// </remarks>
void AddFormattingStrategy()
{
IDocument document = textEditor.Document;
previousFormattingStrategy = document.FormattingStrategy;
textEditor.Document.FormattingStrategy = new IncrementalSearchFormattingStrategy();
}
/// <summary>
/// Removes our dummy formatting strategy and replaces it with
/// the original before the incremental search was triggered.
/// </summary>
void RemoveFormattingStrategy()
{
textEditor.Document.FormattingStrategy = previousFormattingStrategy;
}
}
}
Loading…
Cancel
Save