Fix bug #395
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TreeNode.cs
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.
11 //
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.
19 //
20 // Copyright (c) 2004-2005 Novell, Inc.
21 //
22 // Authors:
23 //      Jackson Harper (jackson@ximian.com)
24 //      Kazuki Oikawa (kazuki@panicode.com)
25
26 using System;
27 using System.ComponentModel;
28 using System.Drawing;
29 using System.Runtime.Serialization;
30 using System.Text;
31
32 namespace System.Windows.Forms
33 {
34         [DefaultProperty ("Text")]
35         [TypeConverter(typeof(TreeNodeConverter))]
36         [Serializable]
37         public class TreeNode : MarshalByRefObject, ICloneable, ISerializable
38         {
39                 #region Fields
40                 private TreeView tree_view;
41                 internal TreeNode parent;
42
43                 private string text;
44                 private int image_index = -1;
45                 private int selected_image_index = -1;
46                 private ContextMenu context_menu;
47                 private ContextMenuStrip context_menu_strip;
48                 private string image_key = String.Empty;
49                 private string selected_image_key = String.Empty;
50                 private int state_image_index = -1;
51                 private string state_image_key = String.Empty;
52                 private string tool_tip_text = String.Empty;
53                 internal TreeNodeCollection nodes;
54                 internal TreeViewAction check_reason = TreeViewAction.Unknown;
55
56                 internal int visible_order = 0;
57                 internal int width = -1;
58                 
59                 internal bool is_expanded = false;
60                 private bool check;
61                 internal OwnerDrawPropertyBag prop_bag;
62
63                 private object tag;
64
65                 internal IntPtr handle;
66                 
67                 private string name = string.Empty;
68                 #endregion      // Fields
69
70                 #region Internal Constructors
71                 internal TreeNode (TreeView tree_view) : this ()
72                 {
73                         this.tree_view = tree_view;
74                         is_expanded = true;
75                 }
76
77                 protected TreeNode (SerializationInfo serializationInfo, StreamingContext context) : this ()
78                 {
79                         SerializationInfoEnumerator     en;
80                         SerializationEntry              e;
81                         int                             children;
82
83                         en = serializationInfo.GetEnumerator();
84                         children = 0;
85                         while (en.MoveNext()) {
86                                 e = en.Current;
87                                 switch(e.Name) {
88                                         case "Text": Text = (string)e.Value; break;
89                                         case "PropBag": prop_bag = (OwnerDrawPropertyBag)e.Value; break;
90                                         case "ImageIndex": image_index = (int)e.Value; break;
91                                         case "SelectedImageIndex": selected_image_index = (int)e.Value; break;
92                                         case "Tag": tag = e.Value; break;
93                                         case "IsChecked": check = (bool)e.Value; break;
94                                         case "ChildCount": children = (int)e.Value; break;
95                                 }
96                         }
97                         if (children > 0) {
98                                 for (int i = 0; i < children; i++) {
99                                         TreeNode node = (TreeNode) serializationInfo.GetValue ("children" + i, typeof (TreeNode));
100                                         Nodes.Add (node);
101                                 }
102                         }
103                 }
104                 #endregion      // Internal Constructors
105
106                 #region Public Constructors
107                 public TreeNode ()
108                 {
109                         nodes = new TreeNodeCollection (this);
110                 }
111
112                 public TreeNode (string text) : this ()
113                 {
114                         Text = text;
115                 }
116
117                 public TreeNode (string text, TreeNode [] children) : this (text)
118                 {
119                         Nodes.AddRange (children);
120                 }
121
122                 public TreeNode (string text, int imageIndex, int selectedImageIndex) : this (text)
123                 {
124                         this.image_index = imageIndex;
125                         this.selected_image_index = selectedImageIndex;
126                 }
127
128                 public TreeNode (string text, int imageIndex, int selectedImageIndex,
129                                 TreeNode[] children)
130                         : this (text, imageIndex, selectedImageIndex)
131                 {
132                         Nodes.AddRange (children);
133                 }
134
135                 #endregion      // Public Constructors
136
137                 #region ICloneable Members
138                 public virtual object Clone ()
139                 {
140                         TreeNode tn = (TreeNode)Activator.CreateInstance (GetType ());
141                         tn.name = name;
142                         tn.text = text;
143                         tn.image_key = image_key;
144                         tn.image_index = image_index;
145                         tn.selected_image_index = selected_image_index;
146                         tn.selected_image_key = selected_image_key;
147                         tn.state_image_index = state_image_index;
148                         tn.state_image_key = state_image_key;
149                         tn.tag = tag;
150                         tn.check = check;
151                         tn.tool_tip_text = tool_tip_text;
152                         tn.context_menu = context_menu;
153                         tn.context_menu_strip = context_menu_strip;
154                         if (nodes != null) {
155                                 foreach (TreeNode child in nodes)
156                                         tn.nodes.Add ((TreeNode)child.Clone ());
157                         }
158                         if (prop_bag != null)
159                                 tn.prop_bag = OwnerDrawPropertyBag.Copy (prop_bag);
160                         return tn;
161                 }
162
163                 #endregion      // ICloneable Members
164
165                 #region ISerializable Members
166                 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext context)
167                 {
168                         si.AddValue ("Text", Text);
169                         si.AddValue ("prop_bag", prop_bag, typeof (OwnerDrawPropertyBag));
170                         si.AddValue ("ImageIndex", ImageIndex);
171                         si.AddValue ("SelectedImageIndex", SelectedImageIndex);
172                         si.AddValue ("Tag", Tag);
173                         si.AddValue ("Checked", Checked);
174
175                         si.AddValue ("NumberOfChildren", Nodes.Count);
176                         for (int i = 0; i < Nodes.Count; i++)
177                                 si.AddValue ("Child-" + i, Nodes [i], typeof (TreeNode));
178                 }
179
180                 protected virtual void Deserialize (SerializationInfo serializationInfo, StreamingContext context)
181                 {
182                         Text = serializationInfo.GetString ("Text");
183                         prop_bag = (OwnerDrawPropertyBag)serializationInfo.GetValue ("prop_bag", typeof (OwnerDrawPropertyBag));
184                         ImageIndex = serializationInfo.GetInt32 ("ImageIndex");
185                         SelectedImageIndex = serializationInfo.GetInt32 ("SelectedImageIndex");
186                         Tag = serializationInfo.GetValue ("Tag", typeof (Object));
187                         Checked = serializationInfo.GetBoolean ("Checked");
188                         
189                         int count = serializationInfo.GetInt32 ("NumberOfChildren");
190                         
191                         for (int i = 0; i < count; i++)
192                                 Nodes.Add ((TreeNode)serializationInfo.GetValue ("Child-" + i, typeof (TreeNode)));
193                 }
194                 
195                 protected virtual void Serialize (SerializationInfo si,  StreamingContext context)
196                 {
197                         si.AddValue ("Text", Text);
198                         si.AddValue ("prop_bag", prop_bag, typeof (OwnerDrawPropertyBag));
199                         si.AddValue ("ImageIndex", ImageIndex);
200                         si.AddValue ("SelectedImageIndex", SelectedImageIndex);
201                         si.AddValue ("Tag", Tag);
202                         si.AddValue ("Checked", Checked);
203
204                         si.AddValue ("NumberOfChildren", Nodes.Count);
205                         for (int i = 0; i < Nodes.Count; i++)
206                                 si.AddValue ("Child-" + i, Nodes[i], typeof (TreeNode));
207                 }
208                 #endregion      // ISerializable Members
209
210                 #region Public Instance Properties
211                 public Color BackColor {
212                         get {
213                                 if (prop_bag != null)
214                                         return prop_bag.BackColor;
215                                 return Color.Empty;
216                         }
217                         set {
218                                 if (prop_bag == null)
219                                         prop_bag = new OwnerDrawPropertyBag ();
220                                 prop_bag.BackColor = value;
221
222                                 TreeView tree_view = TreeView;
223                                 if (tree_view != null)
224                                         tree_view.UpdateNode (this);
225                         }
226                 }
227
228                 [Browsable (false)]
229                 public Rectangle Bounds {
230                         get {
231                                 if (TreeView == null)
232                                         return Rectangle.Empty;
233
234                                 int x = GetX ();
235                                 int y = GetY ();
236                                 
237                                 if (width == -1)
238                                         width = TreeView.GetNodeWidth (this);
239
240                                 Rectangle res = new Rectangle (x, y, width, TreeView.ActualItemHeight);
241                                 return res;
242                         }
243                 }
244
245                 internal int GetY ()
246                 {
247                         if (TreeView == null)
248                                 return 0;
249                         return (visible_order - 1) * TreeView.ActualItemHeight - (TreeView.skipped_nodes * TreeView.ActualItemHeight);
250                 }
251
252                 internal int GetX ()
253                 {
254                         if (TreeView == null)
255                                 return 0;
256                         int indent_level = IndentLevel;
257                         int roots = (TreeView.ShowRootLines ? 1 : 0);
258                         int cb = (TreeView.CheckBoxes ? 19 : 0);
259                         if (!TreeView.CheckBoxes && StateImage != null)
260                                 cb = 19;
261                         int imgs = (TreeView.ImageList != null ?  TreeView.ImageList.ImageSize.Width + 3 : 0);
262                         return ((indent_level + roots) * TreeView.Indent) + cb + imgs - TreeView.hbar_offset;
263                 }
264
265                 internal int GetLinesX ()
266                 {
267                         int roots = (TreeView.ShowRootLines ? 1 : 0);
268                         return (IndentLevel + roots) * TreeView.Indent - TreeView.hbar_offset;
269                 }
270
271                 internal int GetImageX ()
272                 {
273                         return GetLinesX () + (TreeView.CheckBoxes || StateImage != null ? 19 : 0);
274                 }
275
276                 // In theory we should be able to track this instead of computing
277                 // every single time we need it, however for now I am going to
278                 // do it this way to reduce bugs in my new bounds computing code
279                 internal int IndentLevel {
280                         get {
281                                 TreeNode walk = this;
282                                 int res = 0;
283                                 while (walk.Parent != null) {
284                                         walk = walk.Parent;
285                                         res++;
286                                 }
287
288                                 return res;
289                         }
290                 }
291
292                 [DefaultValue (false)]
293                 public bool Checked {
294                         get { return check; }
295                         set {
296                                 if (check == value)
297                                         return;
298                                 TreeViewCancelEventArgs args = new TreeViewCancelEventArgs (this, false, check_reason);
299                                 if (TreeView != null)
300                                         TreeView.OnBeforeCheck (args);
301                                 if (!args.Cancel) {
302                                         check = value;
303
304                                         // TreeView can become null after OnAfterCheck, this the double null check
305                                         if (TreeView != null)
306                                                 TreeView.OnAfterCheck (new TreeViewEventArgs (this, check_reason));
307                                         if (TreeView != null)
308                                                 TreeView.UpdateNode (this);
309                                 }
310                                 check_reason = TreeViewAction.Unknown;
311                         }
312                 }
313
314                 [DefaultValue (null)]
315                 public virtual ContextMenu ContextMenu {
316                         get { return context_menu; }
317                         set { context_menu = value; }
318                 }
319                 
320                 [DefaultValue (null)]
321                 public virtual ContextMenuStrip ContextMenuStrip {
322                         get { return context_menu_strip; }
323                         set { context_menu_strip = value; }
324                 }
325                 
326                 [Browsable (false)]
327                 public TreeNode FirstNode {
328                         get {
329                                 if (nodes.Count > 0)
330                                         return nodes [0];
331                                 return null;
332                         }
333                 }
334
335                 public Color ForeColor {
336                         get {
337                                 if (prop_bag != null)
338                                         return prop_bag.ForeColor;
339                                 if (TreeView != null)
340                                         return TreeView.ForeColor;
341                                 return Color.Empty;
342                         }
343                         set {
344                                 if (prop_bag == null)
345                                         prop_bag = new OwnerDrawPropertyBag ();
346                                 prop_bag.ForeColor = value;
347
348                                 TreeView tree_view = TreeView;
349                                 if (tree_view != null)
350                                         tree_view.UpdateNode (this);
351                         }
352                 }
353
354                 [Browsable (false)]
355                 public string FullPath {
356                         get {
357                                 if (TreeView == null)
358                                         throw new InvalidOperationException ("No TreeView associated");
359
360                                 StringBuilder builder = new StringBuilder ();
361                                 BuildFullPath (builder);
362                                 return builder.ToString ();
363                         }
364                 }
365
366                 [DefaultValue (-1)]
367                 [RelatedImageList ("TreeView.ImageList")]
368                 [TypeConverter (typeof (TreeViewImageIndexConverter))]
369                 [RefreshProperties (RefreshProperties.Repaint)]
370                 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
371                 [Localizable(true)]
372                 public int ImageIndex {
373                         get { return image_index; }
374                         set {
375                                 if (image_index == value)
376                                         return;
377                                 image_index = value;
378                                 image_key = string.Empty;
379                                 TreeView tree = TreeView;
380                                 if (tree != null)
381                                         tree.UpdateNode (this);
382                         }
383                 }
384
385                 [Localizable(true)]
386                 [DefaultValue ("")]
387                 [RelatedImageList ("TreeView.ImageList")]
388                 [TypeConverter (typeof (TreeViewImageKeyConverter))]
389                 [RefreshProperties (RefreshProperties.Repaint)]
390                 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
391                 public string ImageKey {
392                         get { return image_key; }
393                         set {
394                                 if (image_key == value)
395                                         return;
396                                 image_key = value;
397                                 image_index = -1;
398
399                                 TreeView tree = TreeView;
400                                 if (tree != null)
401                                 tree.UpdateNode(this);
402                         }
403                 }
404
405                 [Browsable (false)]
406                 public bool IsEditing {
407                         get {
408                                 TreeView tv = TreeView;
409                                 if (tv == null)
410                                         return false;
411                                 return tv.edit_node == this;
412                         }
413                 }
414
415                 [Browsable (false)]
416                 public bool IsExpanded {
417                         get {
418                                 TreeView tv = TreeView;
419
420                                 if (tv != null && tv.IsHandleCreated) {
421                                         // This is ridiculous
422                                         bool found = false;
423                                         foreach (TreeNode walk in TreeView.Nodes) {
424                                                 if (walk.Nodes.Count > 0)
425                                                         found = true;
426                                         }
427
428                                         if (!found)
429                                                 return false;
430                                 }
431
432                                 return is_expanded;
433                         }
434                 }
435
436                 [Browsable (false)]
437                 public bool IsSelected {
438                         get {
439                                 if (TreeView == null || !TreeView.IsHandleCreated)
440                                         return false;
441                                 return TreeView.SelectedNode == this;
442                         }
443                 }
444
445                 [Browsable (false)]
446                 public bool IsVisible {
447                         get {
448                                 if (TreeView == null || !TreeView.IsHandleCreated || !TreeView.Visible)
449                                         return false;
450
451                                 if (visible_order <= TreeView.skipped_nodes || visible_order - TreeView.skipped_nodes > TreeView.VisibleCount)
452                                         return false;
453
454                                 return ArePreviousNodesExpanded;
455                         }
456                 }
457
458                 [Browsable (false)]
459                 public TreeNode LastNode {
460                         get {
461                                 return (nodes == null || nodes.Count == 0) ? null : nodes [nodes.Count - 1];
462                         }
463                 }
464
465                 [Browsable (false)]
466                 public int Level {
467                         get { return IndentLevel; }
468                 }
469                 
470                 public string Name
471                 {
472                         get { return this.name; }
473                         set {
474                                 // Value should never be null as per spec
475                                 this.name = (value == null) ? string.Empty : value;
476                         }
477                 }
478
479                 [Browsable (false)]
480                 public TreeNode NextNode {
481                         get {
482                                 if (parent == null)
483                                         return null;
484                                 int index = Index;
485                                 if (parent.Nodes.Count > index + 1)
486                                         return parent.Nodes [index + 1];
487                                 return null;
488                         }
489                 }
490
491                 [Browsable (false)]
492                 public TreeNode NextVisibleNode {
493                         get {
494                                 OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (this);
495                                 o.MoveNext (); // move to the node itself
496
497                                 if (!o.MoveNext ())
498                                         return null;
499                                 TreeNode c = o.CurrentNode;
500                                 if (!c.IsInClippingRect)
501                                         return null;
502                                 return c;
503                         }
504                 }
505
506                 [DefaultValue (null)]
507                 [Localizable (true)]
508                 public Font NodeFont {
509                         get {
510                                 if (prop_bag != null)
511                                         return prop_bag.Font;
512                                 if (TreeView != null)
513                                         return TreeView.Font;
514                                 return null;
515                         }
516                         set {
517                                 if (prop_bag == null)
518                                         prop_bag = new OwnerDrawPropertyBag (); 
519                                 prop_bag.Font = value;
520                                 Invalidate ();
521                         }
522                 }
523
524                 [Browsable (false)]
525                 [ListBindable (false)]
526                 public TreeNodeCollection Nodes {
527                         get {
528                                 if (nodes == null)
529                                         nodes = new TreeNodeCollection (this);
530                                 return nodes;
531                         }
532                 }
533
534                 [Browsable (false)]
535                 public TreeNode Parent {
536                         get {
537                                 TreeView tree_view = TreeView;
538                                 if (tree_view != null && tree_view.root_node == parent)
539                                         return null;
540                                 return parent;
541                         }
542                 }
543
544                 [Browsable (false)]
545                 public TreeNode PrevNode {
546                         get {
547                                 if (parent == null)
548                                         return null;
549                                 int index = Index;
550                                 if (index <= 0 || index > parent.Nodes.Count)
551                                         return null;
552                                 return parent.Nodes [index - 1];
553                         }
554                 }
555
556                 [Browsable (false)]
557                 public TreeNode PrevVisibleNode {
558                         get {
559                                 OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (this);
560                                 o.MovePrevious (); // move to the node itself
561
562                                 if (!o.MovePrevious ())
563                                         return null;
564                                 TreeNode c = o.CurrentNode;
565                                 if (!c.IsInClippingRect)
566                                         return null;
567                                 return c;
568                         }
569                 }
570
571                 [DefaultValue (-1)]
572                 [RelatedImageList ("TreeView.ImageList")]
573                 [TypeConverter (typeof (TreeViewImageIndexConverter))]
574                 [RefreshProperties (RefreshProperties.Repaint)]
575                 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
576                 [Localizable (true)]
577                 public int SelectedImageIndex {
578                         get { return selected_image_index; }
579                         set { selected_image_index = value; }
580                 }
581
582                 [Localizable (true)]
583                 [DefaultValue ("")]
584                 [RelatedImageList ("TreeView.ImageList")]
585                 [TypeConverter (typeof (TreeViewImageKeyConverter))]
586                 [RefreshProperties (RefreshProperties.Repaint)]
587                 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
588                 public string SelectedImageKey {
589                         get { return selected_image_key; }
590                         set { selected_image_key = value; }
591                 }
592
593                 [Localizable (true)]
594                 [DefaultValue (-1)]
595                 [RelatedImageList ("TreeView.StateImageList")]
596                 [TypeConverter (typeof (NoneExcludedImageIndexConverter))]
597                 [RefreshProperties (RefreshProperties.Repaint)]
598                 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
599                 public int StateImageIndex {
600                         get { return state_image_index; }
601                         set {
602                                 if (state_image_index != value) {
603                                         state_image_index = value;
604                                         state_image_key = string.Empty;
605                                         Invalidate ();
606                                 }
607                         }
608                 }
609
610                 [Localizable (true)]
611                 [DefaultValue ("")]
612                 [RelatedImageList ("TreeView.StateImageList")]
613                 [TypeConverter (typeof (ImageKeyConverter))]
614                 [RefreshProperties (RefreshProperties.Repaint)]
615                 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
616                 public string StateImageKey {
617                         get { return state_image_key; }
618                         set {
619                                 if (state_image_key != value) {
620                                         state_image_key = value;
621                                         state_image_index = -1;
622                                         Invalidate ();
623                                 }
624                         }
625                 }
626
627                 [Bindable(true)]
628                 [Localizable(false)]
629                 [TypeConverter(typeof(System.ComponentModel.StringConverter))]
630                 [DefaultValue(null)]
631                 public object Tag {
632                         get { return tag; }
633                         set { tag = value; }
634                 }
635
636                 [Localizable(true)]
637                 public string Text {
638                         get {
639                                 if (text == null)
640                                         return String.Empty;
641                                 return text;
642                         }
643                         set {
644                                 if (text == value)
645                                         return;
646                                 text = value;
647                                 Invalidate ();
648                                 // UIA Framework Event: Text Changed
649                                 TreeView view = TreeView;
650                                 if (view != null)
651                                         view.OnUIANodeTextChanged (new TreeViewEventArgs (this));
652                         }
653                 }
654
655                 [DefaultValue ("")]
656                 [Localizable (false)]
657                 public string ToolTipText {
658                         get { return tool_tip_text; }
659                         set { tool_tip_text = value; }
660                 }
661                 
662                 [Browsable (false)]
663                 public TreeView TreeView {
664                         get {
665                                 if (tree_view != null)
666                                         return tree_view;
667                                 TreeNode walk = parent;
668                                 while (walk != null) {
669                                         if (walk.TreeView != null)
670                                                 break;
671                                         walk = walk.parent;
672                                 }
673                                 if (walk == null)
674                                         return null;
675                                 return walk.TreeView;
676                         }
677                 }
678
679                 [Browsable (false)]
680                 public IntPtr Handle {
681                         get {
682                                 // MS throws a NullReferenceException if the TreeView isn't set...
683                                 if (handle == IntPtr.Zero && TreeView != null)
684                                         handle = TreeView.CreateNodeHandle ();
685                                 return handle;
686                         }
687                 }
688
689                 #endregion      // Public Instance Properties
690
691                 
692                 public static TreeNode FromHandle (TreeView tree, IntPtr handle)
693                 {
694                         if (handle == IntPtr.Zero)
695                                 return null;
696                         // No arg checking on MS it just throws a NullRef if treeview is null
697                         return tree.NodeFromHandle (handle);
698                 }
699
700                 #region Public Instance Methods
701                 public void BeginEdit ()
702                 {
703                         TreeView tv = TreeView;
704                         if (tv != null)
705                                 tv.BeginEdit (this);
706                 }
707
708                 public void Collapse ()
709                 {
710                         CollapseInternal (false);
711                 }
712
713                 public void Collapse (bool ignoreChildren)
714                 {
715                         if (ignoreChildren)
716                                 Collapse ();
717                         else
718                                 CollapseRecursive (this);
719                 }
720
721                 public void EndEdit (bool cancel)
722                 {
723                         TreeView tv = TreeView;
724                         if (!cancel && tv != null)
725                                 tv.EndEdit (this);
726                         else if (cancel && tv != null)
727                                 tv.CancelEdit (this);
728                 }
729
730                 public void Expand ()
731                 {
732                         Expand (false);
733                 }
734
735                 public void ExpandAll ()
736                 {
737                         ExpandRecursive (this);
738                         if(TreeView != null)
739                                 TreeView.UpdateNode (TreeView.root_node);
740                 }
741
742                 public void EnsureVisible ()
743                 {
744                         if (TreeView == null)
745                                 return;
746
747                         if (this.Parent != null)
748                                 ExpandParentRecursive (this.Parent);
749
750                         Rectangle bounds = Bounds;
751                         if (bounds.Y < 0) {
752                                 TreeView.SetTop (this);
753                         } else if (bounds.Bottom > TreeView.ViewportRectangle.Bottom) {
754                                 TreeView.SetBottom (this);
755                         }
756                 }
757
758                 public int GetNodeCount (bool includeSubTrees)
759                 {
760                         if (!includeSubTrees)
761                                 return Nodes.Count;
762
763                         int count = 0;
764                         GetNodeCountRecursive (this, ref count);
765
766                         return count;
767                 }
768
769                 public void Remove ()
770                 {
771                         if (parent == null)
772                                 return;
773                         int index = Index;
774                         parent.Nodes.RemoveAt (index);
775                 }
776
777                 public void Toggle ()
778                 {
779                         if (is_expanded)
780                                 Collapse ();
781                         else
782                                 Expand ();
783                 }
784
785                 public override String ToString ()
786                 {
787                         return String.Concat ("TreeNode: ", Text);
788                 }
789
790                 #endregion      // Public Instance Methods
791
792                 #region Internal & Private Methods and Properties
793
794                 internal bool ArePreviousNodesExpanded {
795                         get {
796                                 TreeNode parent = Parent;
797                                 while (parent != null) {
798                                         if (!parent.is_expanded)
799                                                 return false;
800                                         parent = parent.Parent;
801                                 }
802
803                                 return true;
804                         }
805                 }
806
807                 internal bool IsRoot {
808                         get {
809                                 TreeView tree_view = TreeView;
810                                 if (tree_view == null)
811                                         return false;
812                                 if (tree_view.root_node == this)
813                                         return true;
814                                 return false;
815                         }
816                 }
817
818                 bool BuildFullPath (StringBuilder path)
819                 {
820                         if (parent == null)
821                                 return false;
822
823                         if (parent.BuildFullPath (path))
824                                 path.Append (TreeView.PathSeparator);
825
826                         path.Append (text);
827                         return true;
828                 }
829
830                 public int Index {
831                         get {
832                                 if (parent == null)
833                                         return 0;
834                                 return parent.Nodes.IndexOf (this);
835                         }
836                 }
837
838                 private void Expand (bool byInternal)
839                 {
840                         if (is_expanded || nodes.Count < 1) {
841                                 is_expanded = true;
842                                 return;
843                         }
844
845                         bool cancel = false;
846                         TreeView tree_view = TreeView;
847                         if (tree_view != null) {
848                                 TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (this, false, TreeViewAction.Expand);
849                                 tree_view.OnBeforeExpand (e);
850                                 cancel = e.Cancel;
851                         }
852
853                         if (!cancel) {
854                                 is_expanded = true;
855                                 int count_to_next = CountToNext ();
856
857                                 if (tree_view != null) {
858                                         tree_view.OnAfterExpand (new TreeViewEventArgs (this));
859
860                                         tree_view.RecalculateVisibleOrder (this);
861                                         tree_view.UpdateScrollBars (false);
862
863                                         // ExpandBelow if we affect the visible area
864                                         if (visible_order < tree_view.skipped_nodes + tree_view.VisibleCount + 1 && ArePreviousNodesExpanded)
865                                                 tree_view.ExpandBelow (this, count_to_next);
866                                 }
867                         }
868                 }
869
870                 private void CollapseInternal (bool byInternal)
871                 {
872                         if (!is_expanded || nodes.Count < 1)
873                                 return;
874
875                         if (IsRoot)
876                                 return;
877
878                         bool cancel = false;
879                         TreeView tree_view = TreeView;
880                         if (tree_view != null) {
881                                 TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (this, false, TreeViewAction.Collapse);
882                                 tree_view.OnBeforeCollapse (e);
883                                 cancel = e.Cancel;
884                         }
885
886                         if (!cancel) {
887                                 int count_to_next = CountToNext ();
888
889                                 is_expanded = false;
890
891                                 if (tree_view != null) {
892                                         tree_view.OnAfterCollapse (new TreeViewEventArgs (this));
893
894                                         bool hbar_visible = tree_view.hbar.Visible;
895                                         bool vbar_visible = tree_view.vbar.Visible;
896
897                                         tree_view.RecalculateVisibleOrder (this);
898                                         tree_view.UpdateScrollBars (false);
899
900                                         // CollapseBelow if we affect the visible area
901                                         if (visible_order < tree_view.skipped_nodes + tree_view.VisibleCount + 1 && ArePreviousNodesExpanded)
902                                                 tree_view.CollapseBelow (this, count_to_next);
903                                         if(!byInternal && HasFocusInChildren ())
904                                                 tree_view.SelectedNode = this;
905
906                                         // If one or both of our scrollbars disappeared,
907                                         // invalidate everything
908                                         if ((hbar_visible & !tree_view.hbar.Visible) || (vbar_visible & !tree_view.vbar.Visible))
909                                                 tree_view.Invalidate ();
910                                 }
911                         }
912                 }
913
914                 private int CountToNext ()
915                 {
916                         bool expanded = is_expanded;
917                         is_expanded = false;
918                         OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (this);
919
920                         TreeNode next= null;
921                         if (walk.MoveNext () && walk.MoveNext ())
922                                 next = walk.CurrentNode;
923
924                         is_expanded = expanded;
925                         walk.Reset ();
926                         walk.MoveNext ();
927
928                         int count = 0;
929                         while (walk.MoveNext () && walk.CurrentNode != next)
930                                 count++;
931
932                         return count;
933                 }
934
935                 private bool HasFocusInChildren()
936                 {
937                         if (TreeView == null)
938                                 return false;
939                         foreach (TreeNode node in nodes) {
940                                 if(node == TreeView.SelectedNode)
941                                         return true;
942                                 if(node.HasFocusInChildren ())
943                                         return true;
944                         }
945                         return false;
946                 }
947
948                 private void ExpandRecursive (TreeNode node)
949                 {
950                         node.Expand (true);
951                         foreach (TreeNode child in node.Nodes)
952                                 ExpandRecursive (child);
953                 }
954
955                 private void ExpandParentRecursive (TreeNode node)
956                 {
957                         node.Expand (true);
958                         if (node.Parent != null)
959                                 ExpandParentRecursive (node.Parent);
960                 }
961
962                 internal void CollapseAll ()
963                 {
964                         CollapseRecursive (this);
965                 }
966
967                 internal void CollapseAllUncheck ()
968                 {
969                         CollapseUncheckRecursive (this);
970                 }
971
972                 private void CollapseRecursive (TreeNode node)
973                 {
974                         node.Collapse ();
975                         foreach (TreeNode child in node.Nodes)
976                                 CollapseRecursive (child);
977                 }
978
979                 private void CollapseUncheckRecursive (TreeNode node)
980                 {
981                         node.Collapse ();
982                         node.Checked = false;
983                         foreach (TreeNode child in node.Nodes)
984                                 CollapseUncheckRecursive (child);
985                 }
986
987                 internal void SetNodes (TreeNodeCollection nodes)
988                 {
989                         this.nodes = nodes;
990                 }
991
992                 private void GetNodeCountRecursive (TreeNode node, ref int count)
993                 {
994                         count += node.Nodes.Count;
995                         foreach (TreeNode child in node.Nodes)
996                                 GetNodeCountRecursive (child, ref count);
997                 }
998
999                 internal bool NeedsWidth {
1000                         get { return width == -1; }
1001                 }
1002
1003                 internal void Invalidate ()
1004                 {
1005                         // invalidate width first so Bounds retrieves 
1006                         // the updated value (we don't use it here however)
1007                         width = -1;
1008
1009                         TreeView tv = TreeView;
1010                         if (tv == null)
1011                                 return;
1012
1013                         tv.UpdateNode (this);
1014                 }
1015
1016                 internal void InvalidateWidth ()
1017                 {
1018                         // bounds.Width = 0;
1019                         width = -1;
1020                 }
1021
1022                 internal void SetWidth (int width)
1023                 {
1024                         this.width = width;
1025                 }
1026
1027                 internal void SetParent (TreeNode parent)
1028                 {
1029                         this.parent = parent;
1030                 }
1031
1032                 private bool IsInClippingRect {
1033                         get {
1034                                 if (TreeView == null)
1035                                         return false;
1036                                 Rectangle bounds = Bounds;
1037                                 if (bounds.Y < 0 && bounds.Y > TreeView.ClientRectangle.Height)
1038                                         return false;
1039                                 return true;
1040                         }
1041                 }
1042
1043                 internal Image StateImage {
1044                         get {
1045                                 if (TreeView != null) {
1046                                         if (TreeView.StateImageList == null)
1047                                                 return null;
1048                                         if (state_image_index >= 0)
1049                                                 return TreeView.StateImageList.Images[state_image_index];
1050                                         if (state_image_key != string.Empty)
1051                                                 return TreeView.StateImageList.Images[state_image_key];
1052                                 }
1053
1054                                 return null;
1055                         }
1056                 }
1057
1058                 // Order of operation:
1059                 // 1) Node.Image[Key|Index]
1060                 // 2) TreeView.Image[Key|Index]
1061                 // 3) First image in TreeView.ImageList
1062                 internal int Image {
1063                         get {
1064                                 if (TreeView == null || TreeView.ImageList == null)
1065                                         return -1;
1066                                         
1067                                 if (IsSelected) {
1068                                         if (selected_image_index >= 0)
1069                                                 return selected_image_index;
1070                                         if (!string.IsNullOrEmpty (selected_image_key))
1071                                                 return TreeView.ImageList.Images.IndexOfKey (selected_image_key);
1072                                         if (!string.IsNullOrEmpty (TreeView.SelectedImageKey))
1073                                                 return TreeView.ImageList.Images.IndexOfKey (TreeView.SelectedImageKey);
1074                                         if (TreeView.SelectedImageIndex >= 0)
1075                                                 return TreeView.SelectedImageIndex;
1076                                 } else {
1077                                         if (image_index >= 0)
1078                                                 return image_index;
1079                                         if (!string.IsNullOrEmpty (image_key))
1080                                                 return TreeView.ImageList.Images.IndexOfKey (image_key);
1081                                         if (!string.IsNullOrEmpty (TreeView.ImageKey))
1082                                                 return TreeView.ImageList.Images.IndexOfKey (TreeView.ImageKey);
1083                                         if (TreeView.ImageIndex >= 0)
1084                                                 return TreeView.ImageIndex;
1085                                 }
1086
1087                                 if (TreeView.ImageList.Images.Count > 0)
1088                                         return 0;
1089                                         
1090                                 return -1;
1091                         }
1092                 }
1093                 #endregion      // Internal & Private Methods and Properties
1094         }
1095 }