1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2006 Novell, Inc. (http://www.novell.com)
23 // Peter Bartok pbartok@novell.com
31 using System.ComponentModel;
32 using System.ComponentModel.Design;
34 using System.Drawing.Text;
36 using System.Runtime.InteropServices;
38 namespace System.Windows.Forms {
39 [DefaultEvent("TextChanged")]
40 [Designer("System.Windows.Forms.Design.TextBoxBaseDesigner, " + Consts.AssemblySystem_Design)]
41 public abstract class TextBoxBase : Control {
42 #region Local Variables
43 internal HorizontalAlignment alignment;
44 internal bool accepts_tab;
45 internal bool accepts_return;
46 internal bool auto_size;
47 internal CharacterCasing character_casing;
49 internal bool hide_selection;
50 internal int max_length;
51 internal bool modified;
52 internal bool multiline;
53 internal char password_char;
54 internal bool read_only;
55 internal bool word_wrap;
56 internal Document document;
57 internal LineTag caret_tag; // tag our cursor is in
58 internal int caret_pos; // position on the line our cursor is in (can be 0 = beginning of line)
59 internal ImplicitHScrollBar hscroll;
60 internal ImplicitVScrollBar vscroll;
61 internal RichTextBoxScrollBars scrollbars;
62 internal bool richtext;
63 internal int requested_height;
64 internal int canvas_width;
65 internal int canvas_height;
66 static internal int track_width = 20;
67 static internal int track_border = 5;
68 internal DateTime click_last;
69 internal CaretSelection click_mode;
72 internal static bool draw_lines = false;
75 #endregion // Local Variables
77 #region Internal Constructor
78 // Constructor will go when complete, only for testing - pdb
79 internal TextBoxBase() {
80 alignment = HorizontalAlignment.Left;
81 accepts_return = false;
84 border_style = BorderStyle.Fixed3D;
85 character_casing = CharacterCasing.Normal;
87 hide_selection = true;
95 document = new Document(this);
96 document.WidthChanged += new EventHandler(document_WidthChanged);
97 document.HeightChanged += new EventHandler(document_HeightChanged);
98 //document.CaretMoved += new EventHandler(CaretMoved);
99 document.Wrap = false;
100 requested_height = -1;
101 click_last = DateTime.Now;
102 click_mode = CaretSelection.Position;
103 bmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
105 MouseDown += new MouseEventHandler(TextBoxBase_MouseDown);
106 MouseUp += new MouseEventHandler(TextBoxBase_MouseUp);
107 MouseMove += new MouseEventHandler(TextBoxBase_MouseMove);
108 SizeChanged += new EventHandler(TextBoxBase_SizeChanged);
109 FontChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
110 ForeColorChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
111 MouseWheel += new MouseEventHandler(TextBoxBase_MouseWheel);
113 scrollbars = RichTextBoxScrollBars.None;
115 hscroll = new ImplicitHScrollBar();
116 hscroll.ValueChanged += new EventHandler(hscroll_ValueChanged);
117 hscroll.control_style &= ~ControlStyles.Selectable;
118 hscroll.Enabled = false;
119 hscroll.Visible = false;
121 vscroll = new ImplicitVScrollBar();
122 vscroll.ValueChanged += new EventHandler(vscroll_ValueChanged);
123 vscroll.control_style &= ~ControlStyles.Selectable;
124 vscroll.Enabled = false;
125 vscroll.Visible = false;
128 this.Controls.AddImplicit (hscroll);
129 this.Controls.AddImplicit (vscroll);
132 //SetStyle(ControlStyles.ResizeRedraw, true);
133 SetStyle(ControlStyles.UserPaint | ControlStyles.StandardClick, false);
135 canvas_width = ClientSize.Width;
136 canvas_height = ClientSize.Height;
138 CalculateScrollBars();
140 #endregion // Internal Constructor
142 #region Private and Internal Methods
143 internal string CaseAdjust(string s) {
144 if (character_casing == CharacterCasing.Normal) {
147 if (character_casing == CharacterCasing.Lower) {
153 #endregion // Private and Internal Methods
155 #region Public Instance Properties
156 [DefaultValue(false)]
157 public bool AcceptsTab {
163 if (value != accepts_tab) {
165 OnAcceptsTabChanged(EventArgs.Empty);
172 [RefreshProperties(RefreshProperties.Repaint)]
173 public virtual bool AutoSize {
179 if (value != auto_size) {
182 if (PreferredHeight != ClientSize.Height) {
183 ClientSize = new Size(ClientSize.Width, PreferredHeight);
186 OnAutoSizeChanged(EventArgs.Empty);
192 public override System.Drawing.Color BackColor {
194 return base.BackColor;
197 base.BackColor = value;
202 [EditorBrowsable(EditorBrowsableState.Never)]
203 public override System.Drawing.Image BackgroundImage {
205 return base.BackgroundImage;
208 base.BackgroundImage = value;
212 [DefaultValue(BorderStyle.Fixed3D)]
214 public BorderStyle BorderStyle {
215 get { return InternalBorderStyle; }
217 InternalBorderStyle = value;
218 OnBorderStyleChanged(EventArgs.Empty);
223 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
224 public bool CanUndo {
231 public override System.Drawing.Color ForeColor {
233 return base.ForeColor;
236 base.ForeColor = value;
241 public bool HideSelection {
243 return hide_selection;
247 if (value != hide_selection) {
248 hide_selection = value;
249 OnHideSelectionChanged(EventArgs.Empty);
251 if (hide_selection) {
252 document.selection_visible = false;
254 document.selection_visible = true;
256 document.InvalidateSelectionArea();
261 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
262 [Editor("System.Windows.Forms.Design.StringArrayEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
264 public string[] Lines {
271 lines = new string[l];
273 for (i = 1; i <= l; i++) {
274 lines[i - 1] = document.GetLine(i).text.ToString();
288 brush = ThemeEngine.Current.ResPool.GetSolidBrush(this.ForeColor);
290 for (i = 0; i < l; i++) {
291 document.Add(i+1, CaseAdjust(value[i]), alignment, Font, brush);
294 OnTextChanged(EventArgs.Empty);
298 [DefaultValue(32767)]
300 public virtual int MaxLength {
302 if (max_length == 2147483646) { // We don't distinguish between single and multi-line limits
309 if (value != max_length) {
316 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
317 public bool Modified {
323 if (value != modified) {
325 OnModifiedChanged(EventArgs.Empty);
330 [DefaultValue(false)]
332 [RefreshProperties(RefreshProperties.All)]
333 public virtual bool Multiline {
339 if (value != multiline) {
341 // Make sure we update our size; the user may have already set the size before going to multiline
342 if (multiline && requested_height != -1) {
343 Height = requested_height;
344 requested_height = -1;
347 OnMultilineChanged(EventArgs.Empty);
350 document.multiline = multiline;
353 document.Wrap = word_wrap;
354 document.PasswordChar = "";
357 document.Wrap = false;
358 if (this.password_char != '\0') {
359 document.PasswordChar = password_char.ToString();
361 document.PasswordChar = "";
368 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
369 [EditorBrowsable(EditorBrowsableState.Advanced)]
370 public int PreferredHeight {
372 return this.Font.Height + 7; // FIXME - consider border style as well
376 [DefaultValue(false)]
377 public bool ReadOnly {
383 if (value != read_only) {
385 OnReadOnlyChanged(EventArgs.Empty);
391 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
392 public virtual string SelectedText {
394 return document.GetSelection();
399 document.ReplaceSelection(CaseAdjust(value));
401 OnTextChanged(EventArgs.Empty);
407 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
408 public virtual int SelectionLength {
410 return document.SelectionLength();
420 start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
422 document.CharIndexToLineTag(start + value, out line, out tag, out pos);
423 document.SetSelectionEnd(line, pos);
424 document.PositionCaret(line, pos);
426 document.SetSelectionEnd(document.selection_start.line, document.selection_start.pos);
427 document.PositionCaret(document.selection_start.line, document.selection_start.pos);
433 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
434 public int SelectionStart {
438 index = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
448 document.CharIndexToLineTag(value, out line, out tag, out pos);
449 document.SetSelectionStart(line, pos);
454 public override string Text {
456 if (document == null || document.Root == null || document.Root.text == null) {
461 return document.Root.text.ToString();
466 sb = new StringBuilder();
468 for (i = 1; i < document.Lines; i++) {
469 sb.Append(document.GetLine(i).text.ToString() + Environment.NewLine);
471 sb.Append(document.GetLine(document.Lines).text.ToString());
472 return sb.ToString();
477 if (value == base.Text) {
481 if ((value != null) && (value != "")) {
487 lines = value.Split(new char[] {'\n'});
489 for (int i = 0; i < lines.Length; i++) {
490 if (lines[i].EndsWith("\r")) {
491 lines[i] = lines[i].Substring(0, lines[i].Length - 1);
496 line = document.GetLine(1);
497 document.SetSelectionStart(line, 0);
499 line = document.GetLine(document.Lines);
500 document.SetSelectionEnd(line, line.text.Length);
501 document.PositionCaret(line, line.text.Length);
504 document.Add(1, CaseAdjust(value), alignment, Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
506 line = document.GetLine(1);
507 document.SetSelectionStart(line, 0);
508 document.SetSelectionEnd(line, value.Length);
509 document.PositionCaret(line, value.Length);
516 // Not needed, base.Text already fires it
517 // OnTextChanged(EventArgs.Empty);
522 public virtual int TextLength {
524 if (document == null || document.Root == null || document.Root.text == null) {
529 return document.Root.text.Length;
535 for (i = 1; i < document.Lines; i++) {
536 total += document.GetLine(i).text.Length + Environment.NewLine.Length;
538 total += document.GetLine(i).text.Length;
547 public bool WordWrap {
553 if (value != word_wrap) {
556 document.Wrap = value;
561 #endregion // Public Instance Properties
563 #region Protected Instance Properties
564 protected override CreateParams CreateParams {
566 return base.CreateParams;
570 protected override System.Drawing.Size DefaultSize {
572 return new Size(100, 20);
575 #endregion // Protected Instance Properties
577 #region Public Instance Methods
578 public void AppendText(string text) {
580 // Grab the formatting for the last element
581 document.MoveCaret(CaretDirection.CtrlEnd);
583 if (document.CaretTag.next != null) {
584 document.CaretTag = document.CaretTag.next;
586 document.Insert(document.CaretLine, document.CaretTag, document.CaretPosition, false, text);
590 document.MoveCaret(CaretDirection.CtrlEnd);
591 document.InsertStringAtCaret(text, true);
596 document.MoveCaret(CaretDirection.CtrlEnd);
597 document.SetSelectionStart(document.CaretLine, document.CaretPosition);
598 document.SetSelectionEnd(document.CaretLine, document.CaretPosition);
600 OnTextChanged(EventArgs.Empty);
603 public void Clear() {
607 public void ClearUndo() {
608 document.undo.Clear();
614 o = new DataObject(DataFormats.Text, SelectedText);
615 if (this is RichTextBox) {
616 o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
618 Clipboard.SetDataObject(o);
624 o = new DataObject(DataFormats.Text, SelectedText);
625 if (this is RichTextBox) {
626 o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
628 Clipboard.SetDataObject(o);
629 document.ReplaceSelection("");
632 public void Paste() {
636 public void ScrollToCaret() {
637 this.CaretMoved(this, EventArgs.Empty);
640 public void Select(int start, int length) {
641 SelectionStart = start;
642 SelectionLength = length;
646 public void SelectAll() {
649 last = document.GetLine(document.Lines);
650 document.SetSelectionStart(document.GetLine(1), 0);
651 document.SetSelectionEnd(last, last.text.Length);
654 public override string ToString() {
659 document.undo.Undo();
661 #endregion // Public Instance Methods
663 #region Protected Instance Methods
664 protected override void CreateHandle() {
665 base.CreateHandle ();
668 protected override bool IsInputKey(Keys keyData) {
669 if ((keyData & Keys.Alt) != 0) {
670 return base.IsInputKey(keyData);
673 switch (keyData & Keys.KeyCode) {
675 if (multiline && accepts_return) {
682 if (accepts_tab && multiline) {
683 if ((keyData & Keys.Control) == 0) {
705 protected virtual void OnAcceptsTabChanged(EventArgs e) {
706 if (AcceptsTabChanged != null) {
707 AcceptsTabChanged(this, e);
711 protected virtual void OnAutoSizeChanged(EventArgs e) {
712 if (AutoSizeChanged != null) {
713 AutoSizeChanged(this, e);
717 protected virtual void OnBorderStyleChanged(EventArgs e) {
718 if (BorderStyleChanged != null) {
719 BorderStyleChanged(this, e);
723 protected override void OnFontChanged(EventArgs e) {
724 base.OnFontChanged (e);
727 if (PreferredHeight != ClientSize.Height) {
728 Height = PreferredHeight;
733 protected override void OnHandleCreated(EventArgs e) {
734 base.OnHandleCreated (e);
737 protected override void OnHandleDestroyed(EventArgs e) {
738 base.OnHandleDestroyed (e);
741 protected virtual void OnHideSelectionChanged(EventArgs e) {
742 if (HideSelectionChanged != null) {
743 HideSelectionChanged(this, e);
747 protected virtual void OnModifiedChanged(EventArgs e) {
748 if (ModifiedChanged != null) {
749 ModifiedChanged(this, e);
753 protected virtual void OnMultilineChanged(EventArgs e) {
754 if (MultilineChanged != null) {
755 MultilineChanged(this, e);
759 protected virtual void OnReadOnlyChanged(EventArgs e) {
760 if (ReadOnlyChanged != null) {
761 ReadOnlyChanged(this, e);
765 protected override bool ProcessDialogKey(Keys keyData) {
766 return base.ProcessDialogKey(keyData);
769 private bool ProcessKey(Keys keyData) {
773 control = (Control.ModifierKeys & Keys.Control) != 0;
774 shift = (Control.ModifierKeys & Keys.Shift) != 0;
776 switch (keyData & Keys.KeyCode) {
777 case Keys.X: { // Cut (Ctrl-X)
785 case Keys.C: { // Copy (Ctrl-C)
793 case Keys.V: { // Paste (Ctrl-V)
795 return Paste(null, true);
800 case Keys.Z: { // Undo (Ctrl-Z)
810 document.MoveCaret(CaretDirection.WordBack);
812 if (!document.selection_visible || shift) {
813 document.MoveCaret(CaretDirection.CharBack);
815 document.MoveCaret(CaretDirection.SelectionStart);
820 document.SetSelectionToCaret(true);
822 document.SetSelectionToCaret(false);
825 CaretMoved(this, null);
831 document.MoveCaret(CaretDirection.WordForward);
833 if (!document.selection_visible || shift) {
834 document.MoveCaret(CaretDirection.CharForward);
836 document.MoveCaret(CaretDirection.SelectionEnd);
840 document.SetSelectionToCaret(true);
842 document.SetSelectionToCaret(false);
845 CaretMoved(this, null);
851 if (document.CaretPosition == 0) {
852 document.MoveCaret(CaretDirection.LineUp);
854 document.MoveCaret(CaretDirection.Home);
857 document.MoveCaret(CaretDirection.LineUp);
860 if ((Control.ModifierKeys & Keys.Shift) == 0) {
861 document.SetSelectionToCaret(true);
863 document.SetSelectionToCaret(false);
866 CaretMoved(this, null);
872 if (document.CaretPosition == document.CaretLine.Text.Length) {
873 document.MoveCaret(CaretDirection.LineDown);
875 document.MoveCaret(CaretDirection.End);
878 document.MoveCaret(CaretDirection.LineDown);
881 if ((Control.ModifierKeys & Keys.Shift) == 0) {
882 document.SetSelectionToCaret(true);
884 document.SetSelectionToCaret(false);
887 CaretMoved(this, null);
892 if ((Control.ModifierKeys & Keys.Control) != 0) {
893 document.MoveCaret(CaretDirection.CtrlHome);
895 document.MoveCaret(CaretDirection.Home);
898 if ((Control.ModifierKeys & Keys.Shift) == 0) {
899 document.SetSelectionToCaret(true);
901 document.SetSelectionToCaret(false);
904 CaretMoved(this, null);
909 if ((Control.ModifierKeys & Keys.Control) != 0) {
910 document.MoveCaret(CaretDirection.CtrlEnd);
912 document.MoveCaret(CaretDirection.End);
915 if ((Control.ModifierKeys & Keys.Shift) == 0) {
916 document.SetSelectionToCaret(true);
918 document.SetSelectionToCaret(false);
921 CaretMoved(this, null);
926 // ignoring accepts_return, fixes bug #76355
927 if (!read_only && multiline && (accepts_return || (FindForm() != null && FindForm().AcceptButton == null) || ((Control.ModifierKeys & Keys.Control) != 0))) {
930 if (document.selection_visible) {
931 document.ReplaceSelection("");
933 document.SetSelectionToCaret(true);
935 line = document.CaretLine;
937 document.Split(document.CaretLine, document.CaretTag, document.CaretPosition, false);
938 OnTextChanged(EventArgs.Empty);
939 document.UpdateView(line, 2, 0);
940 document.MoveCaret(CaretDirection.CharForward);
941 CaretMoved(this, null);
948 if (!read_only && accepts_tab && multiline) {
949 document.InsertChar(document.CaretLine, document.CaretPosition, '\t');
950 if (document.selection_visible) {
951 document.ReplaceSelection("");
953 document.SetSelectionToCaret(true);
955 OnTextChanged(EventArgs.Empty);
956 CaretMoved(this, null);
968 // delete only deletes on the line, doesn't do the combine
969 if (document.selection_visible) {
970 document.ReplaceSelection("");
972 document.SetSelectionToCaret(true);
974 if (document.CaretPosition == 0) {
975 if (document.CaretLine.LineNo > 1) {
979 line = document.GetLine(document.CaretLine.LineNo - 1);
980 new_caret_pos = line.text.Length;
982 document.Combine(line, document.CaretLine);
983 document.UpdateView(line, 1, 0);
984 document.PositionCaret(line, new_caret_pos);
985 //document.MoveCaret(CaretDirection.CharForward);
986 document.UpdateCaret();
987 OnTextChanged(EventArgs.Empty);
990 if (!control || document.CaretPosition == 0) {
991 document.DeleteChar(document.CaretTag, document.CaretPosition, false);
992 document.MoveCaret(CaretDirection.CharBack);
996 start_pos = document.CaretPosition - 1;
998 while ((start_pos > 0) && !Document.IsWordSeparator(document.CaretLine.Text[start_pos - 1])) {
1001 document.DeleteChars(document.CaretTag, start_pos, document.CaretPosition - start_pos);
1002 document.PositionCaret(document.CaretLine, start_pos);
1004 document.UpdateCaret();
1005 OnTextChanged(EventArgs.Empty);
1007 CaretMoved(this, null);
1022 // FIXME - need overwrite/insert toggle?
1036 if (document.selection_visible) {
1037 document.ReplaceSelection("");
1039 // DeleteChar only deletes on the line, doesn't do the combine
1040 if (document.CaretPosition == document.CaretLine.Text.Length) {
1041 if (document.CaretLine.LineNo < document.Lines) {
1044 line = document.GetLine(document.CaretLine.LineNo + 1);
1045 document.Combine(document.CaretLine, line);
1046 document.UpdateView(document.CaretLine, 2, 0);
1052 check_first = document.GetLine(document.CaretLine.LineNo);
1053 check_second = document.GetLine(check_first.line_no + 1);
1055 Console.WriteLine("Post-UpdateView: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
1058 // Caret doesn't move
1062 document.DeleteChar(document.CaretTag, document.CaretPosition, true);
1066 end_pos = document.CaretPosition;
1068 while ((end_pos < document.CaretLine.Text.Length) && !Document.IsWordSeparator(document.CaretLine.Text[end_pos])) {
1072 if (end_pos < document.CaretLine.Text.Length) {
1075 document.DeleteChars(document.CaretTag, document.CaretPosition, end_pos - document.CaretPosition);
1080 OnTextChanged(EventArgs.Empty);
1081 document.AlignCaret();
1082 document.UpdateCaret();
1083 CaretMoved(this, null);
1090 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
1091 // Make sure we don't get sized bigger than we want to be
1094 if (height != PreferredHeight) {
1095 requested_height = height;
1096 height = PreferredHeight;
1097 specified |= BoundsSpecified.Height;
1102 base.SetBoundsCore (x, y, width, height, specified);
1104 TextBoxBase_SizeChanged(this, EventArgs.Empty);
1105 CalculateDocument();
1108 protected override void WndProc(ref Message m) {
1109 switch ((Msg)m.Msg) {
1110 case Msg.WM_PAINT: {
1111 PaintEventArgs paint_event;
1113 paint_event = XplatUI.PaintEventStart(Handle, true);
1115 PaintControl(paint_event);
1116 XplatUI.PaintEventEnd(Handle, true);
1121 case Msg.WM_SETFOCUS: {
1123 document.CaretHasFocus();
1124 base.WndProc(ref m);
1128 case Msg.WM_KILLFOCUS: {
1130 document.CaretLostFocus();
1131 base.WndProc(ref m);
1135 case Msg.WM_KEYDOWN: {
1136 if (ProcessKeyMessage(ref m) || ProcessKey((Keys)m.WParam.ToInt32() | XplatUI.State.ModifierKeys)) {
1137 m.Result = IntPtr.Zero;
1145 if (ProcessKeyMessage(ref m)) {
1146 m.Result = IntPtr.Zero;
1150 // Ctrl-Backspace generates a real char, whack it
1151 if (m.WParam.ToInt32() == 127) {
1152 base.WndProc(ref m);
1156 if (!read_only && (m.WParam.ToInt32() >= 32)) { // FIXME, tabs should probably go through
1157 if (document.selection_visible) {
1158 document.ReplaceSelection("");
1161 switch (character_casing) {
1162 case CharacterCasing.Normal: {
1163 if (document.Length < max_length) {
1164 document.InsertCharAtCaret((char)m.WParam, true);
1165 OnTextChanged(EventArgs.Empty);
1166 CaretMoved(this, null);
1168 XplatUI.AudibleAlert();
1173 case CharacterCasing.Lower: {
1174 if (document.Length < max_length) {
1175 document.InsertCharAtCaret(Char.ToLower((char)m.WParam), true);
1176 OnTextChanged(EventArgs.Empty);
1177 CaretMoved(this, null);
1179 XplatUI.AudibleAlert();
1184 case CharacterCasing.Upper: {
1185 if (document.Length < max_length) {
1186 document.InsertCharAtCaret(Char.ToUpper((char)m.WParam), true);
1187 OnTextChanged(EventArgs.Empty);
1188 CaretMoved(this, null);
1190 XplatUI.AudibleAlert();
1200 base.WndProc(ref m);
1206 #endregion // Protected Instance Methods
1209 public event EventHandler AcceptsTabChanged;
1210 public event EventHandler AutoSizeChanged;
1212 [EditorBrowsable(EditorBrowsableState.Never)]
1213 public new event EventHandler BackgroundImageChanged {
1214 add { base.BackgroundImageChanged += value; }
1215 remove { base.BackgroundImageChanged -= value; }
1217 public event EventHandler BorderStyleChanged;
1219 [EditorBrowsable(EditorBrowsableState.Advanced)]
1220 public event EventHandler Click;
1221 public event EventHandler HideSelectionChanged;
1222 public event EventHandler ModifiedChanged;
1223 public event EventHandler MultilineChanged;
1225 [EditorBrowsable(EditorBrowsableState.Never)]
1226 public event PaintEventHandler Paint;
1227 public event EventHandler ReadOnlyChanged;
1229 internal event EventHandler HScrolled;
1230 internal event EventHandler VScrolled;
1231 #endregion // Events
1233 #region Private Methods
1234 internal Document Document {
1244 internal Graphics CreateGraphicsInternal() {
1245 if (IsHandleCreated) {
1246 return base.CreateGraphics();
1249 return Graphics.FromImage(bmp);
1256 private void PaintControl(PaintEventArgs pevent) {
1258 pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), pevent.ClipRectangle);
1259 pevent.Graphics.TextRenderingHint=TextRenderingHint.AntiAlias;
1261 // Draw the viewable document
1262 document.Draw(pevent.Graphics, pevent.ClipRectangle);
1264 Rectangle rect = ClientRectangle;
1267 //pevent.Graphics.DrawRectangle(ThemeEngine.Current.ResPool.GetPen(ThemeEngine.Current.ColorControlDark), rect);
1276 p = new Pen(Color.Red, 1);
1278 // First, figure out from what line to what line we need to draw
1279 start = document.GetLineByPixel(pevent.ClipRectangle.Top - document.ViewPortY, false).line_no;
1280 end = document.GetLineByPixel(pevent.ClipRectangle.Bottom - document.ViewPortY, false).line_no;
1282 //Console.WriteLine("Starting drawing on line '{0}'", document.GetLine(start));
1283 //Console.WriteLine("Ending drawing on line '{0}'", document.GetLine(end));
1286 while (line_no <= end) {
1287 line = document.GetLine(line_no);
1290 for (int i = 0; i < line.text.Length; i++) {
1291 pevent.Graphics.DrawLine(p, (int)line.widths[i] - document.ViewPortX, line.Y - document.ViewPortY, (int)line.widths[i] - document.ViewPortX, line.Y + line.height - document.ViewPortY);
1300 private void TextBoxBase_MouseDown(object sender, MouseEventArgs e) {
1301 if (e.Button == MouseButtons.Left) {
1304 interval = DateTime.Now - click_last;
1305 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1306 this.Capture = true;
1308 // Handle place caret/select word/select line behaviour
1309 if (e.Clicks == 1) {
1310 if (SystemInformation.DoubleClickTime < interval.TotalMilliseconds) {
1312 Console.WriteLine("Single Click Invalidating from char {0} to char {1} ({2})", document.selection_start.pos, document.selection_end.pos, document.selection_start.line.text.ToString(document.selection_start.pos, document.selection_end.pos - document.selection_start.pos));
1314 document.SetSelectionToCaret(true);
1315 click_mode = CaretSelection.Position;
1318 Console.WriteLine("Tripple Click Selecting line");
1320 document.ExpandSelection(CaretSelection.Line, false);
1321 click_mode = CaretSelection.Line;
1324 // We select the line if the word is already selected, and vice versa
1325 if (click_mode != CaretSelection.Word) {
1326 if (click_mode == CaretSelection.Line) {
1327 document.Invalidate(document.selection_start.line, 0, document.selection_start.line, document.selection_start.line.text.Length);
1329 click_mode = CaretSelection.Word;
1330 document.ExpandSelection(CaretSelection.Word, false); // Setting initial selection
1332 click_mode = CaretSelection.Line;
1333 document.ExpandSelection(CaretSelection.Line, false); // Setting initial selection
1338 click_last = DateTime.Now;
1347 if (e.Button == MouseButtons.Right) {
1348 draw_lines = !draw_lines;
1350 Console.WriteLine("SelectedText: {0}, length {1}", this.SelectedText, this.SelectionLength);
1351 Console.WriteLine("Selection start: {0}", this.SelectionStart);
1353 this.SelectionStart = 10;
1354 this.SelectionLength = 5;
1359 tag = document.FindTag(e.X + document.ViewPortX, e.Y + document.ViewPortY, out pos, false);
1361 Console.WriteLine("Click found tag {0}, character {1}", tag, pos);
1364 case 4: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, new Font("impact", 20, FontStyle.Bold, GraphicsUnit.Pixel), ThemeEngine.Current.ResPool.GetSolidBrush(Color.Red)); break;
1365 case 1: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, new Font("arial unicode ms", 24, FontStyle.Italic, GraphicsUnit.Pixel), ThemeEngine.Current.ResPool.GetSolidBrush(Color.DarkGoldenrod)); break;
1366 case 2: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, new Font("arial", 10, FontStyle.Regular, GraphicsUnit.Pixel), ThemeEngine.Current.ResPool.GetSolidBrush(Color.Aquamarine)); break;
1367 case 3: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, new Font("times roman", 16, FontStyle.Underline, GraphicsUnit.Pixel), ThemeEngine.Current.ResPool.GetSolidBrush(Color.Turquoise)); break;
1368 case 0: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, new Font("times roman", 64, FontStyle.Italic | FontStyle.Bold, GraphicsUnit.Pixel), ThemeEngine.Current.ResPool.GetSolidBrush(Color.LightSeaGreen)); break;
1369 case 5: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, ((TextBoxBase)sender).Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor)); break;
1376 // Update/Recalculate what we see
1377 document.UpdateView(line, 0);
1379 // Make sure our caret is properly positioned and sized
1380 document.AlignCaret();
1384 private void TextBoxBase_MouseUp(object sender, MouseEventArgs e) {
1385 this.Capture = false;
1386 if (e.Button == MouseButtons.Left) {
1387 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1388 if (click_mode == CaretSelection.Position) {
1389 document.SetSelectionToCaret(false);
1390 document.DisplayCaret();
1392 document.ExpandSelection(click_mode, true);
1398 private void TextBoxBase_SizeChanged(object sender, EventArgs e) {
1399 canvas_width = ClientSize.Width;
1400 canvas_height = ClientSize.Height;
1401 document.ViewPortWidth = canvas_width;
1402 document.ViewPortHeight = canvas_height;
1404 // We always move them, they just might not be displayed
1405 hscroll.Bounds = new Rectangle (ClientRectangle.Left, ClientRectangle.Bottom - hscroll.Height, ClientSize.Width - (vscroll.Visible ? SystemInformation.VerticalScrollBarWidth : 0), hscroll.Height);
1406 vscroll.Bounds = new Rectangle (ClientRectangle.Right - vscroll.Width, ClientRectangle.Top, vscroll.Width, ClientSize.Height - (hscroll.Visible ? SystemInformation.HorizontalScrollBarHeight : 0));
1410 private void TextBoxBase_MouseWheel(object sender, MouseEventArgs e) {
1415 if (!vscroll.Enabled) {
1420 line_no = document.GetLineByPixel(document.ViewPortY, false).line_no + SystemInformation.MouseWheelScrollLines;
1421 if (line_no > document.Lines) {
1422 line_no = document.Lines;
1425 line_no = document.GetLineByPixel(document.ViewPortY, false).line_no - SystemInformation.MouseWheelScrollLines;
1431 line = document.GetLine(line_no);
1432 if (line.Y < vscroll.Maximum) {
1433 vscroll.Value = line.Y;
1435 vscroll.Value = vscroll.Maximum;
1439 internal void CalculateDocument() {
1440 if (!IsHandleCreated) {
1443 document.RecalculateDocument(CreateGraphicsInternal());
1444 CalculateScrollBars();
1445 Invalidate(); // FIXME - do we need this?
1448 internal void CalculateScrollBars() {
1449 // FIXME - need separate calculations for center and right alignment
1450 // No scrollbars for a single line
1451 if (document.Width >= ClientSize.Width) {
1452 hscroll.Enabled = true;
1453 hscroll.Minimum = 0;
1454 hscroll.Maximum = document.Width - ClientSize.Width + track_border;
1456 hscroll.Maximum = document.ViewPortWidth;
1457 hscroll.Enabled = false;
1460 if (document.Height >= ClientSize.Height) {
1461 vscroll.Enabled = true;
1462 vscroll.Minimum = 0;
1463 vscroll.Maximum = document.Height - ClientSize.Height + 1;
1465 vscroll.Maximum = document.ViewPortHeight;
1466 vscroll.Enabled = false;
1475 if ((scrollbars & RichTextBoxScrollBars.Horizontal) != 0) {
1476 if (((scrollbars & RichTextBoxScrollBars.ForcedHorizontal) != 0) || hscroll.Enabled) {
1477 hscroll.Visible = true;
1482 if ((scrollbars & RichTextBoxScrollBars.Vertical) != 0) {
1483 if (((scrollbars & RichTextBoxScrollBars.ForcedVertical) != 0) || vscroll.Enabled) {
1484 vscroll.Visible = true;
1488 if (hscroll.Visible) {
1489 vscroll.Maximum += hscroll.Height;
1490 canvas_height = ClientSize.Height - hscroll.Height;
1493 if (vscroll.Visible) {
1494 hscroll.Maximum += vscroll.Width * 2;
1495 canvas_width = ClientSize.Width - vscroll.Width * 2;
1498 TextBoxBase_SizeChanged(this, EventArgs.Empty);
1501 private void document_WidthChanged(object sender, EventArgs e) {
1502 CalculateScrollBars();
1505 private void document_HeightChanged(object sender, EventArgs e) {
1506 CalculateScrollBars();
1509 private void hscroll_ValueChanged(object sender, EventArgs e) {
1510 XplatUI.ScrollWindow(this.Handle, document.ViewPortX-this.hscroll.Value, 0, false);
1511 document.ViewPortX = this.hscroll.Value;
1512 document.UpdateCaret();
1514 if (HScrolled != null) {
1515 HScrolled(this, EventArgs.Empty);
1519 private void vscroll_ValueChanged(object sender, EventArgs e) {
1520 XplatUI.ScrollWindow(this.Handle, 0, document.ViewPortY-this.vscroll.Value, false);
1521 document.ViewPortY = this.vscroll.Value;
1522 document.UpdateCaret();
1524 if (VScrolled != null) {
1525 VScrolled(this, EventArgs.Empty);
1529 private void TextBoxBase_MouseMove(object sender, MouseEventArgs e) {
1530 // FIXME - handle auto-scrolling if mouse is to the right/left of the window
1532 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1533 if (click_mode == CaretSelection.Position) {
1534 document.SetSelectionToCaret(false);
1535 document.DisplayCaret();
1537 document.ExpandSelection(click_mode, true);
1542 private void TextBoxBase_FontOrColorChanged(object sender, EventArgs e) {
1546 // Font changes apply to the whole document
1547 for (int i = 1; i <= document.Lines; i++) {
1548 line = document.GetLine(i);
1549 LineTag.FormatText(line, 1, line.text.Length, Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
1550 document.UpdateView(line, 0);
1552 // Make sure the caret height is matching the new font height
1553 document.AlignCaret();
1557 /// <summary>Ensure the caret is always visible</summary>
1558 internal void CaretMoved(object sender, EventArgs e) {
1562 pos = document.Caret;
1563 //Console.WriteLine("Caret now at {0} (Thumb: {1}x{2}, Canvas: {3}x{4}, Document {5}x{6})", pos, hscroll.Value, vscroll.Value, canvas_width, canvas_height, document.Width, document.Height);
1565 // Handle horizontal scrolling
1566 if (document.CaretLine.alignment == HorizontalAlignment.Left) {
1567 if (pos.X < (document.ViewPortX + track_width)) {
1569 if ((hscroll.Value - track_width) >= hscroll.Minimum) {
1570 hscroll.Value -= track_width;
1572 hscroll.Value = hscroll.Minimum;
1574 } while (hscroll.Value > pos.X);
1577 if ((pos.X > (this.canvas_width + document.ViewPortX - track_width)) && (hscroll.Enabled && (hscroll.Value != hscroll.Maximum))) {
1579 if ((hscroll.Value + track_width) <= hscroll.Maximum) {
1580 hscroll.Value += track_width;
1582 hscroll.Value = hscroll.Maximum;
1584 } while (pos.X > (hscroll.Value + this.canvas_width));
1586 } else if (document.CaretLine.alignment == HorizontalAlignment.Right) {
1587 if (pos.X < document.ViewPortX) {
1588 if (pos.X > hscroll.Minimum) {
1589 hscroll.Value = pos.X;
1591 hscroll.Value = hscroll.Minimum;
1595 if ((pos.X > (this.canvas_width + document.ViewPortX)) && (hscroll.Enabled && (hscroll.Value != hscroll.Maximum))) {
1596 hscroll.Value = hscroll.Maximum;
1605 // Handle vertical scrolling
1606 height = document.CaretLine.Height + 1;
1608 if (pos.Y < document.ViewPortY) {
1609 vscroll.Value = pos.Y;
1612 if ((pos.Y + height) > (document.ViewPortY + canvas_height)) {
1613 vscroll.Value = pos.Y - canvas_height + height;
1617 internal bool Paste(DataFormats.Format format, bool obey_length) {
1621 clip = Clipboard.GetDataObject();
1625 if (format == null) {
1626 if ((this is RichTextBox) && clip.GetDataPresent(DataFormats.Rtf)) {
1627 format = DataFormats.GetFormat(DataFormats.Rtf);
1628 } else if (clip.GetDataPresent(DataFormats.UnicodeText)) {
1629 format = DataFormats.GetFormat(DataFormats.UnicodeText);
1630 } else if (clip.GetDataPresent(DataFormats.Text)) {
1631 format = DataFormats.GetFormat(DataFormats.Text);
1636 if ((format.Name == DataFormats.Rtf) && !(this is RichTextBox)) {
1640 if (!clip.GetDataPresent(format.Name)) {
1645 if (format.Name == DataFormats.Rtf) {
1646 ((RichTextBox)this).SelectedRtf = (string)clip.GetData(DataFormats.Rtf);
1648 } else if (format.Name == DataFormats.UnicodeText) {
1649 s = (string)clip.GetData(DataFormats.UnicodeText);
1650 } else if (format.Name == DataFormats.Text) {
1651 s = (string)clip.GetData(DataFormats.Text);
1657 this.SelectedText = s;
1659 if ((s.Length + document.Length) < max_length) {
1660 this.SelectedText = s;
1661 } else if (document.Length < max_length) {
1662 this.SelectedText = s.Substring(0, max_length - document.Length);
1668 #endregion // Private Methods