2007-03-19 Chris Toshok <toshok@ximian.com>
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TextBoxBase.cs
blob518971754ebcc914350655a3ffcf5720d990fa63
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;
37 using System.Collections;
39 namespace System.Windows.Forms {
40 [DefaultEvent("TextChanged")]
41 [Designer("System.Windows.Forms.Design.TextBoxBaseDesigner, " + Consts.AssemblySystem_Design)]
42 public abstract class TextBoxBase : Control {
43 #region Local Variables
44 internal HorizontalAlignment alignment;
45 internal bool accepts_tab;
46 internal bool accepts_return;
47 internal bool auto_size;
48 internal bool backcolor_set;
49 internal CharacterCasing character_casing;
50 internal bool hide_selection;
51 internal int max_length;
52 internal bool modified;
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 Timer scroll_timer;
63 internal bool richtext;
64 internal bool show_selection; // set to true to always show selection, even if no focus is set
65 internal int selection_length = -1; // set to the user-specified selection length, or -1 if none
66 internal bool show_caret_w_selection; // TextBox shows the caret when the selection is visible
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 int click_point_x;
74 internal int click_point_y;
75 internal CaretSelection click_mode;
76 internal Bitmap bmp;
77 internal BorderStyle actual_border_style;
78 #if Debug
79 internal static bool draw_lines = false;
80 #endif
82 #endregion // Local Variables
84 #region Internal Constructor
85 // Constructor will go when complete, only for testing - pdb
86 internal TextBoxBase() {
87 alignment = HorizontalAlignment.Left;
88 accepts_return = false;
89 accepts_tab = false;
90 auto_size = true;
91 border_style = BorderStyle.Fixed3D;
92 actual_border_style = BorderStyle.Fixed3D;
93 character_casing = CharacterCasing.Normal;
94 hide_selection = true;
95 max_length = 32767;
96 modified = true;
97 password_char = '\0';
98 read_only = false;
99 word_wrap = true;
100 richtext = false;
101 show_selection = false;
102 show_caret_w_selection = (this is TextBox);
103 document = new Document(this);
104 document.WidthChanged += new EventHandler(document_WidthChanged);
105 document.HeightChanged += new EventHandler(document_HeightChanged);
106 //document.CaretMoved += new EventHandler(CaretMoved);
107 document.Wrap = false;
108 requested_height = -1;
109 click_last = DateTime.Now;
110 click_mode = CaretSelection.Position;
111 bmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
113 MouseDown += new MouseEventHandler(TextBoxBase_MouseDown);
114 MouseUp += new MouseEventHandler(TextBoxBase_MouseUp);
115 MouseMove += new MouseEventHandler(TextBoxBase_MouseMove);
116 SizeChanged += new EventHandler(TextBoxBase_SizeChanged);
117 FontChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
118 ForeColorChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
119 MouseWheel += new MouseEventHandler(TextBoxBase_MouseWheel);
121 scrollbars = RichTextBoxScrollBars.None;
123 hscroll = new ImplicitHScrollBar();
124 hscroll.ValueChanged += new EventHandler(hscroll_ValueChanged);
125 hscroll.SetStyle (ControlStyles.Selectable, false);
126 hscroll.Enabled = false;
127 hscroll.Visible = false;
128 hscroll.Maximum = Int32.MaxValue;
130 vscroll = new ImplicitVScrollBar();
131 vscroll.ValueChanged += new EventHandler(vscroll_ValueChanged);
132 vscroll.SetStyle (ControlStyles.Selectable, false);
133 vscroll.Enabled = false;
134 vscroll.Visible = false;
135 vscroll.Maximum = Int32.MaxValue;
137 SuspendLayout ();
138 this.Controls.AddImplicit (hscroll);
139 this.Controls.AddImplicit (vscroll);
140 ResumeLayout ();
142 SetStyle(ControlStyles.UserPaint | ControlStyles.StandardClick, false);
143 #if NET_2_0
144 SetStyle(ControlStyles.UseTextForAccessibility, false);
145 #endif
147 canvas_width = ClientSize.Width;
148 canvas_height = ClientSize.Height;
149 document.ViewPortWidth = canvas_width;
150 document.ViewPortHeight = canvas_height;
152 Cursor = Cursors.IBeam;
154 CalculateScrollBars();
156 #endregion // Internal Constructor
158 #region Private and Internal Methods
159 internal string CaseAdjust(string s) {
160 if (character_casing == CharacterCasing.Normal) {
161 return s;
163 if (character_casing == CharacterCasing.Lower) {
164 return s.ToLower();
165 } else {
166 return s.ToUpper();
170 internal override void HandleClick(int clicks, MouseEventArgs me) {
171 // MS seems to fire the click event in spite of the styles they set
172 bool click_set = GetStyle (ControlStyles.StandardClick);
173 bool doubleclick_set = GetStyle (ControlStyles.StandardDoubleClick);
175 // so explicitly set them to true first
176 SetStyle (ControlStyles.StandardClick | ControlStyles.StandardDoubleClick, true);
178 base.HandleClick (clicks, me);
180 // then revert to our previous state
181 if (!click_set)
182 SetStyle (ControlStyles.StandardClick, false);
183 if (!doubleclick_set)
184 SetStyle (ControlStyles.StandardDoubleClick, false);
187 #endregion // Private and Internal Methods
189 #region Public Instance Properties
190 [DefaultValue(false)]
191 [MWFCategory("Behavior")]
192 public bool AcceptsTab {
193 get {
194 return accepts_tab;
197 set {
198 if (value != accepts_tab) {
199 accepts_tab = value;
200 OnAcceptsTabChanged(EventArgs.Empty);
205 [DefaultValue(true)]
206 [Localizable(true)]
207 [RefreshProperties(RefreshProperties.Repaint)]
208 [MWFCategory("Behavior")]
209 public
210 #if NET_2_0
211 override
212 #else
213 virtual
214 #endif
215 bool AutoSize {
216 get {
217 return auto_size;
220 set {
221 if (value != auto_size) {
222 auto_size = value;
223 if (auto_size) {
224 if (PreferredHeight != ClientSize.Height) {
225 ClientSize = new Size(ClientSize.Width, PreferredHeight);
228 OnAutoSizeChanged(EventArgs.Empty);
233 [DispId(-501)]
234 public override System.Drawing.Color BackColor {
235 get {
236 return base.BackColor;
238 set {
239 backcolor_set = true;
240 base.BackColor = value;
244 [Browsable(false)]
245 [EditorBrowsable(EditorBrowsableState.Never)]
246 public override System.Drawing.Image BackgroundImage {
247 get {
248 return base.BackgroundImage;
250 set {
251 base.BackgroundImage = value;
255 [DefaultValue(BorderStyle.Fixed3D)]
256 [DispId(-504)]
257 [MWFCategory("Appearance")]
258 public BorderStyle BorderStyle {
259 get { return actual_border_style; }
260 set {
261 if (value == actual_border_style)
262 return;
264 if (actual_border_style != BorderStyle.Fixed3D || value != BorderStyle.Fixed3D)
265 Invalidate ();
267 actual_border_style = value;
268 if (value != BorderStyle.Fixed3D)
269 value = BorderStyle.None;
271 InternalBorderStyle = value;
272 OnBorderStyleChanged(EventArgs.Empty);
276 [Browsable(false)]
277 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
278 public bool CanUndo {
279 get {
280 return document.undo.CanUndo;
284 [DispId(-513)]
285 public override System.Drawing.Color ForeColor {
286 get {
287 return base.ForeColor;
289 set {
290 base.ForeColor = value;
294 [DefaultValue(true)]
295 [MWFCategory("Behavior")]
296 public bool HideSelection {
297 get {
298 return hide_selection;
301 set {
302 if (value != hide_selection) {
303 hide_selection = value;
304 OnHideSelectionChanged(EventArgs.Empty);
306 if (hide_selection) {
307 document.selection_visible = false;
308 } else {
309 document.selection_visible = true;
311 document.InvalidateSelectionArea();
316 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
317 [Editor("System.Windows.Forms.Design.StringArrayEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
318 [Localizable(true)]
319 [MWFCategory("Appearance")]
320 public string[] Lines {
321 get {
322 int count;
323 ArrayList lines;
325 count = document.Lines;
327 // Handle empty document
328 if ((count == 1) && (document.GetLine (1).text.Length == 0)) {
329 return new string [0];
332 lines = new ArrayList ();
334 int i = 1;
335 while (i <= count) {
336 Line line;
337 StringBuilder lt = new StringBuilder ();
339 do {
340 line = document.GetLine (i++);
341 lt.Append (line.text.ToString ());
342 } while (line.soft_break && i <= count);
344 lines.Add (lt.ToString ());
347 return (string []) lines.ToArray (typeof (string));
350 set {
351 int i;
352 int l;
353 SolidBrush brush;
355 document.Empty();
357 l = value.Length;
358 brush = ThemeEngine.Current.ResPool.GetSolidBrush(this.ForeColor);
360 document.SuspendRecalc ();
361 for (i = 0; i < l; i++) {
363 // Don't add the last line if it is just an empty line feed
364 // the line feed is reflected in the previous line's soft_break = false
365 if (i == l - 1 && value [i].Length == 0)
366 break;
368 bool carriage_return = false;
369 if (value [i].EndsWith ("\r")) {
370 value [i] = value [i].Substring (0, value [i].Length - 1);
371 carriage_return = true;
374 document.Add(i+1, CaseAdjust(value[i]), alignment, Font, brush);
375 if (carriage_return) {
376 Line line = document.GetLine (i + 1);
377 line.carriage_return = true;
381 document.ResumeRecalc (true);
383 // CalculateDocument();
384 OnTextChanged(EventArgs.Empty);
388 [DefaultValue(32767)]
389 [Localizable(true)]
390 [MWFCategory("Behavior")]
391 public virtual int MaxLength {
392 get {
393 if (max_length == 2147483646) { // We don't distinguish between single and multi-line limits
394 return 0;
396 return max_length;
399 set {
400 if (value != max_length) {
401 max_length = value;
406 [Browsable(false)]
407 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
408 public bool Modified {
409 get {
410 return modified;
413 set {
414 if (value != modified) {
415 modified = value;
416 OnModifiedChanged(EventArgs.Empty);
421 [DefaultValue(false)]
422 [Localizable(true)]
423 [RefreshProperties(RefreshProperties.All)]
424 [MWFCategory("Behavior")]
425 public virtual bool Multiline {
426 get {
427 return document.multiline;
430 set {
431 if (value != document.multiline) {
432 document.multiline = value;
433 // Make sure we update our size; the user may have already set the size before going to multiline
434 if (document.multiline && requested_height != -1) {
435 Height = requested_height;
436 requested_height = -1;
439 if (Parent != null)
440 Parent.PerformLayout ();
442 OnMultilineChanged(EventArgs.Empty);
445 if (document.multiline) {
446 document.Wrap = word_wrap;
447 document.PasswordChar = "";
449 } else {
450 document.Wrap = false;
451 if (this.password_char != '\0') {
452 document.PasswordChar = password_char.ToString();
453 } else {
454 document.PasswordChar = "";
458 if (IsHandleCreated)
459 CalculateDocument ();
463 [Browsable(false)]
464 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
465 [EditorBrowsable(EditorBrowsableState.Advanced)]
466 public int PreferredHeight {
467 get {
468 return Font.Height + (BorderStyle != BorderStyle.Fixed3D ? 0 : 7);
472 [DefaultValue(false)]
473 [MWFCategory("Behavior")]
474 public bool ReadOnly {
475 get {
476 return read_only;
479 set {
480 if (value != read_only) {
481 read_only = value;
482 #if NET_2_0
483 if (!backcolor_set) {
484 if (read_only)
485 background_color = SystemColors.Control;
486 else
487 background_color = SystemColors.Window;
489 #endif
490 OnReadOnlyChanged(EventArgs.Empty);
491 Invalidate ();
496 [Browsable(false)]
497 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
498 public virtual string SelectedText {
499 get {
500 return document.GetSelection();
503 set {
504 document.ReplaceSelection(CaseAdjust(value), false);
506 ScrollToCaret();
507 OnTextChanged(EventArgs.Empty);
511 [Browsable(false)]
512 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
513 public virtual int SelectionLength {
514 get {
515 int res = document.SelectionLength ();
516 if (res == 0)
517 res = -1;
518 return res;
521 set {
522 if (value < 0) {
523 throw new ArgumentException(String.Format("{0} is not a valid value", value), "value");
526 if (value != 0) {
527 int start;
528 Line line;
529 LineTag tag;
530 int pos;
532 selection_length = value;
534 start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
536 document.CharIndexToLineTag(start + value, out line, out tag, out pos);
537 document.SetSelectionEnd(line, pos);
538 document.PositionCaret(line, pos);
539 } else {
540 selection_length = -1;
542 document.SetSelectionEnd(document.selection_start.line, document.selection_start.pos);
543 document.PositionCaret(document.selection_start.line, document.selection_start.pos);
548 [Browsable(false)]
549 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
550 public int SelectionStart {
551 get {
552 int index;
554 index = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
556 return index;
559 set {
560 document.SetSelectionStart(value);
561 if (selection_length > -1 ) {
562 document.SetSelectionEnd(value + selection_length);
563 } else {
564 document.SetSelectionEnd(value);
566 document.PositionCaret(document.selection_start.line, document.selection_start.pos);
567 ScrollToCaret();
571 [Localizable(true)]
572 public override string Text {
573 get {
574 if (document == null || document.Root == null || document.Root.text == null) {
575 return string.Empty;
578 StringBuilder sb = new StringBuilder();
580 Line line = null;
581 for (int i = 1; i <= document.Lines; i++) {
582 if (line != null && line.carriage_return)
583 sb.Append ("\r");
584 if (line != null && !line.soft_break)
585 sb.Append (Environment.NewLine);
586 line = document.GetLine (i);
587 sb.Append(line.text.ToString());
590 return sb.ToString();
593 set {
594 if (value == Text)
595 return;
597 if ((value != null) && (value != "")) {
599 document.Empty ();
601 document.Insert (document.GetLine (1), 0, false, value);
603 document.PositionCaret (document.GetLine (1), 0);
604 document.SetSelectionToCaret (true);
606 ScrollToCaret ();
607 } else {
608 document.Empty();
609 if (IsHandleCreated)
610 CalculateDocument ();
613 // set the var so OnModifiedChanged is not raised
614 modified = false;
615 OnTextChanged(EventArgs.Empty);
619 [Browsable(false)]
620 public virtual int TextLength {
621 get {
622 if (document == null || document.Root == null || document.Root.text == null) {
623 return 0;
625 return Text.Length;
629 [DefaultValue(true)]
630 [Localizable(true)]
631 [MWFCategory("Behavior")]
632 public bool WordWrap {
633 get {
634 return word_wrap;
637 set {
638 if (value != word_wrap) {
639 if (document.multiline) {
640 word_wrap = value;
641 document.Wrap = value;
646 #endregion // Public Instance Properties
648 #region Protected Instance Properties
649 protected override CreateParams CreateParams {
650 get {
651 return base.CreateParams;
655 protected override System.Drawing.Size DefaultSize {
656 get {
657 return new Size(100, 20);
660 #endregion // Protected Instance Properties
662 #region Public Instance Methods
663 public void AppendText(string text) {
664 document.MoveCaret (CaretDirection.CtrlEnd);
665 document.Insert (document.caret.line, document.caret.pos, false, text);
666 document.MoveCaret (CaretDirection.CtrlEnd);
667 document.SetSelectionToCaret (true);
669 OnTextChanged(EventArgs.Empty);
672 public void Clear() {
673 Text = null;
676 public void ClearUndo() {
677 document.undo.Clear();
680 public void Copy() {
681 DataObject o;
683 o = new DataObject(DataFormats.Text, SelectedText);
684 if (this is RichTextBox) {
685 o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
687 Clipboard.SetDataObject(o);
690 public void Cut() {
691 DataObject o;
693 o = new DataObject(DataFormats.Text, SelectedText);
694 if (this is RichTextBox) {
695 o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
697 Clipboard.SetDataObject (o);
699 document.undo.BeginUserAction (Locale.GetText ("Cut"));
700 document.ReplaceSelection (String.Empty, false);
701 document.undo.EndUserAction ();
704 public void Paste() {
705 Paste(Clipboard.GetDataObject(), null, false);
708 public void ScrollToCaret() {
709 if (IsHandleCreated) {
710 CaretMoved(this, EventArgs.Empty);
714 public void Select(int start, int length) {
715 SelectionStart = start;
716 SelectionLength = length;
720 public void SelectAll() {
721 Line last;
723 last = document.GetLine(document.Lines);
724 document.SetSelectionStart(document.GetLine(1), 0);
725 document.SetSelectionEnd(last, last.text.Length);
726 document.PositionCaret (document.selection_end.line, document.selection_end.pos);
727 selection_length = -1;
730 public override string ToString() {
731 return String.Concat (base.ToString (), ", Text: ", Text);
734 public void Undo() {
735 document.undo.Undo();
737 #endregion // Public Instance Methods
739 #region Protected Instance Methods
740 protected override void CreateHandle() {
741 CalculateDocument ();
742 base.CreateHandle ();
743 document.AlignCaret();
744 ScrollToCaret();
747 protected override bool IsInputKey(Keys keyData) {
748 if ((keyData & Keys.Alt) != 0) {
749 return base.IsInputKey(keyData);
752 switch (keyData & Keys.KeyCode) {
753 case Keys.Enter: {
754 if (accepts_return) {
755 return true;
757 return false;
760 case Keys.Tab: {
761 if (accepts_tab && document.multiline) {
762 if ((keyData & Keys.Control) == 0) {
763 return true;
766 return false;
769 case Keys.Left:
770 case Keys.Right:
771 case Keys.Up:
772 case Keys.Down:
773 case Keys.PageUp:
774 case Keys.PageDown:
775 case Keys.Home:
776 case Keys.End: {
777 return true;
780 return false;
784 protected virtual void OnAcceptsTabChanged(EventArgs e) {
785 EventHandler eh = (EventHandler)(Events [AcceptsTabChangedEvent]);
786 if (eh != null)
787 eh (this, e);
790 protected virtual void OnAutoSizeChanged(EventArgs e) {
791 EventHandler eh = (EventHandler)(Events [AutoSizeChangedEvent]);
792 if (eh != null)
793 eh (this, e);
796 protected virtual void OnBorderStyleChanged(EventArgs e) {
797 EventHandler eh = (EventHandler)(Events [BorderStyleChangedEvent]);
798 if (eh != null)
799 eh (this, e);
802 protected override void OnFontChanged(EventArgs e) {
803 base.OnFontChanged (e);
805 if (auto_size && !document.multiline) {
806 if (PreferredHeight != ClientSize.Height) {
807 Height = PreferredHeight;
812 protected override void OnHandleCreated(EventArgs e) {
813 base.OnHandleCreated (e);
816 protected override void OnHandleDestroyed(EventArgs e) {
817 base.OnHandleDestroyed (e);
820 protected virtual void OnHideSelectionChanged(EventArgs e) {
821 EventHandler eh = (EventHandler)(Events [HideSelectionChangedEvent]);
822 if (eh != null)
823 eh (this, e);
826 protected virtual void OnModifiedChanged(EventArgs e) {
827 EventHandler eh = (EventHandler)(Events [ModifiedChangedEvent]);
828 if (eh != null)
829 eh (this, e);
832 protected virtual void OnMultilineChanged(EventArgs e) {
833 EventHandler eh = (EventHandler)(Events [MultilineChangedEvent]);
834 if (eh != null)
835 eh (this, e);
838 protected virtual void OnReadOnlyChanged(EventArgs e) {
839 EventHandler eh = (EventHandler)(Events [ReadOnlyChangedEvent]);
840 if (eh != null)
841 eh (this, e);
844 protected override bool ProcessDialogKey(Keys keyData) {
845 return base.ProcessDialogKey(keyData);
848 private bool ProcessKey(Keys keyData) {
849 bool control;
850 bool shift;
852 control = (Control.ModifierKeys & Keys.Control) != 0;
853 shift = (Control.ModifierKeys & Keys.Shift) != 0;
855 switch (keyData & Keys.KeyCode) {
856 case Keys.X: { // Cut (Ctrl-X)
857 if (control) {
858 Cut();
859 return true;
861 return false;
864 case Keys.C: { // Copy (Ctrl-C)
865 if (control) {
866 Copy();
867 return true;
869 return false;
872 case Keys.V: { // Paste (Ctrl-V)
873 if (control) {
874 return Paste(Clipboard.GetDataObject(), null, true);
876 return false;
879 case Keys.Z: { // Undo (Ctrl-Z)
880 if (control) {
881 Undo();
882 return true;
884 return false;
887 case Keys.A: { // Select All (Ctrl-A)
888 if (control) {
889 SelectAll();
890 return true;
892 return false;
895 case Keys.Left: {
896 if (control) {
897 document.MoveCaret(CaretDirection.WordBack);
898 } else {
899 if (!document.selection_visible || shift) {
900 document.MoveCaret(CaretDirection.CharBack);
901 } else {
902 document.MoveCaret(CaretDirection.SelectionStart);
906 if (!shift) {
907 document.SetSelectionToCaret(true);
908 } else {
909 document.SetSelectionToCaret(false);
912 CaretMoved(this, null);
913 return true;
916 case Keys.Right: {
917 if (control) {
918 document.MoveCaret(CaretDirection.WordForward);
919 } else {
920 if (!document.selection_visible || shift) {
921 document.MoveCaret(CaretDirection.CharForward);
922 } else {
923 document.MoveCaret(CaretDirection.SelectionEnd);
926 if (!shift) {
927 document.SetSelectionToCaret(true);
928 } else {
929 document.SetSelectionToCaret(false);
932 CaretMoved(this, null);
933 return true;
936 case Keys.Up: {
937 if (control) {
938 if (document.CaretPosition == 0) {
939 document.MoveCaret(CaretDirection.LineUp);
940 } else {
941 document.MoveCaret(CaretDirection.Home);
943 } else {
944 document.MoveCaret(CaretDirection.LineUp);
947 if ((Control.ModifierKeys & Keys.Shift) == 0) {
948 document.SetSelectionToCaret(true);
949 } else {
950 document.SetSelectionToCaret(false);
953 CaretMoved(this, null);
954 return true;
957 case Keys.Down: {
958 if (control) {
959 if (document.CaretPosition == document.CaretLine.Text.Length) {
960 document.MoveCaret(CaretDirection.LineDown);
961 } else {
962 document.MoveCaret(CaretDirection.End);
964 } else {
965 document.MoveCaret(CaretDirection.LineDown);
968 if ((Control.ModifierKeys & Keys.Shift) == 0) {
969 document.SetSelectionToCaret(true);
970 } else {
971 document.SetSelectionToCaret(false);
974 CaretMoved(this, null);
975 return true;
978 case Keys.Home: {
979 if ((Control.ModifierKeys & Keys.Control) != 0) {
980 document.MoveCaret(CaretDirection.CtrlHome);
981 } else {
982 document.MoveCaret(CaretDirection.Home);
985 if ((Control.ModifierKeys & Keys.Shift) == 0) {
986 document.SetSelectionToCaret(true);
987 } else {
988 document.SetSelectionToCaret(false);
991 CaretMoved(this, null);
992 return true;
995 case Keys.End: {
996 if ((Control.ModifierKeys & Keys.Control) != 0) {
997 document.MoveCaret(CaretDirection.CtrlEnd);
998 } else {
999 document.MoveCaret(CaretDirection.End);
1002 if ((Control.ModifierKeys & Keys.Shift) == 0) {
1003 document.SetSelectionToCaret(true);
1004 } else {
1005 document.SetSelectionToCaret(false);
1008 CaretMoved(this, null);
1009 return true;
1012 case Keys.Enter: {
1013 // ignoring accepts_return, fixes bug #76355
1014 if (!read_only && (accepts_return || (FindForm() != null && FindForm().AcceptButton == null) || ((Control.ModifierKeys & Keys.Control) != 0))) {
1015 Line line;
1017 if (document.selection_visible) {
1018 document.ReplaceSelection("", false);
1021 line = document.CaretLine;
1023 document.Split (document.CaretLine, document.CaretTag, document.CaretPosition, line.soft_break);
1024 line.soft_break = false;
1025 OnTextChanged(EventArgs.Empty);
1026 document.UpdateView(line, 2, 0);
1028 CaretMoved(this, null);
1029 return true;
1031 break;
1034 case Keys.Tab: {
1035 if (!read_only && accepts_tab && document.multiline) {
1036 document.InsertChar(document.CaretLine, document.CaretPosition, '\t');
1037 if (document.selection_visible) {
1038 document.ReplaceSelection("", false);
1040 document.SetSelectionToCaret(true);
1042 OnTextChanged(EventArgs.Empty);
1043 CaretMoved(this, null);
1044 return true;
1046 break;
1049 case Keys.Insert: {
1050 if (shift) {
1051 Paste(Clipboard.GetDataObject(), null, true);
1052 return true;
1055 if (control) {
1056 Copy();
1057 return true;
1060 // FIXME - need overwrite/insert toggle?
1061 return false;
1064 case Keys.PageUp: {
1065 if ((Control.ModifierKeys & Keys.Control) != 0) {
1066 document.MoveCaret(CaretDirection.CtrlPgUp);
1067 } else {
1068 document.MoveCaret(CaretDirection.PgUp);
1070 return true;
1073 case Keys.PageDown: {
1074 if ((Control.ModifierKeys & Keys.Control) != 0) {
1075 document.MoveCaret(CaretDirection.CtrlPgDn);
1076 } else {
1077 document.MoveCaret(CaretDirection.PgDn);
1079 return true;
1082 case Keys.Delete: {
1083 if (shift) {
1084 Cut();
1085 return true;
1088 if (read_only) {
1089 break;
1092 if (document.selection_visible) {
1093 document.ReplaceSelection("", false);
1094 } else {
1095 // DeleteChar only deletes on the line, doesn't do the combine
1096 if (document.CaretPosition == document.CaretLine.Text.Length) {
1097 if (document.CaretLine.LineNo < document.Lines) {
1098 Line line;
1100 line = document.GetLine(document.CaretLine.LineNo + 1);
1101 document.Combine(document.CaretLine, line);
1102 document.UpdateView(document.CaretLine, 2, 0);
1104 #if not_Debug
1105 Line check_first;
1106 Line check_second;
1108 check_first = document.GetLine(document.CaretLine.LineNo);
1109 check_second = document.GetLine(check_first.line_no + 1);
1111 Console.WriteLine("Post-UpdateView: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
1112 #endif
1114 // Caret doesn't move
1116 } else {
1117 if (!control) {
1118 document.DeleteChar(document.CaretTag, document.CaretPosition, true);
1119 } else {
1120 int end_pos;
1122 end_pos = document.CaretPosition;
1124 while ((end_pos < document.CaretLine.Text.Length) && !Document.IsWordSeparator(document.CaretLine.Text[end_pos])) {
1125 end_pos++;
1128 if (end_pos < document.CaretLine.Text.Length) {
1129 end_pos++;
1131 document.DeleteChars(document.CaretTag, document.CaretPosition, end_pos - document.CaretPosition);
1136 OnTextChanged(EventArgs.Empty);
1137 document.AlignCaret();
1138 document.UpdateCaret();
1139 CaretMoved(this, null);
1140 return true;
1143 return false;
1146 private void HandleBackspace(bool control) {
1147 bool fire_changed;
1149 fire_changed = false;
1151 // delete only deletes on the line, doesn't do the combine
1152 if (document.selection_visible) {
1153 document.undo.BeginUserAction (Locale.GetText ("Delete"));
1154 document.ReplaceSelection("", false);
1155 document.undo.EndUserAction ();
1156 fire_changed = true;
1158 document.SetSelectionToCaret(true);
1160 if (document.CaretPosition == 0) {
1161 if (document.CaretLine.LineNo > 1) {
1162 Line line;
1163 int new_caret_pos;
1165 line = document.GetLine(document.CaretLine.LineNo - 1);
1166 new_caret_pos = line.text.Length;
1168 document.Combine(line, document.CaretLine);
1169 document.UpdateView(line, 1, 0);
1170 document.PositionCaret(line, new_caret_pos);
1171 document.SetSelectionToCaret (true);
1172 document.UpdateCaret();
1173 fire_changed = true;
1175 } else {
1176 if (!control || document.CaretPosition == 0) {
1178 // Move before we delete because the delete will change positions around
1179 // if we cross a wrap border
1180 LineTag tag = document.CaretTag;
1181 int pos = document.CaretPosition;
1182 document.MoveCaret (CaretDirection.CharBack);
1183 document.DeleteChar (tag, pos, false);
1184 document.SetSelectionToCaret (true);
1185 } else {
1186 int start_pos;
1189 start_pos = document.CaretPosition - 1;
1190 while ((start_pos > 0) && !Document.IsWordSeparator(document.CaretLine.Text[start_pos - 1])) {
1191 start_pos--;
1194 document.undo.BeginUserAction (Locale.GetText ("Delete"));
1195 document.DeleteChars(document.CaretTag, start_pos, document.CaretPosition - start_pos);
1196 document.undo.EndUserAction ();
1197 document.PositionCaret(document.CaretLine, start_pos);
1198 document.SetSelectionToCaret (true);
1200 document.UpdateCaret();
1201 fire_changed = true;
1204 if (fire_changed) {
1205 OnTextChanged(EventArgs.Empty);
1207 CaretMoved(this, null);
1210 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
1211 // Make sure we don't get sized bigger than we want to be
1212 if (!richtext) {
1213 if (!document.multiline) {
1214 if (height != PreferredHeight) {
1215 requested_height = height;
1216 height = PreferredHeight;
1217 specified |= BoundsSpecified.Height;
1222 base.SetBoundsCore (x, y, width, height, specified);
1225 protected override void WndProc(ref Message m) {
1226 switch ((Msg)m.Msg) {
1227 case Msg.WM_KEYDOWN: {
1228 if (ProcessKeyMessage(ref m) || ProcessKey((Keys)m.WParam.ToInt32() | XplatUI.State.ModifierKeys)) {
1229 m.Result = IntPtr.Zero;
1230 return;
1232 DefWndProc (ref m);
1233 return;
1236 case Msg.WM_CHAR: {
1237 int ch;
1239 if (ProcessKeyMessage(ref m)) {
1240 m.Result = IntPtr.Zero;
1241 return;
1244 if (read_only) {
1245 return;
1248 m.Result = IntPtr.Zero;
1250 ch = m.WParam.ToInt32();
1252 if (ch == 127) {
1253 HandleBackspace(true);
1254 } else if (ch >= 32) {
1255 if (document.selection_visible) {
1256 document.ReplaceSelection("", false);
1259 char c = (char)m.WParam;
1260 switch (character_casing) {
1261 case CharacterCasing.Upper:
1262 c = Char.ToUpper((char) m.WParam);
1263 break;
1264 case CharacterCasing.Lower:
1265 c = Char.ToLower((char) m.WParam);
1266 break;
1269 if (document.Length < max_length) {
1270 document.InsertCharAtCaret(c, true);
1271 #if NET_2_0
1272 OnTextUpdate ();
1273 #endif
1274 OnTextChanged(EventArgs.Empty);
1275 CaretMoved(this, null);
1276 } else {
1277 XplatUI.AudibleAlert();
1279 return;
1280 } else if (ch == 8) {
1281 HandleBackspace(false);
1284 return;
1287 case Msg.WM_SETFOCUS:
1288 base.WndProc(ref m);
1289 document.CaretHasFocus ();
1290 break;
1292 case Msg.WM_KILLFOCUS:
1293 base.WndProc(ref m);
1294 document.CaretLostFocus ();
1295 break;
1297 default:
1298 base.WndProc(ref m);
1299 return;
1303 #endregion // Protected Instance Methods
1305 #region Events
1306 static object AcceptsTabChangedEvent = new object ();
1307 static object AutoSizeChangedEvent = new object ();
1308 static object BorderStyleChangedEvent = new object ();
1309 static object HideSelectionChangedEvent = new object ();
1310 static object ModifiedChangedEvent = new object ();
1311 static object MultilineChangedEvent = new object ();
1312 static object ReadOnlyChangedEvent = new object ();
1313 static object HScrolledEvent = new object ();
1314 static object VScrolledEvent = new object ();
1316 public event EventHandler AcceptsTabChanged {
1317 add { Events.AddHandler (AcceptsTabChangedEvent, value); }
1318 remove { Events.RemoveHandler (AcceptsTabChangedEvent, value); }
1321 public new event EventHandler AutoSizeChanged {
1322 add { Events.AddHandler (AutoSizeChangedEvent, value); }
1323 remove { Events.RemoveHandler (AutoSizeChangedEvent, value); }
1326 public event EventHandler BorderStyleChanged {
1327 add { Events.AddHandler (BorderStyleChangedEvent, value); }
1328 remove { Events.RemoveHandler (BorderStyleChangedEvent, value); }
1331 public event EventHandler HideSelectionChanged {
1332 add { Events.AddHandler (HideSelectionChangedEvent, value); }
1333 remove { Events.RemoveHandler (HideSelectionChangedEvent, value); }
1336 public event EventHandler ModifiedChanged {
1337 add { Events.AddHandler (ModifiedChangedEvent, value); }
1338 remove { Events.RemoveHandler (ModifiedChangedEvent, value); }
1341 public event EventHandler MultilineChanged {
1342 add { Events.AddHandler (MultilineChangedEvent, value); }
1343 remove { Events.RemoveHandler (MultilineChangedEvent, value); }
1346 public event EventHandler ReadOnlyChanged {
1347 add { Events.AddHandler (ReadOnlyChangedEvent, value); }
1348 remove { Events.RemoveHandler (ReadOnlyChangedEvent, value); }
1351 internal event EventHandler HScrolled {
1352 add { Events.AddHandler (HScrolledEvent, value); }
1353 remove { Events.RemoveHandler (HScrolledEvent, value); }
1356 internal event EventHandler VScrolled {
1357 add { Events.AddHandler (VScrolledEvent, value); }
1358 remove { Events.RemoveHandler (VScrolledEvent, value); }
1361 [Browsable(false)]
1362 [EditorBrowsable(EditorBrowsableState.Never)]
1363 public new event EventHandler BackgroundImageChanged {
1364 add { base.BackgroundImageChanged += value; }
1365 remove { base.BackgroundImageChanged -= value; }
1367 [Browsable(false)]
1368 [EditorBrowsable(EditorBrowsableState.Advanced)]
1369 public new event EventHandler Click {
1370 add { base.Click += value; }
1371 remove { base.Click -= value; }
1374 // XXX should this not manipulate base.Paint?
1375 [Browsable(false)]
1376 [EditorBrowsable(EditorBrowsableState.Never)]
1377 public new event PaintEventHandler Paint;
1378 #endregion // Events
1380 #region Private Methods
1381 internal Document Document {
1382 get {
1383 return document;
1386 set {
1387 document = value;
1391 internal bool ShowSelection {
1392 get {
1393 if (show_selection || !hide_selection) {
1394 return true;
1397 return has_focus;
1400 set {
1401 if (show_selection == value)
1402 return;
1404 show_selection = value;
1405 // Currently InvalidateSelectionArea is commented out so do a full invalidate
1406 document.InvalidateSelectionArea();
1410 internal Graphics CreateGraphicsInternal() {
1411 if (IsHandleCreated) {
1412 return base.CreateGraphics();
1415 return Graphics.FromImage(bmp);
1418 #if Debug
1419 static int current;
1420 #endif
1422 internal override void OnPaintInternal (PaintEventArgs pevent) {
1423 // Fill background
1424 if (backcolor_set || (Enabled && !read_only)) {
1425 pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), pevent.ClipRectangle);
1426 } else {
1427 pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorControl), pevent.ClipRectangle);
1430 // Draw the viewable document
1431 document.Draw(pevent.Graphics, pevent.ClipRectangle);
1434 // OnPaint does not get raised on MS (see bug #80639)
1436 pevent.Handled = true;
1439 private bool IsDoubleClick (MouseEventArgs e)
1441 TimeSpan interval = DateTime.Now - click_last;
1442 if (interval.TotalMilliseconds > SystemInformation.DoubleClickTime)
1443 return false;
1444 Size dcs = SystemInformation.DoubleClickSize;
1445 if (e.X < click_point_x - dcs.Width / 2 || e.X > click_point_x + dcs.Width / 2)
1446 return false;
1447 if (e.Y < click_point_y - dcs.Height / 2 || e.Y > click_point_y + dcs.Height / 2)
1448 return false;
1449 return true;
1452 private void TextBoxBase_MouseDown (object sender, MouseEventArgs e)
1454 if (e.Button == MouseButtons.Left) {
1456 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1458 if (IsDoubleClick (e)) {
1459 switch (click_mode) {
1460 case CaretSelection.Position:
1461 SelectWord ();
1462 click_mode = CaretSelection.Word;
1463 break;
1464 case CaretSelection.Word:
1466 if (this is TextBox) {
1467 document.SetSelectionToCaret (true);
1468 click_mode = CaretSelection.Position;
1469 break;
1472 document.ExpandSelection (CaretSelection.Line, false);
1473 click_mode = CaretSelection.Line;
1474 break;
1475 case CaretSelection.Line:
1477 // Gotta do this first because Exanding to a word
1478 // from a line doesn't really work
1479 document.SetSelectionToCaret (true);
1481 SelectWord ();
1482 click_mode = CaretSelection.Word;
1483 break;
1485 } else {
1486 document.SetSelectionToCaret (true);
1487 click_mode = CaretSelection.Position;
1490 click_point_x = e.X;
1491 click_point_y = e.Y;
1492 click_last = DateTime.Now;
1495 if ((e.Button == MouseButtons.Middle) && (((int)Environment.OSVersion.Platform == 4) || ((int)Environment.OSVersion.Platform == 128))) {
1496 Document.Marker marker;
1498 marker.tag = document.FindCursor(e.X + document.ViewPortX, e.Y + document.ViewPortY, out marker.pos);
1499 marker.line = marker.tag.line;
1500 marker.height = marker.tag.height;
1502 document.SetSelection(marker.line, marker.pos, marker.line, marker.pos);
1503 Paste (Clipboard.GetDataObject (true), null, true);
1508 private void TextBoxBase_MouseUp(object sender, MouseEventArgs e) {
1509 if (e.Button == MouseButtons.Left) {
1510 if (click_mode == CaretSelection.Position) {
1511 document.SetSelectionToCaret(false);
1512 document.DisplayCaret();
1515 if (scroll_timer != null) {
1516 scroll_timer.Enabled = false;
1518 return;
1522 private void PositionControls ()
1524 if (hscroll.Visible) {
1525 //vscroll.Maximum += hscroll.Height;
1526 canvas_height = ClientSize.Height - hscroll.Height;
1527 } else {
1528 canvas_height = ClientSize.Height;
1531 if (vscroll.Visible) {
1532 //hscroll.Maximum += vscroll.Width;
1533 canvas_width = ClientSize.Width - vscroll.Width;
1534 } else {
1535 canvas_width = ClientSize.Width;
1539 document.ViewPortWidth = canvas_width;
1540 document.ViewPortHeight = canvas_height;
1542 if (canvas_height < 1 || canvas_width < 1)
1543 return;
1545 // We always move them, they just might not be displayed
1546 hscroll.Bounds = new Rectangle (ClientRectangle.Left,
1547 Math.Max (0, ClientRectangle.Height - hscroll.Height),
1548 Math.Max (0, ClientSize.Width - (vscroll.Visible ? vscroll.Width : 0)),
1549 hscroll.Height);
1551 vscroll.Bounds = new Rectangle (Math.Max (0, ClientRectangle.Right - vscroll.Width),
1552 ClientRectangle.Top, vscroll.Width,
1553 Math.Max (0, ClientSize.Height - (hscroll.Visible ? hscroll.Height : 0)));
1557 private void TextBoxBase_SizeChanged(object sender, EventArgs e) {
1558 if (IsHandleCreated)
1559 CalculateDocument ();
1562 private void TextBoxBase_MouseWheel(object sender, MouseEventArgs e) {
1564 if (!vscroll.Enabled) {
1565 return;
1568 if (e.Delta < 0)
1569 vscroll.Value = Math.Min (vscroll.Value + SystemInformation.MouseWheelScrollLines,
1570 Math.Max (0, vscroll.Maximum - document.ViewPortHeight + 1));
1571 else
1572 vscroll.Value = Math.Max (0, vscroll.Value - SystemInformation.MouseWheelScrollLines);
1575 internal virtual void SelectWord ()
1577 StringBuilder s = document.caret.line.text;
1578 int start = document.caret.pos;
1579 int end = document.caret.pos;
1581 if (s.Length < 1) {
1582 if (document.caret.line.line_no >= document.Lines)
1583 return;
1584 Line line = document.GetLine (document.caret.line.line_no + 1);
1585 document.PositionCaret (line, 0);
1586 return;
1589 if (start > 0) {
1590 start--;
1591 end--;
1594 // skip whitespace until we hit a word
1595 while (start > 0 && s [start] == ' ')
1596 start--;
1597 if (start > 0) {
1598 while (start > 0 && (s [start] != ' '))
1599 start--;
1600 if (s [start] == ' ')
1601 start++;
1604 if (s [end] == ' ') {
1605 while (end < s.Length && s [end] == ' ')
1606 end++;
1607 } else {
1608 while (end < s.Length && s [end] != ' ')
1609 end++;
1610 while (end < s.Length && s [end] == ' ')
1611 end++;
1614 document.SetSelection (document.caret.line, start, document.caret.line, end);
1615 document.PositionCaret (document.selection_end.line, document.selection_end.pos);
1616 document.DisplayCaret();
1619 internal void CalculateDocument() {
1621 document.RecalculateDocument(CreateGraphicsInternal());
1622 CalculateScrollBars();
1624 if (document.caret.line != null && document.caret.line.Y < document.ViewPortHeight) {
1625 // The window has probably been resized, making the entire thing visible, so
1626 // we need to set the scroll position back to zero.
1627 vscroll.Value = 0;
1630 Invalidate();
1633 internal void CalculateScrollBars () {
1634 // FIXME - need separate calculations for center and right alignment
1636 if (!document.multiline) {
1637 PositionControls ();
1638 return;
1641 if (document.Width >= document.ViewPortWidth) {
1642 hscroll.SetValues (0, Math.Max (1, document.Width), -1,
1643 document.ViewPortWidth < 0 ? 0 : document.ViewPortWidth);
1644 hscroll.Enabled = true;
1645 } else {
1646 hscroll.Enabled = false;
1647 hscroll.Maximum = document.ViewPortWidth;
1650 if (document.Height >= document.ViewPortHeight) {
1651 vscroll.SetValues (0, Math.Max (1, document.Height), -1,
1652 document.ViewPortHeight < 0 ? 0 : document.ViewPortHeight);
1653 vscroll.Enabled = true;
1654 } else {
1655 vscroll.Enabled = false;
1656 vscroll.Maximum = document.ViewPortHeight;
1660 if (!WordWrap) {
1661 if ((scrollbars & RichTextBoxScrollBars.Horizontal) != 0) {
1662 if (((scrollbars & RichTextBoxScrollBars.ForcedHorizontal) != 0) || hscroll.Enabled) {
1663 hscroll.Visible = true;
1664 } else {
1665 hscroll.Visible = false;
1667 } else {
1668 hscroll.Visible = false;
1672 if ((scrollbars & RichTextBoxScrollBars.Vertical) != 0) {
1673 if (((scrollbars & RichTextBoxScrollBars.ForcedVertical) != 0) || vscroll.Enabled) {
1674 vscroll.Visible = true;
1675 } else {
1676 vscroll.Visible = false;
1678 } else {
1679 vscroll.Visible = false;
1682 PositionControls ();
1685 private void document_WidthChanged(object sender, EventArgs e) {
1686 CalculateScrollBars();
1689 private void document_HeightChanged(object sender, EventArgs e) {
1690 CalculateScrollBars();
1693 private void hscroll_ValueChanged(object sender, EventArgs e) {
1694 int old_viewport_x;
1696 old_viewport_x = document.ViewPortX;
1697 document.ViewPortX = this.hscroll.Value;
1700 // Before scrolling we want to destroy the caret, then draw a new one after the scroll
1701 // the reason for this is that scrolling changes the coordinates of the caret, and we
1702 // will get tracers if we don't
1704 if (Focused)
1705 document.CaretLostFocus ();
1707 if (vscroll.Visible) {
1708 XplatUI.ScrollWindow(this.Handle, new Rectangle(0, 0, ClientSize.Width - vscroll.Width, ClientSize.Height), old_viewport_x - this.hscroll.Value, 0, false);
1709 } else {
1710 XplatUI.ScrollWindow(this.Handle, ClientRectangle, old_viewport_x - this.hscroll.Value, 0, false);
1713 if (Focused)
1714 document.CaretHasFocus ();
1716 EventHandler eh = (EventHandler)(Events [HScrolledEvent]);
1717 if (eh != null)
1718 eh (this, EventArgs.Empty);
1721 private void vscroll_ValueChanged(object sender, EventArgs e) {
1722 int old_viewport_y;
1724 old_viewport_y = document.ViewPortY;
1725 document.ViewPortY = this.vscroll.Value;
1728 // Before scrolling we want to destroy the caret, then draw a new one after the scroll
1729 // the reason for this is that scrolling changes the coordinates of the caret, and we
1730 // will get tracers if we don't
1732 if (Focused)
1733 document.CaretLostFocus ();
1735 if (hscroll.Visible) {
1736 XplatUI.ScrollWindow(this.Handle, new Rectangle(0, 0, ClientSize.Width, ClientSize.Height - hscroll.Height), 0, old_viewport_y - this.vscroll.Value, false);
1737 } else {
1738 XplatUI.ScrollWindow(this.Handle, ClientRectangle, 0, old_viewport_y - this.vscroll.Value, false);
1741 if (Focused)
1742 document.CaretHasFocus ();
1744 EventHandler eh = (EventHandler)(Events [VScrolledEvent]);
1745 if (eh != null)
1746 eh (this, EventArgs.Empty);
1749 private void TextBoxBase_MouseMove(object sender, MouseEventArgs e) {
1750 // FIXME - handle auto-scrolling if mouse is to the right/left of the window
1751 if (e.Button == MouseButtons.Left && Capture) {
1752 if (!ClientRectangle.Contains (e.X, e.Y)) {
1753 if (scroll_timer == null) {
1754 scroll_timer = new Timer ();
1755 scroll_timer.Interval = 100;
1756 scroll_timer.Tick += new EventHandler (ScrollTimerTickHandler);
1759 if (!scroll_timer.Enabled) {
1760 scroll_timer.Start ();
1762 // Force the first tick
1763 ScrollTimerTickHandler (null, EventArgs.Empty);
1767 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1768 if (click_mode == CaretSelection.Position) {
1769 document.SetSelectionToCaret(false);
1770 document.DisplayCaret();
1775 private void TextBoxBase_FontOrColorChanged(object sender, EventArgs e) {
1776 if (!richtext) {
1777 Line line;
1779 document.SuspendRecalc ();
1780 // Font changes apply to the whole document
1781 for (int i = 1; i <= document.Lines; i++) {
1782 line = document.GetLine(i);
1783 LineTag.FormatText(line, 1, line.text.Length, Font,
1784 ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor),
1785 null, FormatSpecified.Font | FormatSpecified.Color);
1787 document.ResumeRecalc (false);
1789 // Make sure the caret height is matching the new font height
1790 document.AlignCaret();
1794 private void ScrollTimerTickHandler (object sender, EventArgs e)
1796 Point pt = Cursor.Position;
1798 pt = PointToClient (pt);
1800 if (pt.X < ClientRectangle.Left) {
1801 document.MoveCaret(CaretDirection.CharBackNoWrap);
1802 document.SetSelectionToCaret(false);
1804 CaretMoved(this, null);
1805 } else if (pt.X > ClientRectangle.Right) {
1806 document.MoveCaret(CaretDirection.CharForwardNoWrap);
1807 document.SetSelectionToCaret(false);
1809 CaretMoved(this, null);
1810 } else if (pt.Y > ClientRectangle.Bottom) {
1811 document.MoveCaret(CaretDirection.LineDown);
1812 document.SetSelectionToCaret(false);
1814 CaretMoved(this, null);
1815 } else if (pt.Y < ClientRectangle.Top) {
1816 document.MoveCaret(CaretDirection.LineUp);
1817 document.SetSelectionToCaret(false);
1819 CaretMoved(this, null);
1823 /// <summary>Ensure the caret is always visible</summary>
1824 internal void CaretMoved(object sender, EventArgs e) {
1825 Point pos;
1826 int height;
1828 if (canvas_width < 1 || canvas_height < 1)
1829 return;
1831 document.MoveCaretToTextTag ();
1832 pos = document.Caret;
1834 //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);
1837 // Horizontal scrolling:
1838 // If the caret moves to the left outside the visible area, we jump the document into view, not just one
1839 // character, but 1/3 of the width of the document
1840 // If the caret moves to the right outside the visible area, we scroll just enough to keep the caret visible
1842 // Handle horizontal scrolling
1843 if (document.CaretLine.alignment == HorizontalAlignment.Left) {
1844 // Check if we moved out of view to the left
1845 if (pos.X < (document.ViewPortX)) {
1846 do {
1847 if ((hscroll.Value - document.ViewPortWidth / 3) >= hscroll.Minimum) {
1848 hscroll.Value -= document.ViewPortWidth / 3;
1849 } else {
1850 hscroll.Value = hscroll.Minimum;
1852 } while (hscroll.Value > pos.X);
1855 // Check if we moved out of view to the right
1856 if ((pos.X >= (document.ViewPortWidth + document.ViewPortX)) && (hscroll.Value != hscroll.Maximum)) {
1857 if ((pos.X - document.ViewPortWidth + 1) <= hscroll.Maximum) {
1858 if (pos.X - document.ViewPortWidth >= 0) {
1859 hscroll.Value = pos.X - document.ViewPortWidth + 1;
1860 } else {
1861 hscroll.Value = 0;
1863 } else {
1864 hscroll.Value = hscroll.Maximum;
1867 } else if (document.CaretLine.alignment == HorizontalAlignment.Right) {
1868 // hscroll.Value = pos.X;
1870 // if ((pos.X > (this.canvas_width + document.ViewPortX)) && (hscroll.Enabled && (hscroll.Value != hscroll.Maximum))) {
1871 // hscroll.Value = hscroll.Maximum;
1872 // }
1873 } else {
1874 // FIXME - implement center cursor alignment
1877 if (!document.multiline) {
1878 return;
1881 // Handle vertical scrolling
1882 height = document.CaretLine.Height + 1;
1884 if (pos.Y < document.ViewPortY) {
1885 vscroll.Value = pos.Y;
1888 if ((pos.Y + height) > (document.ViewPortY + canvas_height)) {
1889 vscroll.Value = Math.Max (0, pos.Y - canvas_height + height);
1893 internal bool Paste(IDataObject clip, DataFormats.Format format, bool obey_length) {
1894 string s;
1896 if (clip == null)
1897 return false;
1899 if (format == null) {
1900 if ((this is RichTextBox) && clip.GetDataPresent(DataFormats.Rtf)) {
1901 format = DataFormats.GetFormat(DataFormats.Rtf);
1902 } else if ((this is RichTextBox) && clip.GetDataPresent (DataFormats.Bitmap)) {
1903 format = DataFormats.GetFormat (DataFormats.Bitmap);
1904 } else if (clip.GetDataPresent(DataFormats.UnicodeText)) {
1905 format = DataFormats.GetFormat(DataFormats.UnicodeText);
1906 } else if (clip.GetDataPresent(DataFormats.Text)) {
1907 format = DataFormats.GetFormat(DataFormats.Text);
1908 } else {
1909 return false;
1911 } else {
1912 if ((format.Name == DataFormats.Rtf) && !(this is RichTextBox)) {
1913 return false;
1916 if (!clip.GetDataPresent(format.Name)) {
1917 return false;
1921 if (format.Name == DataFormats.Rtf) {
1922 document.undo.BeginUserAction (Locale.GetText ("Paste"));
1923 ((RichTextBox)this).SelectedRtf = (string)clip.GetData(DataFormats.Rtf);
1924 document.undo.EndUserAction ();
1925 return true;
1926 } else if (format.Name == DataFormats.Bitmap) {
1927 document.undo.BeginUserAction (Locale.GetText ("Paste"));
1928 // document.InsertImage (document.caret.line, document.caret.pos, (Image) clip.GetData (DataFormats.Bitmap));
1929 document.MoveCaret (CaretDirection.CharForward);
1930 document.undo.EndUserAction ();
1931 return true;
1932 } else if (format.Name == DataFormats.UnicodeText) {
1933 s = (string)clip.GetData(DataFormats.UnicodeText);
1934 } else if (format.Name == DataFormats.Text) {
1935 s = (string)clip.GetData(DataFormats.Text);
1936 } else {
1937 return false;
1940 if (!obey_length) {
1941 document.undo.BeginUserAction (Locale.GetText ("Paste"));
1942 this.SelectedText = s;
1943 document.undo.EndUserAction ();
1944 } else {
1945 if ((s.Length + document.Length) < max_length) {
1946 document.undo.BeginUserAction (Locale.GetText ("Paste"));
1947 this.SelectedText = s;
1948 document.undo.EndUserAction ();
1949 } else if (document.Length < max_length) {
1950 document.undo.BeginUserAction (Locale.GetText ("Paste"));
1951 this.SelectedText = s.Substring (0, max_length - document.Length);
1952 document.undo.EndUserAction ();
1956 return true;
1958 #endregion // Private Methods
1960 #if NET_2_0
1961 // This is called just before OnTextChanged is called.
1962 internal virtual void OnTextUpdate ()
1966 protected override void OnTextChanged (EventArgs e)
1968 base.OnTextChanged (e);
1971 public virtual int GetLineFromCharIndex (int index)
1973 Line line_out;
1974 LineTag tag_out;
1975 int pos;
1977 document.CharIndexToLineTag (index, out line_out, out tag_out, out pos);
1979 return line_out.LineNo;
1982 protected override void OnMouseUp (MouseEventArgs e)
1984 base.OnMouseUp (e);
1987 #endif