SuperDesign/Source/RySmartEditor/Controls/FindAndReplaceForm.cs
zilinsoft 993f1ca1a9 ### 2024-12-20 星期五更新
------
#### SuperDesign    V3.0.2412.2001
- *.[新增]新增程序更新日志设置和自动发布功能。
- *.[修复]修复Post数据格式不正确时双击文本框会导致软件闪退的BUG。
2024-12-20 08:15:19 +08:00

521 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor;
using System.Diagnostics;
using System.IO;
namespace XmlPad
{
public partial class FindAndReplaceForm : Form
{
public FindAndReplaceForm()
{
InitializeComponent();
_search = new TextEditorSearcher();
}
TextEditorSearcher _search;
FastColoredTextBoxNS.FastColoredTextBox _editor;
FastColoredTextBoxNS.FastColoredTextBox Editor
{
get { return _editor; }
set
{
_editor = value;
_search.Document = _editor.Document;
UpdateTitleBar();
}
}
private void UpdateTitleBar()
{
string text = ReplaceMode ? "查找和替换" : "查找";
if (_editor != null && _editor.FileName != null)
text += " - " + Path.GetFileName(_editor.FileName);
if (_search.HasScanRegion)
text += " (只可选)";
this.Text = text;
}
public void ShowFor(FastColoredTextBoxNS.FastColoredTextBox editor, bool replaceMode)
{
Editor = editor;
_search.ClearScanRegion();
SelectionManager sm = editor.ActiveTextAreaControl.SelectionManager;
if (sm.HasSomethingSelected && sm.SelectionCollection.Count == 1)
{
ISelection sel = sm.SelectionCollection[0];
if (sel.StartPosition.Y == sel.EndPosition.Y)
txtLookFor.Text = sm.SelectedText;
else
_search.SetScanRegion(sel);
}
else
{
// Get the current word that the caret is on
Caret caret = editor.ActiveTextAreaControl.Caret;
int start = TextUtilities.FindWordStart(editor.Document, caret.Offset);
int endAt = TextUtilities.FindWordEnd(editor.Document, caret.Offset);
txtLookFor.Text = editor.Document.GetText(start, endAt - start);
}
ReplaceMode = replaceMode;
this.Owner = (Form)editor.TopLevelControl;
this.Show();
txtLookFor.SelectAll();
txtLookFor.Focus();
}
public bool ReplaceMode
{
get { return txtReplaceWith.Visible; }
set
{
btnReplace.Visible = btnReplaceAll.Visible = value;
lblReplaceWith.Visible = txtReplaceWith.Visible = value;
btnFindAllHighlightAll.Visible = !value;
this.AcceptButton = value ? btnReplace : btnFindNext;
UpdateTitleBar();
}
}
private void BtnFindPrevious_Click(object sender, EventArgs e)
{
FindNext(false, true, "内容没有找到!");
}
private void BtnFindNext_Click(object sender, EventArgs e)
{
FindNext(false, false, "内容没有找到");
}
public bool _lastSearchWasBackward = false;
public bool _lastSearchLoopedAround;
public TextRange FindNext(bool viaF3, bool searchBackward, string messageIfNotFound)
{
if (string.IsNullOrEmpty(txtLookFor.Text))
{
MessageBox.Show("没有指定要查找的内容!", "提示");
return null;
}
_lastSearchWasBackward = searchBackward;
_search.LookFor = txtLookFor.Text;
_search.MatchCase = chkMatchCase.Checked;
_search.MatchWholeWordOnly = chkMatchWholeWord.Checked;
Caret caret = _editor.ActiveTextAreaControl.Caret;
if (viaF3 && _search.HasScanRegion && !Globals.IsInRange(caret.Offset, _search.BeginOffset, _search.EndOffset))
{
// user moved outside of the originally selected region
_search.ClearScanRegion();
UpdateTitleBar();
}
int startFrom = caret.Offset - (searchBackward ? 1 : 0);
TextRange range = _search.FindNext(startFrom, searchBackward, out _lastSearchLoopedAround);
if (range != null)
SelectResult(range);
else if (messageIfNotFound != null)
MessageBox.Show(messageIfNotFound, "提示");
return range;
}
private void SelectResult(TextRange range)
{
TextLocation p1 = _editor.Document.OffsetToPosition(range.Offset);
TextLocation p2 = _editor.Document.OffsetToPosition(range.Offset + range.Length);
_editor.ActiveTextAreaControl.SelectionManager.SetSelection(p1, p2);
//_editor.ActiveTextAreaControl.ScrollTo(p1.Line, p1.Column);
// Also move the caret to the end of the selection, because when the user
// presses F3, the caret is where we start searching next time.
_editor.ActiveTextAreaControl.Caret.Position =
_editor.Document.OffsetToPosition(range.Offset + range.Length);
}
Dictionary<TextEditorControl, HighlightGroup> _highlightGroups = new Dictionary<TextEditorControl, HighlightGroup>();
private void btnHighlightAll_Click(object sender, EventArgs e)
{
if (!_highlightGroups.ContainsKey(_editor))
_highlightGroups[_editor] = new HighlightGroup(_editor);
HighlightGroup group = _highlightGroups[_editor];
if (string.IsNullOrEmpty(LookFor))
// Clear highlights
group.ClearMarkers();
else
{
_search.LookFor = txtLookFor.Text;
_search.MatchCase = chkMatchCase.Checked;
_search.MatchWholeWordOnly = chkMatchWholeWord.Checked;
bool looped = false;
int offset = 0, count = 0;
for (; ; )
{
TextRange range = _search.FindNext(offset, false, out looped);
if (range == null || looped)
break;
offset = range.Offset + range.Length;
count++;
TextMarker m = new TextMarker(range.Offset, range.Length,
TextMarkerType.SolidBlock, Color.Yellow, Color.Black);
group.AddMarker(m);
}
if (count == 0)
MessageBox.Show("没有找到你要查找的内容", "提示");
else
Close();
}
}
private void FindAndReplaceForm_FormClosing(object sender, FormClosingEventArgs e)
{ // Prevent dispose, as this form can be re-used
if (e.CloseReason != CloseReason.FormOwnerClosing)
{
if (this.Owner != null)
this.Owner.Select(); // prevent another app from being activated instead
e.Cancel = true;
Hide();
// Discard search region
_search.ClearScanRegion();
_editor.Refresh(); // must repaint manually
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
Close();
}
private void btnReplace_Click(object sender, EventArgs e)
{
SelectionManager sm = _editor.ActiveTextAreaControl.SelectionManager;
if (string.Equals(sm.SelectedText, txtLookFor.Text, StringComparison.OrdinalIgnoreCase))
InsertText(txtReplaceWith.Text);
FindNext(false, _lastSearchWasBackward, "内容没有找到!");
}
private void btnReplaceAll_Click(object sender, EventArgs e)
{
int count = 0;
// BUG FIX: if the replacement string contains the original search string
// (e.g. replace "red" with "very red") we must avoid looping around and
// replacing forever! To fix, start replacing at beginning of region (by
// moving the caret) and stop as soon as we loop around.
_editor.ActiveTextAreaControl.Caret.Position =
_editor.Document.OffsetToPosition(_search.BeginOffset);
//_editor.Document.UndoStack.StartUndoGroup();
//try
//{
lock (_editor.Document.UndoStack)
{
while (FindNext(false, false, null) != null)
{
if (_lastSearchLoopedAround)
break;
// Replace
count++;
InsertText(txtReplaceWith.Text);
}
}
//}
//finally
//{
// _editor.Document.UndoStack.EndUndoGroup();
//}
if (count == 0)
MessageBox.Show(string.Format("没有找到目标内容: {0}", txtLookFor.Text), "提示");
else
{
MessageBox.Show(string.Format("替换了 {0} 处内容!", count), "提示");
Close();
}
}
private void InsertText(string text)
{
TextArea textArea = _editor.ActiveTextAreaControl.TextArea;
//textArea.Document.UndoStack.StartUndoGroup();
//try
//{
lock (textArea.Document.UndoStack)
{
if (textArea.SelectionManager.HasSomethingSelected)
{
textArea.Caret.Position = textArea.SelectionManager.SelectionCollection[0].StartPosition;
textArea.SelectionManager.RemoveSelectedText();
}
textArea.InsertString(text);
}
//}
//finally
//{
// textArea.Document.UndoStack.EndUndoGroup();
//}
}
public string LookFor { get { return txtLookFor.Text; } }
}
public class TextRange : AbstractSegment
{
IDocument _document;
public TextRange(IDocument document, int offset, int length)
{
_document = document;
this.offset = offset;
this.length = length;
}
}
/// <summary>This class finds occurrances of a search string in a text
/// editor's IDocument... it's like Find box without a GUI.</summary>
public class TextEditorSearcher : IDisposable
{
IDocument _document;
public IDocument Document
{
get { return _document; }
set
{
if (_document != value)
{
ClearScanRegion();
_document = value;
}
}
}
// I would have used the TextAnchor class to represent the beginning and
// end of the region to scan while automatically adjusting to changes in
// the document--but for some reason it is sealed and its constructor is
// internal. Instead I use a TextMarker, which is perhaps even better as
// it gives me the opportunity to highlight the region. Note that all the
// markers and coloring information is associated with the text document,
// not the editor control, so TextEditorSearcher doesn't need a reference
// to the TextEditorControl. After adding the marker to the document, we
// must remember to remove it when it is no longer needed.
TextMarker _region = null;
/// <summary>Sets the region to search. The region is updated
/// automatically as the document changes.</summary>
public void SetScanRegion(ISelection sel)
{
SetScanRegion(sel.Offset, sel.Length);
}
/// <summary>Sets the region to search. The region is updated
/// automatically as the document changes.</summary>
public void SetScanRegion(int offset, int length)
{
Color bkgColor = _document.HighlightingStrategy.GetColorFor("Default").BackgroundColor;
_region = new TextMarker(offset, length, TextMarkerType.SolidBlock,
Globals.HalfMix(bkgColor, Color.FromArgb(160, 160, 160)));
_document.MarkerStrategy.AddMarker(_region);
}
public bool HasScanRegion
{
get { return _region != null; }
}
public void ClearScanRegion()
{
if (_region != null)
{
_document.MarkerStrategy.RemoveMarker(_region);
_region = null;
}
}
public void Dispose() { ClearScanRegion(); GC.SuppressFinalize(this); }
~TextEditorSearcher() { Dispose(); }
/// <summary>Begins the start offset for searching</summary>
public int BeginOffset
{
get
{
if (_region != null)
return _region.Offset;
else
return 0;
}
}
/// <summary>Begins the end offset for searching</summary>
public int EndOffset
{
get
{
if (_region != null)
return _region.EndOffset;
else
return _document.TextLength;
}
}
public bool MatchCase;
public bool MatchWholeWordOnly;
string _lookFor;
string _lookFor2; // uppercase in case-insensitive mode
public string LookFor
{
get { return _lookFor; }
set { _lookFor = value; }
}
/// <summary>Finds next instance of LookFor, according to the search rules
/// (MatchCase, MatchWholeWordOnly).</summary>
/// <param name="beginAtOffset">Offset in Document at which to begin the search</param>
/// <remarks>If there is a match at beginAtOffset precisely, it will be returned.</remarks>
/// <returns>Region of document that matches the search string</returns>
public TextRange FindNext(int beginAtOffset, bool searchBackward, out bool loopedAround)
{
Debug.Assert(!string.IsNullOrEmpty(_lookFor));
loopedAround = false;
int startAt = BeginOffset, endAt = EndOffset;
int curOffs = Globals.InRange(beginAtOffset, startAt, endAt);
_lookFor2 = MatchCase ? _lookFor : _lookFor.ToUpperInvariant();
TextRange result;
if (searchBackward)
{
result = FindNextIn(startAt, curOffs, true);
if (result == null)
{
loopedAround = true;
result = FindNextIn(curOffs, endAt, true);
}
}
else
{
result = FindNextIn(curOffs, endAt, false);
if (result == null)
{
loopedAround = true;
result = FindNextIn(startAt, curOffs, false);
}
}
return result;
}
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
public delegate TResult Func<T, TResult>(T arg);
private TextRange FindNextIn(int offset1, int offset2, bool searchBackward)
{
Debug.Assert(offset2 >= offset1);
offset2 -= _lookFor.Length;
// Make behavior decisions before starting search loop
Func<char, char, bool> matchFirstCh;
Func<int, bool> matchWord;
if (MatchCase)
matchFirstCh = delegate(char lookFor, char c)
{
return lookFor == c;
};
else
matchFirstCh = delegate(char lookFor, char c)
{
return lookFor == Char.ToUpperInvariant(c);
};
if (MatchWholeWordOnly)
matchWord = IsWholeWordMatch;
else
matchWord = IsPartWordMatch;
// Search
char lookForCh = _lookFor2[0];
if (searchBackward)
{
for (int offset = offset2; offset >= offset1; offset--)
{
if (matchFirstCh(lookForCh, _document.GetCharAt(offset))
&& matchWord(offset))
return new TextRange(_document, offset, _lookFor.Length);
}
}
else
{
for (int offset = offset1; offset <= offset2; offset++)
{
if (matchFirstCh(lookForCh, _document.GetCharAt(offset))
&& matchWord(offset))
return new TextRange(_document, offset, _lookFor.Length);
}
}
return null;
}
private bool IsWholeWordMatch(int offset)
{
if (IsWordBoundary(offset) && IsWordBoundary(offset + _lookFor.Length))
return IsPartWordMatch(offset);
else
return false;
}
private bool IsWordBoundary(int offset)
{
return offset <= 0 || offset >= _document.TextLength ||
!IsAlphaNumeric(offset - 1) || !IsAlphaNumeric(offset);
}
private bool IsAlphaNumeric(int offset)
{
char c = _document.GetCharAt(offset);
return Char.IsLetterOrDigit(c) || c == '_';
}
private bool IsPartWordMatch(int offset)
{
string substr = _document.GetText(offset, _lookFor.Length);
if (!MatchCase)
substr = substr.ToUpperInvariant();
return substr == _lookFor2;
}
}
/// <summary>Bundles a group of markers together so that they can be cleared
/// together.</summary>
public class HighlightGroup : IDisposable
{
List<TextMarker> _markers = new List<TextMarker>();
TextEditorControl _editor;
IDocument _document;
public HighlightGroup(TextEditorControl editor)
{
_editor = editor;
_document = editor.Document;
}
public void AddMarker(TextMarker marker)
{
_markers.Add(marker);
_document.MarkerStrategy.AddMarker(marker);
}
public void ClearMarkers()
{
foreach (TextMarker m in _markers)
_document.MarkerStrategy.RemoveMarker(m);
_markers.Clear();
_editor.Refresh();
}
public void Dispose() { ClearMarkers(); GC.SuppressFinalize(this); }
~HighlightGroup() { Dispose(); }
public IList<TextMarker> Markers { get { return _markers.AsReadOnly(); } }
}
}