* TreeView.cs: We need to check scrollbar visibility when window
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TreeView.cs
blob667eda24f9cff7339bf719c1f6009c4d90da4c7e
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-2005 Novell, Inc.
22 // Authors:
23 // Jackson Harper (jackson@ximian.com)
24 // Kazuki Oikawa (kazuki@panicode.com)
26 using System;
27 using System.Collections;
28 using System.ComponentModel;
29 using System.ComponentModel.Design;
30 using System.Drawing;
31 using System.Drawing.Drawing2D;
32 using System.Runtime.InteropServices;
34 namespace System.Windows.Forms {
35 [DefaultProperty("Nodes")]
36 [DefaultEvent("AfterSelect")]
37 [Designer("System.Windows.Forms.Design.TreeViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
38 public class TreeView : Control {
39 #region Fields
40 private string path_separator = "\\";
41 private int item_height = -1;
42 private bool sorted;
43 internal TreeNode top_node;
44 internal TreeNode root_node;
45 internal bool nodes_added;
46 private TreeNodeCollection nodes;
48 private TreeViewAction selection_action = TreeViewAction.Unknown;
49 internal TreeNode selected_node = null;
50 private TreeNode focused_node = null;
51 private TreeNode highlighted_node;
52 private Rectangle mouse_rect = Rectangle.Empty;
53 private bool select_mmove = false;
55 private ImageList image_list;
56 private int image_index = -1;
57 private int selected_image_index = -1;
59 private bool full_row_select;
60 private bool hot_tracking;
61 private int indent = 19;
63 private NodeLabelEditEventArgs edit_args;
64 private LabelEditTextBox edit_text_box;
65 internal TreeNode edit_node;
67 private bool checkboxes;
68 private bool label_edit;
69 private bool scrollable = true;
70 private bool show_lines = true;
71 private bool show_root_lines = true;
72 private bool show_plus_minus = true;
73 private bool hide_selection = true;
75 private int max_visible_order = -1;
76 private VScrollBar vbar;
77 private HScrollBar hbar;
78 private bool vbar_bounds_set;
79 private bool hbar_bounds_set;
80 internal int skipped_nodes;
81 internal int hbar_offset;
83 private int update_stack;
84 private bool update_needed;
86 private Pen dash;
87 private StringFormat string_format;
89 private int drag_begin_x = 0;
90 private int drag_begin_y = 0;
91 private long handle_count = 1;
93 #endregion // Fields
95 #region Public Constructors
96 public TreeView ()
98 border_style = BorderStyle.Fixed3D;
99 base.background_color = ThemeEngine.Current.ColorWindow;
100 base.foreground_color = ThemeEngine.Current.ColorWindowText;
102 root_node = new TreeNode (this);
103 root_node.Text = "ROOT NODE";
104 nodes = new TreeNodeCollection (root_node);
105 root_node.SetNodes (nodes);
107 MouseDown += new MouseEventHandler (MouseDownHandler);
108 MouseUp += new MouseEventHandler(MouseUpHandler);
109 MouseMove += new MouseEventHandler(MouseMoveHandler);
110 SizeChanged += new EventHandler (SizeChangedHandler);
111 FontChanged += new EventHandler (FontChangedHandler);
112 LostFocus += new EventHandler (LostFocusHandler);
113 GotFocus += new EventHandler (GotFocusHandler);
114 MouseWheel += new MouseEventHandler(MouseWheelHandler);
115 VisibleChanged += new EventHandler (VisibleChangedHandler);
117 SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
118 #if NET_2_0
119 | ControlStyles.UseTextForAccessibility
120 #endif
121 , false);
123 string_format = new StringFormat ();
124 string_format.LineAlignment = StringAlignment.Center;
125 string_format.Alignment = StringAlignment.Center;
127 vbar = new ImplicitVScrollBar ();
128 hbar = new ImplicitHScrollBar ();
130 vbar.Visible = false;
131 hbar.Visible = false;
132 vbar.ValueChanged += new EventHandler (VScrollBarValueChanged);
133 hbar.ValueChanged += new EventHandler (HScrollBarValueChanged);
135 SuspendLayout ();
136 Controls.AddImplicit (vbar);
137 Controls.AddImplicit (hbar);
138 ResumeLayout ();
141 #endregion // Public Constructors
143 #region Public Instance Properties
144 public override Color BackColor {
145 get { return base.BackColor;}
146 set { base.BackColor = value; }
150 [Browsable(false)]
151 [EditorBrowsable(EditorBrowsableState.Never)]
152 public override Image BackgroundImage {
153 get { return base.BackgroundImage; }
154 set { base.BackgroundImage = value; }
157 [DefaultValue(BorderStyle.Fixed3D)]
158 [DispId(-504)]
159 public BorderStyle BorderStyle {
160 get { return InternalBorderStyle; }
161 set { InternalBorderStyle = value; }
164 [DefaultValue(false)]
165 public bool CheckBoxes {
166 get { return checkboxes; }
167 set {
168 if (value == checkboxes)
169 return;
170 checkboxes = value;
172 // Match a "bug" in the MS implementation where disabling checkboxes
173 // collapses the entire tree, but enabling them does not affect the
174 // state of the tree.
175 if (!checkboxes)
176 root_node.CollapseAllUncheck ();
178 Invalidate ();
182 public override Color ForeColor {
183 get { return base.ForeColor; }
184 set { base.ForeColor = value; }
186 [DefaultValue(false)]
187 public bool FullRowSelect {
188 get { return full_row_select; }
189 set {
190 if (value == full_row_select)
191 return;
192 full_row_select = value;
193 Invalidate ();
196 [DefaultValue(true)]
197 public bool HideSelection {
198 get { return hide_selection; }
199 set {
200 if (hide_selection == value)
201 return;
202 hide_selection = value;
203 Invalidate ();
207 [DefaultValue(false)]
208 public bool HotTracking {
209 get { return hot_tracking; }
210 set { hot_tracking = value; }
213 [DefaultValue(0)]
214 [Editor("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
215 [Localizable(true)]
216 [TypeConverter(typeof(TreeViewImageIndexConverter))]
217 public int ImageIndex {
218 get { return image_index; }
219 set {
220 if (value < -1) {
221 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
222 "'value' must be greater than or equal to 0.");
224 if (image_index == value)
225 return;
226 image_index = value;
227 Invalidate ();
231 [DefaultValue(null)]
232 public ImageList ImageList {
233 get { return image_list; }
234 set {
235 image_list = value;
236 Invalidate ();
240 [Localizable(true)]
241 public int Indent {
242 get { return indent; }
243 set {
244 if (indent == value)
245 return;
246 if (value > 32000) {
247 throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
248 "'Indent' must be less than or equal to 32000");
250 if (value < 0) {
251 throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
252 "'Indent' must be greater than or equal to 0.");
254 indent = value;
255 Invalidate ();
259 [Localizable(true)]
260 public int ItemHeight {
261 get {
262 if (item_height == -1)
263 return FontHeight + 3;
264 return item_height;
266 set {
267 if (value == item_height)
268 return;
269 item_height = value;
270 Invalidate ();
274 internal int ActualItemHeight {
275 get {
276 int res = ItemHeight;
277 if (ImageList != null && ImageList.ImageSize.Height > res)
278 res = ImageList.ImageSize.Height;
279 return res;
283 [DefaultValue(false)]
284 public bool LabelEdit {
285 get { return label_edit; }
286 set { label_edit = value; }
289 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
290 [MergableProperty(false)]
291 [Localizable(true)]
292 public TreeNodeCollection Nodes {
293 get { return nodes; }
296 [DefaultValue("\\")]
297 public string PathSeparator {
298 get { return path_separator; }
299 set { path_separator = value; }
302 [DefaultValue(true)]
303 public bool Scrollable {
304 get { return scrollable; }
305 set {
306 if (scrollable == value)
307 return;
308 scrollable = value;
309 UpdateScrollBars ();
313 [Editor("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
314 [TypeConverter(typeof(TreeViewImageIndexConverter))]
315 [Localizable(true)]
316 [DefaultValue(0)]
317 public int SelectedImageIndex {
318 get { return selected_image_index; }
319 set {
320 if (value < -1) {
321 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
322 "'value' must be greater than or equal to 0.");
324 UpdateNode (SelectedNode);
328 [Browsable(false)]
329 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
330 public TreeNode SelectedNode {
331 get { return selected_node; }
332 set {
333 if (selected_node == value) {
334 selection_action = TreeViewAction.Unknown;
335 return;
338 TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (value, false, selection_action);
339 OnBeforeSelect (e);
341 if (e.Cancel)
342 return;
344 Rectangle invalid = Rectangle.Empty;
346 if (selected_node != null) {
347 invalid = Bloat (selected_node.Bounds);
349 if (focused_node != null) {
350 invalid = Rectangle.Union (invalid,
351 Bloat (focused_node.Bounds));
354 if (value != null)
355 invalid = Rectangle.Union (invalid, Bloat (value.Bounds));
357 highlighted_node = value;
358 selected_node = value;
359 focused_node = value;
361 if (full_row_select) {
362 invalid.X = 0;
363 invalid.Width = ViewportRectangle.Width;
366 if (invalid != Rectangle.Empty)
367 Invalidate (invalid);
369 // We ensure its visible after we update because
370 // scrolling is used for insure visible
371 if (selected_node != null)
372 selected_node.EnsureVisible ();
374 OnAfterSelect (new TreeViewEventArgs (value, TreeViewAction.Unknown));
375 selection_action = TreeViewAction.Unknown;
379 private Rectangle Bloat (Rectangle rect)
381 rect.Y--;
382 rect.X--;
383 rect.Height += 2;
384 rect.Width += 2;
385 return rect;
388 [DefaultValue(true)]
389 public bool ShowLines {
390 get { return show_lines; }
391 set {
392 if (show_lines == value)
393 return;
394 show_lines = value;
395 Invalidate ();
399 [DefaultValue(true)]
400 public bool ShowPlusMinus {
401 get { return show_plus_minus; }
402 set {
403 if (show_plus_minus == value)
404 return;
405 show_plus_minus = value;
406 Invalidate ();
410 [DefaultValue(true)]
411 public bool ShowRootLines {
412 get { return show_root_lines; }
413 set {
414 if (show_root_lines == value)
415 return;
416 show_root_lines = value;
417 Invalidate ();
421 [DefaultValue(false)]
422 public bool Sorted {
423 get { return sorted; }
424 set {
425 if (sorted != value)
426 sorted = value;
427 if (sorted) {
428 Nodes.Sort ();
429 top_node = root_node;
430 RecalculateVisibleOrder (root_node);
431 UpdateScrollBars ();
432 Invalidate ();
437 [Browsable(false)]
438 [EditorBrowsable(EditorBrowsableState.Never)]
439 [Bindable(false)]
440 public override string Text {
441 get { return base.Text; }
442 set { base.Text = value; }
445 [Browsable(false)]
446 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
447 public TreeNode TopNode {
448 get { return top_node; }
451 [Browsable(false)]
452 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
453 public int VisibleCount {
454 get {
455 return ViewportRectangle.Height / ActualItemHeight;
459 #endregion // Public Instance Properties
461 #region Protected Instance Properties
462 protected override CreateParams CreateParams {
463 get {
464 CreateParams cp = base.CreateParams;
465 return cp;
469 protected override Size DefaultSize {
470 get { return new Size (121, 97); }
473 #endregion // Protected Instance Properties
475 #region Public Instance Methods
476 public void BeginUpdate ()
478 update_stack++;
481 public void EndUpdate ()
483 if (update_stack > 1) {
484 update_stack--;
485 } else {
486 update_stack = 0;
487 if (update_needed) {
488 RecalculateVisibleOrder (root_node);
489 UpdateScrollBars ();
490 if (SelectedNode != null)
491 SelectedNode.EnsureVisible ();
492 Invalidate (ViewportRectangle);
493 update_needed = false;
498 public void ExpandAll ()
500 BeginUpdate ();
501 root_node.ExpandAll ();
503 EndUpdate ();
505 // Walk all the way to the end, then walk back visible count
506 //to find the new top node
507 OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (root_node);
508 while (walk.MoveNext ())
511 for (int i = 0; i < VisibleCount - 1; i++)
512 walk.MovePrevious ();
514 SetTop (walk.CurrentNode);
518 public void CollapseAll ()
520 TreeNode walk = top_node;
522 while (walk.parent != root_node)
523 walk = walk.parent;
525 BeginUpdate ();
526 root_node.CollapseAll ();
527 EndUpdate ();
529 SetTop (walk);
532 public TreeNode GetNodeAt (Point pt) {
533 return GetNodeAt (pt.Y);
536 public TreeNode GetNodeAt (int x, int y)
538 return GetNodeAt (y);
541 private TreeNode GetNodeAtUseX (int x, int y) {
542 TreeNode node = GetNodeAt (y);
543 if (node == null || !(IsTextArea (node, x) || full_row_select))
544 return null;
545 return node;
549 public int GetNodeCount (bool include_subtrees) {
550 return root_node.GetNodeCount (include_subtrees);
553 public override string ToString () {
554 int count = Nodes.Count;
555 if (count <= 0)
556 return String.Concat (base.ToString (), "Node Count: 0");
557 return String.Concat (base.ToString (), "Node Count: ", count, " Nodes[0]: ", Nodes [0]);
561 #endregion // Public Instance Methods
563 #region Protected Instance Methods
564 protected override void CreateHandle () {
565 base.CreateHandle ();
567 UpdateScrollBars ();
570 protected override void Dispose (bool disposing) {
571 if (disposing)
572 image_list = null;
574 base.Dispose (disposing);
577 protected OwnerDrawPropertyBag GetItemRenderStyles (TreeNode node, int state) {
578 return node.prop_bag;
581 protected override bool IsInputKey (Keys key_data)
583 if ((key_data & Keys.Alt) == 0) {
584 switch (key_data & Keys.KeyCode) {
585 case Keys.Enter:
586 case Keys.Escape:
587 case Keys.Prior:
588 case Keys.Next:
589 case Keys.End:
590 case Keys.Home:
591 case Keys.Left:
592 case Keys.Up:
593 case Keys.Right:
594 case Keys.Down:
595 return true;
598 return base.IsInputKey (key_data);
601 protected override void OnKeyDown (KeyEventArgs e)
603 OpenTreeNodeEnumerator ne;
605 switch (e.KeyData & Keys.KeyCode) {
606 case Keys.Add:
607 if (selected_node != null && selected_node.IsExpanded)
608 selected_node.Expand ();
609 break;
610 case Keys.Subtract:
611 if (selected_node != null && selected_node.IsExpanded)
612 selected_node.Collapse ();
613 break;
614 case Keys.Left:
615 if (selected_node != null) {
616 if (selected_node.IsExpanded)
617 selected_node.Collapse ();
618 else {
619 TreeNode parent = selected_node.Parent;
620 if (parent != null) {
621 selection_action = TreeViewAction.ByKeyboard;
622 SelectedNode = parent;
626 break;
627 case Keys.Right:
628 if (selected_node != null) {
629 if (!selected_node.IsExpanded)
630 selected_node.Expand ();
631 else {
632 TreeNode child = selected_node.FirstNode;
633 if (child != null)
634 SelectedNode = child;
637 break;
638 case Keys.Up:
639 if (selected_node != null) {
640 ne = new OpenTreeNodeEnumerator (selected_node);
641 if (ne.MovePrevious () && ne.MovePrevious ()) {
642 selection_action = TreeViewAction.ByKeyboard;
643 SelectedNode = ne.CurrentNode;
646 break;
647 case Keys.Down:
648 if (selected_node != null) {
649 ne = new OpenTreeNodeEnumerator (selected_node);
650 if (ne.MoveNext () && ne.MoveNext ()) {
651 selection_action = TreeViewAction.ByKeyboard;
652 SelectedNode = ne.CurrentNode;
655 break;
656 case Keys.Home:
657 if (root_node.Nodes.Count > 0) {
658 ne = new OpenTreeNodeEnumerator (root_node.Nodes [0]);
659 if (ne.MoveNext ()) {
660 selection_action = TreeViewAction.ByKeyboard;
661 SelectedNode = ne.CurrentNode;
664 break;
665 case Keys.End:
666 if (root_node.Nodes.Count > 0) {
667 ne = new OpenTreeNodeEnumerator (root_node.Nodes [0]);
668 while (ne.MoveNext ())
670 selection_action = TreeViewAction.ByKeyboard;
671 SelectedNode = ne.CurrentNode;
673 break;
674 case Keys.PageDown:
675 if (selected_node != null) {
676 ne = new OpenTreeNodeEnumerator (selected_node);
677 int move = VisibleCount;
678 for (int i = 0; i < move && ne.MoveNext (); i++) {
681 selection_action = TreeViewAction.ByKeyboard;
682 SelectedNode = ne.CurrentNode;
684 break;
685 case Keys.PageUp:
686 if (selected_node != null) {
687 ne = new OpenTreeNodeEnumerator (selected_node);
688 int move = VisibleCount;
689 for (int i = 0; i < move && ne.MovePrevious (); i++)
691 selection_action = TreeViewAction.ByKeyboard;
692 SelectedNode = ne.CurrentNode;
694 break;
695 case Keys.Multiply:
696 if (selected_node != null)
697 selected_node.ExpandAll ();
698 break;
700 base.OnKeyDown (e);
702 if (!e.Handled && checkboxes &&
703 selected_node != null &&
704 (e.KeyData & Keys.KeyCode) == Keys.Space) {
705 selected_node.check_reason = TreeViewAction.ByKeyboard;
706 selected_node.Checked = !selected_node.Checked;
707 e.Handled = true;
711 protected override void OnKeyPress (KeyPressEventArgs e)
713 base.OnKeyPress (e);
714 if (e.KeyChar == ' ')
715 e.Handled = true;
718 protected override void OnKeyUp (KeyEventArgs e)
720 base.OnKeyUp (e);
721 if ((e.KeyData & Keys.KeyCode) == Keys.Space)
722 e.Handled = true;
725 protected virtual void OnItemDrag (ItemDragEventArgs e)
727 ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
728 if (eh != null)
729 eh (this, e);
732 protected internal virtual void OnAfterCheck (TreeViewEventArgs e) {
733 TreeViewEventHandler eh = (TreeViewEventHandler)(Events [AfterCheckEvent]);
734 if (eh != null)
735 eh (this, e);
738 protected internal virtual void OnAfterCollapse (TreeViewEventArgs e) {
739 TreeViewEventHandler eh = (TreeViewEventHandler)(Events [AfterCollapseEvent]);
740 if (eh != null)
741 eh (this, e);
744 protected internal virtual void OnAfterExpand (TreeViewEventArgs e) {
745 TreeViewEventHandler eh = (TreeViewEventHandler)(Events [AfterExpandEvent]);
746 if (eh != null)
747 eh (this, e);
750 protected virtual void OnAfterLabelEdit (NodeLabelEditEventArgs e) {
751 NodeLabelEditEventHandler eh = (NodeLabelEditEventHandler)(Events [AfterLabelEditEvent]);
752 if (eh != null)
753 eh (this, e);
756 protected virtual void OnAfterSelect (TreeViewEventArgs e) {
757 TreeViewEventHandler eh = (TreeViewEventHandler)(Events [AfterSelectEvent]);
758 if (eh != null)
759 eh (this, e);
762 protected internal virtual void OnBeforeCheck (TreeViewCancelEventArgs e) {
763 TreeViewCancelEventHandler eh = (TreeViewCancelEventHandler)(Events [BeforeCheckEvent]);
764 if (eh != null)
765 eh (this, e);
768 protected internal virtual void OnBeforeCollapse (TreeViewCancelEventArgs e) {
769 TreeViewCancelEventHandler eh = (TreeViewCancelEventHandler)(Events [BeforeCollapseEvent]);
770 if (eh != null)
771 eh (this, e);
774 protected internal virtual void OnBeforeExpand (TreeViewCancelEventArgs e) {
775 TreeViewCancelEventHandler eh = (TreeViewCancelEventHandler)(Events [BeforeExpandEvent]);
776 if (eh != null)
777 eh (this, e);
780 protected virtual void OnBeforeLabelEdit (NodeLabelEditEventArgs e) {
781 NodeLabelEditEventHandler eh = (NodeLabelEditEventHandler)(Events [BeforeLabelEditEvent]);
782 if (eh != null)
783 eh (this, e);
786 protected virtual void OnBeforeSelect (TreeViewCancelEventArgs e) {
787 TreeViewCancelEventHandler eh = (TreeViewCancelEventHandler)(Events [BeforeSelectEvent]);
788 if (eh != null)
789 eh (this, e);
792 protected override void OnHandleCreated (EventArgs e) {
793 base.OnHandleCreated (e);
796 protected override void OnHandleDestroyed (EventArgs e) {
797 base.OnHandleDestroyed (e);
800 protected override void WndProc(ref Message m) {
801 switch ((Msg) m.Msg) {
803 case Msg.WM_LBUTTONDBLCLK:
804 int val = m.LParam.ToInt32();
805 DoubleClickHandler (null, new
806 MouseEventArgs (MouseButtons.Left,
807 2, val & 0xffff,
808 (val>>16) & 0xffff, 0));
809 break;
811 base.WndProc (ref m);
814 #endregion // Protected Instance Methods
816 #region Internal & Private Methods and Properties
817 internal IntPtr CreateNodeHandle ()
819 return (IntPtr) handle_count++;
822 internal TreeNode NodeFromHandle (IntPtr handle)
824 // This method is called rarely, so instead of maintaining a table
825 // we just walk the tree nodes to find the matching handle
826 return NodeFromHandleRecursive (root_node, handle);
829 private TreeNode NodeFromHandleRecursive (TreeNode node, IntPtr handle)
831 if (node.handle == handle)
832 return node;
833 foreach (TreeNode child in node.Nodes) {
834 TreeNode match = NodeFromHandleRecursive (child, handle);
835 if (match != null)
836 return match;
838 return null;
841 internal Rectangle ViewportRectangle {
842 get {
843 Rectangle res = ClientRectangle;
845 if (vbar != null && vbar.Visible)
846 res.Width -= vbar.Width;
847 if (hbar != null && hbar.Visible)
848 res.Height -= hbar.Height;
849 return res;
853 private TreeNode GetNodeAt (int y)
855 if (nodes.Count <= 0)
856 return null;
858 if (top_node == null)
859 top_node = nodes [0];
861 OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (TopNode);
862 int move = y / ActualItemHeight;
863 for (int i = -1; i < move; i++) {
864 if (!o.MoveNext ())
865 return null;
868 return o.CurrentNode;
871 private bool IsTextArea (TreeNode node, int x)
873 return node != null && node.Bounds.Left <= x && node.Bounds.Right >= x;
876 private bool IsSelectableArea (TreeNode node, int x)
878 if (node == null)
879 return false;
880 int l = node.Bounds.Left;
881 if (ImageList != null)
882 l -= ImageList.ImageSize.Width;
883 return l <= x && node.Bounds.Right >= x;
887 private bool IsPlusMinusArea (TreeNode node, int x)
889 if (node.Nodes.Count == 0 || (node.parent == root_node && !show_root_lines))
890 return false;
892 int l = node.Bounds.Left + 5;
894 if (show_root_lines || node.Parent != null)
895 l -= indent;
896 if (ImageList != null)
897 l -= ImageList.ImageSize.Width + 3;
898 if (checkboxes)
899 l -= 19;
900 return (x > l && x < l + 8);
903 private bool IsCheckboxArea (TreeNode node, int x)
905 int l = CheckBoxLeft (node);
906 return (x > l && x < l + 10);
909 private int CheckBoxLeft (TreeNode node)
911 int l = node.Bounds.Left + 5;
913 if (show_root_lines || node.Parent != null)
914 l -= indent;
915 if (ImageList != null)
916 l -= ImageList.ImageSize.Width + 3;
918 return l;
921 internal void RecalculateVisibleOrder (TreeNode start)
923 if (update_stack > 0)
924 return;
926 int order;
927 if (start == null) {
928 start = root_node;
929 order = 0;
930 } else
931 order = start.visible_order;
933 OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (start);
934 while (walk.MoveNext ()) {
935 walk.CurrentNode.visible_order = order;
936 order++;
939 max_visible_order = order;
942 internal void SetTop (TreeNode node)
944 if (!vbar.is_visible)
945 return;
947 TreeNode first = root_node.FirstNode;
949 if (first == null)
950 return; // I don't think its possible for this to happen
952 OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (first);
953 int offset = -1;
954 int vc = VisibleCount;
955 while (walk.CurrentNode != node && walk.MoveNext () && offset < vbar.Maximum - vc)
956 offset++;
958 SetVScrollTop (walk.CurrentNode);
961 internal void SetBottom (TreeNode node)
963 if (!vbar.is_visible)
964 return;
966 OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (node);
968 int bottom = ViewportRectangle.Bottom;
969 int offset = 0;
970 while (walk.MovePrevious ()) {
971 if (walk.CurrentNode.Bounds.Bottom <= bottom)
972 break;
973 offset++;
976 int nv = vbar.Value + offset;
977 if (vbar.Value + offset < vbar.Maximum) {
978 vbar.Value = nv;
979 } else {
980 #if DEBUG
981 Console.Error.WriteLine ("setting bottom to value greater then maximum ({0}, {1})",
982 nv, vbar.Maximum);
983 #endif
988 internal void UpdateBelow (TreeNode node)
990 if (update_stack > 0) {
991 update_needed = true;
992 return;
995 if (node == root_node) {
996 Invalidate (ViewportRectangle);
997 return;
1000 // We need to update the current node so the plus/minus block gets update too
1001 int top = Math.Max (node.Bounds.Top - 1, 0);
1002 Rectangle invalid = new Rectangle (0, top,
1003 Width, Height - top);
1004 Invalidate (invalid);
1007 internal void UpdateNode (TreeNode node)
1009 if (node == null)
1010 return;
1012 if (update_stack > 0) {
1013 update_needed = true;
1014 return;
1017 if (node == root_node) {
1018 Invalidate ();
1019 return;
1022 Rectangle invalid = new Rectangle (0, node.Bounds.Top - 1, Width,
1023 node.Bounds.Height + 1);
1024 Invalidate (invalid);
1027 internal void UpdateNodePlusMinus (TreeNode node)
1029 if (update_stack > 0) {
1030 update_needed = true;
1031 return;
1034 int l = node.Bounds.Left + 5;
1036 if (show_root_lines || node.Parent != null)
1037 l -= indent;
1038 if (ImageList != null)
1039 l -= ImageList.ImageSize.Width + 3;
1040 if (checkboxes)
1041 l -= 19;
1043 Invalidate (new Rectangle (l, node.Bounds.Top, 8, node.Bounds.Height));
1046 internal override void OnPaintInternal (PaintEventArgs pe)
1048 Draw (pe.ClipRectangle, pe.Graphics);
1051 private void Draw (Rectangle clip, Graphics dc)
1053 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), clip);
1055 Color dash_color = ControlPaint.Dark (BackColor);
1056 if (dash_color == BackColor)
1057 dash_color = ControlPaint.Light (BackColor);
1058 dash = new Pen (dash_color, 1);
1059 dash.DashStyle = DashStyle.Dot;
1061 Rectangle viewport = ViewportRectangle;
1062 Rectangle original_clip = clip;
1063 if (clip.Bottom > viewport.Bottom)
1064 clip.Height = viewport.Bottom - clip.Top;
1066 OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (TopNode);
1067 while (walk.MoveNext ()) {
1068 TreeNode current = walk.CurrentNode;
1070 // Haven't gotten to visible nodes yet
1071 if (current.GetY () + ActualItemHeight < clip.Top)
1072 continue;
1074 // Past the visible nodes
1075 if (current.GetY () > clip.Bottom)
1076 break;
1078 DrawNode (current, dc, clip);
1081 if (hbar.Visible && vbar.Visible) {
1082 Rectangle corner = new Rectangle (hbar.Right, vbar.Bottom, vbar.Width, hbar.Height);
1083 if (original_clip.IntersectsWith (corner))
1084 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorControl),
1085 corner);
1089 private void DrawNodePlusMinus (TreeNode node, Graphics dc, int x, int middle)
1091 dc.DrawRectangle (SystemPens.ControlDark, x, middle - 4, 8, 8);
1093 if (node.IsExpanded) {
1094 dc.DrawLine (SystemPens.ControlDarkDark, x + 2, middle, x + 6, middle);
1095 } else {
1096 dc.DrawLine (SystemPens.ControlDarkDark, x + 2, middle, x + 6, middle);
1097 dc.DrawLine (SystemPens.ControlDarkDark, x + 4, middle - 2, x + 4, middle + 2);
1101 private void DrawNodeCheckBox (TreeNode node, Graphics dc, int x, int middle)
1103 Pen pen = ThemeEngine.Current.ResPool.GetSizedPen(Color.Black, 2);
1104 dc.DrawRectangle (pen, x + 3, middle - 4, 11, 11);
1106 if (node.Checked) {
1107 Pen check_pen = ThemeEngine.Current.ResPool.GetPen(Color.Black);
1109 int check_size = 5;
1110 int lineWidth = 3;
1112 Rectangle rect = new Rectangle (x + 5, middle - 2, check_size, check_size);
1114 for (int i = 0; i < lineWidth; i++) {
1115 dc.DrawLine (check_pen, rect.Left + 1, rect.Top + lineWidth + i, rect.Left + 3, rect.Top + 5 + i);
1116 dc.DrawLine (check_pen, rect.Left + 3, rect.Top + 5 + i, rect.Left + 7, rect.Top + 1 + i);
1121 private void DrawNodeLines (TreeNode node, Graphics dc, Rectangle clip, Pen dash, int x, int y, int middle)
1123 int ladjust = 9;
1124 int radjust = 0;
1126 if (node.nodes.Count > 0 && show_plus_minus)
1127 ladjust = 13;
1128 if (checkboxes)
1129 radjust = 3;
1131 dc.DrawLine (dash, x - indent + ladjust, middle, x + radjust, middle);
1133 if (node.PrevNode != null || node.Parent != null) {
1134 ladjust = 9;
1135 dc.DrawLine (dash, x - indent + ladjust, node.Bounds.Top,
1136 x - indent + ladjust, middle - (show_plus_minus && node.Nodes.Count > 0 ? 4 : 0));
1139 if (node.NextNode != null) {
1140 ladjust = 9;
1141 dc.DrawLine (dash, x - indent + ladjust, middle + (show_plus_minus && node.Nodes.Count > 0 ? 4 : 0),
1142 x - indent + ladjust, node.Bounds.Bottom);
1146 ladjust = 0;
1147 if (show_plus_minus)
1148 ladjust = 9;
1149 TreeNode parent = node.Parent;
1150 while (parent != null) {
1151 if (parent.NextNode != null) {
1152 int px = parent.GetLinesX () - indent + ladjust;
1153 dc.DrawLine (dash, px, node.Bounds.Top, px, node.Bounds.Bottom);
1155 parent = parent.Parent;
1159 private void DrawNodeImage (TreeNode node, Graphics dc, Rectangle clip, int x, int y)
1161 // Rectangle r = new Rectangle (x, y + 2, ImageList.ImageSize.Width, ImageList.ImageSize.Height);
1163 if (!RectsIntersect (clip, x, y + 2, ImageList.ImageSize.Width, ImageList.ImageSize.Height))
1164 return;
1166 if (ImageList == null)
1167 return;
1169 int use_index = -1;
1170 if (node.ImageIndex > -1 && node.ImageIndex < ImageList.Images.Count) {
1171 use_index = node.ImageIndex;
1172 } else if (ImageIndex > -1 && ImageIndex < ImageList.Images.Count) {
1173 use_index = ImageIndex;
1176 if (use_index == -1 && ImageList.Images.Count > 0) {
1177 use_index = 0;
1180 if (use_index != -1) {
1181 ImageList.Draw (dc, x, y + 2, ImageList.ImageSize.Width,
1182 ImageList.ImageSize.Height, use_index);
1186 private void LabelEditFinished (object sender, EventArgs e)
1188 EndEdit (edit_node);
1191 internal void BeginEdit (TreeNode node)
1193 if (edit_node != null)
1194 EndEdit (edit_node);
1196 if (edit_text_box == null) {
1197 edit_text_box = new LabelEditTextBox ();
1198 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
1199 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
1200 Controls.Add (edit_text_box);
1203 edit_text_box.Bounds = node.Bounds;
1204 edit_text_box.Width += 4;
1206 edit_text_box.Text = node.Text;
1207 edit_text_box.Visible = true;
1208 edit_text_box.Focus ();
1209 edit_text_box.SelectAll ();
1211 edit_args = new NodeLabelEditEventArgs (edit_node);
1212 OnBeforeLabelEdit (edit_args);
1214 if (edit_args.CancelEdit)
1215 EndEdit (node);
1217 edit_node = node;
1220 internal void EndEdit (TreeNode node)
1222 if (edit_text_box != null && edit_text_box.Visible) {
1223 edit_text_box.Visible = false;
1226 if (edit_node != null && edit_node == node) {
1227 OnAfterLabelEdit (edit_args);
1229 if (!edit_args.CancelEdit) {
1230 if (edit_args.Label != null)
1231 edit_node.Text = edit_args.Label;
1232 else
1233 edit_node.Text = edit_text_box.Text;
1239 edit_node = null;
1240 UpdateNode (node);
1243 internal int GetNodeWidth (TreeNode node)
1245 Font font = node.NodeFont;
1246 if (node.NodeFont == null)
1247 font = Font;
1248 return (int) DeviceContext.MeasureString (node.Text, font, 0, string_format).Width + 3;
1251 private void DrawSelectionAndFocus(TreeNode node, Graphics dc, Rectangle r)
1253 if (Focused && focused_node == node) {
1254 ControlPaint.DrawFocusRectangle (dc, r, ForeColor, BackColor);
1256 r.Inflate(-1, -1);
1257 if (Focused && node == highlighted_node) {
1258 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorHighlight), r);
1259 } else if (!hide_selection && node == highlighted_node) {
1260 dc.FillRectangle (SystemBrushes.Control, r);
1261 } else {
1262 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (node.BackColor), r);
1266 private void DrawStaticNode (TreeNode node, Graphics dc)
1268 if (!full_row_select)
1269 DrawSelectionAndFocus(node, dc, node.Bounds);
1271 Font font = node.NodeFont;
1272 if (node.NodeFont == null)
1273 font = Font;
1274 Color text_color = (Focused && node == highlighted_node ?
1275 ThemeEngine.Current.ColorHighlightText : node.ForeColor);
1276 dc.DrawString (node.Text, font,
1277 ThemeEngine.Current.ResPool.GetSolidBrush (text_color),
1278 node.Bounds, string_format);
1281 private void DrawNode (TreeNode node, Graphics dc, Rectangle clip)
1283 int child_count = node.nodes.Count;
1284 int y = node.GetY ();
1285 int middle = y + (ActualItemHeight / 2);
1287 if (full_row_select) {
1288 Rectangle r = new Rectangle (1, y + 2, ViewportRectangle.Width - 2, ActualItemHeight);
1289 DrawSelectionAndFocus (node, dc, r);
1292 if ((show_root_lines || node.Parent != null) && show_plus_minus && child_count > 0)
1293 DrawNodePlusMinus (node, dc, node.GetLinesX () - Indent + 5, middle);
1295 if (checkboxes)
1296 DrawNodeCheckBox (node, dc, CheckBoxLeft (node) - 3, middle);
1298 if (show_lines)
1299 DrawNodeLines (node, dc, clip, dash, node.GetLinesX (), y, middle);
1301 if (ImageList != null)
1302 DrawNodeImage (node, dc, clip, node.GetImageX (), y);
1304 if (!node.IsEditing)
1305 DrawStaticNode (node, dc);
1308 internal void UpdateScrollBars ()
1310 if (IsDisposed || update_stack > 0 || !IsHandleCreated || !Visible)
1311 return;
1313 bool vert = false;
1314 bool horz = false;
1315 int height = -1;
1316 int width = -1;
1318 if (scrollable) {
1319 OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (root_node);
1321 while (walk.MoveNext ()) {
1322 int r = walk.CurrentNode.Bounds.Right;
1323 int b = walk.CurrentNode.Bounds.Bottom;
1325 if (r > width)
1326 width = r;
1327 if (b > height)
1328 height = b;
1331 // Remove scroll adjustments
1332 if (nodes.Count > 0)
1333 height -= nodes [0].Bounds.Top;
1334 width += hbar_offset;
1336 if (height > ClientRectangle.Height) {
1337 vert = true;
1339 if (width > ClientRectangle.Width - SystemInformation.VerticalScrollBarWidth)
1340 horz = true;
1341 } else if (width > ClientRectangle.Width) {
1342 horz = true;
1345 if (!vert && horz && height > ClientRectangle.Height - SystemInformation.HorizontalScrollBarHeight)
1346 vert = true;
1349 if (vert) {
1350 int visible_height = horz ? ClientRectangle.Height - hbar.Height : ClientRectangle.Height;
1351 vbar.SetValues (Math.Max (0, max_visible_order - 2), visible_height / ActualItemHeight);
1353 vbar.Maximum = max_visible_order;
1354 vbar.LargeChange = ClientRectangle.Height / ItemHeight;
1357 if (!vbar_bounds_set) {
1358 vbar.Bounds = new Rectangle (ClientRectangle.Width - vbar.Width, 0, vbar.Width,
1359 ClientRectangle.Height -
1360 (horz ? SystemInformation.VerticalScrollBarWidth : 0));
1361 vbar_bounds_set = true;
1363 // We need to recalc the hbar if the vbar is now visible
1364 hbar_bounds_set = false;
1367 vbar.Visible = true;
1368 } else {
1369 skipped_nodes = 0;
1370 top_node = root_node.FirstNode;
1371 RecalculateVisibleOrder (root_node);
1372 vbar.Visible = false;
1373 vbar_bounds_set = false;
1376 if (horz) {
1377 hbar.SetValues (width + 1, ClientRectangle.Width - (vert ? SystemInformation.VerticalScrollBarWidth : 0));
1379 hbar.LargeChange = ClientRectangle.Width;
1380 hbar.Maximum = width + 1;
1383 if (!hbar_bounds_set) {
1384 hbar.Bounds = new Rectangle (0, ClientRectangle.Height - hbar.Height,
1385 ClientRectangle.Width - (vert ? SystemInformation.VerticalScrollBarWidth : 0),
1386 hbar.Height);
1387 hbar_bounds_set = true;
1389 hbar.Visible = true;
1390 } else {
1391 hbar_offset = 0;
1392 hbar.Visible = false;
1393 hbar_bounds_set = false;
1397 private void SizeChangedHandler (object sender, EventArgs e)
1399 if (IsHandleCreated) {
1400 if (max_visible_order == -1)
1401 RecalculateVisibleOrder (root_node);
1402 UpdateScrollBars ();
1405 if (vbar.Visible) {
1406 vbar.Bounds = new Rectangle (ClientRectangle.Width - vbar.Width, 0, vbar.Width,
1407 ClientRectangle.Height - (hbar.Visible ? SystemInformation.HorizontalScrollBarHeight : 0));
1410 if (hbar.Visible) {
1411 hbar.Bounds = new Rectangle (0, ClientRectangle.Height - hbar.Height,
1412 ClientRectangle.Width - (vbar.Visible ? SystemInformation.VerticalScrollBarWidth : 0), hbar.Height);
1416 private void VScrollBarValueChanged (object sender, EventArgs e)
1418 EndEdit (edit_node);
1420 SetVScrollPos (vbar.Value, null);
1423 private void SetVScrollPos (int pos, TreeNode new_top)
1425 if (pos < 0)
1426 pos = 0;
1428 if (skipped_nodes == pos)
1429 return;
1431 int old_skip = skipped_nodes;
1432 skipped_nodes = pos;
1433 int diff = old_skip - skipped_nodes;
1435 // Determine the new top node if we have to
1436 if (new_top == null) {
1437 if (top_node == null)
1438 top_node = nodes [0];
1440 OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (TopNode);
1441 if (diff < 0) {
1442 for (int i = diff; i <= 0; i++)
1443 walk.MoveNext ();
1444 new_top = walk.CurrentNode;
1445 } else {
1446 for (int i = 0; i <= diff; i++)
1447 walk.MovePrevious ();
1448 new_top = walk.CurrentNode;
1452 top_node = new_top;
1453 int y_move = diff * ActualItemHeight;
1454 XplatUI.ScrollWindow (Handle, ViewportRectangle, 0, y_move, false);
1457 private void SetVScrollTop (TreeNode new_top)
1459 OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (root_node);
1461 skipped_nodes = -1;
1462 while (walk.MoveNext () && walk.CurrentNode != new_top)
1463 skipped_nodes++;
1465 top_node = new_top;
1466 vbar.Value = skipped_nodes;
1469 private void HScrollBarValueChanged(object sender, EventArgs e)
1471 EndEdit (edit_node);
1473 int old_offset = hbar_offset;
1474 hbar_offset = hbar.Value;
1476 if (hbar_offset < 0) {
1477 hbar_offset = 0;
1480 XplatUI.ScrollWindow (Handle, ViewportRectangle, old_offset - hbar_offset, 0, false);
1483 internal void ExpandBelow (TreeNode node, int count_to_next)
1485 if (update_stack > 0) {
1486 update_needed = true;
1487 return;
1490 Rectangle below = new Rectangle (0, node.Bounds.Bottom + 2, ViewportRectangle.Width,
1491 ViewportRectangle.Height - node.Bounds.Bottom);
1493 int amount = count_to_next * ActualItemHeight;
1495 if (amount > 0)
1496 XplatUI.ScrollWindow (Handle, below, 0, amount, false);
1498 if (show_plus_minus) {
1499 //int linesx = node.GetLinesX ();
1500 Invalidate (new Rectangle (0, node.GetY (), Width, ActualItemHeight));
1504 internal void CollapseBelow (TreeNode node, int count_to_next)
1506 if (update_stack > 0) {
1507 update_needed = true;
1508 return;
1511 Rectangle below = new Rectangle (0, node.Bounds.Bottom + 2, ViewportRectangle.Width,
1512 ViewportRectangle.Height - node.Bounds.Bottom + 2);
1514 int amount = count_to_next * ActualItemHeight;
1516 if (amount > 0)
1517 XplatUI.ScrollWindow (Handle, below, 0, -amount, false);
1519 if (show_plus_minus) {
1520 //int linesx = node.GetLinesX ();
1521 Invalidate (new Rectangle (0, node.GetY (), Width, ActualItemHeight));
1525 private void MouseWheelHandler(object sender, MouseEventArgs e) {
1527 if (vbar == null || !vbar.is_visible) {
1528 return;
1531 if (e.Delta < 0) {
1532 vbar.Value = Math.Min(vbar.Value + SystemInformation.MouseWheelScrollLines, vbar.Maximum - VisibleCount + 1);
1533 } else {
1534 vbar.Value = Math.Max(0, vbar.Value - SystemInformation.MouseWheelScrollLines);
1538 private void VisibleChangedHandler (object sender, EventArgs e)
1540 if (Visible) {
1541 UpdateScrollBars ();
1545 private void FontChangedHandler (object sender, EventArgs e)
1547 InvalidateNodeWidthRecursive (root_node);
1550 private void InvalidateNodeWidthRecursive (TreeNode node)
1552 node.InvalidateWidth ();
1553 foreach (TreeNode child in node.Nodes) {
1554 InvalidateNodeWidthRecursive (child);
1558 private void GotFocusHandler (object sender, EventArgs e)
1560 if (selected_node == null)
1561 SelectedNode = top_node;
1562 else
1563 UpdateNode (selected_node);
1566 private void LostFocusHandler (object sender, EventArgs e)
1568 UpdateNode (SelectedNode);
1571 private void MouseDownHandler (object sender, MouseEventArgs e)
1573 if (e.Button == MouseButtons.Right)
1574 Focus ();
1576 TreeNode node = GetNodeAt (e.Y);
1577 if (node == null)
1578 return;
1580 if (show_plus_minus && IsPlusMinusArea (node, e.X)) {
1581 node.Toggle ();
1582 return;
1583 } else if (checkboxes && IsCheckboxArea (node, e.X)) {
1584 node.check_reason = TreeViewAction.ByMouse;
1585 node.Checked = !node.Checked;
1586 UpdateNode(node);
1587 return;
1588 } else if (IsSelectableArea (node, e.X) || full_row_select) {
1589 TreeNode old_highlighted = highlighted_node;
1590 highlighted_node = node;
1591 if (label_edit && e.Clicks == 1 && highlighted_node == old_highlighted) {
1592 BeginEdit (node);
1593 } else if (highlighted_node != focused_node) {
1594 Size ds = SystemInformation.DragSize;
1595 mouse_rect.X = e.X - ds.Width;
1596 mouse_rect.Y = e.Y - ds.Height;
1597 mouse_rect.Width = ds.Width * 2;
1598 mouse_rect.Height = ds.Height * 2;
1600 select_mmove = true;
1603 Invalidate (highlighted_node.Bounds);
1604 if (old_highlighted != null)
1605 Invalidate (Bloat (old_highlighted.Bounds));
1609 private void MouseUpHandler (object sender, MouseEventArgs e) {
1611 drag_begin_x = -1;
1612 drag_begin_y = -1;
1614 if (!select_mmove)
1615 return;
1617 if (e.Button == MouseButtons.Right) {
1618 Invalidate (highlighted_node.Bounds);
1619 highlighted_node = selected_node;
1620 Invalidate (selected_node.Bounds);
1622 select_mmove = false;
1624 TreeViewCancelEventArgs ce = new TreeViewCancelEventArgs (highlighted_node, false, TreeViewAction.ByMouse);
1625 OnBeforeSelect (ce);
1627 Rectangle invalid;
1628 if (!ce.Cancel) {
1629 if (focused_node != null) {
1630 invalid = Rectangle.Union (Bloat (focused_node.Bounds),
1631 Bloat (highlighted_node.Bounds));
1632 } else {
1633 invalid = Bloat (highlighted_node.Bounds);
1635 selected_node = highlighted_node;
1636 focused_node = highlighted_node;
1637 OnAfterSelect (new TreeViewEventArgs (selected_node, TreeViewAction.ByMouse));
1639 Invalidate (invalid);
1640 } else {
1641 highlighted_node = focused_node;
1642 selected_node = focused_node;
1646 private void MouseMoveHandler (object sender, MouseEventArgs e) {
1648 if (e.Button == MouseButtons.Left || e.Button == MouseButtons.Right) {
1649 if (drag_begin_x == -1 && drag_begin_y == -1) {
1650 drag_begin_x = e.X;
1651 drag_begin_y = e.Y;
1652 } else {
1653 double rise = Math.Pow (drag_begin_x - e.X, 2);
1654 double run = Math.Pow (drag_begin_y - e.Y, 2);
1655 double move = Math.Sqrt (rise + run);
1656 if (move > 3) {
1657 TreeNode drag = GetNodeAtUseX (e.X, e.Y);
1659 if (drag != null) {
1660 OnItemDrag (new ItemDragEventArgs (e.Button, drag));
1662 drag_begin_x = -1;
1663 drag_begin_y = -1;
1669 // If there is enough movement before the mouse comes up,
1670 // selection is reverted back to the originally selected node
1671 if (!select_mmove || mouse_rect.Contains (e.X, e.Y))
1672 return;
1674 Invalidate (highlighted_node.Bounds);
1675 Invalidate (selected_node.Bounds);
1676 Invalidate (focused_node.Bounds);
1678 highlighted_node = selected_node;
1679 focused_node = selected_node;
1681 select_mmove = false;
1684 private void DoubleClickHandler (object sender, MouseEventArgs e) {
1685 TreeNode node = GetNodeAtUseX (e.X,e.Y);
1686 if(node != null) {
1687 node.Toggle();
1692 private bool RectsIntersect (Rectangle r, int left, int top, int width, int height)
1694 return !((r.Left > left + width) || (r.Right < left) ||
1695 (r.Top > top + height) || (r.Bottom < top));
1698 #endregion // Internal & Private Methods and Properties
1700 #region Events
1701 static object ItemDragEvent = new object ();
1702 static object AfterCheckEvent = new object ();
1703 static object AfterCollapseEvent = new object ();
1704 static object AfterExpandEvent = new object ();
1705 static object AfterLabelEditEvent = new object ();
1706 static object AfterSelectEvent = new object ();
1707 static object BeforeCheckEvent = new object ();
1708 static object BeforeCollapseEvent = new object ();
1709 static object BeforeExpandEvent = new object ();
1710 static object BeforeLabelEditEvent = new object ();
1711 static object BeforeSelectEvent = new object ();
1713 public event ItemDragEventHandler ItemDrag {
1714 add { Events.AddHandler (ItemDragEvent, value); }
1715 remove { Events.RemoveHandler (ItemDragEvent, value); }
1718 public event TreeViewEventHandler AfterCheck {
1719 add { Events.AddHandler (AfterCheckEvent, value); }
1720 remove { Events.RemoveHandler (AfterCheckEvent, value); }
1723 public event TreeViewEventHandler AfterCollapse {
1724 add { Events.AddHandler (AfterCollapseEvent, value); }
1725 remove { Events.RemoveHandler (AfterCollapseEvent, value); }
1728 public event TreeViewEventHandler AfterExpand {
1729 add { Events.AddHandler (AfterExpandEvent, value); }
1730 remove { Events.RemoveHandler (AfterExpandEvent, value); }
1733 public event NodeLabelEditEventHandler AfterLabelEdit {
1734 add { Events.AddHandler (AfterLabelEditEvent, value); }
1735 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
1738 public event TreeViewEventHandler AfterSelect {
1739 add { Events.AddHandler (AfterSelectEvent, value); }
1740 remove { Events.RemoveHandler (AfterSelectEvent, value); }
1743 public event TreeViewCancelEventHandler BeforeCheck {
1744 add { Events.AddHandler (BeforeCheckEvent, value); }
1745 remove { Events.RemoveHandler (BeforeCheckEvent, value); }
1748 public event TreeViewCancelEventHandler BeforeCollapse {
1749 add { Events.AddHandler (BeforeCollapseEvent, value); }
1750 remove { Events.RemoveHandler (BeforeCollapseEvent, value); }
1753 public event TreeViewCancelEventHandler BeforeExpand {
1754 add { Events.AddHandler (BeforeExpandEvent, value); }
1755 remove { Events.RemoveHandler (BeforeExpandEvent, value); }
1758 public event NodeLabelEditEventHandler BeforeLabelEdit {
1759 add { Events.AddHandler (BeforeLabelEditEvent, value); }
1760 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
1763 public event TreeViewCancelEventHandler BeforeSelect {
1764 add { Events.AddHandler (BeforeSelectEvent, value); }
1765 remove { Events.RemoveHandler (BeforeSelectEvent, value); }
1768 [Browsable (false)]
1769 [EditorBrowsable (EditorBrowsableState.Never)]
1770 public new event EventHandler BackgroundImageChanged {
1771 add { base.BackgroundImageChanged += value; }
1772 remove { base.BackgroundImageChanged -= value; }
1775 [EditorBrowsable (EditorBrowsableState.Never)]
1776 [Browsable (false)]
1777 public new event PaintEventHandler Paint {
1778 add { base.Paint += value; }
1779 remove { base.Paint -= value; }
1782 [EditorBrowsable (EditorBrowsableState.Never)]
1783 [Browsable (false)]
1784 public new event EventHandler TextChanged {
1785 add { base.TextChanged += value; }
1786 remove { base.TextChanged -= value; }
1788 #endregion // Events