テキストエディタで複数行を選択しているときにTabキーを押すと各行の先頭にTab文字が入ります。ところがRichTextBoxはそのようになっていません。そこで複数行選択されているときは各行の先頭にタブ文字が入るように改良してみます。
まず行頭がどこにあるか知る必要があります。
行頭は文書の先頭と改行の次の文字と考えることができます。そこで改行の位置をまとめて取得してしまいましょう。
1 2 3 4 5 6 7 |
System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("\n"); var rets = reg.Matches(richTextBox1.Text); foreach (System.Text.RegularExpressions.Match m in rets) { // 後述 } |
これを使えばできそうです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public partial class SyncRichTextBox : UserControl { public List<int> GetSelectedLineHeads() { System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("\n"); var match = reg.Matches(richTextBoxEx1.Text); int start = richTextBoxEx1.SelectionStart; int end = start + richTextBoxEx1.SelectionLength; List<int> heads = new List<int>(); heads.Add(0); foreach (System.Text.RegularExpressions.Match m in match) { heads.Add(m.Index+1); } int head = heads.Where(x => x <= start).Max(); heads = heads.Where(x => start <= x && x < end).ToList(); heads.Add(head); heads = heads.Distinct().OrderBy(x => x).ToList(); return heads; } public int GetSelectedLineTail() { string seltext = richTextBoxEx1.SelectedText; if (seltext.Length > 0) { string last = seltext.Substring(seltext.Length - 1); if (seltext.Length > 0 && last == "\n") return richTextBoxEx1.SelectionStart + richTextBoxEx1.SelectionLength; } int a = richTextBoxEx1.Text.IndexOf("\n", richTextBoxEx1.SelectionStart + richTextBoxEx1.SelectionLength); if (a == -1) return richTextBoxEx1.Text.Length; else return a; } } |
これでタブ文字を追加する場所はわかります。あとは追加するだけ・・・
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
public partial class SyncRichTextBox : UserControl { protected override void OnKeyDown(KeyEventArgs e) { // 他のキーに関する処理は省略 if (e.KeyCode == Keys.Tab && !e.Shift) { e.Handled = true; InsertTab(); return; } } void InsertTab() { // 選択文字列がない場合は普通にその場所にタブ文字を挿入する if (SelectionLength == 0) { SetSelectedText("\t", "InsertTab"); return; } List<int> vs = GetSelectedLineHeads(); int start = vs.Min(); int len = GetSelectedLineTail() - start; SelectionStart = start; SelectionLength = len; Undobuf buf = new Undobuf(); buf.oldSelectionStart = SelectionStart; buf.oldSelectionLength = SelectionLength; buf.tabInsertIndexes = vs; buf.insertText = "\t"; buf.newSelectionStart = start; buf.newSelectionLength = len + vs.Count; buf.action = "InsertTab"; List<int> vs0 = new List<int>(); for (int i = 0; i < vs.Count; i++) { vs0.Add(vs[i] + i); } buf.tabInsertIndexesUndo = vs0; if(DoInsertTab(buf)) Data.InsertUndobuf(buf); } bool DoInsertTab(Undobuf buf) { foreach (var sync in Data.SyncRichTextBoxes) { var info = Data.GetRichTextBoxInfo(sync); if (info != null) { info.SelectionStart = sync.richTextBoxEx1.SelectionStart; } } OnTextChanging(buf); if (buf.IsCancel) return false; var vs1 = buf.tabInsertIndexes.OrderByDescending(x => x).ToList(); foreach (var sync in Data.SyncRichTextBoxes) { var info = Data.GetRichTextBoxInfo(sync); int oldStart = info.SelectionStart; sync.richTextBoxEx1.Select(buf.removeStart, buf.removeLength); if (sync.Data == this.Data && sync != this) { foreach (int i in vs1) { sync.richTextBoxEx1.Select(i, 0); sync.richTextBoxEx1.SelectedText = "\t"; } int count = vs1.Where(x => x <= oldStart).Count(); sync.richTextBoxEx1.Select(oldStart + count, 0); } else if (sync.Data == this.Data && sync == this) { foreach (int i in vs1) { sync.richTextBoxEx1.Select(i, 0); sync.richTextBoxEx1.SelectedText = "\t"; } sync.richTextBoxEx1.Select(buf.NewSelectionStart, buf.NewSelectionLength); } else if (sync.Data != this.Data) { var info = Data.GetRichTextBoxInfo(sync); if (info != null) { int i = info.SelectionStart; int count = vs1.Where(x => x <= oldStart).Count(); info.SelectionStart = i + count; } } } return true; } } public partial class Undobuf { // 以下を追加 internal List<int> tabInsertIndexes = new List<int>(); internal List<int> tabInsertIndexesUndo = new List<int>(); } |
それからUndoのことも考えないといけません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
public partial class SyncRichTextBox : UserControl { public void Undo() { var buf = Data.GetUndobuf(); buf.IsUndo = true; if (buf.tabInsertIndexesUndo.Count > 0) UndoInsertTab(buf); else DoUndo(buf); buf.IsUndo = false; Data.MoveToRedobufs(); } public void Redo() { var buf = Data.GetRedobuf(); if (buf.tabInsertIndexes.Count > 0) DoInsertTab(buf); else DoUndo(buf); Data.MoveToUndobufs(); } void UndoInsertTab(Undobuf buf) { var vs1 = buf.tabInsertIndexesUndo.OrderByDescending(x => x).ToList(); foreach (var sync in Data.SyncRichTextBoxes) { int oldStart = sync.richTextBoxEx1.SelectionStart; int oldLen = sync.richTextBoxEx1.SelectionLength; sync.richTextBoxEx1.Select(buf.removeStart, buf.removeLength); if (sync.Data == this.Data && sync != this) { foreach (int i in vs1) { sync.richTextBoxEx1.Select(i, 1); sync.richTextBoxEx1.SelectedText = ""; } int count = vs1.Where(x => x <= oldStart).Count(); sync.richTextBoxEx1.Select(oldStart - count, 0); } else if (sync.Data == this.Data && sync == this) { foreach (int i in vs1) { sync.richTextBoxEx1.Select(i, 1); sync.richTextBoxEx1.SelectedText = ""; } sync.richTextBoxEx1.Select(buf.NewSelectionStart, buf.NewSelectionLength); } else if (sync.Data != this.Data) { var info = Data.GetRichTextBoxInfo(sync); if (info != null) { int i = info.SelectionStart; int count = vs1.Where(x => x <= oldStart).Count(); info.SelectionStart = i - count; } } } } } |
テストしてみるとちゃんと動きます。