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:c
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2006 Novell, Inc. (http://www.novell.com)
23 // Peter Bartok pbartok@novell.com
31 using System
.ComponentModel
;
32 using System
.ComponentModel
.Design
;
34 using System
.Drawing
.Text
;
36 using System
.Runtime
.InteropServices
;
37 using System
.Collections
;
39 namespace System
.Windows
.Forms
42 [DefaultBindingProperty ("Text")]
43 [ClassInterface (ClassInterfaceType
.AutoDispatch
)]
44 [DefaultEvent("TextChanged")]
45 [Designer("System.Windows.Forms.Design.TextBoxBaseDesigner, " + Consts
.AssemblySystem_Design
)]
46 public abstract class TextBoxBase
: Control
48 #region Local Variables
49 internal HorizontalAlignment alignment
;
50 internal bool accepts_tab
;
51 internal bool accepts_return
;
52 internal bool auto_size
;
53 internal bool backcolor_set
;
54 internal CharacterCasing character_casing
;
55 internal bool hide_selection
;
57 internal bool modified
;
58 internal char password_char
;
59 internal bool read_only
;
60 internal bool word_wrap
;
61 internal Document document
;
62 internal LineTag caret_tag
; // tag our cursor is in
63 internal int caret_pos
; // position on the line our cursor is in (can be 0 = beginning of line)
64 internal ImplicitHScrollBar hscroll
;
65 internal ImplicitVScrollBar vscroll
;
66 internal RichTextBoxScrollBars scrollbars
;
67 internal Timer scroll_timer
;
68 internal bool richtext
;
69 internal bool show_selection
; // set to true to always show selection, even if no focus is set
70 internal ArrayList list_links
; // currently showing links
71 private LinkRectangle current_link
; // currently hovering link
72 private bool enable_links
; // whether links are enabled
74 internal bool has_been_focused
;
76 private bool delayed_font_or_color_change
;
77 private int requested_height
;
79 internal int selection_length
= -1; // set to the user-specified selection length, or -1 if none
80 internal bool show_caret_w_selection
; // TextBox shows the caret when the selection is visible
81 internal int canvas_width
;
82 internal int canvas_height
;
83 static internal int track_width
= 2; //
84 static internal int track_border
= 5; //
85 internal DateTime click_last
;
86 internal int click_point_x
;
87 internal int click_point_y
;
88 internal CaretSelection click_mode
;
89 internal BorderStyle actual_border_style
= BorderStyle
.Fixed3D
;
90 internal bool shortcuts_enabled
= true;
92 internal static bool draw_lines
= false;
95 #endregion // Local Variables
97 #region Internal Constructor
98 // Constructor will go when complete, only for testing - pdb
99 internal TextBoxBase ()
101 alignment
= HorizontalAlignment
.Left
;
102 accepts_return
= false;
105 InternalBorderStyle
= BorderStyle
.Fixed3D
;
106 character_casing
= CharacterCasing
.Normal
;
107 hide_selection
= true;
108 max_length
= short.MaxValue
;
109 password_char
= '\0';
113 show_selection
= false;
114 enable_links
= false;
115 list_links
= new ArrayList ();
117 show_caret_w_selection
= (this is TextBox
);
118 document
= new Document(this);
119 document
.WidthChanged
+= new EventHandler(document_WidthChanged
);
120 document
.HeightChanged
+= new EventHandler(document_HeightChanged
);
121 //document.CaretMoved += new EventHandler(CaretMoved);
122 document
.Wrap
= false;
123 click_last
= DateTime
.Now
;
124 click_mode
= CaretSelection
.Position
;
126 MouseDown
+= new MouseEventHandler(TextBoxBase_MouseDown
);
127 MouseUp
+= new MouseEventHandler(TextBoxBase_MouseUp
);
128 MouseMove
+= new MouseEventHandler(TextBoxBase_MouseMove
);
129 SizeChanged
+= new EventHandler(TextBoxBase_SizeChanged
);
130 FontChanged
+= new EventHandler(TextBoxBase_FontOrColorChanged
);
131 ForeColorChanged
+= new EventHandler(TextBoxBase_FontOrColorChanged
);
132 MouseWheel
+= new MouseEventHandler(TextBoxBase_MouseWheel
);
133 RightToLeftChanged
+= new EventHandler (TextBoxBase_RightToLeftChanged
);
135 scrollbars
= RichTextBoxScrollBars
.None
;
137 hscroll
= new ImplicitHScrollBar();
138 hscroll
.ValueChanged
+= new EventHandler(hscroll_ValueChanged
);
139 hscroll
.SetStyle (ControlStyles
.Selectable
, false);
140 hscroll
.Enabled
= false;
141 hscroll
.Visible
= false;
142 hscroll
.Maximum
= Int32
.MaxValue
;
144 vscroll
= new ImplicitVScrollBar();
145 vscroll
.ValueChanged
+= new EventHandler(vscroll_ValueChanged
);
146 vscroll
.SetStyle (ControlStyles
.Selectable
, false);
147 vscroll
.Enabled
= false;
148 vscroll
.Visible
= false;
149 vscroll
.Maximum
= Int32
.MaxValue
;
152 this.Controls
.AddImplicit (hscroll
);
153 this.Controls
.AddImplicit (vscroll
);
156 SetStyle (ControlStyles
.UserPaint
| ControlStyles
.StandardClick
, false);
157 SetStyle (ControlStyles
.UseTextForAccessibility
, false);
158 SetStyle (ControlStyles
.FixedHeight
, true);
160 //base.SetAutoSizeMode (AutoSizeMode.GrowAndShrink);
162 canvas_width
= ClientSize
.Width
;
163 canvas_height
= ClientSize
.Height
;
164 document
.ViewPortWidth
= canvas_width
;
165 document
.ViewPortHeight
= canvas_height
;
167 Cursor
= Cursors
.IBeam
;
169 requested_height
= Height
;
171 can_cache_preferred_size
= true;
173 #endregion // Internal Constructor
175 #region Private and Internal Methods
176 internal string CaseAdjust (string s
)
178 if (character_casing
== CharacterCasing
.Normal
)
180 if (character_casing
== CharacterCasing
.Lower
)
185 internal override Size
GetPreferredSizeCore (Size proposedSize
)
187 Size bordersAndPadding
= SizeFromClientSize(Size
.Empty
) + Padding
.Size
;
188 if (BorderStyle
!= BorderStyle
.None
)
189 bordersAndPadding
+= new Size(0, 7);
190 proposedSize
-= bordersAndPadding
;
192 TextFormatFlags format
= TextFormatFlags
.NoPrefix
;
194 format
|= TextFormatFlags
.SingleLine
;
196 format
|= TextFormatFlags
.WordBreak
;
198 Size textSize
= TextRenderer
.MeasureText(this.Text
, this.Font
, proposedSize
, format
);
199 textSize
.Height
= Math
.Max(textSize
.Height
, FontHeight
);
200 return textSize
+ bordersAndPadding
;
203 internal override void HandleClick (int clicks
, MouseEventArgs me
)
205 // MS seems to fire the click event in spite of the styles they set
206 bool click_set
= GetStyle (ControlStyles
.StandardClick
);
207 bool doubleclick_set
= GetStyle (ControlStyles
.StandardDoubleClick
);
209 // so explicitly set them to true first
210 SetStyle (ControlStyles
.StandardClick
| ControlStyles
.StandardDoubleClick
, true);
212 base.HandleClick (clicks
, me
);
214 // then revert to our previous state
216 SetStyle (ControlStyles
.StandardClick
, false);
217 if (!doubleclick_set
)
218 SetStyle (ControlStyles
.StandardDoubleClick
, false);
221 internal override void PaintControlBackground (PaintEventArgs pevent
)
223 if (!ThemeEngine
.Current
.TextBoxBaseShouldPaintBackground (this))
225 base.PaintControlBackground (pevent
);
227 #endregion // Private and Internal Methods
229 #region Public Instance Properties
230 [DefaultValue(false)]
231 [MWFCategory("Behavior")]
232 public bool AcceptsTab
{
238 if (value != accepts_tab
) {
240 OnAcceptsTabChanged(EventArgs
.Empty
);
246 [EditorBrowsable (EditorBrowsableState
.Never
)]
249 [RefreshProperties(RefreshProperties
.Repaint
)]
250 [MWFCategory("Behavior")]
251 public override bool AutoSize
{
257 if (value != auto_size
) {
260 if (PreferredHeight
!= Height
) {
261 Height
= PreferredHeight
;
269 public override System
.Drawing
.Color BackColor
{
271 return base.BackColor
;
274 backcolor_set
= true;
275 base.BackColor
= ChangeBackColor (value);
280 [EditorBrowsable(EditorBrowsableState
.Never
)]
281 public override System
.Drawing
.Image BackgroundImage
{
283 return base.BackgroundImage
;
286 base.BackgroundImage
= value;
290 [DefaultValue(BorderStyle
.Fixed3D
)]
292 [MWFCategory("Appearance")]
293 public BorderStyle BorderStyle
{
294 get { return actual_border_style; }
296 if (value == actual_border_style
)
299 if (actual_border_style
!= BorderStyle
.Fixed3D
|| value != BorderStyle
.Fixed3D
)
302 actual_border_style
= value;
303 document
.UpdateMargins ();
305 if (value != BorderStyle
.Fixed3D
)
306 value = BorderStyle
.None
;
308 InternalBorderStyle
= value;
309 OnBorderStyleChanged(EventArgs
.Empty
);
314 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
315 public bool CanUndo
{
317 return document
.undo
.CanUndo
;
322 public override System
.Drawing
.Color ForeColor
{
324 return base.ForeColor
;
327 base.ForeColor
= value;
332 [MWFCategory("Behavior")]
333 public bool HideSelection
{
335 return hide_selection
;
339 if (value != hide_selection
) {
340 hide_selection
= value;
341 OnHideSelectionChanged(EventArgs
.Empty
);
343 document
.selection_visible
= !hide_selection
;
344 document
.InvalidateSelectionArea();
348 [MergableProperty (false)]
349 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
350 [Editor("System.Windows.Forms.Design.StringArrayEditor, " + Consts
.AssemblySystem_Design
, typeof(System
.Drawing
.Design
.UITypeEditor
))]
352 [MWFCategory("Appearance")]
353 public string[] Lines
{
358 count
= document
.Lines
;
360 // Handle empty document
361 if ((count
== 1) && (document
.GetLine (1).text
.Length
== 0)) {
362 return new string [0];
365 lines
= new ArrayList ();
370 StringBuilder lt
= new StringBuilder ();
373 line
= document
.GetLine (i
++);
374 lt
.Append (line
.TextWithoutEnding ());
375 } while (line
.ending
== LineEnding
.Wrap
&& i
<= count
);
377 lines
.Add (lt
.ToString ());
380 return (string []) lines
.ToArray (typeof (string));
384 StringBuilder sb
= new StringBuilder ();
386 for (int i
= 0; i
< value.Length
; i
++) {
387 // Don't add the last line if it is just an empty line feed
388 // the line feed is reflected in the previous line's ending
389 if (i
== value.Length
- 1 && value[i
].Length
== 0)
392 sb
.Append (value[i
] + Environment
.NewLine
);
395 int newline_length
= Environment
.NewLine
.Length
;
397 // We want to remove the final new line character
398 if (sb
.Length
>= newline_length
)
399 sb
.Remove (sb
.Length
- newline_length
, newline_length
);
401 Text
= sb
.ToString ();
405 [DefaultValue(32767)]
407 [MWFCategory("Behavior")]
408 public virtual int MaxLength
{
410 if (max_length
== (int.MaxValue
- 1)) { // We don't distinguish between single and multi-line limits
417 if (value != max_length
) {
419 value = int.MaxValue
- 1;
427 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
428 public bool Modified
{
434 if (value != modified
) {
436 OnModifiedChanged(EventArgs
.Empty
);
441 [DefaultValue(false)]
443 [RefreshProperties(RefreshProperties
.All
)]
444 [MWFCategory("Behavior")]
445 public virtual bool Multiline
{
447 return document
.multiline
;
451 if (value != document
.multiline
) {
452 document
.multiline
= value;
454 SetStyle (ControlStyles
.FixedHeight
, !value);
458 Parent
.PerformLayout ();
460 OnMultilineChanged(EventArgs
.Empty
);
463 if (document
.multiline
) {
464 document
.Wrap
= word_wrap
;
465 document
.PasswordChar
= "";
468 document
.Wrap
= false;
469 if (this.password_char
!= '\0') {
471 document
.PasswordChar
= (this as TextBox
).PasswordChar
.ToString ();
473 document
.PasswordChar
= "";
477 CalculateDocument ();
482 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
483 [EditorBrowsable(EditorBrowsableState
.Advanced
)]
484 // This returns the preferred outer height, not the client height.
485 public int PreferredHeight
{
487 int height
= FontHeight
;
488 if (BorderStyle
!= BorderStyle
.None
) {
495 [RefreshProperties (RefreshProperties
.Repaint
)]
496 [DefaultValue(false)]
497 [MWFCategory("Behavior")]
498 public bool ReadOnly
{
504 if (value != read_only
) {
506 if (!backcolor_set
) {
508 background_color
= SystemColors
.Control
;
510 background_color
= SystemColors
.Window
;
512 OnReadOnlyChanged(EventArgs
.Empty
);
519 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
520 public virtual string SelectedText
{
522 string retval
= document
.GetSelection ();
529 value = String
.Empty
;
531 document
.ReplaceSelection(CaseAdjust(value), false);
534 OnTextChanged(EventArgs
.Empty
);
539 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
540 public virtual int SelectionLength
{
542 int res
= document
.SelectionLength ();
549 string msg
= String
.Format ("'{0}' is not a valid value for 'SelectionLength'", value);
550 throw new ArgumentOutOfRangeException ("SelectionLength", msg
);
553 document
.InvalidateSelectionArea ();
560 selection_length
= value;
561 start
= document
.LineTagToCharIndex (document
.selection_start
.line
, document
.selection_start
.pos
);
562 document
.CharIndexToLineTag (start
+ value, out line
, out tag
, out pos
);
563 document
.SetSelectionEnd (line
, pos
, true);
564 document
.PositionCaret (line
, pos
);
566 selection_length
= -1;
567 document
.SetSelectionEnd (document
.selection_start
.line
, document
.selection_start
.pos
, true);
568 document
.PositionCaret (document
.selection_start
.line
, document
.selection_start
.pos
);
574 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
575 public int SelectionStart
{
577 return document
.LineTagToCharIndex(document
.selection_start
.line
,
578 document
.selection_start
.pos
);
583 string msg
= String
.Format ("'{0}' is not a valid value for 'SelectionStart'", value);
584 throw new ArgumentOutOfRangeException ("SelectionStart", msg
);
587 // If SelectionStart has been used, we don't highlight on focus
588 has_been_focused
= true;
590 document
.InvalidateSelectionArea ();
591 document
.SetSelectionStart (value, false);
592 if (selection_length
> -1)
593 document
.SetSelectionEnd (value + selection_length
, true);
595 document
.SetSelectionEnd (value, true);
596 document
.PositionCaret (document
.selection_start
.line
, document
.selection_start
.pos
);
601 [DefaultValue (true)]
602 public virtual bool ShortcutsEnabled
{
603 get { return shortcuts_enabled; }
604 set { shortcuts_enabled = value; }
607 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts
.AssemblySystem_Design
,
608 "System.Drawing.Design.UITypeEditor, " + Consts
.AssemblySystem_Drawing
)]
610 public override string Text
{
612 if (document
== null || document
.Root
== null || document
.Root
.text
== null)
615 StringBuilder sb
= new StringBuilder();
618 for (int i
= 1; i
<= document
.Lines
; i
++) {
619 line
= document
.GetLine (i
);
620 sb
.Append(line
.text
.ToString ());
623 return sb
.ToString();
627 // reset to force a select all next time the box gets focus
628 has_been_focused
= false;
634 if ((value != null) && (value != "")) {
635 document
.Insert (document
.GetLine (1), 0, false, value);
637 if (IsHandleCreated
) {
638 document
.SetSelectionToCaret (true);
639 CalculateDocument ();
643 document
.PositionCaret (document
.GetLine (1), 0);
644 document
.SetSelectionToCaret (true);
648 OnTextChanged(EventArgs
.Empty
);
653 public virtual int TextLength
{
655 if (document
== null || document
.Root
== null || document
.Root
.text
== null)
663 [MWFCategory("Behavior")]
664 public bool WordWrap
{
670 if (value != word_wrap
) {
671 if (document
.multiline
) {
673 document
.Wrap
= value;
675 CalculateDocument ();
681 [EditorBrowsable (EditorBrowsableState
.Never
)]
682 public override ImageLayout BackgroundImageLayout
{
683 get { return base.BackgroundImageLayout; }
684 set { base.BackgroundImageLayout = value; }
688 [EditorBrowsable (EditorBrowsableState
.Never
)]
689 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
690 public new Padding Padding
{
691 get { return base.Padding; }
692 set { base.Padding = value; }
695 protected override Cursor DefaultCursor
{
696 get { return Cursors.IBeam; }
698 #endregion // Public Instance Properties
700 #region Protected Instance Properties
701 protected override bool CanEnableIme
{
703 if (ReadOnly
|| password_char
!= '\0')
710 protected override CreateParams CreateParams
{
712 return base.CreateParams
;
716 protected override System
.Drawing
.Size DefaultSize
{
718 return new Size(100, PreferredHeight
);
722 // Currently our double buffering breaks our scrolling, so don't let people enable this
723 [EditorBrowsable (EditorBrowsableState
.Never
)]
724 protected override bool DoubleBuffered
{
725 get { return false; }
729 #endregion // Protected Instance Properties
731 #region Public Instance Methods
732 public void AppendText (string text
)
734 // Save some cycles and only check the Text if we are one line
735 bool is_empty
= document
.Lines
== 1 && Text
== String
.Empty
;
737 // make sure the caret begins at the end
738 if (document
.caret
.line
.line_no
!= document
.Lines
||
739 (document
.caret
.pos
) != document
.caret
.line
.TextLengthWithoutEnding ()) {
740 document
.MoveCaret (CaretDirection
.CtrlEnd
);
742 document
.Insert (document
.caret
.line
, document
.caret
.pos
, false, text
, document
.CaretTag
);
743 document
.MoveCaret (CaretDirection
.CtrlEnd
);
744 document
.SetSelectionToCaret (true);
750 // Avoid the initial focus selecting all when append text is used
752 has_been_focused
= true;
755 OnTextChanged(EventArgs
.Empty
);
764 public void ClearUndo ()
766 document
.undo
.Clear();
773 o
= new DataObject(DataFormats
.Text
, SelectedText
);
774 if (this is RichTextBox
)
775 o
.SetData(DataFormats
.Rtf
, ((RichTextBox
)this).SelectedRtf
);
776 Clipboard
.SetDataObject(o
);
783 o
= new DataObject(DataFormats
.Text
, SelectedText
);
784 if (this is RichTextBox
)
785 o
.SetData(DataFormats
.Rtf
, ((RichTextBox
)this).SelectedRtf
);
786 Clipboard
.SetDataObject (o
);
788 document
.undo
.BeginUserAction (Locale
.GetText ("Cut"));
789 document
.ReplaceSelection (String
.Empty
, false);
790 document
.undo
.EndUserAction ();
793 OnTextChanged (EventArgs
.Empty
);
798 Paste (Clipboard
.GetDataObject(), null, false);
801 public void ScrollToCaret ()
804 CaretMoved (this, EventArgs
.Empty
);
807 public void Select(int start
, int length
)
809 SelectionStart
= start
;
810 SelectionLength
= length
;
813 public void SelectAll ()
817 last
= document
.GetLine(document
.Lines
);
818 document
.SetSelectionStart(document
.GetLine(1), 0, false);
819 document
.SetSelectionEnd(last
, last
.text
.Length
, true);
820 document
.PositionCaret (document
.selection_end
.line
, document
.selection_end
.pos
);
821 selection_length
= -1;
823 CaretMoved (this, null);
825 document
.DisplayCaret ();
828 internal void SelectAllNoScroll ()
832 last
= document
.GetLine(document
.Lines
);
833 document
.SetSelectionStart(document
.GetLine(1), 0, false);
834 document
.SetSelectionEnd(last
, last
.text
.Length
, false);
835 document
.PositionCaret (document
.selection_end
.line
, document
.selection_end
.pos
);
836 selection_length
= -1;
838 document
.DisplayCaret ();
841 public override string ToString ()
843 return String
.Concat (base.ToString (), ", Text: ", Text
);
846 [MonoInternalNote ("Deleting is classed as Typing, instead of its own Undo event")]
849 if (document
.undo
.Undo ()) {
851 OnTextChanged (EventArgs
.Empty
);
855 public void DeselectAll ()
860 public virtual char GetCharFromPosition (Point pt
)
862 return GetCharFromPositionInternal (pt
);
865 internal virtual char GetCharFromPositionInternal (Point p
)
868 LineTag tag
= document
.FindCursor (p
.X
, p
.Y
, out index
);
870 return (char) 0; // Shouldn't happen
872 if (index
>= tag
.Line
.text
.Length
) {
874 if (tag
.Line
.ending
== LineEnding
.Wrap
) {
875 // If we have wrapped text, we return the first char of the next line
876 Line line
= document
.GetLine (tag
.Line
.line_no
+ 1);
878 return line
.text
[0];
882 if (tag
.Line
.line_no
== document
.Lines
) {
883 // Last line returns the last char
884 return tag
.Line
.text
[tag
.Line
.text
.Length
- 1];
887 // This really shouldn't happen
890 return tag
.Line
.text
[index
];
893 public virtual int GetCharIndexFromPosition (Point pt
)
896 LineTag tag
= document
.FindCursor (pt
.X
, pt
.Y
, out line_index
);
900 if (line_index
>= tag
.Line
.text
.Length
) {
902 if (tag
.Line
.ending
== LineEnding
.Wrap
) {
903 // If we have wrapped text, we return the first char of the next line
904 Line line
= document
.GetLine (tag
.Line
.line_no
+ 1);
906 return document
.LineTagToCharIndex (line
, 0);
909 if (tag
.Line
.line_no
== document
.Lines
) {
910 // Last line returns the last char
911 return document
.LineTagToCharIndex (tag
.Line
, tag
.Line
.text
.Length
- 1);
917 return document
.LineTagToCharIndex (tag
.Line
, line_index
);
920 public virtual Point
GetPositionFromCharIndex (int index
)
926 document
.CharIndexToLineTag (index
, out line
, out tag
, out pos
);
928 return new Point ((int) (line
.widths
[pos
] +
929 line
.X
+ document
.viewport_x
),
930 line
.Y
+ document
.viewport_y
+ tag
.Shift
);
933 public int GetFirstCharIndexFromLine (int lineNumber
)
935 Line line
= document
.GetLine (lineNumber
+ 1);
939 return document
.LineTagToCharIndex (line
, 0);
942 public int GetFirstCharIndexOfCurrentLine ()
944 return document
.LineTagToCharIndex (document
.caret
.line
, 0);
946 #endregion // Public Instance Methods
948 #region Protected Instance Methods
949 protected override void CreateHandle ()
951 CalculateDocument ();
952 base.CreateHandle ();
953 document
.AlignCaret();
957 internal virtual void HandleLinkClicked (LinkRectangle link_clicked
)
961 protected override bool IsInputKey (Keys keyData
)
963 if ((keyData
& Keys
.Alt
) != 0)
964 return base.IsInputKey(keyData
);
966 switch (keyData
& Keys
.KeyCode
) {
968 return (accepts_return
&& document
.multiline
);
972 if (accepts_tab
&& document
.multiline
)
973 if ((keyData
& Keys
.Control
) == 0)
993 protected virtual void OnAcceptsTabChanged(EventArgs e
)
995 EventHandler eh
= (EventHandler
)(Events
[AcceptsTabChangedEvent
]);
1000 protected virtual void OnBorderStyleChanged (EventArgs e
)
1002 EventHandler eh
= (EventHandler
)(Events
[BorderStyleChangedEvent
]);
1007 protected override void OnFontChanged (EventArgs e
)
1009 base.OnFontChanged (e
);
1013 protected override void OnHandleCreated (EventArgs e
)
1015 base.OnHandleCreated (e
);
1016 if (delayed_font_or_color_change
) {
1017 TextBoxBase_FontOrColorChanged (this, e
);
1022 protected override void OnHandleDestroyed (EventArgs e
)
1024 base.OnHandleDestroyed (e
);
1027 protected virtual void OnHideSelectionChanged (EventArgs e
)
1029 EventHandler eh
= (EventHandler
)(Events
[HideSelectionChangedEvent
]);
1034 protected virtual void OnModifiedChanged (EventArgs e
)
1036 EventHandler eh
= (EventHandler
)(Events
[ModifiedChangedEvent
]);
1041 protected virtual void OnMultilineChanged (EventArgs e
)
1043 EventHandler eh
= (EventHandler
)(Events
[MultilineChangedEvent
]);
1048 protected override void OnPaddingChanged (EventArgs e
)
1050 base.OnPaddingChanged (e
);
1053 protected virtual void OnReadOnlyChanged (EventArgs e
)
1055 EventHandler eh
= (EventHandler
)(Events
[ReadOnlyChangedEvent
]);
1060 protected override bool ProcessCmdKey (ref Message msg
, Keys keyData
)
1062 return base.ProcessCmdKey (ref msg
, keyData
);
1064 protected override bool ProcessDialogKey (Keys keyData
)
1066 // The user can use Ctrl-Tab or Ctrl-Shift-Tab to move control focus
1067 // instead of inserting a Tab. However, the focus-moving-tab-stuffs
1068 // doesn't work if Ctrl is pushed, so we remove it before sending it.
1069 if (accepts_tab
&& (keyData
& (Keys
.Control
| Keys
.Tab
)) == (Keys
.Control
| Keys
.Tab
))
1070 keyData ^
= Keys
.Control
;
1072 return base.ProcessDialogKey(keyData
);
1075 private bool ProcessKey (Keys keyData
)
1080 control
= (Control
.ModifierKeys
& Keys
.Control
) != 0;
1081 shift
= (Control
.ModifierKeys
& Keys
.Shift
) != 0;
1083 if (shortcuts_enabled
) {
1084 switch (keyData
& Keys
.KeyCode
) {
1086 if (control
&& read_only
== false) {
1100 if (control
&& read_only
== false) {
1101 return Paste(Clipboard
.GetDataObject(), null, true);
1106 if (control
&& read_only
== false) {
1121 if (read_only
== false) {
1123 Paste (Clipboard
.GetDataObject (), null, true);
1140 if (shift
&& read_only
== false) {
1145 if (document
.selection_visible
) {
1146 document
.ReplaceSelection("", false);
1148 // DeleteChar only deletes on the line, doesn't do the combine
1149 if (document
.CaretPosition
>= document
.CaretLine
.TextLengthWithoutEnding ()) {
1150 if (document
.CaretLine
.LineNo
< document
.Lines
) {
1153 line
= document
.GetLine(document
.CaretLine
.LineNo
+ 1);
1155 // this line needs to be invalidated before it is combined
1156 // because once it is combined, all it's coordinates will
1158 document
.Invalidate (line
, 0, line
, line
.text
.Length
);
1159 document
.Combine(document
.CaretLine
, line
);
1161 document
.UpdateView(document
.CaretLine
,
1167 document
.DeleteChar(document
.CaretTag
.Line
, document
.CaretPosition
, true);
1171 end_pos
= document
.CaretPosition
;
1173 while ((end_pos
< document
.CaretLine
.Text
.Length
) && !Document
.IsWordSeparator(document
.CaretLine
.Text
[end_pos
])) {
1177 if (end_pos
< document
.CaretLine
.Text
.Length
) {
1180 document
.DeleteChars(document
.CaretTag
.Line
, document
.CaretPosition
, end_pos
- document
.CaretPosition
);
1185 document
.AlignCaret();
1186 document
.UpdateCaret();
1187 CaretMoved(this, null);
1190 OnTextChanged (EventArgs
.Empty
);
1196 switch (keyData
& Keys
.KeyCode
) {
1199 document
.MoveCaret(CaretDirection
.WordBack
);
1201 if (!document
.selection_visible
|| shift
) {
1202 document
.MoveCaret(CaretDirection
.CharBack
);
1204 document
.MoveCaret(CaretDirection
.SelectionStart
);
1209 document
.SetSelectionToCaret(true);
1211 document
.SetSelectionToCaret(false);
1214 CaretMoved(this, null);
1220 document
.MoveCaret(CaretDirection
.WordForward
);
1222 if (!document
.selection_visible
|| shift
) {
1223 document
.MoveCaret(CaretDirection
.CharForward
);
1225 document
.MoveCaret(CaretDirection
.SelectionEnd
);
1229 document
.SetSelectionToCaret(true);
1231 document
.SetSelectionToCaret(false);
1234 CaretMoved(this, null);
1240 if (document
.CaretPosition
== 0) {
1241 document
.MoveCaret(CaretDirection
.LineUp
);
1243 document
.MoveCaret(CaretDirection
.Home
);
1246 document
.MoveCaret(CaretDirection
.LineUp
);
1249 if ((Control
.ModifierKeys
& Keys
.Shift
) == 0) {
1250 document
.SetSelectionToCaret(true);
1252 document
.SetSelectionToCaret(false);
1255 CaretMoved(this, null);
1261 if (document
.CaretPosition
== document
.CaretLine
.Text
.Length
) {
1262 document
.MoveCaret(CaretDirection
.LineDown
);
1264 document
.MoveCaret(CaretDirection
.End
);
1267 document
.MoveCaret(CaretDirection
.LineDown
);
1270 if ((Control
.ModifierKeys
& Keys
.Shift
) == 0) {
1271 document
.SetSelectionToCaret(true);
1273 document
.SetSelectionToCaret(false);
1276 CaretMoved(this, null);
1281 if ((Control
.ModifierKeys
& Keys
.Control
) != 0) {
1282 document
.MoveCaret(CaretDirection
.CtrlHome
);
1284 document
.MoveCaret(CaretDirection
.Home
);
1287 if ((Control
.ModifierKeys
& Keys
.Shift
) == 0) {
1288 document
.SetSelectionToCaret(true);
1290 document
.SetSelectionToCaret(false);
1293 CaretMoved(this, null);
1298 if ((Control
.ModifierKeys
& Keys
.Control
) != 0) {
1299 document
.MoveCaret(CaretDirection
.CtrlEnd
);
1301 document
.MoveCaret(CaretDirection
.End
);
1304 if ((Control
.ModifierKeys
& Keys
.Shift
) == 0) {
1305 document
.SetSelectionToCaret(true);
1307 document
.SetSelectionToCaret(false);
1310 CaretMoved(this, null);
1314 //case Keys.Enter: {
1315 // // ignoring accepts_return, fixes bug #76355
1316 // if (!read_only && document.multiline && (accepts_return || (FindForm() != null && FindForm().AcceptButton == null) || ((Control.ModifierKeys & Keys.Control) != 0))) {
1319 // if (document.selection_visible) {
1320 // document.ReplaceSelection("\n", false);
1323 // line = document.CaretLine;
1325 // document.Split (document.CaretLine, document.CaretTag, document.CaretPosition);
1326 // line.ending = LineEnding.Rich;
1327 // document.InsertString (line, line.text.Length,
1328 // document.LineEndingToString (line.ending));
1329 // OnTextChanged(EventArgs.Empty);
1331 // document.UpdateView (line, document.Lines - line.line_no, 0);
1332 // CaretMoved(this, null);
1339 if (!read_only
&& accepts_tab
&& document
.multiline
) {
1340 document
.InsertCharAtCaret ('\t', true);
1342 CaretMoved(this, null);
1344 OnTextChanged (EventArgs
.Empty
);
1352 if ((Control
.ModifierKeys
& Keys
.Control
) != 0) {
1353 document
.MoveCaret(CaretDirection
.CtrlPgUp
);
1355 document
.MoveCaret(CaretDirection
.PgUp
);
1357 document
.DisplayCaret ();
1361 case Keys
.PageDown
: {
1362 if ((Control
.ModifierKeys
& Keys
.Control
) != 0) {
1363 document
.MoveCaret(CaretDirection
.CtrlPgDn
);
1365 document
.MoveCaret(CaretDirection
.PgDn
);
1367 document
.DisplayCaret ();
1375 internal virtual void RaiseSelectionChanged ()
1377 // Do nothing, overridden in RTB
1380 private void HandleBackspace (bool control
)
1384 fire_changed
= false;
1386 // delete only deletes on the line, doesn't do the combine
1387 if (document
.selection_visible
) {
1388 document
.undo
.BeginUserAction (Locale
.GetText ("Delete"));
1389 document
.ReplaceSelection("", false);
1390 document
.undo
.EndUserAction ();
1391 fire_changed
= true;
1392 document
.SetSelectionToCaret (true);
1394 document
.SetSelectionToCaret (true);
1396 if (document
.CaretPosition
== 0) {
1397 if (document
.CaretLine
.LineNo
> 1) {
1401 line
= document
.GetLine(document
.CaretLine
.LineNo
- 1);
1402 new_caret_pos
= line
.TextLengthWithoutEnding ();
1404 // Invalidate the old line position before we do the combine
1405 document
.Invalidate (line
, 0, line
, line
.text
.Length
);
1406 document
.Combine(line
, document
.CaretLine
);
1408 document
.UpdateView(line
, document
.Lines
- line
.LineNo
, 0);
1409 document
.PositionCaret(line
, new_caret_pos
);
1410 document
.SetSelectionToCaret (true);
1411 document
.UpdateCaret();
1412 fire_changed
= true;
1415 if (!control
|| document
.CaretPosition
== 0) {
1417 // Move before we delete because the delete will change positions around
1418 // if we cross a wrap border
1419 LineTag tag
= document
.CaretTag
;
1420 int pos
= document
.CaretPosition
;
1421 document
.MoveCaret (CaretDirection
.CharBack
);
1422 document
.DeleteChar (tag
.Line
, pos
, false);
1423 document
.SetSelectionToCaret (true);
1428 start_pos
= document
.CaretPosition
- 1;
1429 while ((start_pos
> 0) && !Document
.IsWordSeparator(document
.CaretLine
.Text
[start_pos
- 1])) {
1433 document
.undo
.BeginUserAction (Locale
.GetText ("Delete"));
1434 document
.DeleteChars(document
.CaretTag
.Line
, start_pos
, document
.CaretPosition
- start_pos
);
1435 document
.undo
.EndUserAction ();
1436 document
.PositionCaret(document
.CaretLine
, start_pos
);
1437 document
.SetSelectionToCaret (true);
1439 document
.UpdateCaret();
1440 fire_changed
= true;
1444 CaretMoved (this, null);
1448 OnTextChanged(EventArgs
.Empty
);
1452 private void HandleEnter ()
1454 // ignoring accepts_return, fixes bug #76355
1455 if (!read_only
&& document
.multiline
&& (accepts_return
|| (FindForm() != null && FindForm().AcceptButton
== null) || ((Control
.ModifierKeys
& Keys
.Control
) != 0))) {
1458 if (document
.selection_visible
)
1459 document
.ReplaceSelection ("", false);
1461 line
= document
.CaretLine
;
1463 document
.Split (document
.CaretLine
, document
.CaretTag
, document
.CaretPosition
);
1464 line
.ending
= document
.StringToLineEnding (Environment
.NewLine
);
1465 document
.InsertString (line
, line
.text
.Length
, document
.LineEndingToString (line
.ending
));
1467 document
.UpdateView (line
, document
.Lines
- line
.line_no
, 0);
1468 CaretMoved (this, null);
1470 OnTextChanged (EventArgs
.Empty
);
1474 protected override void SetBoundsCore (int x
, int y
, int width
, int height
, BoundsSpecified specified
)
1476 // Make sure we don't get sized bigger than we want to be
1478 if ((specified
& BoundsSpecified
.Height
) != 0) {
1479 requested_height
= height
;
1483 if (!document
.multiline
) {
1484 height
= PreferredHeight
;
1488 base.SetBoundsCore (x
, y
, width
, height
, specified
);
1491 protected override void WndProc (ref Message m
)
1493 switch ((Msg
)m
.Msg
) {
1494 case Msg
.WM_KEYDOWN
: {
1495 if (ProcessKeyMessage(ref m
) || ProcessKey((Keys
)m
.WParam
.ToInt32() | XplatUI
.State
.ModifierKeys
)) {
1496 m
.Result
= IntPtr
.Zero
;
1506 if (ProcessKeyMessage(ref m
)) {
1507 m
.Result
= IntPtr
.Zero
;
1515 m
.Result
= IntPtr
.Zero
;
1517 ch
= m
.WParam
.ToInt32();
1520 HandleBackspace(true);
1521 } else if (ch
>= 32) {
1522 if (document
.selection_visible
) {
1523 document
.ReplaceSelection("", false);
1526 char c
= (char)m
.WParam
;
1527 switch (character_casing
) {
1528 case CharacterCasing
.Upper
:
1529 c
= Char
.ToUpper((char) m
.WParam
);
1531 case CharacterCasing
.Lower
:
1532 c
= Char
.ToLower((char) m
.WParam
);
1536 if (document
.Length
< max_length
) {
1537 document
.InsertCharAtCaret(c
, true);
1539 CaretMoved (this, null);
1541 OnTextChanged(EventArgs
.Empty
);
1544 XplatUI
.AudibleAlert(AlertType
.Default
);
1547 } else if (ch
== 8) {
1548 HandleBackspace(false);
1549 } else if (ch
== 13)
1555 case Msg
.WM_SETFOCUS
:
1556 base.WndProc(ref m
);
1557 document
.CaretHasFocus ();
1560 case Msg
.WM_KILLFOCUS
:
1561 base.WndProc(ref m
);
1562 document
.CaretLostFocus ();
1565 case Msg
.WM_NCPAINT
:
1566 if (!ThemeEngine
.Current
.TextBoxBaseHandleWmNcPaint (this, ref m
))
1567 base.WndProc(ref m
);
1571 base.WndProc(ref m
);
1576 #endregion // Protected Instance Methods
1579 static object AcceptsTabChangedEvent
= new object ();
1580 static object AutoSizeChangedEvent
= new object ();
1581 static object BorderStyleChangedEvent
= new object ();
1582 static object HideSelectionChangedEvent
= new object ();
1583 static object ModifiedChangedEvent
= new object ();
1584 static object MultilineChangedEvent
= new object ();
1585 static object ReadOnlyChangedEvent
= new object ();
1586 static object HScrolledEvent
= new object ();
1587 static object VScrolledEvent
= new object ();
1589 public event EventHandler AcceptsTabChanged
{
1590 add { Events.AddHandler (AcceptsTabChangedEvent, value); }
1591 remove { Events.RemoveHandler (AcceptsTabChangedEvent, value); }
1595 [EditorBrowsable (EditorBrowsableState
.Never
)]
1596 public new event EventHandler AutoSizeChanged
{
1597 add { Events.AddHandler (AutoSizeChangedEvent, value); }
1598 remove { Events.RemoveHandler (AutoSizeChangedEvent, value); }
1601 public event EventHandler BorderStyleChanged
{
1602 add { Events.AddHandler (BorderStyleChangedEvent, value); }
1603 remove { Events.RemoveHandler (BorderStyleChangedEvent, value); }
1606 public event EventHandler HideSelectionChanged
{
1607 add { Events.AddHandler (HideSelectionChangedEvent, value); }
1608 remove { Events.RemoveHandler (HideSelectionChangedEvent, value); }
1611 public event EventHandler ModifiedChanged
{
1612 add { Events.AddHandler (ModifiedChangedEvent, value); }
1613 remove { Events.RemoveHandler (ModifiedChangedEvent, value); }
1616 public event EventHandler MultilineChanged
{
1617 add { Events.AddHandler (MultilineChangedEvent, value); }
1618 remove { Events.RemoveHandler (MultilineChangedEvent, value); }
1621 public event EventHandler ReadOnlyChanged
{
1622 add { Events.AddHandler (ReadOnlyChangedEvent, value); }
1623 remove { Events.RemoveHandler (ReadOnlyChangedEvent, value); }
1626 internal event EventHandler HScrolled
{
1627 add { Events.AddHandler (HScrolledEvent, value); }
1628 remove { Events.RemoveHandler (HScrolledEvent, value); }
1631 internal event EventHandler VScrolled
{
1632 add { Events.AddHandler (VScrolledEvent, value); }
1633 remove { Events.RemoveHandler (VScrolledEvent, value); }
1637 [EditorBrowsable(EditorBrowsableState
.Never
)]
1638 public new event EventHandler BackgroundImageChanged
{
1639 add { base.BackgroundImageChanged += value; }
1640 remove { base.BackgroundImageChanged -= value; }
1644 [EditorBrowsable (EditorBrowsableState
.Never
)]
1645 public new event EventHandler BackgroundImageLayoutChanged
{
1646 add { base.BackgroundImageLayoutChanged += value; }
1647 remove { base.BackgroundImageLayoutChanged -= value; }
1651 [EditorBrowsable (EditorBrowsableState
.Always
)]
1652 public new event MouseEventHandler MouseClick
{
1653 add { base.MouseClick += value; }
1654 remove { base.MouseClick -= value; }
1658 [EditorBrowsable (EditorBrowsableState
.Never
)]
1659 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
1660 public new event EventHandler PaddingChanged
{
1661 add { base.PaddingChanged += value; }
1662 remove { base.PaddingChanged -= value; }
1666 [EditorBrowsable (EditorBrowsableState
.Always
)]
1667 public new event EventHandler Click
{
1668 add { base.Click += value; }
1669 remove { base.Click -= value; }
1672 // XXX should this not manipulate base.Paint?
1673 #pragma warning disable 0067
1676 [EditorBrowsable(EditorBrowsableState
.Never
)]
1677 public new event PaintEventHandler Paint
;
1678 #pragma warning restore 0067
1680 #endregion // Events
1682 #region Private Methods
1683 internal Document Document
{
1693 internal bool EnableLinks
{
1694 get { return enable_links; }
1696 enable_links
= value;
1698 document
.EnableLinks
= value;
1702 internal override bool ScaleChildrenInternal
{
1703 get { return false; }
1706 internal bool ShowSelection
{
1708 if (show_selection
|| !hide_selection
) {
1716 if (show_selection
== value)
1719 show_selection
= value;
1720 // Currently InvalidateSelectionArea is commented out so do a full invalidate
1721 document
.InvalidateSelectionArea();
1725 internal int TopMargin
{
1727 return document
== null ? 0 : document
.top_margin
;
1730 document
.top_margin
= value;
1734 #region UIA Framework Properties
1736 internal ScrollBar UIAHScrollBar
{
1737 get { return hscroll; }
1740 internal ScrollBar UIAVScrollBar
{
1741 get { return vscroll; }
1744 #endregion UIA Framework Properties
1746 internal override void OnPaintInternal (PaintEventArgs pevent
)
1748 Draw (pevent
.Graphics
, pevent
.ClipRectangle
);
1751 // OnPaint does not get raised on MS (see bug #80639)
1753 pevent
.Handled
= true;
1756 internal void Draw (Graphics g
, Rectangle clippingArea
)
1758 ThemeEngine
.Current
.TextBoxBaseFillBackground (this, g
, clippingArea
);
1760 // Draw the viewable document
1761 document
.Draw(g
, clippingArea
);
1764 private void FixupHeight ()
1767 int saved_requested_height
= requested_height
;
1768 if (!document
.multiline
) {
1769 if (PreferredHeight
!= Height
) {
1770 SetBoundsCore (Left
, Top
, Width
, PreferredHeight
, BoundsSpecified
.Height
);
1773 SetBoundsCore (Left
, Top
, Width
, Math
.Max(PreferredHeight
, requested_height
), BoundsSpecified
.Height
);
1775 requested_height
= saved_requested_height
;
1779 private bool IsDoubleClick (MouseEventArgs e
)
1781 TimeSpan interval
= DateTime
.Now
- click_last
;
1782 if (interval
.TotalMilliseconds
> SystemInformation
.DoubleClickTime
)
1784 Size dcs
= SystemInformation
.DoubleClickSize
;
1785 if (e
.X
< click_point_x
- dcs
.Width
/ 2 || e
.X
> click_point_x
+ dcs
.Width
/ 2)
1787 if (e
.Y
< click_point_y
- dcs
.Height
/ 2 || e
.Y
> click_point_y
+ dcs
.Height
/ 2)
1792 private void TextBoxBase_MouseDown (object sender
, MouseEventArgs e
)
1794 bool dbliclick
= false;
1796 if (e
.Button
== MouseButtons
.Left
) {
1798 // Special case when shift key is pressed and
1799 // left mouse is clicked.. set selection from
1800 // current cursor to mouse
1801 if ((Control
.ModifierKeys
& Keys
.Shift
) > 0) {
1802 document
.PositionCaret (e
.X
+ document
.ViewPortX
, e
.Y
+ document
.ViewPortY
);
1803 document
.SetSelectionToCaret (false);
1804 document
.DisplayCaret ();
1808 dbliclick
= IsDoubleClick (e
);
1810 if (current_link
!= null) {
1811 HandleLinkClicked (current_link
);
1815 //ensure nothing is selected anymore BEFORE we
1816 //position the caret, so the caret is recreated
1817 //(caret is only visible when nothing is selected)
1818 if (document
.selection_visible
&& dbliclick
== false) {
1819 document
.SetSelectionToCaret (true);
1820 click_mode
= CaretSelection
.Position
;
1823 document
.PositionCaret(e
.X
+ document
.ViewPortX
, e
.Y
+ document
.ViewPortY
);
1824 document
.DisplayCaret ();
1827 switch (click_mode
) {
1828 case CaretSelection
.Position
:
1830 click_mode
= CaretSelection
.Word
;
1832 case CaretSelection
.Word
:
1834 if (this is TextBox
) {
1835 document
.SetSelectionToCaret (true);
1836 click_mode
= CaretSelection
.Position
;
1840 document
.ExpandSelection (CaretSelection
.Line
, false);
1841 click_mode
= CaretSelection
.Line
;
1843 case CaretSelection
.Line
:
1845 // Gotta do this first because Exanding to a word
1846 // from a line doesn't really work
1847 document
.SetSelectionToCaret (true);
1850 click_mode
= CaretSelection
.Word
;
1854 document
.SetSelectionToCaret (true);
1855 click_mode
= CaretSelection
.Position
;
1858 click_point_x
= e
.X
;
1859 click_point_y
= e
.Y
;
1860 click_last
= DateTime
.Now
;
1863 if ((e
.Button
== MouseButtons
.Middle
) && XplatUI
.RunningOnUnix
) {
1864 Document
.Marker marker
;
1866 marker
.tag
= document
.FindCursor(e
.X
+ document
.ViewPortX
, e
.Y
+ document
.ViewPortY
, out marker
.pos
);
1867 marker
.line
= marker
.tag
.Line
;
1868 marker
.height
= marker
.tag
.Height
;
1870 document
.SetSelection(marker
.line
, marker
.pos
, marker
.line
, marker
.pos
);
1871 Paste (Clipboard
.GetDataObject (true), null, true);
1875 private void TextBoxBase_MouseUp (object sender
, MouseEventArgs e
)
1877 if (e
.Button
== MouseButtons
.Left
) {
1878 if (click_mode
== CaretSelection
.Position
) {
1879 document
.SetSelectionToCaret(false);
1880 document
.DisplayCaret();
1882 // Only raise if there is text.
1883 if (Text
.Length
> 0)
1884 RaiseSelectionChanged ();
1887 if (scroll_timer
!= null) {
1888 scroll_timer
.Enabled
= false;
1894 private void SizeControls ()
1896 if (hscroll
.Visible
) {
1897 //vscroll.Maximum += hscroll.Height;
1898 canvas_height
= ClientSize
.Height
- hscroll
.Height
;
1900 canvas_height
= ClientSize
.Height
;
1903 if (vscroll
.Visible
) {
1904 //hscroll.Maximum += vscroll.Width;
1905 canvas_width
= ClientSize
.Width
- vscroll
.Width
;
1907 if (GetInheritedRtoL () == RightToLeft
.Yes
) {
1908 document
.OffsetX
= vscroll
.Width
;
1910 document
.OffsetX
= 0;
1914 canvas_width
= ClientSize
.Width
;
1915 document
.OffsetX
= 0;
1918 document
.ViewPortWidth
= canvas_width
;
1919 document
.ViewPortHeight
= canvas_height
;
1922 private void PositionControls ()
1924 if (canvas_height
< 1 || canvas_width
< 1)
1927 int hmod
= vscroll
.Visible
? vscroll
.Width
: 0;
1928 int vmod
= hscroll
.Visible
? hscroll
.Height
: 0;
1930 if (GetInheritedRtoL () == RightToLeft
.Yes
) {
1931 hscroll
.Bounds
= new Rectangle (ClientRectangle
.Left
+ hmod
,
1932 Math
.Max(0, ClientRectangle
.Height
- hscroll
.Height
),
1936 vscroll
.Bounds
= new Rectangle (ClientRectangle
.Left
,
1937 ClientRectangle
.Top
,
1939 Math
.Max(0, ClientSize
.Height
- (vmod
)));
1941 hscroll
.Bounds
= new Rectangle (ClientRectangle
.Left
,
1942 Math
.Max(0, ClientRectangle
.Height
- hscroll
.Height
),
1943 Math
.Max(0, ClientSize
.Width
- hmod
),
1946 vscroll
.Bounds
= new Rectangle (
1947 Math
.Max(0, ClientRectangle
.Right
- vscroll
.Width
),
1948 ClientRectangle
.Top
,
1950 Math
.Max(0, ClientSize
.Height
- vmod
));
1954 internal RightToLeft
GetInheritedRtoL ()
1956 for (Control c
= this; c
!= null; c
= c
.Parent
)
1957 if (c
.RightToLeft
!= RightToLeft
.Inherit
)
1958 return c
.RightToLeft
;
1959 return RightToLeft
.No
;
1962 private void TextBoxBase_SizeChanged (object sender
, EventArgs e
)
1964 CalculateDocument ();
1967 private void TextBoxBase_RightToLeftChanged (object o
, EventArgs e
)
1969 CalculateDocument ();
1972 private void TextBoxBase_MouseWheel (object sender
, MouseEventArgs e
)
1974 if (!vscroll
.Enabled
)
1978 vscroll
.Value
= Math
.Min (vscroll
.Value
+ SystemInformation
.MouseWheelScrollLines
* 5,
1979 Math
.Max (0, vscroll
.Maximum
- document
.ViewPortHeight
+ 1));
1981 vscroll
.Value
= Math
.Max (0, vscroll
.Value
- SystemInformation
.MouseWheelScrollLines
* 5);
1984 internal virtual void SelectWord ()
1986 StringBuilder s
= document
.caret
.line
.text
;
1987 int start
= document
.caret
.pos
;
1988 int end
= document
.caret
.pos
;
1991 if (document
.caret
.line
.line_no
>= document
.Lines
)
1993 Line line
= document
.GetLine (document
.caret
.line
.line_no
+ 1);
1994 document
.PositionCaret (line
, 0);
2003 // skip whitespace until we hit a word
2004 while (start
> 0 && s
[start
] == ' ')
2007 while (start
> 0 && (s
[start
] != ' '))
2009 if (s
[start
] == ' ')
2013 if (s
[end
] == ' ') {
2014 while (end
< s
.Length
&& s
[end
] == ' ')
2017 while (end
< s
.Length
&& s
[end
] != ' ')
2019 while (end
< s
.Length
&& s
[end
] == ' ')
2023 document
.SetSelection (document
.caret
.line
, start
, document
.caret
.line
, end
);
2024 document
.PositionCaret (document
.selection_end
.line
, document
.selection_end
.pos
);
2025 document
.DisplayCaret();
2028 internal void CalculateDocument()
2030 if (!IsHandleCreated
)
2033 CalculateScrollBars ();
2034 using (var graphics
= CreateGraphics())
2035 document
.RecalculateDocument (graphics
);
2037 if (document
.caret
.line
!= null && document
.caret
.line
.Y
< document
.ViewPortHeight
) {
2038 // The window has probably been resized, making the entire thing visible, so
2039 // we need to set the scroll position back to zero.
2046 internal void CalculateScrollBars ()
2048 // FIXME - need separate calculations for center and right alignment
2051 if (document
.Width
>= document
.ViewPortWidth
) {
2052 hscroll
.SetValues (0, Math
.Max (1, document
.Width
), -1,
2053 document
.ViewPortWidth
< 0 ? 0 : document
.ViewPortWidth
);
2054 if (document
.multiline
)
2055 hscroll
.Enabled
= true;
2057 hscroll
.Enabled
= false;
2058 hscroll
.Maximum
= document
.ViewPortWidth
;
2061 if (document
.Height
>= document
.ViewPortHeight
) {
2062 vscroll
.SetValues (0, Math
.Max (1, document
.Height
), -1,
2063 document
.ViewPortHeight
< 0 ? 0 : document
.ViewPortHeight
);
2064 if (document
.multiline
)
2065 vscroll
.Enabled
= true;
2067 vscroll
.Enabled
= false;
2068 vscroll
.Maximum
= document
.ViewPortHeight
;
2072 switch (scrollbars
) {
2073 case RichTextBoxScrollBars
.Both
:
2074 case RichTextBoxScrollBars
.Horizontal
:
2076 hscroll
.Visible
= hscroll
.Enabled
;
2078 hscroll
.Visible
= this.Multiline
;
2080 case RichTextBoxScrollBars
.ForcedBoth
:
2081 case RichTextBoxScrollBars
.ForcedHorizontal
:
2082 hscroll
.Visible
= true;
2085 hscroll
.Visible
= false;
2089 hscroll
.Visible
= false;
2092 switch (scrollbars
) {
2093 case RichTextBoxScrollBars
.Both
:
2094 case RichTextBoxScrollBars
.Vertical
:
2096 vscroll
.Visible
= vscroll
.Enabled
;
2098 vscroll
.Visible
= this.Multiline
;
2100 case RichTextBoxScrollBars
.ForcedBoth
:
2101 case RichTextBoxScrollBars
.ForcedVertical
:
2102 vscroll
.Visible
= true;
2105 vscroll
.Visible
= false;
2109 PositionControls ();
2111 SizeControls (); //Update sizings now we've decided whats visible
2114 private void document_WidthChanged (object sender
, EventArgs e
)
2116 CalculateScrollBars();
2119 private void document_HeightChanged (object sender
, EventArgs e
)
2121 CalculateScrollBars();
2124 private void ScrollLinks (int xChange
, int yChange
)
2126 foreach (LinkRectangle link
in list_links
)
2127 link
.Scroll (xChange
, yChange
);
2130 private void hscroll_ValueChanged (object sender
, EventArgs e
)
2134 old_viewport_x
= document
.ViewPortX
;
2135 document
.ViewPortX
= this.hscroll
.Value
;
2138 // Before scrolling we want to destroy the caret, then draw a new one after the scroll
2139 // the reason for this is that scrolling changes the coordinates of the caret, and we
2140 // will get tracers if we don't
2143 document
.CaretLostFocus ();
2145 if (vscroll
.Visible
) {
2146 if (GetInheritedRtoL () == RightToLeft
.Yes
) {
2147 XplatUI
.ScrollWindow (this.Handle
, new Rectangle (vscroll
.Width
, 0, ClientSize
.Width
- vscroll
.Width
, ClientSize
.Height
), old_viewport_x
- this.hscroll
.Value
, 0, false);
2149 XplatUI
.ScrollWindow (this.Handle
, new Rectangle (0, 0, ClientSize
.Width
- vscroll
.Width
, ClientSize
.Height
), old_viewport_x
- this.hscroll
.Value
, 0, false);
2152 XplatUI
.ScrollWindow(this.Handle
, ClientRectangle
, old_viewport_x
- this.hscroll
.Value
, 0, false);
2155 ScrollLinks (old_viewport_x
- this.hscroll
.Value
, 0);
2158 document
.CaretHasFocus ();
2160 EventHandler eh
= (EventHandler
)(Events
[HScrolledEvent
]);
2162 eh (this, EventArgs
.Empty
);
2165 private void vscroll_ValueChanged (object sender
, EventArgs e
)
2169 old_viewport_y
= document
.ViewPortY
;
2170 document
.ViewPortY
= this.vscroll
.Value
;
2173 // Before scrolling we want to destroy the caret, then draw a new one after the scroll
2174 // the reason for this is that scrolling changes the coordinates of the caret, and we
2175 // will get tracers if we don't
2178 document
.CaretLostFocus ();
2180 if (hscroll
.Visible
) {
2181 XplatUI
.ScrollWindow(this.Handle
, new Rectangle(0, 0, ClientSize
.Width
, ClientSize
.Height
- hscroll
.Height
), 0, old_viewport_y
- this.vscroll
.Value
, false);
2183 XplatUI
.ScrollWindow(this.Handle
, ClientRectangle
, 0, old_viewport_y
- this.vscroll
.Value
, false);
2186 ScrollLinks (0, old_viewport_y
- this.vscroll
.Value
);
2189 document
.CaretHasFocus ();
2191 EventHandler eh
= (EventHandler
)(Events
[VScrolledEvent
]);
2193 eh (this, EventArgs
.Empty
);
2196 private void TextBoxBase_MouseMove (object sender
, MouseEventArgs e
)
2198 // FIXME - handle auto-scrolling if mouse is to the right/left of the window
2199 if (e
.Button
== MouseButtons
.Left
&& Capture
) {
2200 if (!ClientRectangle
.Contains (e
.X
, e
.Y
)) {
2201 if (scroll_timer
== null) {
2202 scroll_timer
= new Timer ();
2203 scroll_timer
.Interval
= 100;
2204 scroll_timer
.Tick
+= new EventHandler (ScrollTimerTickHandler
);
2207 if (!scroll_timer
.Enabled
) {
2208 scroll_timer
.Start ();
2210 // Force the first tick
2211 ScrollTimerTickHandler (null, EventArgs
.Empty
);
2215 document
.PositionCaret(e
.X
+ document
.ViewPortX
, e
.Y
+ document
.ViewPortY
);
2216 if (click_mode
== CaretSelection
.Position
) {
2217 document
.SetSelectionToCaret(false);
2218 document
.DisplayCaret();
2222 //search through link boxes to see if the mouse is over one of them
2224 bool found_link
= false;
2225 foreach (LinkRectangle link
in list_links
) {
2226 if (link
.LinkAreaRectangle
.Contains (e
.X
, e
.Y
)) {
2227 XplatUI
.SetCursor (window
.Handle
, Cursors
.Hand
.handle
);
2230 current_link
= link
;
2235 if (found_link
== false) {
2236 XplatUI
.SetCursor (window
.Handle
, DefaultCursor
.handle
);
2237 current_link
= null;
2241 private void TextBoxBase_FontOrColorChanged (object sender
, EventArgs e
)
2245 if (!IsHandleCreated
) {
2246 delayed_font_or_color_change
= true;
2250 document
.SuspendRecalc ();
2251 // Font changes apply to the whole document
2252 for (int i
= 1; i
<= document
.Lines
; i
++) {
2253 line
= document
.GetLine(i
);
2254 if (LineTag
.FormatText(line
, 1, line
.text
.Length
, Font
, ForeColor
,
2255 Color
.Empty
, FormatSpecified
.Font
| FormatSpecified
.Color
)) {
2256 using (var graphics
= CreateGraphics())
2257 document
.RecalculateDocument (graphics
, line
.LineNo
, line
.LineNo
, false);
2260 document
.ResumeRecalc (false);
2262 // Make sure the caret height is matching the new font height
2263 document
.AlignCaret();
2266 private void ScrollTimerTickHandler (object sender
, EventArgs e
)
2268 Point pt
= Cursor
.Position
;
2270 pt
= PointToClient (pt
);
2272 if (pt
.X
< ClientRectangle
.Left
) {
2273 document
.MoveCaret(CaretDirection
.CharBackNoWrap
);
2274 document
.SetSelectionToCaret(false);
2276 CaretMoved(this, null);
2277 } else if (pt
.X
> ClientRectangle
.Right
) {
2278 document
.MoveCaret(CaretDirection
.CharForwardNoWrap
);
2279 document
.SetSelectionToCaret(false);
2281 CaretMoved(this, null);
2282 } else if (pt
.Y
> ClientRectangle
.Bottom
) {
2283 document
.MoveCaret(CaretDirection
.LineDown
);
2284 document
.SetSelectionToCaret(false);
2286 CaretMoved(this, null);
2287 } else if (pt
.Y
< ClientRectangle
.Top
) {
2288 document
.MoveCaret(CaretDirection
.LineUp
);
2289 document
.SetSelectionToCaret(false);
2291 CaretMoved(this, null);
2295 /// <summary>Ensure the caret is always visible</summary>
2296 internal void CaretMoved (object sender
, EventArgs e
)
2301 if (!IsHandleCreated
|| canvas_width
< 1 || canvas_height
< 1)
2304 document
.MoveCaretToTextTag ();
2305 pos
= document
.Caret
;
2307 //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);
2310 // Horizontal scrolling:
2311 // If the caret moves to the left outside the visible area, we jump the document into view, not just one
2312 // character, but 1/3 of the width of the document
2313 // If the caret moves to the right outside the visible area, we scroll just enough to keep the caret visible
2315 // Handle horizontal scrolling
2316 if (document
.CaretLine
.alignment
== HorizontalAlignment
.Left
) {
2317 // Check if we moved out of view to the left
2318 if (pos
.X
< (document
.ViewPortX
)) {
2320 if ((hscroll
.Value
- document
.ViewPortWidth
/ 3) >= hscroll
.Minimum
) {
2321 hscroll
.SafeValueSet (hscroll
.Value
- document
.ViewPortWidth
/ 3);
2323 hscroll
.Value
= hscroll
.Minimum
;
2325 } while (hscroll
.Value
> pos
.X
);
2328 // Check if we moved out of view to the right
2329 if ((pos
.X
>= (document
.ViewPortWidth
+ document
.ViewPortX
)) && (hscroll
.Value
!= hscroll
.Maximum
)) {
2330 if ((pos
.X
- document
.ViewPortWidth
+ 1) <= hscroll
.Maximum
) {
2331 if (pos
.X
- document
.ViewPortWidth
>= 0) {
2332 hscroll
.SafeValueSet (pos
.X
- document
.ViewPortWidth
+ 1);
2337 hscroll
.Value
= hscroll
.Maximum
;
2340 } else if (document
.CaretLine
.alignment
== HorizontalAlignment
.Right
) {
2341 // hscroll.Value = pos.X;
2343 // if ((pos.X > (this.canvas_width + document.ViewPortX)) && (hscroll.Enabled && (hscroll.Value != hscroll.Maximum))) {
2344 // hscroll.Value = hscroll.Maximum;
2347 // FIXME - implement center cursor alignment
2350 if (Text
.Length
> 0)
2351 RaiseSelectionChanged ();
2353 if (!document
.multiline
)
2356 // Handle vertical scrolling
2357 height
= document
.CaretLine
.Height
+ 1;
2359 if (pos
.Y
< document
.ViewPortY
)
2360 vscroll
.SafeValueSet (pos
.Y
);
2361 if ((pos
.Y
+ height
) > (document
.ViewPortY
+ canvas_height
))
2362 vscroll
.Value
= Math
.Min (vscroll
.Maximum
, pos
.Y
- canvas_height
+ height
);
2365 internal bool Paste (IDataObject clip
, DataFormats
.Format format
, bool obey_length
)
2372 if (format
== null) {
2373 if ((this is RichTextBox
) && clip
.GetDataPresent(DataFormats
.Rtf
)) {
2374 format
= DataFormats
.GetFormat(DataFormats
.Rtf
);
2375 } else if ((this is RichTextBox
) && clip
.GetDataPresent (DataFormats
.Bitmap
)) {
2376 format
= DataFormats
.GetFormat (DataFormats
.Bitmap
);
2377 } else if (clip
.GetDataPresent(DataFormats
.UnicodeText
)) {
2378 format
= DataFormats
.GetFormat(DataFormats
.UnicodeText
);
2379 } else if (clip
.GetDataPresent(DataFormats
.Text
)) {
2380 format
= DataFormats
.GetFormat(DataFormats
.Text
);
2385 if ((format
.Name
== DataFormats
.Rtf
) && !(this is RichTextBox
)) {
2389 if (!clip
.GetDataPresent(format
.Name
)) {
2394 if (format
.Name
== DataFormats
.Rtf
) {
2395 document
.undo
.BeginUserAction (Locale
.GetText ("Paste"));
2396 ((RichTextBox
)this).SelectedRtf
= (string)clip
.GetData(DataFormats
.Rtf
);
2397 document
.undo
.EndUserAction ();
2400 } else if (format
.Name
== DataFormats
.Bitmap
) {
2401 document
.undo
.BeginUserAction (Locale
.GetText ("Paste"));
2402 // document.InsertImage (document.caret.line, document.caret.pos, (Image) clip.GetData (DataFormats.Bitmap));
2403 document
.MoveCaret (CaretDirection
.CharForward
);
2404 document
.undo
.EndUserAction ();
2406 } else if (format
.Name
== DataFormats
.UnicodeText
) {
2407 s
= (string)clip
.GetData(DataFormats
.UnicodeText
);
2408 } else if (format
.Name
== DataFormats
.Text
) {
2409 s
= (string)clip
.GetData(DataFormats
.Text
);
2415 document
.undo
.BeginUserAction (Locale
.GetText ("Paste"));
2416 this.SelectedText
= s
;
2417 document
.undo
.EndUserAction ();
2419 if ((s
.Length
+ (document
.Length
- SelectedText
.Length
)) < max_length
) {
2420 document
.undo
.BeginUserAction (Locale
.GetText ("Paste"));
2421 this.SelectedText
= s
;
2422 document
.undo
.EndUserAction ();
2423 } else if ((document
.Length
- SelectedText
.Length
) < max_length
) {
2424 document
.undo
.BeginUserAction (Locale
.GetText ("Paste"));
2425 this.SelectedText
= s
.Substring (0, max_length
- (document
.Length
- SelectedText
.Length
));
2426 document
.undo
.EndUserAction ();
2434 internal virtual Color
ChangeBackColor (Color backColor
)
2439 internal override bool IsInputCharInternal (char charCode
)
2443 #endregion // Private Methods
2445 #region Private Classes
2446 internal class LinkRectangle
{
2447 private Rectangle link_area_rectangle
;
2448 private LineTag link_tag
;
2450 public LinkRectangle (Rectangle rect
)
2453 link_area_rectangle
= rect
;
2456 public Rectangle LinkAreaRectangle
{
2457 get { return link_area_rectangle; }
2458 set { link_area_rectangle = value; }
2461 public LineTag LinkTag
{
2462 get { return link_tag; }
2463 set { link_tag = value; }
2466 public void Scroll (int x_change
, int y_change
)
2468 link_area_rectangle
.X
+= x_change
;
2469 link_area_rectangle
.Y
+= y_change
;
2474 // This is called just before OnTextChanged is called.
2475 internal virtual void OnTextUpdate ()
2479 protected override void OnTextChanged (EventArgs e
)
2481 base.OnTextChanged (e
);
2484 public virtual int GetLineFromCharIndex (int index
)
2490 document
.CharIndexToLineTag (index
, out line_out
, out tag_out
, out pos
);
2492 return line_out
.LineNo
;
2495 protected override void OnMouseUp (MouseEventArgs mevent
)
2497 base.OnMouseUp (mevent
);