* TextBoxBase.cs: Default selection length is -1, need to do
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TextBoxBase.cs
blob94bf84fbe53004ea47f6be3c91c547cde7ac1638
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 undo;
51 internal bool hide_selection;
52 internal int max_length;
53 internal bool modified;
54 internal bool multiline;
55 internal char password_char;
56 internal bool read_only;
57 internal bool word_wrap;
58 internal Document document;
59 internal LineTag caret_tag; // tag our cursor is in
60 internal int caret_pos; // position on the line our cursor is in (can be 0 = beginning of line)
61 internal ImplicitHScrollBar hscroll;
62 internal ImplicitVScrollBar vscroll;
63 internal RichTextBoxScrollBars scrollbars;
64 internal Timer scroll_timer;
65 internal bool richtext;
66 internal bool show_selection; // set to true to always show selection, even if no focus is set
67 internal int selection_length = -1; // set to the user-specified selection length, or -1 if none
68 internal int requested_height;
69 internal int canvas_width;
70 internal int canvas_height;
71 static internal int track_width = 2; //
72 static internal int track_border = 5; //
73 internal DateTime click_last;
74 internal CaretSelection click_mode;
75 internal Bitmap bmp;
76 #if Debug
77 internal static bool draw_lines = false;
78 #endif
80 #endregion // Local Variables
82 #region Internal Constructor
83 // Constructor will go when complete, only for testing - pdb
84 internal TextBoxBase() {
85 alignment = HorizontalAlignment.Left;
86 accepts_return = false;
87 accepts_tab = false;
88 auto_size = true;
89 border_style = BorderStyle.Fixed3D;
90 character_casing = CharacterCasing.Normal;
91 undo = false;
92 hide_selection = true;
93 max_length = 32767;
94 modified = false;
95 multiline = false;
96 password_char = '\0';
97 read_only = false;
98 word_wrap = true;
99 richtext = false;
100 show_selection = false;
101 document = new Document(this);
102 document.WidthChanged += new EventHandler(document_WidthChanged);
103 document.HeightChanged += new EventHandler(document_HeightChanged);
104 //document.CaretMoved += new EventHandler(CaretMoved);
105 document.Wrap = false;
106 requested_height = -1;
107 click_last = DateTime.Now;
108 click_mode = CaretSelection.Position;
109 bmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
111 MouseDown += new MouseEventHandler(TextBoxBase_MouseDown);
112 MouseUp += new MouseEventHandler(TextBoxBase_MouseUp);
113 MouseMove += new MouseEventHandler(TextBoxBase_MouseMove);
114 SizeChanged += new EventHandler(TextBoxBase_SizeChanged);
115 FontChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
116 ForeColorChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
117 MouseWheel += new MouseEventHandler(TextBoxBase_MouseWheel);
119 scrollbars = RichTextBoxScrollBars.None;
121 hscroll = new ImplicitHScrollBar();
122 hscroll.ValueChanged += new EventHandler(hscroll_ValueChanged);
123 hscroll.control_style &= ~ControlStyles.Selectable;
124 hscroll.Enabled = false;
125 hscroll.Visible = false;
126 hscroll.Maximum = Int32.MaxValue;
128 vscroll = new ImplicitVScrollBar();
129 vscroll.ValueChanged += new EventHandler(vscroll_ValueChanged);
130 vscroll.control_style &= ~ControlStyles.Selectable;
131 vscroll.Enabled = false;
132 vscroll.Visible = false;
133 vscroll.Maximum = Int32.MaxValue;
135 SuspendLayout ();
136 this.Controls.AddImplicit (hscroll);
137 this.Controls.AddImplicit (vscroll);
138 ResumeLayout ();
140 SetStyle(ControlStyles.UserPaint | ControlStyles.StandardClick, false);
141 #if NET_2_0
142 SetStyle(ControlStyles.UseTextForAccessibility, false);
143 #endif
145 canvas_width = ClientSize.Width;
146 canvas_height = ClientSize.Height;
147 document.ViewPortWidth = canvas_width;
148 document.ViewPortHeight = canvas_height;
150 Cursor = Cursors.IBeam;
152 CalculateScrollBars();
154 #endregion // Internal Constructor
156 #region Private and Internal Methods
157 internal string CaseAdjust(string s) {
158 if (character_casing == CharacterCasing.Normal) {
159 return s;
161 if (character_casing == CharacterCasing.Lower) {
162 return s.ToLower();
163 } else {
164 return s.ToUpper();
168 internal override void HandleClick(int clicks, MouseEventArgs me) {
169 // MS seems to fire the click event in spite of the styles they set
170 control_style |= ControlStyles.StandardClick | ControlStyles.StandardDoubleClick;
171 base.HandleClick (clicks, me);
172 control_style ^= ControlStyles.StandardClick | ControlStyles.StandardDoubleClick;
175 #endregion // Private and Internal Methods
177 #region Public Instance Properties
178 [DefaultValue(false)]
179 [MWFCategory("Behavior")]
180 public bool AcceptsTab {
181 get {
182 return accepts_tab;
185 set {
186 if (value != accepts_tab) {
187 accepts_tab = value;
188 OnAcceptsTabChanged(EventArgs.Empty);
193 [DefaultValue(true)]
194 [Localizable(true)]
195 [RefreshProperties(RefreshProperties.Repaint)]
196 [MWFCategory("Behavior")]
197 public virtual bool AutoSize {
198 get {
199 return auto_size;
202 set {
203 if (value != auto_size) {
204 auto_size = value;
205 if (auto_size) {
206 if (PreferredHeight != ClientSize.Height) {
207 ClientSize = new Size(ClientSize.Width, PreferredHeight);
210 OnAutoSizeChanged(EventArgs.Empty);
215 [DispId(-501)]
216 public override System.Drawing.Color BackColor {
217 get {
218 return base.BackColor;
220 set {
221 if (value != ThemeEngine.Current.ColorWindow) {
222 backcolor_set = true;
223 } else {
224 backcolor_set = false;
226 base.BackColor = value;
230 [Browsable(false)]
231 [EditorBrowsable(EditorBrowsableState.Never)]
232 public override System.Drawing.Image BackgroundImage {
233 get {
234 return base.BackgroundImage;
236 set {
237 base.BackgroundImage = value;
241 [DefaultValue(BorderStyle.Fixed3D)]
242 [DispId(-504)]
243 [MWFCategory("Appearance")]
244 public BorderStyle BorderStyle {
245 get { return InternalBorderStyle; }
246 set {
247 InternalBorderStyle = value;
248 OnBorderStyleChanged(EventArgs.Empty);
252 [Browsable(false)]
253 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
254 public bool CanUndo {
255 get {
256 return document.undo.UndoLevels != 0;
260 [DispId(-513)]
261 public override System.Drawing.Color ForeColor {
262 get {
263 return base.ForeColor;
265 set {
266 base.ForeColor = value;
270 [DefaultValue(true)]
271 [MWFCategory("Behavior")]
272 public bool HideSelection {
273 get {
274 return hide_selection;
277 set {
278 if (value != hide_selection) {
279 hide_selection = value;
280 OnHideSelectionChanged(EventArgs.Empty);
282 if (hide_selection) {
283 document.selection_visible = false;
284 } else {
285 document.selection_visible = true;
287 document.InvalidateSelectionArea();
292 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
293 [Editor("System.Windows.Forms.Design.StringArrayEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
294 [Localizable(true)]
295 [MWFCategory("Appearance")]
296 public string[] Lines {
297 get {
298 int count;
299 ArrayList lines;
301 count = document.Lines;
303 // Handle empty document
304 if ((count == 1) && (document.GetLine (1).text.Length == 0)) {
305 return new string [0];
308 lines = new ArrayList ();
310 int i = 1;
311 while (i <= count) {
312 Line line;
313 StringBuilder lt = new StringBuilder ();
315 do {
316 line = document.GetLine (i++);
317 lt.Append (line.text.ToString ());
318 } while (line.soft_break && i < count);
320 lines.Add (lt.ToString ());
323 return (string []) lines.ToArray (typeof (string));
326 set {
327 int i;
328 int l;
329 Brush brush;
331 document.Empty();
333 l = value.Length;
334 brush = ThemeEngine.Current.ResPool.GetSolidBrush(this.ForeColor);
336 document.NoRecalc = true;
337 for (i = 0; i < l; i++) {
339 // Don't add the last line if it is just an empty line feed
340 // the line feed is reflected in the previous line's soft_break = false
341 if (i == l - 1 && value [i].Length == 0)
342 break;
344 bool carriage_return = false;
345 if (value [i].EndsWith ("\r")) {
346 value [i] = value [i].Substring (0, value [i].Length - 1);
347 carriage_return = true;
350 document.Add(i+1, CaseAdjust(value[i]), alignment, Font, brush);
351 if (carriage_return) {
352 Line line = document.GetLine (i + 1);
353 line.carriage_return = true;
357 document.NoRecalc = false;
359 // CalculateDocument();
360 OnTextChanged(EventArgs.Empty);
364 [DefaultValue(32767)]
365 [Localizable(true)]
366 [MWFCategory("Behavior")]
367 public virtual int MaxLength {
368 get {
369 if (max_length == 2147483646) { // We don't distinguish between single and multi-line limits
370 return 0;
372 return max_length;
375 set {
376 if (value != max_length) {
377 max_length = value;
382 [Browsable(false)]
383 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
384 public bool Modified {
385 get {
386 return modified;
389 set {
390 if (value != modified) {
391 modified = value;
392 OnModifiedChanged(EventArgs.Empty);
397 [DefaultValue(false)]
398 [Localizable(true)]
399 [RefreshProperties(RefreshProperties.All)]
400 [MWFCategory("Behavior")]
401 public virtual bool Multiline {
402 get {
403 return multiline;
406 set {
407 if (value != multiline) {
408 multiline = value;
409 // Make sure we update our size; the user may have already set the size before going to multiline
410 if (multiline && requested_height != -1) {
411 Height = requested_height;
412 requested_height = -1;
415 if (parent != null)
416 parent.PerformLayout ();
418 OnMultilineChanged(EventArgs.Empty);
421 document.multiline = multiline;
423 if (multiline) {
424 document.Wrap = word_wrap;
425 document.PasswordChar = "";
427 } else {
428 document.Wrap = false;
429 if (this.password_char != '\0') {
430 document.PasswordChar = password_char.ToString();
431 } else {
432 document.PasswordChar = "";
436 CalculateDocument ();
440 [Browsable(false)]
441 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
442 [EditorBrowsable(EditorBrowsableState.Advanced)]
443 public int PreferredHeight {
444 get {
445 return this.Font.Height + 7; // FIXME - consider border style as well
449 [DefaultValue(false)]
450 [MWFCategory("Behavior")]
451 public bool ReadOnly {
452 get {
453 return read_only;
456 set {
457 if (value != read_only) {
458 read_only = value;
459 OnReadOnlyChanged(EventArgs.Empty);
464 [Browsable(false)]
465 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
466 public virtual string SelectedText {
467 get {
468 return document.GetSelection();
471 set {
472 document.ReplaceSelection(CaseAdjust(value), false);
474 ScrollToCaret();
475 OnTextChanged(EventArgs.Empty);
479 [Browsable(false)]
480 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
481 public virtual int SelectionLength {
482 get {
483 int res = document.SelectionLength ();
484 if (res == 0)
485 res = -1;
486 return res;
489 set {
490 if (value < 0) {
491 throw new ArgumentException(String.Format("{0} is not a valid value", value), "value");
494 if (value != 0) {
495 int start;
496 Line line;
497 LineTag tag;
498 int pos;
500 selection_length = value;
502 start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
504 document.CharIndexToLineTag(start + value, out line, out tag, out pos);
505 document.SetSelectionEnd(line, pos);
506 document.PositionCaret(line, pos);
507 } else {
508 selection_length = -1;
510 document.SetSelectionEnd(document.selection_start.line, document.selection_start.pos);
511 document.PositionCaret(document.selection_start.line, document.selection_start.pos);
516 [Browsable(false)]
517 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
518 public int SelectionStart {
519 get {
520 int index;
522 index = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
524 return index;
527 set {
528 document.SetSelectionStart(value);
529 if (selection_length > -1 ) {
530 document.SetSelectionEnd(value + selection_length);
531 } else {
532 document.SetSelectionEnd(value);
534 document.PositionCaret(document.selection_start.line, document.selection_start.pos);
535 ScrollToCaret();
539 [Localizable(true)]
540 public override string Text {
541 get {
542 if (document == null || document.Root == null || document.Root.text == null) {
543 return string.Empty;
546 if (!multiline) {
547 return document.Root.text.ToString();
548 } else {
549 StringBuilder sb;
550 int i;
552 sb = new StringBuilder();
554 Line line = null;
555 for (i = 1; i < document.Lines; i++) {
556 if (line != null && !line.soft_break)
557 sb.Append (Environment.NewLine);
558 line = document.GetLine (i);
559 sb.Append(line.text.ToString());
560 if (line.carriage_return)
561 sb.Append ("\r");
563 sb.Append(document.GetLine(document.Lines).text.ToString());
564 return sb.ToString();
568 set {
569 if (value == Text)
570 return;
572 if ((value != null) && (value != "")) {
573 Line line;
575 if (multiline) {
576 string[] lines;
578 lines = value.Split(new char[] {'\n'});
580 this.Lines = lines;
582 document.PositionCaret (document.GetLine (1), 0);
583 document.SetSelectionToCaret (true);
585 ScrollToCaret ();
586 } else {
587 document.Clear();
588 document.Add(1, CaseAdjust(value), alignment, Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
589 CalculateDocument();
591 document.PositionCaret (document.GetLine (1), 0);
592 document.SetSelectionToCaret (true);
594 ScrollToCaret ();
596 } else {
597 document.Empty();
598 CalculateDocument();
601 OnTextChanged(EventArgs.Empty);
605 [Browsable(false)]
606 public virtual int TextLength {
607 get {
608 if (document == null || document.Root == null || document.Root.text == null) {
609 return 0;
612 if (!multiline) {
613 return document.Root.text.Length;
614 } else {
615 int total;
616 int i;
618 total = 0;
619 for (i = 1; i < document.Lines; i++) {
620 total += document.GetLine(i).text.Length + Environment.NewLine.Length;
622 total += document.GetLine(i).text.Length;
624 return total;
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 (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 if (multiline) {
666 document.MoveCaret(CaretDirection.CtrlEnd);
667 document.Insert (document.caret.line, document.caret.tag, document.caret.pos, false, text);
668 CalculateDocument();
669 } else {
670 document.MoveCaret(CaretDirection.CtrlEnd);
671 document.InsertStringAtCaret(text, true);
673 Invalidate();
676 document.MoveCaret(CaretDirection.CtrlEnd);
677 document.SetSelectionToCaret (true);
679 OnTextChanged(EventArgs.Empty);
682 public void Clear() {
683 Text = null;
686 public void ClearUndo() {
687 document.undo.Clear();
690 public void Copy() {
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);
700 public void Cut() {
701 DataObject o;
703 o = new DataObject(DataFormats.Text, SelectedText);
704 if (this is RichTextBox) {
705 o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
707 Clipboard.SetDataObject(o);
708 document.ReplaceSelection("", false);
711 public void Paste() {
712 Paste(Clipboard.GetDataObject(), null, false);
715 public void ScrollToCaret() {
716 if (IsHandleCreated) {
717 CaretMoved(this, EventArgs.Empty);
721 public void Select(int start, int length) {
722 SelectionStart = start;
723 SelectionLength = length;
727 public void SelectAll() {
728 Line last;
730 last = document.GetLine(document.Lines);
731 document.SetSelectionStart(document.GetLine(1), 0);
732 document.SetSelectionEnd(last, last.text.Length);
733 selection_length = -1;
736 public override string ToString() {
737 return String.Concat (base.ToString (), ", Text: ", Text);
740 public void Undo() {
741 document.undo.Undo();
743 #endregion // Public Instance Methods
745 #region Protected Instance Methods
746 protected override void CreateHandle() {
747 base.CreateHandle ();
748 document.AlignCaret();
749 ScrollToCaret();
752 protected override bool IsInputKey(Keys keyData) {
753 if ((keyData & Keys.Alt) != 0) {
754 return base.IsInputKey(keyData);
757 switch (keyData & Keys.KeyCode) {
758 case Keys.Enter: {
759 if (multiline && accepts_return) {
760 return true;
762 return false;
765 case Keys.Tab: {
766 if (accepts_tab && multiline) {
767 if ((keyData & Keys.Control) == 0) {
768 return true;
771 return false;
774 case Keys.Left:
775 case Keys.Right:
776 case Keys.Up:
777 case Keys.Down:
778 case Keys.PageUp:
779 case Keys.PageDown:
780 case Keys.Home:
781 case Keys.End: {
782 return true;
785 return false;
789 protected virtual void OnAcceptsTabChanged(EventArgs e) {
790 EventHandler eh = (EventHandler)(Events [AcceptsTabChangedEvent]);
791 if (eh != null)
792 eh (this, e);
795 protected virtual void OnAutoSizeChanged(EventArgs e) {
796 EventHandler eh = (EventHandler)(Events [AutoSizeChangedEvent]);
797 if (eh != null)
798 eh (this, e);
801 protected virtual void OnBorderStyleChanged(EventArgs e) {
802 EventHandler eh = (EventHandler)(Events [BorderStyleChangedEvent]);
803 if (eh != null)
804 eh (this, e);
807 protected override void OnFontChanged(EventArgs e) {
808 base.OnFontChanged (e);
810 if (auto_size && !multiline) {
811 if (PreferredHeight != ClientSize.Height) {
812 Height = PreferredHeight;
817 protected override void OnHandleCreated(EventArgs e) {
818 base.OnHandleCreated (e);
821 protected override void OnHandleDestroyed(EventArgs e) {
822 base.OnHandleDestroyed (e);
825 protected virtual void OnHideSelectionChanged(EventArgs e) {
826 EventHandler eh = (EventHandler)(Events [HideSelectionChangedEvent]);
827 if (eh != null)
828 eh (this, e);
831 protected virtual void OnModifiedChanged(EventArgs e) {
832 EventHandler eh = (EventHandler)(Events [ModifiedChangedEvent]);
833 if (eh != null)
834 eh (this, e);
837 protected virtual void OnMultilineChanged(EventArgs e) {
838 EventHandler eh = (EventHandler)(Events [MultilineChangedEvent]);
839 if (eh != null)
840 eh (this, e);
843 protected virtual void OnReadOnlyChanged(EventArgs e) {
844 EventHandler eh = (EventHandler)(Events [ReadOnlyChangedEvent]);
845 if (eh != null)
846 eh (this, e);
849 protected override bool ProcessDialogKey(Keys keyData) {
850 return base.ProcessDialogKey(keyData);
853 private bool ProcessKey(Keys keyData) {
854 bool control;
855 bool shift;
857 control = (Control.ModifierKeys & Keys.Control) != 0;
858 shift = (Control.ModifierKeys & Keys.Shift) != 0;
860 switch (keyData & Keys.KeyCode) {
861 case Keys.X: { // Cut (Ctrl-X)
862 if (control) {
863 Cut();
864 return true;
866 return false;
869 case Keys.C: { // Copy (Ctrl-C)
870 if (control) {
871 Copy();
872 return true;
874 return false;
877 case Keys.V: { // Paste (Ctrl-V)
878 if (control) {
879 return Paste(Clipboard.GetDataObject(), null, true);
881 return false;
884 case Keys.Z: { // Undo (Ctrl-Z)
885 if (control) {
886 Undo();
887 return true;
889 return false;
892 case Keys.A: { // Select All (Ctrl-A)
893 if (control) {
894 SelectAll();
895 return true;
897 return false;
900 case Keys.Left: {
901 if (control) {
902 document.MoveCaret(CaretDirection.WordBack);
903 } else {
904 if (!document.selection_visible || shift) {
905 document.MoveCaret(CaretDirection.CharBack);
906 } else {
907 document.MoveCaret(CaretDirection.SelectionStart);
911 if (!shift) {
912 document.SetSelectionToCaret(true);
913 } else {
914 document.SetSelectionToCaret(false);
917 CaretMoved(this, null);
918 return true;
921 case Keys.Right: {
922 if (control) {
923 document.MoveCaret(CaretDirection.WordForward);
924 } else {
925 if (!document.selection_visible || shift) {
926 document.MoveCaret(CaretDirection.CharForward);
927 } else {
928 document.MoveCaret(CaretDirection.SelectionEnd);
931 if (!shift) {
932 document.SetSelectionToCaret(true);
933 } else {
934 document.SetSelectionToCaret(false);
937 CaretMoved(this, null);
938 return true;
941 case Keys.Up: {
942 if (control) {
943 if (document.CaretPosition == 0) {
944 document.MoveCaret(CaretDirection.LineUp);
945 } else {
946 document.MoveCaret(CaretDirection.Home);
948 } else {
949 document.MoveCaret(CaretDirection.LineUp);
952 if ((Control.ModifierKeys & Keys.Shift) == 0) {
953 document.SetSelectionToCaret(true);
954 } else {
955 document.SetSelectionToCaret(false);
958 CaretMoved(this, null);
959 return true;
962 case Keys.Down: {
963 if (control) {
964 if (document.CaretPosition == document.CaretLine.Text.Length) {
965 document.MoveCaret(CaretDirection.LineDown);
966 } else {
967 document.MoveCaret(CaretDirection.End);
969 } else {
970 document.MoveCaret(CaretDirection.LineDown);
973 if ((Control.ModifierKeys & Keys.Shift) == 0) {
974 document.SetSelectionToCaret(true);
975 } else {
976 document.SetSelectionToCaret(false);
979 CaretMoved(this, null);
980 return true;
983 case Keys.Home: {
984 if ((Control.ModifierKeys & Keys.Control) != 0) {
985 document.MoveCaret(CaretDirection.CtrlHome);
986 } else {
987 document.MoveCaret(CaretDirection.Home);
990 if ((Control.ModifierKeys & Keys.Shift) == 0) {
991 document.SetSelectionToCaret(true);
992 } else {
993 document.SetSelectionToCaret(false);
996 CaretMoved(this, null);
997 return true;
1000 case Keys.End: {
1001 if ((Control.ModifierKeys & Keys.Control) != 0) {
1002 document.MoveCaret(CaretDirection.CtrlEnd);
1003 } else {
1004 document.MoveCaret(CaretDirection.End);
1007 if ((Control.ModifierKeys & Keys.Shift) == 0) {
1008 document.SetSelectionToCaret(true);
1009 } else {
1010 document.SetSelectionToCaret(false);
1013 CaretMoved(this, null);
1014 return true;
1017 case Keys.Enter: {
1018 // ignoring accepts_return, fixes bug #76355
1019 if (!read_only && multiline && (accepts_return || (FindForm() != null && FindForm().AcceptButton == null) || ((Control.ModifierKeys & Keys.Control) != 0))) {
1020 Line line;
1022 if (document.selection_visible) {
1023 document.ReplaceSelection("", false);
1026 line = document.CaretLine;
1028 document.Split(document.CaretLine, document.CaretTag, document.CaretPosition, false);
1029 OnTextChanged(EventArgs.Empty);
1030 document.UpdateView(line, 2, 0);
1031 document.MoveCaret(CaretDirection.CharForward);
1032 document.SetSelectionToCaret(true);
1033 CaretMoved(this, null);
1034 return true;
1036 break;
1039 case Keys.Tab: {
1040 if (!read_only && accepts_tab && multiline) {
1041 document.InsertChar(document.CaretLine, document.CaretPosition, '\t');
1042 if (document.selection_visible) {
1043 document.ReplaceSelection("", false);
1045 document.SetSelectionToCaret(true);
1047 OnTextChanged(EventArgs.Empty);
1048 CaretMoved(this, null);
1049 return true;
1051 break;
1054 case Keys.Insert: {
1055 if (shift) {
1056 Paste(Clipboard.GetDataObject(), null, true);
1057 return true;
1060 if (control) {
1061 Copy();
1062 return true;
1065 // FIXME - need overwrite/insert toggle?
1066 return false;
1069 case Keys.PageUp: {
1070 if ((Control.ModifierKeys & Keys.Control) != 0) {
1071 document.MoveCaret(CaretDirection.CtrlPgUp);
1072 } else {
1073 document.MoveCaret(CaretDirection.PgUp);
1075 return true;
1078 case Keys.PageDown: {
1079 if ((Control.ModifierKeys & Keys.Control) != 0) {
1080 document.MoveCaret(CaretDirection.CtrlPgDn);
1081 } else {
1082 document.MoveCaret(CaretDirection.PgDn);
1084 return true;
1087 case Keys.Delete: {
1088 if (shift) {
1089 Cut();
1090 return true;
1093 if (read_only) {
1094 break;
1097 if (document.selection_visible) {
1098 document.ReplaceSelection("", false);
1099 } else {
1100 // DeleteChar only deletes on the line, doesn't do the combine
1101 if (document.CaretPosition == document.CaretLine.Text.Length) {
1102 if (document.CaretLine.LineNo < document.Lines) {
1103 Line line;
1105 line = document.GetLine(document.CaretLine.LineNo + 1);
1106 document.Combine(document.CaretLine, line);
1107 document.UpdateView(document.CaretLine, 2, 0);
1109 #if not_Debug
1110 Line check_first;
1111 Line check_second;
1113 check_first = document.GetLine(document.CaretLine.LineNo);
1114 check_second = document.GetLine(check_first.line_no + 1);
1116 Console.WriteLine("Post-UpdateView: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
1117 #endif
1119 // Caret doesn't move
1121 } else {
1122 if (!control) {
1123 document.DeleteChar(document.CaretTag, document.CaretPosition, true);
1124 } else {
1125 int end_pos;
1127 end_pos = document.CaretPosition;
1129 while ((end_pos < document.CaretLine.Text.Length) && !Document.IsWordSeparator(document.CaretLine.Text[end_pos])) {
1130 end_pos++;
1133 if (end_pos < document.CaretLine.Text.Length) {
1134 end_pos++;
1136 document.DeleteChars(document.CaretTag, document.CaretPosition, end_pos - document.CaretPosition);
1141 OnTextChanged(EventArgs.Empty);
1142 document.AlignCaret();
1143 document.UpdateCaret();
1144 CaretMoved(this, null);
1145 return true;
1148 return false;
1151 private void HandleBackspace(bool control) {
1152 bool fire_changed;
1154 fire_changed = false;
1156 // delete only deletes on the line, doesn't do the combine
1157 if (document.selection_visible) {
1158 document.ReplaceSelection("", false);
1159 fire_changed = true;
1161 document.SetSelectionToCaret(true);
1163 if (document.CaretPosition == 0) {
1164 if (document.CaretLine.LineNo > 1) {
1165 Line line;
1166 int new_caret_pos;
1168 line = document.GetLine(document.CaretLine.LineNo - 1);
1169 new_caret_pos = line.text.Length;
1171 document.Combine(line, document.CaretLine);
1172 document.UpdateView(line, 1, 0);
1173 document.PositionCaret(line, new_caret_pos);
1174 //document.MoveCaret(CaretDirection.CharForward);
1175 document.UpdateCaret();
1176 fire_changed = true;
1178 } else {
1179 if (!control || document.CaretPosition == 0) {
1180 document.DeleteChar(document.CaretTag, document.CaretPosition, false);
1181 document.MoveCaret(CaretDirection.CharBack);
1182 } else {
1183 int start_pos;
1185 start_pos = document.CaretPosition - 1;
1187 while ((start_pos > 0) && !Document.IsWordSeparator(document.CaretLine.Text[start_pos - 1])) {
1188 start_pos--;
1190 document.DeleteChars(document.CaretTag, start_pos, document.CaretPosition - start_pos);
1191 document.PositionCaret(document.CaretLine, start_pos);
1193 document.UpdateCaret();
1194 fire_changed = true;
1196 if (fire_changed) {
1197 OnTextChanged(EventArgs.Empty);
1199 CaretMoved(this, null);
1202 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
1203 // Make sure we don't get sized bigger than we want to be
1204 if (!richtext) {
1205 if (!multiline) {
1206 if (height != PreferredHeight) {
1207 requested_height = height;
1208 height = PreferredHeight;
1209 specified |= BoundsSpecified.Height;
1214 base.SetBoundsCore (x, y, width, height, specified);
1217 protected override void WndProc(ref Message m) {
1218 switch ((Msg)m.Msg) {
1219 case Msg.WM_KEYDOWN: {
1220 if (ProcessKeyMessage(ref m) || ProcessKey((Keys)m.WParam.ToInt32() | XplatUI.State.ModifierKeys)) {
1221 m.Result = IntPtr.Zero;
1222 return;
1224 DefWndProc (ref m);
1225 return;
1228 case Msg.WM_CHAR: {
1229 int ch;
1231 if (ProcessKeyMessage(ref m)) {
1232 m.Result = IntPtr.Zero;
1233 return;
1236 if (read_only) {
1237 return;
1240 m.Result = IntPtr.Zero;
1242 ch = m.WParam.ToInt32();
1244 if (ch == 127) {
1245 HandleBackspace(true);
1246 } else if (ch >= 32) {
1247 if (document.selection_visible) {
1248 document.ReplaceSelection("", false);
1251 char c = (char)m.WParam;
1252 switch (character_casing) {
1253 case CharacterCasing.Upper:
1254 c = Char.ToUpper((char) m.WParam);
1255 break;
1256 case CharacterCasing.Lower:
1257 c = Char.ToLower((char) m.WParam);
1258 break;
1261 if (document.Length < max_length) {
1262 document.InsertCharAtCaret(c, true);
1263 OnTextChanged(EventArgs.Empty);
1264 CaretMoved(this, null);
1265 } else {
1266 XplatUI.AudibleAlert();
1268 return;
1269 } else if (ch == 8) {
1270 HandleBackspace(false);
1273 return;
1276 default: {
1277 base.WndProc(ref m);
1278 return;
1283 #endregion // Protected Instance Methods
1285 #region Events
1286 static object AcceptsTabChangedEvent = new object ();
1287 static object AutoSizeChangedEvent = new object ();
1288 static object BorderStyleChangedEvent = new object ();
1289 static object HideSelectionChangedEvent = new object ();
1290 static object ModifiedChangedEvent = new object ();
1291 static object MultilineChangedEvent = new object ();
1292 static object ReadOnlyChangedEvent = new object ();
1293 static object HScrolledEvent = new object ();
1294 static object VScrolledEvent = new object ();
1296 public event EventHandler AcceptsTabChanged {
1297 add { Events.AddHandler (AcceptsTabChangedEvent, value); }
1298 remove { Events.RemoveHandler (AcceptsTabChangedEvent, value); }
1301 public event EventHandler AutoSizeChanged {
1302 add { Events.AddHandler (AutoSizeChangedEvent, value); }
1303 remove { Events.RemoveHandler (AutoSizeChangedEvent, value); }
1306 public event EventHandler BorderStyleChanged {
1307 add { Events.AddHandler (BorderStyleChangedEvent, value); }
1308 remove { Events.RemoveHandler (BorderStyleChangedEvent, value); }
1311 public event EventHandler HideSelectionChanged {
1312 add { Events.AddHandler (HideSelectionChangedEvent, value); }
1313 remove { Events.RemoveHandler (HideSelectionChangedEvent, value); }
1316 public event EventHandler ModifiedChanged {
1317 add { Events.AddHandler (ModifiedChangedEvent, value); }
1318 remove { Events.RemoveHandler (ModifiedChangedEvent, value); }
1321 public event EventHandler MultilineChanged {
1322 add { Events.AddHandler (MultilineChangedEvent, value); }
1323 remove { Events.RemoveHandler (MultilineChangedEvent, value); }
1326 public event EventHandler ReadOnlyChanged {
1327 add { Events.AddHandler (ReadOnlyChangedEvent, value); }
1328 remove { Events.RemoveHandler (ReadOnlyChangedEvent, value); }
1331 internal event EventHandler HScrolled {
1332 add { Events.AddHandler (HScrolledEvent, value); }
1333 remove { Events.RemoveHandler (HScrolledEvent, value); }
1336 internal event EventHandler VScrolled {
1337 add { Events.AddHandler (VScrolledEvent, value); }
1338 remove { Events.RemoveHandler (VScrolledEvent, value); }
1341 [Browsable(false)]
1342 [EditorBrowsable(EditorBrowsableState.Never)]
1343 public new event EventHandler BackgroundImageChanged {
1344 add { base.BackgroundImageChanged += value; }
1345 remove { base.BackgroundImageChanged -= value; }
1347 [Browsable(false)]
1348 [EditorBrowsable(EditorBrowsableState.Advanced)]
1349 public new event EventHandler Click {
1350 add { base.Click += value; }
1351 remove { base.Click -= value; }
1354 // XXX should this not manipulate base.Paint?
1355 [Browsable(false)]
1356 [EditorBrowsable(EditorBrowsableState.Never)]
1357 public new event PaintEventHandler Paint;
1358 #endregion // Events
1360 #region Private Methods
1361 internal Document Document {
1362 get {
1363 return document;
1366 set {
1367 document = value;
1371 internal bool ShowSelection {
1372 get {
1373 if (show_selection) {
1374 return true;
1377 return has_focus;
1380 set {
1381 if (show_selection == value)
1382 return;
1384 show_selection = value;
1385 // Currently InvalidateSelectionArea is commented out so do a full invalidate
1386 document.InvalidateSelectionArea();
1390 internal Graphics CreateGraphicsInternal() {
1391 if (IsHandleCreated) {
1392 return base.CreateGraphics();
1395 return Graphics.FromImage(bmp);
1398 #if Debug
1399 static int current;
1400 #endif
1402 internal override void OnPaintInternal (PaintEventArgs pevent) {
1403 // Fill background
1404 if (backcolor_set || (Enabled && !read_only)) {
1405 pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), pevent.ClipRectangle);
1406 } else {
1407 pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorControl), pevent.ClipRectangle);
1409 pevent.Graphics.TextRenderingHint=TextRenderingHint.AntiAlias;
1411 // Draw the viewable document
1412 document.Draw(pevent.Graphics, pevent.ClipRectangle);
1414 Rectangle rect = ClientRectangle;
1415 rect.Width--;
1416 rect.Height--;
1417 //pevent.Graphics.DrawRectangle(ThemeEngine.Current.ResPool.GetPen(ThemeEngine.Current.ColorControlDark), rect);
1419 #if Debug
1420 int start;
1421 int end;
1422 Line line;
1423 int line_no;
1424 Pen p;
1426 p = new Pen(Color.Red, 1);
1428 // First, figure out from what line to what line we need to draw
1429 start = document.GetLineByPixel(pevent.ClipRectangle.Top - document.ViewPortY, false).line_no;
1430 end = document.GetLineByPixel(pevent.ClipRectangle.Bottom - document.ViewPortY, false).line_no;
1432 //Console.WriteLine("Starting drawing on line '{0}'", document.GetLine(start));
1433 //Console.WriteLine("Ending drawing on line '{0}'", document.GetLine(end));
1435 line_no = start;
1436 while (line_no <= end) {
1437 line = document.GetLine(line_no);
1439 if (draw_lines) {
1440 for (int i = 0; i < line.text.Length; i++) {
1441 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);
1445 line_no++;
1447 #endif
1450 internal override void OnGotFocusInternal (EventArgs e)
1452 document.CaretHasFocus ();
1453 base.OnGotFocusInternal (e);
1456 internal override void OnLostFocusInternal (EventArgs e)
1458 document.CaretLostFocus ();
1459 base.OnLostFocusInternal (e);
1462 private void TextBoxBase_MouseDown(object sender, MouseEventArgs e) {
1463 if (e.Button == MouseButtons.Left) {
1464 TimeSpan interval;
1466 interval = DateTime.Now - click_last;
1467 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1469 // Handle place caret/select word/select line behaviour
1470 if (e.Clicks == 1) {
1471 if (SystemInformation.DoubleClickTime < interval.TotalMilliseconds) {
1472 #if DebugClick
1473 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));
1474 #endif
1475 document.SetSelectionToCaret(true);
1476 click_mode = CaretSelection.Position;
1477 } else if (this is RichTextBox) {
1478 #if DebugClick
1479 Console.WriteLine("Tripple Click Selecting line");
1480 #endif
1481 document.ExpandSelection(CaretSelection.Line, false);
1482 click_mode = CaretSelection.Line;
1483 } else {
1484 document.SetSelectionToCaret(true);
1486 } else {
1487 // We select the line if the word is already selected, and vice versa
1488 if (click_mode != CaretSelection.Word) {
1489 if (click_mode == CaretSelection.Line) {
1490 document.Invalidate(document.selection_start.line, 0, document.selection_start.line, document.selection_start.line.text.Length);
1492 click_mode = CaretSelection.Word;
1494 SelectWord ();
1495 } else if (this is RichTextBox) {
1496 click_mode = CaretSelection.Line;
1497 document.ExpandSelection(CaretSelection.Line, false); // Setting initial selection
1501 // Reset
1502 click_last = DateTime.Now;
1503 return;
1506 if ((e.Button == MouseButtons.Middle) && (((int)Environment.OSVersion.Platform == 4) || ((int)Environment.OSVersion.Platform == 128))) {
1507 Document.Marker marker;
1509 marker.tag = document.FindCursor(e.X + document.ViewPortX, e.Y + document.ViewPortY, out marker.pos);
1510 marker.line = marker.tag.line;
1511 marker.height = marker.tag.height;
1513 document.SetSelection(marker.line, marker.pos, marker.line, marker.pos);
1514 Paste (Clipboard.GetDataObject (true), null, true);
1518 #if Debug
1519 LineTag tag;
1520 Line line;
1521 int pos;
1523 if (e.Button == MouseButtons.Right) {
1524 draw_lines = !draw_lines;
1525 this.Invalidate();
1526 Console.WriteLine("SelectedText: {0}, length {1}", this.SelectedText, this.SelectionLength);
1527 Console.WriteLine("Selection start: {0}", this.SelectionStart);
1529 this.SelectionStart = 10;
1530 this.SelectionLength = 5;
1532 return;
1535 tag = document.FindTag(e.X + document.ViewPortX, e.Y + document.ViewPortY, out pos, false);
1537 Console.WriteLine("Click found tag {0}, character {1}", tag, pos);
1538 line = tag.line;
1539 switch(current) {
1540 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;
1541 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;
1542 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;
1543 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;
1544 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;
1545 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;
1547 current++;
1548 if (current==6) {
1549 current=0;
1552 // Update/Recalculate what we see
1553 document.UpdateView(line, 0);
1555 // Make sure our caret is properly positioned and sized
1556 document.AlignCaret();
1557 #endif
1560 private void TextBoxBase_MouseUp(object sender, MouseEventArgs e) {
1561 if (e.Button == MouseButtons.Left) {
1562 if (click_mode == CaretSelection.Position) {
1563 document.SetSelectionToCaret(false);
1564 document.DisplayCaret();
1567 if (scroll_timer != null) {
1568 scroll_timer.Enabled = false;
1570 return;
1574 private void PositionControls ()
1576 if (hscroll.Visible) {
1577 //vscroll.Maximum += hscroll.Height;
1578 canvas_height = ClientSize.Height - hscroll.Height;
1579 } else {
1580 canvas_height = ClientSize.Height;
1583 if (vscroll.Visible) {
1584 //hscroll.Maximum += vscroll.Width;
1585 canvas_width = ClientSize.Width - vscroll.Width;
1586 } else {
1587 canvas_width = ClientSize.Width;
1591 document.ViewPortWidth = canvas_width;
1592 document.ViewPortHeight = canvas_height;
1594 // We always move them, they just might not be displayed
1595 hscroll.Bounds = new Rectangle (ClientRectangle.Left, ClientRectangle.Height - hscroll.Height, ClientSize.Width - (vscroll.Visible ? vscroll.Width : 0), hscroll.Height);
1596 vscroll.Bounds = new Rectangle (ClientRectangle.Right - vscroll.Width, ClientRectangle.Top, vscroll.Width, ClientSize.Height - (hscroll.Visible ? hscroll.Height : 0));
1600 private void TextBoxBase_SizeChanged(object sender, EventArgs e) {
1601 CalculateDocument();
1604 private void TextBoxBase_MouseWheel(object sender, MouseEventArgs e) {
1605 Line line;
1606 int line_no;
1608 if (!vscroll.Enabled) {
1609 return;
1612 if (e.Delta < 0)
1613 vscroll.Value = Math.Min (vscroll.Value + SystemInformation.MouseWheelScrollLines, vscroll.Maximum - document.ViewPortHeight + 1);
1614 else
1615 vscroll.Value = Math.Max (0, vscroll.Value - SystemInformation.MouseWheelScrollLines);
1618 internal virtual void SelectWord ()
1620 StringBuilder s = document.caret.line.text;
1621 int start = document.caret.pos;
1622 int end = document.caret.pos;
1624 if (s.Length < 1) {
1625 if (document.caret.line.line_no >= document.Lines)
1626 return;
1627 Line line = document.GetLine (document.caret.line.line_no + 1);
1628 document.PositionCaret (line, 0);
1629 return;
1632 if (start > 0) {
1633 start--;
1634 end--;
1637 // skip whitespace until we hit a word
1638 while (start > 0 && s [start] == ' ')
1639 start--;
1640 if (start > 0) {
1641 while (start > 0 && (s [start] != ' '))
1642 start--;
1643 if (s [start] == ' ')
1644 start++;
1647 if (s [end] == ' ') {
1648 while (end < s.Length && s [end] == ' ')
1649 end++;
1650 } else {
1651 while (end < s.Length && s [end] != ' ')
1652 end++;
1653 while (end < s.Length && s [end] == ' ')
1654 end++;
1657 document.SetSelection (document.caret.line, start, document.caret.line, end);
1660 internal void CalculateDocument() {
1661 if (!IsHandleCreated) {
1662 return;
1664 document.RecalculateDocument(CreateGraphicsInternal());
1665 CalculateScrollBars();
1666 Invalidate();
1669 internal void CalculateScrollBars () {
1670 // FIXME - need separate calculations for center and right alignment
1672 if (!multiline) {
1673 PositionControls ();
1674 return;
1677 if (document.Width >= document.ViewPortWidth) {
1678 hscroll.SetValues (0, Math.Max (1, document.Width), -1,
1679 document.ViewPortWidth < 0 ? 0 : document.ViewPortWidth);
1680 hscroll.Enabled = true;
1681 } else {
1682 hscroll.Enabled = false;
1683 hscroll.Maximum = document.ViewPortWidth;
1686 if (document.Height >= document.ViewPortHeight) {
1687 vscroll.SetValues (0, Math.Max (1, document.Height), -1,
1688 document.ViewPortHeight < 0 ? 0 : document.ViewPortHeight);
1689 vscroll.Enabled = true;
1690 } else {
1691 vscroll.Enabled = false;
1692 vscroll.Maximum = document.ViewPortHeight;
1696 if (!WordWrap) {
1697 if ((scrollbars & RichTextBoxScrollBars.Horizontal) != 0) {
1698 if (((scrollbars & RichTextBoxScrollBars.ForcedHorizontal) != 0) || hscroll.Enabled) {
1699 hscroll.Visible = true;
1700 } else {
1701 hscroll.Visible = false;
1703 } else {
1704 hscroll.Visible = false;
1708 if ((scrollbars & RichTextBoxScrollBars.Vertical) != 0) {
1709 if (((scrollbars & RichTextBoxScrollBars.ForcedVertical) != 0) || vscroll.Enabled) {
1710 vscroll.Visible = true;
1711 } else {
1712 vscroll.Visible = false;
1714 } else {
1715 vscroll.Visible = false;
1718 PositionControls ();
1721 private void document_WidthChanged(object sender, EventArgs e) {
1722 CalculateScrollBars();
1725 private void document_HeightChanged(object sender, EventArgs e) {
1726 CalculateScrollBars();
1729 private void hscroll_ValueChanged(object sender, EventArgs e) {
1730 int old_viewport_x;
1732 old_viewport_x = document.ViewPortX;
1733 document.ViewPortX = this.hscroll.Value;
1735 if (vscroll.Visible) {
1736 XplatUI.ScrollWindow(this.Handle, new Rectangle(0, 0, ClientSize.Width - vscroll.Width, ClientSize.Height), old_viewport_x - this.hscroll.Value, 0, false);
1737 } else {
1738 XplatUI.ScrollWindow(this.Handle, ClientRectangle, old_viewport_x - this.hscroll.Value, 0, false);
1740 document.UpdateCaret();
1742 EventHandler eh = (EventHandler)(Events [HScrolledEvent]);
1743 if (eh != null)
1744 eh (this, EventArgs.Empty);
1747 private void vscroll_ValueChanged(object sender, EventArgs e) {
1748 int old_viewport_y;
1750 old_viewport_y = document.ViewPortY;
1751 document.ViewPortY = this.vscroll.Value;
1753 if (hscroll.Visible) {
1754 XplatUI.ScrollWindow(this.Handle, new Rectangle(0, 0, ClientSize.Width, ClientSize.Height - hscroll.Height), 0, old_viewport_y - this.vscroll.Value, false);
1755 } else {
1756 XplatUI.ScrollWindow(this.Handle, ClientRectangle, 0, old_viewport_y - this.vscroll.Value, false);
1758 document.UpdateCaret();
1760 EventHandler eh = (EventHandler)(Events [VScrolledEvent]);
1761 if (eh != null)
1762 eh (this, EventArgs.Empty);
1765 private void TextBoxBase_MouseMove(object sender, MouseEventArgs e) {
1766 // FIXME - handle auto-scrolling if mouse is to the right/left of the window
1767 if (Capture) {
1768 if (!ClientRectangle.Contains (e.X, e.Y)) {
1769 if (scroll_timer == null) {
1770 scroll_timer = new Timer ();
1771 scroll_timer.Interval = 100;
1772 scroll_timer.Tick += new EventHandler (ScrollTimerTickHandler);
1775 if (!scroll_timer.Enabled) {
1776 scroll_timer.Start ();
1778 // Force the first tick
1779 ScrollTimerTickHandler (null, EventArgs.Empty);
1782 return;
1785 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1786 if (click_mode == CaretSelection.Position) {
1787 document.SetSelectionToCaret(false);
1788 document.DisplayCaret();
1793 private void TextBoxBase_FontOrColorChanged(object sender, EventArgs e) {
1794 if (!richtext) {
1795 Line line;
1797 // Font changes apply to the whole document
1798 for (int i = 1; i <= document.Lines; i++) {
1799 line = document.GetLine(i);
1800 LineTag.FormatText(line, 1, line.text.Length, Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
1801 document.UpdateView(line, 0);
1803 // Make sure the caret height is matching the new font height
1804 document.AlignCaret();
1808 private void ScrollTimerTickHandler (object sender, EventArgs e)
1810 Point pt = Cursor.Position;
1812 pt = PointToClient (pt);
1814 if (pt.X < ClientRectangle.Left) {
1815 document.MoveCaret(CaretDirection.CharBackNoWrap);
1816 document.SetSelectionToCaret(false);
1818 CaretMoved(this, null);
1819 } else if (pt.X > ClientRectangle.Right) {
1820 document.MoveCaret(CaretDirection.CharForwardNoWrap);
1821 document.SetSelectionToCaret(false);
1823 CaretMoved(this, null);
1824 } else if (pt.Y > ClientRectangle.Bottom) {
1825 document.MoveCaret(CaretDirection.LineDown);
1826 document.SetSelectionToCaret(false);
1828 CaretMoved(this, null);
1829 } else if (pt.Y < ClientRectangle.Top) {
1830 document.MoveCaret(CaretDirection.LineUp);
1831 document.SetSelectionToCaret(false);
1833 CaretMoved(this, null);
1837 /// <summary>Ensure the caret is always visible</summary>
1838 internal void CaretMoved(object sender, EventArgs e) {
1839 Point pos;
1840 int height;
1842 pos = document.Caret;
1843 //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);
1846 // Horizontal scrolling:
1847 // If the caret moves to the left outside the visible area, we jump the document into view, not just one
1848 // character, but 1/3 of the width of the document
1849 // If the caret moves to the right outside the visible area, we scroll just enough to keep the caret visible
1851 // Handle horizontal scrolling
1852 if (document.CaretLine.alignment == HorizontalAlignment.Left) {
1853 // Check if we moved out of view to the left
1854 if (pos.X < (document.ViewPortX)) {
1855 do {
1856 if ((hscroll.Value - document.ViewPortWidth / 3) >= hscroll.Minimum) {
1857 hscroll.Value -= document.ViewPortWidth / 3;
1858 } else {
1859 hscroll.Value = hscroll.Minimum;
1861 } while (hscroll.Value > pos.X);
1864 // Check if we moved out of view to the right
1865 if ((pos.X >= (document.ViewPortWidth + document.ViewPortX)) && (hscroll.Value != hscroll.Maximum)) {
1866 if ((pos.X - document.ViewPortWidth + 1) <= hscroll.Maximum) {
1867 if (pos.X - document.ViewPortWidth >= 0) {
1868 hscroll.Value = pos.X - document.ViewPortWidth + 1;
1869 } else {
1870 hscroll.Value = 0;
1872 } else {
1873 hscroll.Value = hscroll.Maximum;
1876 } else if (document.CaretLine.alignment == HorizontalAlignment.Right) {
1877 // hscroll.Value = pos.X;
1879 // if ((pos.X > (this.canvas_width + document.ViewPortX)) && (hscroll.Enabled && (hscroll.Value != hscroll.Maximum))) {
1880 // hscroll.Value = hscroll.Maximum;
1881 // }
1882 } else {
1883 // FIXME - implement center cursor alignment
1886 if (!multiline) {
1887 return;
1890 // Handle vertical scrolling
1891 height = document.CaretLine.Height + 1;
1893 if (pos.Y < document.ViewPortY) {
1894 vscroll.Value = pos.Y;
1897 if ((pos.Y + height) > (document.ViewPortY + canvas_height)) {
1898 vscroll.Value = pos.Y - canvas_height + height;
1902 internal bool Paste(IDataObject clip, DataFormats.Format format, bool obey_length) {
1903 string s;
1905 if (clip == null)
1906 return false;
1908 if (format == null) {
1909 if ((this is RichTextBox) && clip.GetDataPresent(DataFormats.Rtf)) {
1910 format = DataFormats.GetFormat(DataFormats.Rtf);
1911 } else if (clip.GetDataPresent(DataFormats.UnicodeText)) {
1912 format = DataFormats.GetFormat(DataFormats.UnicodeText);
1913 } else if (clip.GetDataPresent(DataFormats.Text)) {
1914 format = DataFormats.GetFormat(DataFormats.Text);
1915 } else {
1916 return false;
1918 } else {
1919 if ((format.Name == DataFormats.Rtf) && !(this is RichTextBox)) {
1920 return false;
1923 if (!clip.GetDataPresent(format.Name)) {
1924 return false;
1928 if (format.Name == DataFormats.Rtf) {
1929 ((RichTextBox)this).SelectedRtf = (string)clip.GetData(DataFormats.Rtf);
1930 return true;
1931 } else if (format.Name == DataFormats.UnicodeText) {
1932 s = (string)clip.GetData(DataFormats.UnicodeText);
1933 } else if (format.Name == DataFormats.Text) {
1934 s = (string)clip.GetData(DataFormats.Text);
1935 } else {
1936 return false;
1939 if (!obey_length) {
1940 this.SelectedText = s;
1941 } else {
1942 if ((s.Length + document.Length) < max_length) {
1943 this.SelectedText = s;
1944 } else if (document.Length < max_length) {
1945 this.SelectedText = s.Substring(0, max_length - document.Length);
1949 return true;
1951 #endregion // Private Methods