Shouldn't select the text in this path.
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TextBoxBase.cs
blob3cc2b2ffa5f677a3f50e7bad2cb0eac6027086b1
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:
8 //
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)
22 // Authors:
23 // Peter Bartok pbartok@novell.com
27 // NOT COMPLETE
28 #undef Debug
29 #undef DebugClick
31 using System.ComponentModel;
32 using System.ComponentModel.Design;
33 using System.Drawing;
34 using System.Drawing.Text;
35 using System.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 bool backcolor_set;
48 internal CharacterCasing character_casing;
49 internal bool undo;
50 internal bool hide_selection;
51 internal int max_length;
52 internal bool modified;
53 internal bool multiline;
54 internal char password_char;
55 internal bool read_only;
56 internal bool word_wrap;
57 internal Document document;
58 internal LineTag caret_tag; // tag our cursor is in
59 internal int caret_pos; // position on the line our cursor is in (can be 0 = beginning of line)
60 internal ImplicitHScrollBar hscroll;
61 internal ImplicitVScrollBar vscroll;
62 internal RichTextBoxScrollBars scrollbars;
63 internal Timer scroll_timer;
64 internal bool richtext;
65 internal bool show_selection; // set to true to always show selection, even if no focus is set
66 internal int selection_length; // set to the user-specified selection length, or -1 if none
67 internal int requested_height;
68 internal int canvas_width;
69 internal int canvas_height;
70 static internal int track_width = 2; //
71 static internal int track_border = 5; //
72 internal DateTime click_last;
73 internal CaretSelection click_mode;
74 internal Bitmap bmp;
75 #if Debug
76 internal static bool draw_lines = false;
77 #endif
79 #endregion // Local Variables
81 #region Internal Constructor
82 // Constructor will go when complete, only for testing - pdb
83 internal TextBoxBase() {
84 alignment = HorizontalAlignment.Left;
85 accepts_return = false;
86 accepts_tab = false;
87 auto_size = true;
88 border_style = BorderStyle.Fixed3D;
89 character_casing = CharacterCasing.Normal;
90 undo = false;
91 hide_selection = true;
92 max_length = 32767;
93 modified = false;
94 multiline = false;
95 password_char = '\0';
96 read_only = false;
97 word_wrap = true;
98 richtext = false;
99 show_selection = false;
100 document = new Document(this);
101 document.WidthChanged += new EventHandler(document_WidthChanged);
102 document.HeightChanged += new EventHandler(document_HeightChanged);
103 //document.CaretMoved += new EventHandler(CaretMoved);
104 document.Wrap = false;
105 requested_height = -1;
106 click_last = DateTime.Now;
107 click_mode = CaretSelection.Position;
108 bmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
110 MouseDown += new MouseEventHandler(TextBoxBase_MouseDown);
111 MouseUp += new MouseEventHandler(TextBoxBase_MouseUp);
112 MouseMove += new MouseEventHandler(TextBoxBase_MouseMove);
113 SizeChanged += new EventHandler(TextBoxBase_SizeChanged);
114 FontChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
115 ForeColorChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
116 MouseWheel += new MouseEventHandler(TextBoxBase_MouseWheel);
118 scrollbars = RichTextBoxScrollBars.None;
120 hscroll = new ImplicitHScrollBar();
121 hscroll.ValueChanged += new EventHandler(hscroll_ValueChanged);
122 hscroll.control_style &= ~ControlStyles.Selectable;
123 hscroll.Enabled = false;
124 hscroll.Visible = false;
125 hscroll.Maximum = Int32.MaxValue;
127 vscroll = new ImplicitVScrollBar();
128 vscroll.ValueChanged += new EventHandler(vscroll_ValueChanged);
129 vscroll.control_style &= ~ControlStyles.Selectable;
130 vscroll.Enabled = false;
131 vscroll.Visible = false;
132 vscroll.Maximum = Int32.MaxValue;
134 SuspendLayout ();
135 this.Controls.AddImplicit (hscroll);
136 this.Controls.AddImplicit (vscroll);
137 ResumeLayout ();
139 SetStyle(ControlStyles.UserPaint | ControlStyles.StandardClick, false);
140 #if NET_2_0
141 SetStyle(ControlStyles.UseTextForAccessibility, false);
142 #endif
144 canvas_width = ClientSize.Width;
145 canvas_height = ClientSize.Height;
146 document.ViewPortWidth = canvas_width;
147 document.ViewPortHeight = canvas_height;
149 Cursor = Cursors.IBeam;
151 CalculateScrollBars();
153 #endregion // Internal Constructor
155 #region Private and Internal Methods
156 internal string CaseAdjust(string s) {
157 if (character_casing == CharacterCasing.Normal) {
158 return s;
160 if (character_casing == CharacterCasing.Lower) {
161 return s.ToLower();
162 } else {
163 return s.ToUpper();
167 internal override void HandleClick(int clicks, MouseEventArgs me) {
168 // MS seems to fire the click event in spite of the styles they set
169 control_style |= ControlStyles.StandardClick | ControlStyles.StandardDoubleClick;
170 base.HandleClick (clicks, me);
171 control_style ^= ControlStyles.StandardClick | ControlStyles.StandardDoubleClick;
174 #endregion // Private and Internal Methods
176 #region Public Instance Properties
177 [DefaultValue(false)]
178 [MWFCategory("Behavior")]
179 public bool AcceptsTab {
180 get {
181 return accepts_tab;
184 set {
185 if (value != accepts_tab) {
186 accepts_tab = value;
187 OnAcceptsTabChanged(EventArgs.Empty);
192 [DefaultValue(true)]
193 [Localizable(true)]
194 [RefreshProperties(RefreshProperties.Repaint)]
195 [MWFCategory("Behavior")]
196 public virtual bool AutoSize {
197 get {
198 return auto_size;
201 set {
202 if (value != auto_size) {
203 auto_size = value;
204 if (auto_size) {
205 if (PreferredHeight != ClientSize.Height) {
206 ClientSize = new Size(ClientSize.Width, PreferredHeight);
209 OnAutoSizeChanged(EventArgs.Empty);
214 [DispId(-501)]
215 public override System.Drawing.Color BackColor {
216 get {
217 return base.BackColor;
219 set {
220 if (value != ThemeEngine.Current.ColorWindow) {
221 backcolor_set = true;
222 } else {
223 backcolor_set = false;
225 base.BackColor = value;
229 [Browsable(false)]
230 [EditorBrowsable(EditorBrowsableState.Never)]
231 public override System.Drawing.Image BackgroundImage {
232 get {
233 return base.BackgroundImage;
235 set {
236 base.BackgroundImage = value;
240 [DefaultValue(BorderStyle.Fixed3D)]
241 [DispId(-504)]
242 [MWFCategory("Appearance")]
243 public BorderStyle BorderStyle {
244 get { return InternalBorderStyle; }
245 set {
246 InternalBorderStyle = value;
247 OnBorderStyleChanged(EventArgs.Empty);
251 [Browsable(false)]
252 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
253 public bool CanUndo {
254 get {
255 return undo;
259 [DispId(-513)]
260 public override System.Drawing.Color ForeColor {
261 get {
262 return base.ForeColor;
264 set {
265 base.ForeColor = value;
269 [DefaultValue(true)]
270 [MWFCategory("Behavior")]
271 public bool HideSelection {
272 get {
273 return hide_selection;
276 set {
277 if (value != hide_selection) {
278 hide_selection = value;
279 OnHideSelectionChanged(EventArgs.Empty);
281 if (hide_selection) {
282 document.selection_visible = false;
283 } else {
284 document.selection_visible = true;
286 document.InvalidateSelectionArea();
291 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
292 [Editor("System.Windows.Forms.Design.StringArrayEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
293 [Localizable(true)]
294 [MWFCategory("Appearance")]
295 public string[] Lines {
296 get {
297 string[] lines;
298 int i;
299 int l;
301 l = document.Lines;
303 // Handle empty document
304 if ((l == 1) && (document.GetLine(1).text.Length == 0)) {
305 return new string[0];
308 lines = new string[l];
310 for (i = 1; i <= l; i++) {
311 lines[i - 1] = document.GetLine(i).text.ToString();
314 return lines;
317 set {
318 int i;
319 int l;
320 Brush brush;
322 document.Empty();
324 l = value.Length;
325 brush = ThemeEngine.Current.ResPool.GetSolidBrush(this.ForeColor);
327 for (i = 0; i < l; i++) {
328 document.Add(i+1, CaseAdjust(value[i]), alignment, Font, brush);
330 CalculateDocument();
331 OnTextChanged(EventArgs.Empty);
335 [DefaultValue(32767)]
336 [Localizable(true)]
337 [MWFCategory("Behavior")]
338 public virtual int MaxLength {
339 get {
340 if (max_length == 2147483646) { // We don't distinguish between single and multi-line limits
341 return 0;
343 return max_length;
346 set {
347 if (value != max_length) {
348 max_length = value;
353 [Browsable(false)]
354 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
355 public bool Modified {
356 get {
357 return modified;
360 set {
361 if (value != modified) {
362 modified = value;
363 OnModifiedChanged(EventArgs.Empty);
368 [DefaultValue(false)]
369 [Localizable(true)]
370 [RefreshProperties(RefreshProperties.All)]
371 [MWFCategory("Behavior")]
372 public virtual bool Multiline {
373 get {
374 return multiline;
377 set {
378 if (value != multiline) {
379 multiline = value;
380 // Make sure we update our size; the user may have already set the size before going to multiline
381 if (multiline && requested_height != -1) {
382 Height = requested_height;
383 requested_height = -1;
386 OnMultilineChanged(EventArgs.Empty);
389 document.multiline = multiline;
391 if (multiline) {
392 document.Wrap = word_wrap;
393 document.PasswordChar = "";
395 } else {
396 document.Wrap = false;
397 if (this.password_char != '\0') {
398 document.PasswordChar = password_char.ToString();
399 } else {
400 document.PasswordChar = "";
406 [Browsable(false)]
407 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
408 [EditorBrowsable(EditorBrowsableState.Advanced)]
409 public int PreferredHeight {
410 get {
411 return this.Font.Height + 7; // FIXME - consider border style as well
415 [DefaultValue(false)]
416 [MWFCategory("Behavior")]
417 public bool ReadOnly {
418 get {
419 return read_only;
422 set {
423 if (value != read_only) {
424 read_only = value;
425 OnReadOnlyChanged(EventArgs.Empty);
430 [Browsable(false)]
431 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
432 public virtual string SelectedText {
433 get {
434 return document.GetSelection();
437 set {
438 if (!read_only) {
439 document.ReplaceSelection(CaseAdjust(value), false);
440 // document.PositionCaret (document.selection_end.line, document.selection_end.pos);
441 ScrollToCaret();
442 OnTextChanged(EventArgs.Empty);
447 [Browsable(false)]
448 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
449 public virtual int SelectionLength {
450 get {
451 return document.SelectionLength();
454 set {
455 if (value < 0) {
456 throw new ArgumentException(String.Format("{0} is not a valid value", value), "value");
459 if (value != 0) {
460 int start;
461 Line line;
462 LineTag tag;
463 int pos;
465 selection_length = value;
467 start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
469 document.CharIndexToLineTag(start + value, out line, out tag, out pos);
470 document.SetSelectionEnd(line, pos);
471 document.PositionCaret(line, pos);
472 } else {
473 selection_length = -1;
475 document.SetSelectionEnd(document.selection_start.line, document.selection_start.pos);
476 document.PositionCaret(document.selection_start.line, document.selection_start.pos);
481 [Browsable(false)]
482 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
483 public int SelectionStart {
484 get {
485 int index;
487 index = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
489 return index;
492 set {
493 document.SetSelectionStart(value);
494 if (selection_length > -1 ) {
495 document.SetSelectionEnd(value + selection_length);
496 } else {
497 document.SetSelectionEnd(value);
499 document.PositionCaret(document.selection_start.line, document.selection_start.pos);
500 ScrollToCaret();
504 [Localizable(true)]
505 public override string Text {
506 get {
507 if (document == null || document.Root == null || document.Root.text == null) {
508 return string.Empty;
511 if (!multiline) {
512 return document.Root.text.ToString();
513 } else {
514 StringBuilder sb;
515 int i;
517 sb = new StringBuilder();
519 Line line = null;
520 for (i = 1; i < document.Lines; i++) {
521 if (line != null && !line.soft_break)
522 sb.Append (Environment.NewLine);
523 line = document.GetLine (i);
524 sb.Append(line.text.ToString());
526 sb.Append(document.GetLine(document.Lines).text.ToString());
527 return sb.ToString();
531 set {
532 if (value == Text)
533 return;
535 if ((value != null) && (value != "")) {
536 Line line;
538 if (multiline) {
539 string[] lines;
541 lines = value.Split(new char[] {'\n'});
543 for (int i = 0; i < lines.Length; i++) {
544 if (lines[i].EndsWith("\r")) {
545 lines[i] = lines[i].Substring(0, lines[i].Length - 1);
548 this.Lines = lines;
550 document.PositionCaret (document.GetLine (1), 0);
551 document.SetSelectionToCaret (true);
553 ScrollToCaret ();
554 } else {
555 document.Clear();
556 document.Add(1, CaseAdjust(value), alignment, Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
557 CalculateDocument();
559 document.PositionCaret (document.GetLine (1), 0);
560 document.SetSelectionToCaret (true);
562 ScrollToCaret ();
564 } else {
565 document.Empty();
566 CalculateDocument();
569 OnTextChanged(EventArgs.Empty);
573 [Browsable(false)]
574 public virtual int TextLength {
575 get {
576 if (document == null || document.Root == null || document.Root.text == null) {
577 return 0;
580 if (!multiline) {
581 return document.Root.text.Length;
582 } else {
583 int total;
584 int i;
586 total = 0;
587 for (i = 1; i < document.Lines; i++) {
588 total += document.GetLine(i).text.Length + Environment.NewLine.Length;
590 total += document.GetLine(i).text.Length;
592 return total;
597 [DefaultValue(true)]
598 [Localizable(true)]
599 [MWFCategory("Behavior")]
600 public bool WordWrap {
601 get {
602 return word_wrap;
605 set {
606 if (value != word_wrap) {
607 if (multiline) {
608 word_wrap = value;
609 document.Wrap = value;
614 #endregion // Public Instance Properties
616 #region Protected Instance Properties
617 protected override CreateParams CreateParams {
618 get {
619 return base.CreateParams;
623 protected override System.Drawing.Size DefaultSize {
624 get {
625 return new Size(100, 20);
628 #endregion // Protected Instance Properties
630 #region Public Instance Methods
631 public void AppendText(string text) {
632 if (multiline) {
633 // Grab the formatting for the last element
634 document.MoveCaret(CaretDirection.CtrlEnd);
635 // grab the end tag
636 if (document.CaretTag.next != null) {
637 document.CaretTag = document.CaretTag.next;
639 document.Insert(document.CaretLine, document.CaretTag, document.CaretPosition, false, text);
641 CalculateDocument();
642 } else {
643 document.MoveCaret(CaretDirection.CtrlEnd);
644 document.InsertStringAtCaret(text, true);
646 Invalidate();
649 document.MoveCaret(CaretDirection.CtrlEnd);
650 document.SetSelectionStart(document.CaretLine, document.CaretPosition);
651 document.SetSelectionEnd(document.CaretLine, document.CaretPosition);
652 selection_length = -1;
654 OnTextChanged(EventArgs.Empty);
657 public void Clear() {
658 Text = null;
661 public void ClearUndo() {
662 document.undo.Clear();
665 public void Copy() {
666 DataObject o;
668 o = new DataObject(DataFormats.Text, SelectedText);
669 if (this is RichTextBox) {
670 o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
672 Clipboard.SetDataObject(o);
675 public void Cut() {
676 DataObject o;
678 o = new DataObject(DataFormats.Text, SelectedText);
679 if (this is RichTextBox) {
680 o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
682 Clipboard.SetDataObject(o);
683 document.ReplaceSelection("", false);
686 public void Paste() {
687 Paste(Clipboard.GetDataObject(), null, false);
690 public void ScrollToCaret() {
691 if (IsHandleCreated) {
692 CaretMoved(this, EventArgs.Empty);
696 public void Select(int start, int length) {
697 SelectionStart = start;
698 SelectionLength = length;
702 public void SelectAll() {
703 Line last;
705 last = document.GetLine(document.Lines);
706 document.SetSelectionStart(document.GetLine(1), 0);
707 document.SetSelectionEnd(last, last.text.Length);
708 selection_length = -1;
711 public override string ToString() {
712 return String.Concat (base.ToString (), ", Text: ", Text);
715 public void Undo() {
716 document.undo.Undo();
718 #endregion // Public Instance Methods
720 #region Protected Instance Methods
721 protected override void CreateHandle() {
722 base.CreateHandle ();
723 document.AlignCaret();
724 ScrollToCaret();
727 protected override bool IsInputKey(Keys keyData) {
728 if ((keyData & Keys.Alt) != 0) {
729 return base.IsInputKey(keyData);
732 switch (keyData & Keys.KeyCode) {
733 case Keys.Enter: {
734 if (multiline && accepts_return) {
735 return true;
737 return false;
740 case Keys.Tab: {
741 if (accepts_tab && multiline) {
742 if ((keyData & Keys.Control) == 0) {
743 return true;
746 return false;
749 case Keys.Left:
750 case Keys.Right:
751 case Keys.Up:
752 case Keys.Down:
753 case Keys.PageUp:
754 case Keys.PageDown:
755 case Keys.Home:
756 case Keys.End: {
757 return true;
760 return false;
764 protected virtual void OnAcceptsTabChanged(EventArgs e) {
765 if (AcceptsTabChanged != null) {
766 AcceptsTabChanged(this, e);
770 protected virtual void OnAutoSizeChanged(EventArgs e) {
771 if (AutoSizeChanged != null) {
772 AutoSizeChanged(this, e);
776 protected virtual void OnBorderStyleChanged(EventArgs e) {
777 if (BorderStyleChanged != null) {
778 BorderStyleChanged(this, e);
782 protected override void OnFontChanged(EventArgs e) {
783 base.OnFontChanged (e);
785 if (auto_size && !multiline) {
786 if (PreferredHeight != ClientSize.Height) {
787 Height = PreferredHeight;
792 protected override void OnHandleCreated(EventArgs e) {
793 base.OnHandleCreated (e);
796 protected override void OnHandleDestroyed(EventArgs e) {
797 base.OnHandleDestroyed (e);
800 protected virtual void OnHideSelectionChanged(EventArgs e) {
801 if (HideSelectionChanged != null) {
802 HideSelectionChanged(this, e);
806 protected virtual void OnModifiedChanged(EventArgs e) {
807 if (ModifiedChanged != null) {
808 ModifiedChanged(this, e);
812 protected virtual void OnMultilineChanged(EventArgs e) {
813 if (MultilineChanged != null) {
814 MultilineChanged(this, e);
818 protected virtual void OnReadOnlyChanged(EventArgs e) {
819 if (ReadOnlyChanged != null) {
820 ReadOnlyChanged(this, e);
824 protected override bool ProcessDialogKey(Keys keyData) {
825 return base.ProcessDialogKey(keyData);
828 private bool ProcessKey(Keys keyData) {
829 bool control;
830 bool shift;
832 control = (Control.ModifierKeys & Keys.Control) != 0;
833 shift = (Control.ModifierKeys & Keys.Shift) != 0;
835 switch (keyData & Keys.KeyCode) {
836 case Keys.X: { // Cut (Ctrl-X)
837 if (control) {
838 Cut();
839 return true;
841 return false;
844 case Keys.C: { // Copy (Ctrl-C)
845 if (control) {
846 Copy();
847 return true;
849 return false;
852 case Keys.V: { // Paste (Ctrl-V)
853 if (control) {
854 return Paste(Clipboard.GetDataObject(), null, true);
856 return false;
859 case Keys.Z: { // Undo (Ctrl-Z)
860 if (control) {
861 Undo();
862 return true;
864 return false;
867 case Keys.A: { // Select All (Ctrl-A)
868 if (control) {
869 SelectAll();
870 return true;
872 return false;
875 case Keys.Left: {
876 if (control) {
877 document.MoveCaret(CaretDirection.WordBack);
878 } else {
879 if (!document.selection_visible || shift) {
880 document.MoveCaret(CaretDirection.CharBack);
881 } else {
882 document.MoveCaret(CaretDirection.SelectionStart);
886 if (!shift) {
887 document.SetSelectionToCaret(true);
888 } else {
889 document.SetSelectionToCaret(false);
892 CaretMoved(this, null);
893 return true;
896 case Keys.Right: {
897 if (control) {
898 document.MoveCaret(CaretDirection.WordForward);
899 } else {
900 if (!document.selection_visible || shift) {
901 document.MoveCaret(CaretDirection.CharForward);
902 } else {
903 document.MoveCaret(CaretDirection.SelectionEnd);
906 if (!shift) {
907 document.SetSelectionToCaret(true);
908 } else {
909 document.SetSelectionToCaret(false);
912 CaretMoved(this, null);
913 return true;
916 case Keys.Up: {
917 if (control) {
918 if (document.CaretPosition == 0) {
919 document.MoveCaret(CaretDirection.LineUp);
920 } else {
921 document.MoveCaret(CaretDirection.Home);
923 } else {
924 document.MoveCaret(CaretDirection.LineUp);
927 if ((Control.ModifierKeys & Keys.Shift) == 0) {
928 document.SetSelectionToCaret(true);
929 } else {
930 document.SetSelectionToCaret(false);
933 CaretMoved(this, null);
934 return true;
937 case Keys.Down: {
938 if (control) {
939 if (document.CaretPosition == document.CaretLine.Text.Length) {
940 document.MoveCaret(CaretDirection.LineDown);
941 } else {
942 document.MoveCaret(CaretDirection.End);
944 } else {
945 document.MoveCaret(CaretDirection.LineDown);
948 if ((Control.ModifierKeys & Keys.Shift) == 0) {
949 document.SetSelectionToCaret(true);
950 } else {
951 document.SetSelectionToCaret(false);
954 CaretMoved(this, null);
955 return true;
958 case Keys.Home: {
959 if ((Control.ModifierKeys & Keys.Control) != 0) {
960 document.MoveCaret(CaretDirection.CtrlHome);
961 } else {
962 document.MoveCaret(CaretDirection.Home);
965 if ((Control.ModifierKeys & Keys.Shift) == 0) {
966 document.SetSelectionToCaret(true);
967 } else {
968 document.SetSelectionToCaret(false);
971 CaretMoved(this, null);
972 return true;
975 case Keys.End: {
976 if ((Control.ModifierKeys & Keys.Control) != 0) {
977 document.MoveCaret(CaretDirection.CtrlEnd);
978 } else {
979 document.MoveCaret(CaretDirection.End);
982 if ((Control.ModifierKeys & Keys.Shift) == 0) {
983 document.SetSelectionToCaret(true);
984 } else {
985 document.SetSelectionToCaret(false);
988 CaretMoved(this, null);
989 return true;
992 case Keys.Enter: {
993 // ignoring accepts_return, fixes bug #76355
994 if (!read_only && multiline && (accepts_return || (FindForm() != null && FindForm().AcceptButton == null) || ((Control.ModifierKeys & Keys.Control) != 0))) {
995 Line line;
997 if (document.selection_visible) {
998 document.ReplaceSelection("", false);
1001 line = document.CaretLine;
1003 document.Split(document.CaretLine, document.CaretTag, document.CaretPosition, false);
1004 OnTextChanged(EventArgs.Empty);
1005 document.UpdateView(line, 2, 0);
1006 document.MoveCaret(CaretDirection.CharForward);
1007 document.SetSelectionToCaret(true);
1008 CaretMoved(this, null);
1009 return true;
1011 break;
1014 case Keys.Tab: {
1015 if (!read_only && accepts_tab && multiline) {
1016 document.InsertChar(document.CaretLine, document.CaretPosition, '\t');
1017 if (document.selection_visible) {
1018 document.ReplaceSelection("", false);
1020 document.SetSelectionToCaret(true);
1022 OnTextChanged(EventArgs.Empty);
1023 CaretMoved(this, null);
1024 return true;
1026 break;
1029 case Keys.Insert: {
1030 if (shift) {
1031 Paste(Clipboard.GetDataObject(), null, true);
1032 return true;
1035 if (control) {
1036 Copy();
1037 return true;
1040 // FIXME - need overwrite/insert toggle?
1041 return false;
1044 case Keys.PageUp: {
1045 if ((Control.ModifierKeys & Keys.Control) != 0) {
1046 document.MoveCaret(CaretDirection.CtrlPgUp);
1047 } else {
1048 document.MoveCaret(CaretDirection.PgUp);
1050 return true;
1053 case Keys.PageDown: {
1054 if ((Control.ModifierKeys & Keys.Control) != 0) {
1055 document.MoveCaret(CaretDirection.CtrlPgDn);
1056 } else {
1057 document.MoveCaret(CaretDirection.PgDn);
1059 return true;
1062 case Keys.Delete: {
1063 if (shift) {
1064 Cut();
1065 return true;
1068 if (read_only) {
1069 break;
1072 if (document.selection_visible) {
1073 document.ReplaceSelection("", false);
1074 } else {
1075 // DeleteChar only deletes on the line, doesn't do the combine
1076 if (document.CaretPosition == document.CaretLine.Text.Length) {
1077 if (document.CaretLine.LineNo < document.Lines) {
1078 Line line;
1080 line = document.GetLine(document.CaretLine.LineNo + 1);
1081 document.Combine(document.CaretLine, line);
1082 document.UpdateView(document.CaretLine, 2, 0);
1084 #if not_Debug
1085 Line check_first;
1086 Line check_second;
1088 check_first = document.GetLine(document.CaretLine.LineNo);
1089 check_second = document.GetLine(check_first.line_no + 1);
1091 Console.WriteLine("Post-UpdateView: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
1092 #endif
1094 // Caret doesn't move
1096 } else {
1097 if (!control) {
1098 document.DeleteChar(document.CaretTag, document.CaretPosition, true);
1099 } else {
1100 int end_pos;
1102 end_pos = document.CaretPosition;
1104 while ((end_pos < document.CaretLine.Text.Length) && !Document.IsWordSeparator(document.CaretLine.Text[end_pos])) {
1105 end_pos++;
1108 if (end_pos < document.CaretLine.Text.Length) {
1109 end_pos++;
1111 document.DeleteChars(document.CaretTag, document.CaretPosition, end_pos - document.CaretPosition);
1116 OnTextChanged(EventArgs.Empty);
1117 document.AlignCaret();
1118 document.UpdateCaret();
1119 CaretMoved(this, null);
1120 return true;
1123 return false;
1126 private void HandleBackspace(bool control) {
1127 bool fire_changed;
1129 fire_changed = false;
1131 // delete only deletes on the line, doesn't do the combine
1132 if (document.selection_visible) {
1133 document.ReplaceSelection("", false);
1134 fire_changed = true;
1136 document.SetSelectionToCaret(true);
1138 if (document.CaretPosition == 0) {
1139 if (document.CaretLine.LineNo > 1) {
1140 Line line;
1141 int new_caret_pos;
1143 line = document.GetLine(document.CaretLine.LineNo - 1);
1144 new_caret_pos = line.text.Length;
1146 document.Combine(line, document.CaretLine);
1147 document.UpdateView(line, 1, 0);
1148 document.PositionCaret(line, new_caret_pos);
1149 //document.MoveCaret(CaretDirection.CharForward);
1150 document.UpdateCaret();
1151 fire_changed = true;
1153 } else {
1154 if (!control || document.CaretPosition == 0) {
1155 document.DeleteChar(document.CaretTag, document.CaretPosition, false);
1156 document.MoveCaret(CaretDirection.CharBack);
1157 } else {
1158 int start_pos;
1160 start_pos = document.CaretPosition - 1;
1162 while ((start_pos > 0) && !Document.IsWordSeparator(document.CaretLine.Text[start_pos - 1])) {
1163 start_pos--;
1165 document.DeleteChars(document.CaretTag, start_pos, document.CaretPosition - start_pos);
1166 document.PositionCaret(document.CaretLine, start_pos);
1168 document.UpdateCaret();
1169 fire_changed = true;
1171 if (fire_changed) {
1172 OnTextChanged(EventArgs.Empty);
1174 CaretMoved(this, null);
1177 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
1178 // Make sure we don't get sized bigger than we want to be
1179 if (!richtext) {
1180 if (!multiline) {
1181 if (height != PreferredHeight) {
1182 requested_height = height;
1183 height = PreferredHeight;
1184 specified |= BoundsSpecified.Height;
1189 base.SetBoundsCore (x, y, width, height, specified);
1192 protected override void WndProc(ref Message m) {
1193 switch ((Msg)m.Msg) {
1194 case Msg.WM_KEYDOWN: {
1195 if (ProcessKeyMessage(ref m) || ProcessKey((Keys)m.WParam.ToInt32() | XplatUI.State.ModifierKeys)) {
1196 m.Result = IntPtr.Zero;
1197 return;
1199 DefWndProc (ref m);
1200 return;
1203 case Msg.WM_CHAR: {
1204 int ch;
1206 if (ProcessKeyMessage(ref m)) {
1207 m.Result = IntPtr.Zero;
1208 return;
1211 if (read_only) {
1212 return;
1215 m.Result = IntPtr.Zero;
1217 ch = m.WParam.ToInt32();
1219 if (ch == 127) {
1220 HandleBackspace(true);
1221 } else if (ch >= 32) {
1222 if (document.selection_visible) {
1223 document.ReplaceSelection("", false);
1226 char c = (char)m.WParam;
1227 switch (character_casing) {
1228 case CharacterCasing.Upper:
1229 c = Char.ToUpper((char) m.WParam);
1230 break;
1231 case CharacterCasing.Lower:
1232 c = Char.ToLower((char) m.WParam);
1233 break;
1236 if (document.Length < max_length) {
1237 document.InsertCharAtCaret(c, true);
1238 OnTextChanged(EventArgs.Empty);
1239 CaretMoved(this, null);
1240 } else {
1241 XplatUI.AudibleAlert();
1243 return;
1244 } else if (ch == 8) {
1245 HandleBackspace(false);
1248 return;
1251 default: {
1252 base.WndProc(ref m);
1253 return;
1258 #endregion // Protected Instance Methods
1260 #region Events
1261 public event EventHandler AcceptsTabChanged;
1262 public event EventHandler AutoSizeChanged;
1263 [Browsable(false)]
1264 [EditorBrowsable(EditorBrowsableState.Never)]
1265 public new event EventHandler BackgroundImageChanged {
1266 add { base.BackgroundImageChanged += value; }
1267 remove { base.BackgroundImageChanged -= value; }
1269 public event EventHandler BorderStyleChanged;
1270 [Browsable(false)]
1271 [EditorBrowsable(EditorBrowsableState.Advanced)]
1272 public event EventHandler Click {
1273 add { base.Click += value; }
1274 remove { base.Click -= value; }
1277 public event EventHandler HideSelectionChanged;
1278 public event EventHandler ModifiedChanged;
1279 public event EventHandler MultilineChanged;
1280 [Browsable(false)]
1281 [EditorBrowsable(EditorBrowsableState.Never)]
1282 public event PaintEventHandler Paint;
1283 public event EventHandler ReadOnlyChanged;
1285 internal event EventHandler HScrolled;
1286 internal event EventHandler VScrolled;
1287 #endregion // Events
1289 #region Private Methods
1290 internal Document Document {
1291 get {
1292 return document;
1295 set {
1296 document = value;
1300 internal bool ShowSelection {
1301 get {
1302 if (show_selection) {
1303 return true;
1306 return has_focus;
1309 set {
1310 if (show_selection == value)
1311 return;
1313 show_selection = value;
1314 // Currently InvalidateSelectionArea is commented out so do a full invalidate
1315 document.InvalidateSelectionArea();
1319 internal Graphics CreateGraphicsInternal() {
1320 if (IsHandleCreated) {
1321 return base.CreateGraphics();
1324 return Graphics.FromImage(bmp);
1327 #if Debug
1328 static int current;
1329 #endif
1331 internal override void OnPaintInternal (PaintEventArgs pevent) {
1332 // Fill background
1333 if (backcolor_set || (Enabled && !read_only)) {
1334 pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), pevent.ClipRectangle);
1335 } else {
1336 pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorControl), pevent.ClipRectangle);
1338 pevent.Graphics.TextRenderingHint=TextRenderingHint.AntiAlias;
1340 // Draw the viewable document
1341 document.Draw(pevent.Graphics, pevent.ClipRectangle);
1343 Rectangle rect = ClientRectangle;
1344 rect.Width--;
1345 rect.Height--;
1346 //pevent.Graphics.DrawRectangle(ThemeEngine.Current.ResPool.GetPen(ThemeEngine.Current.ColorControlDark), rect);
1348 #if Debug
1349 int start;
1350 int end;
1351 Line line;
1352 int line_no;
1353 Pen p;
1355 p = new Pen(Color.Red, 1);
1357 // First, figure out from what line to what line we need to draw
1358 start = document.GetLineByPixel(pevent.ClipRectangle.Top - document.ViewPortY, false).line_no;
1359 end = document.GetLineByPixel(pevent.ClipRectangle.Bottom - document.ViewPortY, false).line_no;
1361 //Console.WriteLine("Starting drawing on line '{0}'", document.GetLine(start));
1362 //Console.WriteLine("Ending drawing on line '{0}'", document.GetLine(end));
1364 line_no = start;
1365 while (line_no <= end) {
1366 line = document.GetLine(line_no);
1368 if (draw_lines) {
1369 for (int i = 0; i < line.text.Length; i++) {
1370 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);
1374 line_no++;
1376 #endif
1379 internal override void OnGotFocusInternal (EventArgs e)
1381 document.CaretHasFocus ();
1382 base.OnGotFocusInternal (e);
1385 internal override void OnLostFocusInternal (EventArgs e)
1387 document.CaretLostFocus ();
1388 base.OnLostFocusInternal (e);
1391 private void TextBoxBase_MouseDown(object sender, MouseEventArgs e) {
1392 if (e.Button == MouseButtons.Left) {
1393 TimeSpan interval;
1395 interval = DateTime.Now - click_last;
1396 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1398 // Handle place caret/select word/select line behaviour
1399 if (e.Clicks == 1) {
1400 if (SystemInformation.DoubleClickTime < interval.TotalMilliseconds) {
1401 #if DebugClick
1402 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));
1403 #endif
1404 document.SetSelectionToCaret(true);
1405 click_mode = CaretSelection.Position;
1406 } else if (this is RichTextBox) {
1407 #if DebugClick
1408 Console.WriteLine("Tripple Click Selecting line");
1409 #endif
1410 document.ExpandSelection(CaretSelection.Line, false);
1411 click_mode = CaretSelection.Line;
1412 } else {
1413 document.SetSelectionToCaret(true);
1415 } else {
1416 // We select the line if the word is already selected, and vice versa
1417 if (click_mode != CaretSelection.Word) {
1418 if (click_mode == CaretSelection.Line) {
1419 document.Invalidate(document.selection_start.line, 0, document.selection_start.line, document.selection_start.line.text.Length);
1421 click_mode = CaretSelection.Word;
1423 SelectWord ();
1424 } else if (this is RichTextBox) {
1425 click_mode = CaretSelection.Line;
1426 document.ExpandSelection(CaretSelection.Line, false); // Setting initial selection
1430 // Reset
1431 click_last = DateTime.Now;
1432 return;
1435 if ((e.Button == MouseButtons.Middle) && (((int)Environment.OSVersion.Platform == 4) || ((int)Environment.OSVersion.Platform == 128))) {
1436 Document.Marker marker;
1438 marker.tag = document.FindCursor(e.X + document.ViewPortX, e.Y + document.ViewPortY, out marker.pos);
1439 marker.line = marker.tag.line;
1440 marker.height = marker.tag.height;
1442 document.SetSelection(marker.line, marker.pos, marker.line, marker.pos);
1443 Paste (Clipboard.GetDataObject (true), null, true);
1447 #if Debug
1448 LineTag tag;
1449 Line line;
1450 int pos;
1452 if (e.Button == MouseButtons.Right) {
1453 draw_lines = !draw_lines;
1454 this.Invalidate();
1455 Console.WriteLine("SelectedText: {0}, length {1}", this.SelectedText, this.SelectionLength);
1456 Console.WriteLine("Selection start: {0}", this.SelectionStart);
1458 this.SelectionStart = 10;
1459 this.SelectionLength = 5;
1461 return;
1464 tag = document.FindTag(e.X + document.ViewPortX, e.Y + document.ViewPortY, out pos, false);
1466 Console.WriteLine("Click found tag {0}, character {1}", tag, pos);
1467 line = tag.line;
1468 switch(current) {
1469 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;
1470 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;
1471 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;
1472 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;
1473 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;
1474 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;
1476 current++;
1477 if (current==6) {
1478 current=0;
1481 // Update/Recalculate what we see
1482 document.UpdateView(line, 0);
1484 // Make sure our caret is properly positioned and sized
1485 document.AlignCaret();
1486 #endif
1489 private void TextBoxBase_MouseUp(object sender, MouseEventArgs e) {
1490 if (e.Button == MouseButtons.Left) {
1491 if (click_mode == CaretSelection.Position) {
1492 document.SetSelectionToCaret(false);
1493 document.DisplayCaret();
1496 if (scroll_timer != null) {
1497 scroll_timer.Enabled = false;
1499 return;
1503 private void PositionControls ()
1505 if (hscroll.Visible) {
1506 //vscroll.Maximum += hscroll.Height;
1507 canvas_height = ClientSize.Height - hscroll.Height;
1508 } else {
1509 canvas_height = ClientSize.Height;
1512 if (vscroll.Visible) {
1513 //hscroll.Maximum += vscroll.Width;
1514 canvas_width = ClientSize.Width - vscroll.Width;
1515 } else {
1516 canvas_width = ClientSize.Width;
1520 document.ViewPortWidth = canvas_width;
1521 document.ViewPortHeight = canvas_height;
1523 // We always move them, they just might not be displayed
1524 hscroll.Bounds = new Rectangle (ClientRectangle.Left, ClientRectangle.Height - hscroll.Height, ClientSize.Width - (vscroll.Visible ? vscroll.Width : 0), hscroll.Height);
1525 vscroll.Bounds = new Rectangle (ClientRectangle.Right - vscroll.Width, ClientRectangle.Top, vscroll.Width, ClientSize.Height - (hscroll.Visible ? hscroll.Height : 0));
1529 private void TextBoxBase_SizeChanged(object sender, EventArgs e) {
1530 CalculateDocument();
1533 private void TextBoxBase_MouseWheel(object sender, MouseEventArgs e) {
1534 Line line;
1535 int line_no;
1537 if (!vscroll.Enabled) {
1538 return;
1541 if (e.Delta < 0)
1542 vscroll.Value = Math.Min (vscroll.Value + SystemInformation.MouseWheelScrollLines, vscroll.Maximum - document.ViewPortHeight + 1);
1543 else
1544 vscroll.Value = Math.Max (0, vscroll.Value - SystemInformation.MouseWheelScrollLines);
1547 internal virtual void SelectWord ()
1549 StringBuilder s = document.caret.line.text;
1550 int start = document.caret.pos;
1551 int end = document.caret.pos;
1553 if (s.Length < 1) {
1554 if (document.caret.line.line_no >= document.Lines)
1555 return;
1556 Line line = document.GetLine (document.caret.line.line_no + 1);
1557 document.PositionCaret (line, 0);
1558 return;
1561 if (start > 0) {
1562 start--;
1563 end--;
1566 // skip whitespace until we hit a word
1567 while (start > 0 && s [start] == ' ')
1568 start--;
1569 if (start > 0) {
1570 while (start > 0 && (s [start] != ' '))
1571 start--;
1572 if (s [start] == ' ')
1573 start++;
1576 if (s [end] == ' ') {
1577 while (end < s.Length && s [end] == ' ')
1578 end++;
1579 } else {
1580 while (end < s.Length && s [end] != ' ')
1581 end++;
1582 while (end < s.Length && s [end] == ' ')
1583 end++;
1586 document.SetSelection (document.caret.line, start, document.caret.line, end);
1589 internal void CalculateDocument() {
1590 if (!IsHandleCreated) {
1591 return;
1593 document.RecalculateDocument(CreateGraphicsInternal());
1594 CalculateScrollBars();
1595 Invalidate();
1598 internal void CalculateScrollBars() {
1599 // FIXME - need separate calculations for center and right alignment
1601 if (!multiline) {
1602 PositionControls ();
1603 return;
1606 if (document.Width >= document.ViewPortWidth) {
1607 hscroll.Enabled = true;
1608 hscroll.Minimum = 0;
1609 hscroll.LargeChange = document.ViewPortWidth < 0 ? 0 : document.ViewPortWidth;
1610 hscroll.Maximum = document.Width;
1611 } else {
1612 hscroll.Maximum = document.ViewPortWidth;
1613 hscroll.Enabled = false;
1616 if (document.Height >= document.ViewPortHeight) {
1617 vscroll.Enabled = true;
1618 vscroll.Minimum = 0;
1619 vscroll.LargeChange = document.ViewPortHeight < 0 ? 0 : document.ViewPortHeight;
1620 vscroll.Maximum = document.Height;
1621 } else {
1622 vscroll.Maximum = document.ViewPortHeight;
1623 vscroll.Enabled = false;
1627 if (!WordWrap) {
1628 if ((scrollbars & RichTextBoxScrollBars.Horizontal) != 0) {
1629 if (((scrollbars & RichTextBoxScrollBars.ForcedHorizontal) != 0) || hscroll.Enabled) {
1630 hscroll.Visible = true;
1631 } else {
1632 hscroll.Visible = false;
1634 } else {
1635 hscroll.Visible = false;
1639 if ((scrollbars & RichTextBoxScrollBars.Vertical) != 0) {
1640 if (((scrollbars & RichTextBoxScrollBars.ForcedVertical) != 0) || vscroll.Enabled) {
1641 vscroll.Visible = true;
1642 } else {
1643 vscroll.Visible = false;
1645 } else {
1646 vscroll.Visible = false;
1649 PositionControls ();
1652 private void document_WidthChanged(object sender, EventArgs e) {
1653 CalculateScrollBars();
1656 private void document_HeightChanged(object sender, EventArgs e) {
1657 CalculateScrollBars();
1660 private void hscroll_ValueChanged(object sender, EventArgs e) {
1661 int old_viewport_x;
1663 old_viewport_x = document.ViewPortX;
1664 document.ViewPortX = this.hscroll.Value;
1666 if (vscroll.Visible) {
1667 XplatUI.ScrollWindow(this.Handle, new Rectangle(0, 0, ClientSize.Width - vscroll.Width, ClientSize.Height), old_viewport_x - this.hscroll.Value, 0, false);
1668 } else {
1669 XplatUI.ScrollWindow(this.Handle, ClientRectangle, old_viewport_x - this.hscroll.Value, 0, false);
1671 document.UpdateCaret();
1673 if (HScrolled != null) {
1674 HScrolled(this, EventArgs.Empty);
1678 private void vscroll_ValueChanged(object sender, EventArgs e) {
1679 int old_viewport_y;
1681 old_viewport_y = document.ViewPortY;
1682 document.ViewPortY = this.vscroll.Value;
1684 if (hscroll.Visible) {
1685 XplatUI.ScrollWindow(this.Handle, new Rectangle(0, 0, ClientSize.Width, ClientSize.Height - hscroll.Height), 0, old_viewport_y - this.vscroll.Value, false);
1686 } else {
1687 XplatUI.ScrollWindow(this.Handle, ClientRectangle, 0, old_viewport_y - this.vscroll.Value, false);
1689 document.UpdateCaret();
1691 if (VScrolled != null) {
1692 VScrolled(this, EventArgs.Empty);
1696 private void TextBoxBase_MouseMove(object sender, MouseEventArgs e) {
1697 // FIXME - handle auto-scrolling if mouse is to the right/left of the window
1698 if (Capture) {
1699 if (!ClientRectangle.Contains (e.X, e.Y)) {
1700 if (scroll_timer == null) {
1701 scroll_timer = new Timer ();
1702 scroll_timer.Interval = 100;
1703 scroll_timer.Tick += new EventHandler (ScrollTimerTickHandler);
1706 if (!scroll_timer.Enabled) {
1707 scroll_timer.Start ();
1709 // Force the first tick
1710 ScrollTimerTickHandler (null, EventArgs.Empty);
1713 return;
1716 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1717 if (click_mode == CaretSelection.Position) {
1718 document.SetSelectionToCaret(false);
1719 document.DisplayCaret();
1724 private void TextBoxBase_FontOrColorChanged(object sender, EventArgs e) {
1725 if (!richtext) {
1726 Line line;
1728 // Font changes apply to the whole document
1729 for (int i = 1; i <= document.Lines; i++) {
1730 line = document.GetLine(i);
1731 LineTag.FormatText(line, 1, line.text.Length, Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
1732 document.UpdateView(line, 0);
1734 // Make sure the caret height is matching the new font height
1735 document.AlignCaret();
1739 private void ScrollTimerTickHandler (object sender, EventArgs e)
1741 Point pt = Cursor.Position;
1743 pt = PointToClient (pt);
1745 if (pt.X < ClientRectangle.Left) {
1746 document.MoveCaret(CaretDirection.CharBackNoWrap);
1747 document.SetSelectionToCaret(false);
1749 CaretMoved(this, null);
1750 } else if (pt.X > ClientRectangle.Right) {
1751 document.MoveCaret(CaretDirection.CharForwardNoWrap);
1752 document.SetSelectionToCaret(false);
1754 CaretMoved(this, null);
1755 } else if (pt.Y > ClientRectangle.Bottom) {
1756 document.MoveCaret(CaretDirection.LineDown);
1757 document.SetSelectionToCaret(false);
1759 CaretMoved(this, null);
1760 } else if (pt.Y < ClientRectangle.Top) {
1761 document.MoveCaret(CaretDirection.LineUp);
1762 document.SetSelectionToCaret(false);
1764 CaretMoved(this, null);
1768 /// <summary>Ensure the caret is always visible</summary>
1769 internal void CaretMoved(object sender, EventArgs e) {
1770 Point pos;
1771 int height;
1773 pos = document.Caret;
1774 //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);
1777 // Horizontal scrolling:
1778 // If the caret moves to the left outside the visible area, we jump the document into view, not just one
1779 // character, but 1/3 of the width of the document
1780 // If the caret moves to the right outside the visible area, we scroll just enough to keep the caret visible
1782 // Handle horizontal scrolling
1783 if (document.CaretLine.alignment == HorizontalAlignment.Left) {
1784 // Check if we moved out of view to the left
1785 if (pos.X < (document.ViewPortX)) {
1786 do {
1787 if ((hscroll.Value - document.ViewPortWidth / 3) >= hscroll.Minimum) {
1788 hscroll.Value -= document.ViewPortWidth / 3;
1789 } else {
1790 hscroll.Value = hscroll.Minimum;
1792 } while (hscroll.Value > pos.X);
1795 // Check if we moved out of view to the right
1796 if ((pos.X >= (document.ViewPortWidth + document.ViewPortX)) && (hscroll.Value != hscroll.Maximum)) {
1797 if ((pos.X - document.ViewPortWidth + 1) <= hscroll.Maximum) {
1798 if (pos.X - document.ViewPortWidth >= 0) {
1799 hscroll.Value = pos.X - document.ViewPortWidth + 1;
1800 } else {
1801 hscroll.Value = 0;
1803 } else {
1804 hscroll.Value = hscroll.Maximum;
1807 } else if (document.CaretLine.alignment == HorizontalAlignment.Right) {
1808 // hscroll.Value = pos.X;
1810 // if ((pos.X > (this.canvas_width + document.ViewPortX)) && (hscroll.Enabled && (hscroll.Value != hscroll.Maximum))) {
1811 // hscroll.Value = hscroll.Maximum;
1812 // }
1813 } else {
1814 // FIXME - implement center cursor alignment
1817 if (!multiline) {
1818 return;
1821 // Handle vertical scrolling
1822 height = document.CaretLine.Height + 1;
1824 if (pos.Y < document.ViewPortY) {
1825 vscroll.Value = pos.Y;
1828 if ((pos.Y + height) > (document.ViewPortY + canvas_height)) {
1829 vscroll.Value = pos.Y - canvas_height + height;
1833 internal bool Paste(IDataObject clip, DataFormats.Format format, bool obey_length) {
1834 string s;
1836 if (clip == null)
1837 return false;
1839 if (format == null) {
1840 if ((this is RichTextBox) && clip.GetDataPresent(DataFormats.Rtf)) {
1841 format = DataFormats.GetFormat(DataFormats.Rtf);
1842 } else if (clip.GetDataPresent(DataFormats.UnicodeText)) {
1843 format = DataFormats.GetFormat(DataFormats.UnicodeText);
1844 } else if (clip.GetDataPresent(DataFormats.Text)) {
1845 format = DataFormats.GetFormat(DataFormats.Text);
1846 } else {
1847 return false;
1849 } else {
1850 if ((format.Name == DataFormats.Rtf) && !(this is RichTextBox)) {
1851 return false;
1854 if (!clip.GetDataPresent(format.Name)) {
1855 return false;
1859 if (format.Name == DataFormats.Rtf) {
1860 ((RichTextBox)this).SelectedRtf = (string)clip.GetData(DataFormats.Rtf);
1861 return true;
1862 } else if (format.Name == DataFormats.UnicodeText) {
1863 s = (string)clip.GetData(DataFormats.UnicodeText);
1864 } else if (format.Name == DataFormats.Text) {
1865 s = (string)clip.GetData(DataFormats.Text);
1866 } else {
1867 return false;
1870 if (!obey_length) {
1871 this.SelectedText = s;
1872 } else {
1873 if ((s.Length + document.Length) < max_length) {
1874 this.SelectedText = s;
1875 } else if (document.Length < max_length) {
1876 this.SelectedText = s.Substring(0, max_length - document.Length);
1880 return true;
1882 #endregion // Private Methods