using System; using System.Collections.Generic; namespace LB_VisionControls { /// /// Insert single char /// /// This operation includes also insertion of new line and removing char by backspace internal class InsertCharCommand : UndoableCommand { internal char c; char deletedChar = '\x0'; /// /// Constructor /// /// Underlaying textbox /// Inserting char public InsertCharCommand(TextSource ts, char c) : base(ts) { this.c = c; } /// /// Undo operation /// public override void Undo() { ts.OnTextChanging(); switch (c) { case '\n': MergeLines(sel.Start.iLine, ts); break; case '\r': break; case '\b': ts.CurrentTB.Selection.Start = lastSel.Start; char cc = '\x0'; if (deletedChar != '\x0') { ts.CurrentTB.ExpandBlock(ts.CurrentTB.Selection.Start.iLine); InsertChar(deletedChar, ref cc, ts); } break; default: ts.CurrentTB.ExpandBlock(sel.Start.iLine); ts[sel.Start.iLine].RemoveAt(sel.Start.iChar); ts.CurrentTB.Selection.Start = sel.Start; break; } ts.NeedRecalc(new TextSource.TextChangedEventArgs(sel.Start.iLine, sel.Start.iLine)); base.Undo(); } /// /// Execute operation /// public override void Execute() { ts.CurrentTB.ExpandBlock(ts.CurrentTB.Selection.Start.iLine); string s = c.ToString(); ts.OnTextChanging(ref s); if (s.Length == 1) c = s[0]; if (String.IsNullOrEmpty(s)) throw new ArgumentOutOfRangeException(); if (ts.Count == 0) InsertLine(ts); InsertChar(c, ref deletedChar, ts); ts.NeedRecalc(new TextSource.TextChangedEventArgs(ts.CurrentTB.Selection.Start.iLine, ts.CurrentTB.Selection.Start.iLine)); base.Execute(); } internal static void InsertChar(char c, ref char deletedChar, TextSource ts) { var tb = ts.CurrentTB; switch (c) { case '\n': if (!ts.CurrentTB.allowInsertRemoveLines) throw new ArgumentOutOfRangeException("Cant insert this char in ColumnRange mode"); if (ts.Count == 0) InsertLine(ts); InsertLine(ts); break; case '\r': break; case '\b'://backspace if (tb.Selection.Start.iChar == 0 && tb.Selection.Start.iLine == 0) return; if (tb.Selection.Start.iChar == 0) { if (!ts.CurrentTB.allowInsertRemoveLines) throw new ArgumentOutOfRangeException("Cant insert this char in ColumnRange mode"); if (tb.lineInfos[tb.Selection.Start.iLine - 1].VisibleState != VisibleState.Visible) tb.ExpandBlock(tb.Selection.Start.iLine - 1); deletedChar = '\n'; MergeLines(tb.Selection.Start.iLine - 1, ts); } else { deletedChar = ts[tb.Selection.Start.iLine][tb.Selection.Start.iChar - 1].c; ts[tb.Selection.Start.iLine].RemoveAt(tb.Selection.Start.iChar - 1); tb.Selection.Start = new Place(tb.Selection.Start.iChar - 1, tb.Selection.Start.iLine); } break; case '\t': for (int i = 0; i < tb.TabLength; i++) ts[tb.Selection.Start.iLine].Insert(tb.Selection.Start.iChar, new Char(' ')); tb.Selection.Start = new Place(tb.Selection.Start.iChar + tb.TabLength, tb.Selection.Start.iLine); break; default: ts[tb.Selection.Start.iLine].Insert(tb.Selection.Start.iChar, new Char(c)); tb.Selection.Start = new Place(tb.Selection.Start.iChar + 1, tb.Selection.Start.iLine); break; } } internal static void InsertLine(TextSource ts) { var tb = ts.CurrentTB; if (!tb.Multiline && tb.LinesCount > 0) return; if (ts.Count == 0) ts.InsertLine(0, ts.CreateLine()); else BreakLines(tb.Selection.Start.iLine, tb.Selection.Start.iChar, ts); tb.Selection.Start = new Place(0, tb.Selection.Start.iLine + 1); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } /// /// Merge lines i and i+1 /// internal static void MergeLines(int i, TextSource ts) { var tb = ts.CurrentTB; if (i + 1 >= ts.Count) return; tb.ExpandBlock(i); tb.ExpandBlock(i + 1); int pos = ts[i].Count; // if (ts[i].Count == 0) ts.RemoveLine(i); else if (ts[i + 1].Count == 0) ts.RemoveLine(i + 1); else { ts[i].AddRange(ts[i + 1]); ts.RemoveLine(i + 1); } tb.Selection.Start = new Place(pos, i); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } internal static void BreakLines(int iLine, int pos, TextSource ts) { Line newLine = ts.CreateLine(); for (int i = pos; i < ts[iLine].Count; i++) newLine.Add(ts[iLine][i]); ts[iLine].RemoveRange(pos, ts[iLine].Count - pos); // ts.InsertLine(iLine + 1, newLine); } public override UndoableCommand Clone() { return new InsertCharCommand(ts, c); } } /// /// Insert text /// internal class InsertTextCommand : UndoableCommand { internal string insertedText; /// /// Constructor /// /// Underlaying textbox /// Text for inserting public InsertTextCommand(TextSource ts, string insertedText) : base(ts) { this.insertedText = insertedText; } /// /// Undo operation /// public override void Undo() { ts.CurrentTB.Selection.Start = sel.Start; ts.CurrentTB.Selection.End = lastSel.Start; ts.OnTextChanging(); ClearSelectedCommand.ClearSelected(ts); base.Undo(); } /// /// Execute operation /// public override void Execute() { ts.OnTextChanging(ref insertedText); InsertText(insertedText, ts); base.Execute(); } internal static void InsertText(string insertedText, TextSource ts) { var tb = ts.CurrentTB; try { tb.Selection.BeginUpdate(); char cc = '\x0'; if (ts.Count == 0) InsertCharCommand.InsertLine(ts); tb.ExpandBlock(tb.Selection.Start.iLine); foreach (char c in insertedText) InsertCharCommand.InsertChar(c, ref cc, ts); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } finally { tb.Selection.EndUpdate(); } } public override UndoableCommand Clone() { return new InsertTextCommand(ts, insertedText); } } /// /// Insert text into given ranges /// internal class ReplaceTextCommand : UndoableCommand { string insertedText; List ranges; List prevText = new List(); /// /// Constructor /// /// Underlaying textbox /// List of ranges for replace /// Text for inserting public ReplaceTextCommand(TextSource ts, List ranges, string insertedText) : base(ts) { //sort ranges by place ranges.Sort((r1, r2) => { if (r1.Start.iLine == r2.Start.iLine) return r1.Start.iChar.CompareTo(r2.Start.iChar); return r1.Start.iLine.CompareTo(r2.Start.iLine); }); // this.ranges = ranges; this.insertedText = insertedText; lastSel = sel = new RangeInfo(ts.CurrentTB.Selection); } /// /// Undo operation /// public override void Undo() { var tb = ts.CurrentTB; ts.OnTextChanging(); tb.Selection.BeginUpdate(); for (int i = 0; i < ranges.Count; i++) { tb.Selection.Start = ranges[i].Start; for (int j = 0; j < insertedText.Length; j++) tb.Selection.GoRight(true); ClearSelectedCommand.ClearSelected(ts); InsertTextCommand.InsertText(prevText[prevText.Count - i - 1], ts); ts.OnTextChanged(ranges[i].Start.iLine, ranges[i].Start.iLine); } tb.Selection.EndUpdate(); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } /// /// Execute operation /// public override void Execute() { var tb = ts.CurrentTB; prevText.Clear(); ts.OnTextChanging(ref insertedText); tb.Selection.BeginUpdate(); for (int i = ranges.Count - 1; i >= 0; i--) { tb.Selection.Start = ranges[i].Start; tb.Selection.End = ranges[i].End; prevText.Add(tb.Selection.Text); ClearSelectedCommand.ClearSelected(ts); InsertTextCommand.InsertText(insertedText, ts); ts.OnTextChanged(ranges[i].Start.iLine, ranges[i].End.iLine); } tb.Selection.EndUpdate(); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); lastSel = new RangeInfo(tb.Selection); } public override UndoableCommand Clone() { return new ReplaceTextCommand(ts, new List(ranges), insertedText); } } /// /// Clear selected text /// internal class ClearSelectedCommand : UndoableCommand { string deletedText; /// /// Construstor /// /// Underlaying textbox public ClearSelectedCommand(TextSource ts) : base(ts) { } /// /// Undo operation /// public override void Undo() { ts.CurrentTB.Selection.Start = new Place(sel.FromX, Math.Min(sel.Start.iLine, sel.End.iLine)); ts.OnTextChanging(); InsertTextCommand.InsertText(deletedText, ts); ts.OnTextChanged(sel.Start.iLine, sel.End.iLine); ts.CurrentTB.Selection.Start = sel.Start; ts.CurrentTB.Selection.End = sel.End; } /// /// Execute operation /// public override void Execute() { var tb = ts.CurrentTB; string temp = null; ts.OnTextChanging(ref temp); if (temp == "") throw new ArgumentOutOfRangeException(); deletedText = tb.Selection.Text; ClearSelected(ts); lastSel = new RangeInfo(tb.Selection); ts.OnTextChanged(lastSel.Start.iLine, lastSel.Start.iLine); } internal static void ClearSelected(TextSource ts) { var tb = ts.CurrentTB; Place start = tb.Selection.Start; Place end = tb.Selection.End; int fromLine = Math.Min(end.iLine, start.iLine); int toLine = Math.Max(end.iLine, start.iLine); int fromChar = tb.Selection.FromX; int toChar = tb.Selection.ToX; if (fromLine < 0) return; // if (fromLine == toLine) ts[fromLine].RemoveRange(fromChar, toChar - fromChar); else { ts[fromLine].RemoveRange(fromChar, ts[fromLine].Count - fromChar); ts[toLine].RemoveRange(0, toChar); ts.RemoveLine(fromLine + 1, toLine - fromLine - 1); InsertCharCommand.MergeLines(fromLine, ts); } // tb.Selection.Start = new Place(fromChar, fromLine); // ts.NeedRecalc(new TextSource.TextChangedEventArgs(fromLine, toLine)); } public override UndoableCommand Clone() { return new ClearSelectedCommand(ts); } } /// /// Replaces text /// internal class ReplaceMultipleTextCommand : UndoableCommand { List ranges; List prevText = new List(); public class ReplaceRange { public Range ReplacedRange { get; set; } public String ReplaceText { get; set; } } /// /// Constructor /// /// Underlaying textsource /// List of ranges for replace public ReplaceMultipleTextCommand(TextSource ts, List ranges) : base(ts) { //sort ranges by place ranges.Sort((r1, r2) => { if (r1.ReplacedRange.Start.iLine == r2.ReplacedRange.Start.iLine) return r1.ReplacedRange.Start.iChar.CompareTo(r2.ReplacedRange.Start.iChar); return r1.ReplacedRange.Start.iLine.CompareTo(r2.ReplacedRange.Start.iLine); }); // this.ranges = ranges; lastSel = sel = new RangeInfo(ts.CurrentTB.Selection); } /// /// Undo operation /// public override void Undo() { var tb = ts.CurrentTB; ts.OnTextChanging(); tb.Selection.BeginUpdate(); for (int i = 0; i < ranges.Count; i++) { tb.Selection.Start = ranges[i].ReplacedRange.Start; for (int j = 0; j < ranges[i].ReplaceText.Length; j++) tb.Selection.GoRight(true); ClearSelectedCommand.ClearSelected(ts); var prevTextIndex = ranges.Count - 1 - i; InsertTextCommand.InsertText(prevText[prevTextIndex], ts); ts.OnTextChanged(ranges[i].ReplacedRange.Start.iLine, ranges[i].ReplacedRange.Start.iLine); } tb.Selection.EndUpdate(); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } /// /// Execute operation /// public override void Execute() { var tb = ts.CurrentTB; prevText.Clear(); ts.OnTextChanging(); tb.Selection.BeginUpdate(); for (int i = ranges.Count - 1; i >= 0; i--) { tb.Selection.Start = ranges[i].ReplacedRange.Start; tb.Selection.End = ranges[i].ReplacedRange.End; prevText.Add(tb.Selection.Text); ClearSelectedCommand.ClearSelected(ts); InsertTextCommand.InsertText(ranges[i].ReplaceText, ts); ts.OnTextChanged(ranges[i].ReplacedRange.Start.iLine, ranges[i].ReplacedRange.End.iLine); } tb.Selection.EndUpdate(); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); lastSel = new RangeInfo(tb.Selection); } public override UndoableCommand Clone() { return new ReplaceMultipleTextCommand(ts, new List(ranges)); } } /// /// Removes lines /// internal class RemoveLinesCommand : UndoableCommand { List iLines; List prevText = new List(); /// /// Constructor /// /// Underlaying textbox /// List of ranges for replace /// Text for inserting public RemoveLinesCommand(TextSource ts, List iLines) : base(ts) { //sort iLines iLines.Sort(); // this.iLines = iLines; lastSel = sel = new RangeInfo(ts.CurrentTB.Selection); } /// /// Undo operation /// public override void Undo() { var tb = ts.CurrentTB; ts.OnTextChanging(); tb.Selection.BeginUpdate(); //tb.BeginUpdate(); for (int i = 0; i < iLines.Count; i++) { var iLine = iLines[i]; if (iLine < ts.Count) tb.Selection.Start = new Place(0, iLine); else tb.Selection.Start = new Place(ts[ts.Count - 1].Count, ts.Count - 1); InsertCharCommand.InsertLine(ts); tb.Selection.Start = new Place(0, iLine); var text = prevText[prevText.Count - i - 1]; InsertTextCommand.InsertText(text, ts); ts[iLine].IsChanged = true; if (iLine < ts.Count - 1) ts[iLine + 1].IsChanged = true; else ts[iLine - 1].IsChanged = true; if (text.Trim() != string.Empty) ts.OnTextChanged(iLine, iLine); } //tb.EndUpdate(); tb.Selection.EndUpdate(); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } /// /// Execute operation /// public override void Execute() { var tb = ts.CurrentTB; prevText.Clear(); ts.OnTextChanging(); tb.Selection.BeginUpdate(); for (int i = iLines.Count - 1; i >= 0; i--) { var iLine = iLines[i]; prevText.Add(ts[iLine].Text);//backward ts.RemoveLine(iLine); //ts.OnTextChanged(ranges[i].Start.iLine, ranges[i].End.iLine); } tb.Selection.Start = new Place(0, 0); tb.Selection.EndUpdate(); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); lastSel = new RangeInfo(tb.Selection); } public override UndoableCommand Clone() { return new RemoveLinesCommand(ts, new List(iLines)); } } /// /// Wrapper for multirange commands /// internal class MultiRangeCommand : UndoableCommand { private UndoableCommand cmd; private Range range; private List commandsByRanges = new List(); public MultiRangeCommand(UndoableCommand command) : base(command.ts) { this.cmd = command; range = ts.CurrentTB.Selection.Clone(); } public override void Execute() { commandsByRanges.Clear(); var prevSelection = range.Clone(); var iChar = -1; var iStartLine = prevSelection.Start.iLine; var iEndLine = prevSelection.End.iLine; ts.CurrentTB.Selection.ColumnSelectionMode = false; ts.CurrentTB.Selection.BeginUpdate(); ts.CurrentTB.BeginUpdate(); ts.CurrentTB.allowInsertRemoveLines = false; try { if (cmd is InsertTextCommand) ExecuteInsertTextCommand(ref iChar, (cmd as InsertTextCommand).insertedText); else if (cmd is InsertCharCommand && (cmd as InsertCharCommand).c != '\x0' && (cmd as InsertCharCommand).c != '\b')//if not DEL or BACKSPACE ExecuteInsertTextCommand(ref iChar, (cmd as InsertCharCommand).c.ToString()); else ExecuteCommand(ref iChar); } catch (ArgumentOutOfRangeException) { } finally { ts.CurrentTB.allowInsertRemoveLines = true; ts.CurrentTB.EndUpdate(); ts.CurrentTB.Selection = range; if (iChar >= 0) { ts.CurrentTB.Selection.Start = new Place(iChar, iStartLine); ts.CurrentTB.Selection.End = new Place(iChar, iEndLine); } ts.CurrentTB.Selection.ColumnSelectionMode = true; ts.CurrentTB.Selection.EndUpdate(); } } private void ExecuteInsertTextCommand(ref int iChar, string text) { var lines = text.Split('\n'); var iLine = 0; foreach (var r in range.GetSubRanges(true)) { var line = ts.CurrentTB[r.Start.iLine]; var lineIsEmpty = r.End < r.Start && line.StartSpacesCount == line.Count; if (!lineIsEmpty) { var insertedText = lines[iLine % lines.Length]; if (r.End < r.Start && insertedText != "") { //add forwarding spaces insertedText = new string(' ', r.Start.iChar - r.End.iChar) + insertedText; r.Start = r.End; } ts.CurrentTB.Selection = r; var c = new InsertTextCommand(ts, insertedText); c.Execute(); if (ts.CurrentTB.Selection.End.iChar > iChar) iChar = ts.CurrentTB.Selection.End.iChar; commandsByRanges.Add(c); } iLine++; } } private void ExecuteCommand(ref int iChar) { foreach (var r in range.GetSubRanges(false)) { ts.CurrentTB.Selection = r; var c = cmd.Clone(); c.Execute(); if (ts.CurrentTB.Selection.End.iChar > iChar) iChar = ts.CurrentTB.Selection.End.iChar; commandsByRanges.Add(c); } } public override void Undo() { ts.CurrentTB.BeginUpdate(); ts.CurrentTB.Selection.BeginUpdate(); try { for (int i = commandsByRanges.Count - 1; i >= 0; i--) commandsByRanges[i].Undo(); } finally { ts.CurrentTB.Selection.EndUpdate(); ts.CurrentTB.EndUpdate(); } ts.CurrentTB.Selection = range.Clone(); ts.CurrentTB.OnTextChanged(range); ts.CurrentTB.OnSelectionChanged(); ts.CurrentTB.Selection.ColumnSelectionMode = true; } public override UndoableCommand Clone() { throw new NotImplementedException(); } } }