* TreeView.cs: Don't draw the selected node when we lose
[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         [TypeConverter(typeof(TreeNodeConverter))]
34         [Serializable]
35         public class TreeNode : MarshalByRefObject, ICloneable, ISerializable {
36                 #region Fields
37                 private TreeView tree_view;
38                 internal TreeNode parent;
39                 private int index;
40
41                 private string text;
42                 private int image_index = -1;
43                 private int selected_image_index = -1;
44                 internal TreeNodeCollection nodes;
45                 
46                 private bool is_expanded = false;
47                 private Rectangle bounds = Rectangle.Empty;
48                 private bool check;
49                 private bool is_editing;
50                 internal OwnerDrawPropertyBag prop_bag;
51
52                 private object tag;
53
54                 internal IntPtr handle;
55                 
56                 #endregion      // Fields
57
58                 #region Internal Constructors           
59                 internal TreeNode (TreeView tree_view) : this ()
60                 {
61                         this.tree_view = tree_view;
62                         is_expanded = true;
63                 }
64
65                 private TreeNode (SerializationInfo info, StreamingContext context) : this ()
66                 {
67                         Text = (string) info.GetValue ("Text", typeof (string));
68                         prop_bag = (OwnerDrawPropertyBag) info.GetValue ("prop_bag", typeof (OwnerDrawPropertyBag));
69                         image_index = (int) info.GetValue ("ImageIndex", typeof (int));
70                         selected_image_index = (int) info.GetValue ("SelectedImageIndex", typeof (int));
71                         tag = info.GetValue ("Tag", typeof (object));
72                         check = (bool) info.GetValue ("Checked", typeof (bool));
73
74                         int count = (int) info.GetValue ("NumberOfChildren", typeof (int));
75                         for (int i = 0; i < count; i++) {
76                                 TreeNode node = (TreeNode) info.GetValue ("Child-" + i, typeof (TreeNode));
77                                 Nodes.Add (node);
78                         }
79                 }
80
81                 #endregion      // Internal Constructors
82
83                 #region Public Constructors
84                 public TreeNode ()
85                 {
86                         nodes = new TreeNodeCollection (this);
87                 }
88
89                 public TreeNode (string text) : this ()
90                 {
91                         Text = text;
92                 }
93
94                 public TreeNode (string text, TreeNode [] children) : this (text)
95                 {
96                         Nodes.AddRange (children);
97                 }
98
99                 public TreeNode (string text, int image_index, int selected_image_index) : this (text)
100                 {
101                         this.image_index = image_index;
102                         this.selected_image_index = selected_image_index;
103                 }
104
105                 public TreeNode (string text, int image_index, int selected_image_index,
106                                 TreeNode [] children) : this (text, image_index, selected_image_index)
107                 {
108                         Nodes.AddRange (children);
109                 }
110
111                 #endregion      // Public Constructors
112
113                 #region ICloneable Members
114                 public object Clone()
115                 {
116                         TreeNode tn = new TreeNode (text, image_index, selected_image_index);
117                         if (nodes != null) {
118                                 foreach (TreeNode child in nodes)
119                                         tn.Nodes.Add ((TreeNode)child.Clone ());
120                         }
121                         tn.Tag = tag;
122                         tn.Checked = Checked;
123                         if (prop_bag != null)
124                                 tn.prop_bag = OwnerDrawPropertyBag.Copy (prop_bag);
125                         return tn;
126                 }
127
128                 #endregion      // ICloneable Members
129
130                 #region ISerializable Members
131                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
132                 {
133                         info.AddValue ("Text", Text);
134                         info.AddValue ("prop_bag", prop_bag, typeof (OwnerDrawPropertyBag));
135                         info.AddValue ("ImageIndex", ImageIndex);
136                         info.AddValue ("SelectedImageIndex", SelectedImageIndex);
137                         info.AddValue ("Tag", Tag);
138                         info.AddValue ("Checked", Checked);
139                         
140                         info.AddValue ("NumberOfChildren", Nodes.Count);
141                         for (int i = 0; i < Nodes.Count; i++)
142                                 info.AddValue ("Child-" + i, Nodes [i], typeof (TreeNode));
143                 }
144                 #endregion      // ISerializable Members
145
146                 #region Public Instance Properties
147                 public Color BackColor {
148                         get { 
149                                 if (prop_bag != null)
150                                         return prop_bag.BackColor;
151                                 if (TreeView != null)
152                                         return TreeView.BackColor;
153                                 return Color.Empty;
154                         }
155                         set { 
156                                 if (prop_bag == null)
157                                         prop_bag = new OwnerDrawPropertyBag ();
158                                 prop_bag.BackColor = value;
159                         }
160                 }
161
162                 public Rectangle Bounds {
163                         get { return bounds; }
164                 }
165
166                 public bool Checked {
167                         get { return check; }
168                         set {
169                                 if (check == value)
170                                         return;
171                                 check = value;
172
173                                 if (TreeView != null)
174                                         TreeView.UpdateNode (this);
175                         }
176                 }
177
178                 public TreeNode FirstNode {
179                         get {
180                                 if (nodes.Count > 0)
181                                         return nodes [0];
182                                 return null;
183                         }
184                 }
185
186                 public Color ForeColor {
187                         get {
188                                 if (prop_bag != null)
189                                         return prop_bag.ForeColor;
190                                 if (TreeView != null)
191                                         return TreeView.ForeColor;
192                                 return Color.Empty;
193                         }
194                         set {
195                                 if (prop_bag == null)
196                                         prop_bag = new OwnerDrawPropertyBag ();
197                                 prop_bag.ForeColor = value;
198                         }
199                 }
200
201                 public string FullPath {
202                         get {
203                                 if (tree_view == null)
204                                         throw new Exception ("No TreeView associated");
205
206                                 StringBuilder builder = new StringBuilder ();
207                                 BuildFullPath (builder);
208                                 return builder.ToString ();
209                         }
210                 }
211
212                 [Localizable(true)]
213                 public int ImageIndex {
214                         get { return image_index; }
215                         set { image_index = value; }
216                 }
217
218                 public bool IsEditing {
219                         get { return is_editing; }
220                 }
221
222                 public bool IsExpanded {
223                         get { return is_expanded; }
224                 }
225
226                 public bool IsSelected {
227                         get {
228                                 if (TreeView == null)
229                                         return false;
230                                 return TreeView.SelectedNode == this;
231                         }
232                 }
233
234                 public bool IsVisible {
235                         get {
236                                 if (TreeView == null)
237                                         return false;
238
239                                 if (bounds.Y < 0 && bounds.Y > TreeView.ClientRectangle.Height)
240                                         return false;
241
242                                 TreeNode parent = Parent;
243                                 while (parent != null) {
244                                         if (!parent.IsExpanded)
245                                                 return false;
246                                         parent = parent.Parent;
247                                 }
248                                 return true;
249                         }
250                 }
251
252                 public TreeNode LastNode {
253                         get {
254                                 return (nodes == null || nodes.Count == 0) ? null : nodes [nodes.Count - 1];
255                         }
256                 }
257
258                 public TreeNode NextNode {
259                         get {
260                                 if (parent == null)
261                                         return null;
262                                 if (parent.Nodes.Count > index + 1)
263                                         return parent.Nodes [index + 1];
264                                 return null;
265                         }
266                 }
267                 
268                 public TreeNode NextVisibleNode {
269                         get {
270                                 OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (this);
271                                 if (!o.MoveNext ())
272                                         return null;
273                                 TreeNode c = (TreeNode) o.Current;
274                                 if (!c.IsInClippingRect)
275                                         return null;
276                                 return c;
277                         }
278                 }
279
280                 [Localizable(true)]
281                 public Font NodeFont {
282                         get {
283                                 if (prop_bag != null)
284                                         return prop_bag.Font;
285                                 if (TreeView != null)
286                                         return TreeView.Font;
287                                 return null;
288                         }
289                         set {
290                                 if (prop_bag == null)
291                                         prop_bag = new OwnerDrawPropertyBag (); 
292                                 prop_bag.Font = value;
293                                 InvalidateWidth ();
294                         }
295                 }
296
297                 [ListBindable(false)]
298                 public TreeNodeCollection Nodes {
299                         get {
300                                 if (nodes == null)
301                                         nodes = new TreeNodeCollection (this);
302                                 return nodes;
303                         }
304                 }
305
306                 public TreeNode Parent {
307                         get {
308                                 if (tree_view != null && tree_view.root_node == parent)
309                                         return null;
310                                 return parent;
311                         }
312                 }
313
314                 public TreeNode PrevNode {
315                         get {
316                                 if (parent == null)
317                                         return null;
318                                 if (index == 0 || index > parent.Nodes.Count)
319                                         return null;
320                                 return parent.Nodes [index - 1];
321                         }
322                 }
323
324                 public TreeNode PrevVisibleNode {
325                         get {
326                                 OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (this);
327                                 if (!o.MovePrevious ())
328                                         return null;
329                                 TreeNode c = (TreeNode) o.Current;
330                                 if (!c.IsInClippingRect)
331                                         return null;
332                                 return c;
333                         }
334                 }
335
336                 [Localizable(true)]
337                 public int SelectedImageIndex {
338                         get { return selected_image_index; }
339                         set { selected_image_index = value; }
340                 }
341
342                 [Bindable(true)]
343                 [Localizable(false)]
344                 [TypeConverter(typeof(System.ComponentModel.StringConverter))]
345                 [DefaultValue(null)]
346                 public object Tag {
347                         get { return tag; }
348                         set { tag = value; }
349                 }
350
351                 [Localizable(true)]
352                 public string Text {
353                         get {
354                                 if (text == null)
355                                         return String.Empty;
356                                 return text;
357                         }
358                         set {
359                                 if (text == value)
360                                         return;
361                                 text = value;
362                                 bounds.Width = 0;
363                         }
364                 }
365
366                 public TreeView TreeView {
367                         get {
368                                 if (tree_view != null)
369                                         return tree_view;
370                                 TreeNode walk = parent;
371                                 while (walk != null) {
372                                         if (walk.TreeView != null)
373                                                 tree_view = walk.TreeView;
374                                         walk = walk.parent;
375                                 }
376                                 return tree_view;
377                         }
378                 }
379
380                 public IntPtr Handle {
381                         get {
382                                 // MS throws a NullReferenceException if the TreeView isn't set...
383                                 if (handle == IntPtr.Zero)
384                                         handle = TreeView.CreateNodeHandle ();
385                                 return handle;
386                         }
387                 }
388
389                 #endregion      // Public Instance Properties
390
391                 
392                 public static TreeNode FromHandle (TreeView tree, IntPtr handle)
393                 {
394                         if (handle == IntPtr.Zero)
395                                 return null;
396                         // No arg checking on MS it just throws a NullRef if treeview is null
397                         return tree.NodeFromHandle (handle);
398                 }
399
400                 #region Public Instance Methods
401                 public void BeginEdit () {
402                         is_editing = true;
403                 }
404
405                 public void Collapse () {
406                         Collapse(false);
407                 }
408
409                 public void EndEdit (bool cancel) {
410                         is_editing = false;
411                         if (!cancel && TreeView != null)
412                                 Text = TreeView.LabelEditText;
413                 }
414
415                 public void Expand () {
416                         Expand(false);
417                 }
418
419                 public void ExpandAll () {
420                         ExpandRecursive (this);
421                         if(TreeView != null)
422                                 TreeView.UpdateNode (TreeView.root_node);
423                 }
424
425                 public void EnsureVisible ()
426                 {
427                         if (TreeView == null)
428                                 return;
429
430                         if (this.Parent != null)
431                                 ExpandParentRecursive (this.Parent);
432
433                         if (bounds.Y < 0) {
434                                 TreeView.SetTop (this);
435                         } else if (bounds.Bottom > TreeView.ViewportRectangle.Bottom) {
436                                 TreeView.SetBottom (this);
437                         }
438                 }
439
440                 public int GetNodeCount (bool include_subtrees) {
441                         if (!include_subtrees)
442                                 return Nodes.Count;
443
444                         int count = 0;
445                         GetNodeCountRecursive (this, ref count);
446
447                         return count;
448                 }
449
450                 public void Remove () {
451                         if (parent == null)
452                                 return;
453                         parent.Nodes.RemoveAt (Index);
454                 }
455
456                 public void Toggle () {
457                         if (is_expanded)
458                                 Collapse ();
459                         else
460                                 Expand ();
461                 }
462
463                 public override String ToString () {
464                         return String.Concat ("TreeNode: ", Text);
465                 }
466
467                 #endregion      // Public Instance Methods
468
469                 #region Internal & Private Methods and Properties
470
471                 internal bool IsRoot {
472                         get {
473                                 if (tree_view == null)
474                                         return false;
475                                 if (tree_view.root_node == this)
476                                         return true;
477                                 return false;
478                         }
479                 }
480
481                 bool BuildFullPath (StringBuilder path)
482                 {
483                         if (parent == null)
484                                 return false;
485
486                         if (parent.BuildFullPath (path))
487                                 path.Append (tree_view.PathSeparator);
488
489                         path.Append (text);
490                         return true;
491                 }
492
493                 public int Index {
494                         get { return index; }
495                 }
496
497                 private void Expand (bool byInternal)
498                 {
499                         if (is_expanded)
500                                 return;
501                         bool cancel = false;
502                         if (TreeView != null) {
503                                 TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (this, false, TreeViewAction.Expand);
504                                 TreeView.OnBeforeExpand (e);
505                                 cancel = e.Cancel;
506                         }
507
508                         if (!cancel) {
509                                 is_expanded = true;
510                                 if (TreeView != null)
511                                         TreeView.OnAfterExpand (new TreeViewEventArgs (this));
512                                 if (IsVisible && TreeView != null)
513                                         TreeView.UpdateBelow (this);
514                         }
515                 }
516
517                 private void Collapse (bool byInternal)
518                 {
519                         if (!is_expanded)
520                                 return;
521
522                         if (tree_view != null && tree_view.root_node == this)
523                                 return;
524
525                         bool cancel = false;
526                         if (TreeView != null) {
527                                 TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (this, false, TreeViewAction.Collapse);
528                                 TreeView.OnBeforeCollapse (e);
529                                 cancel = e.Cancel;
530                         }
531
532                         if (!cancel) {
533                                 is_expanded = false;
534                                 if (TreeView != null)
535                                         TreeView.OnAfterCollapse (new TreeViewEventArgs (this));
536                                 if (IsVisible && TreeView != null)
537                                         TreeView.UpdateBelow (this);
538                                 if(!byInternal && TreeView != null && HasFocusInChildren ())
539                                         TreeView.SelectedNode = this;
540                         }
541                 }
542
543                 private bool HasFocusInChildren()
544                 {
545                         if(TreeView == null) return false;
546                         foreach(TreeNode node in nodes) {
547                                 if(node == TreeView.SelectedNode) return true;
548                                 if(node.HasFocusInChildren())
549                                         return true;
550                         }
551                         return false;
552                 }
553
554                 private void ExpandRecursive (TreeNode node)
555                 {
556                         node.Expand (true);
557                         foreach (TreeNode child in node.Nodes) {
558                                 ExpandRecursive (child);
559                         }
560                 }
561
562                 private void ExpandParentRecursive (TreeNode node)
563                 {
564                         node.Expand (true);
565                         if (node.Parent != null)
566                                 ExpandParentRecursive (node.Parent);
567                 }
568
569                 internal void CollapseAll ()
570                 {
571                         CollapseRecursive (this);
572                 }
573
574                 internal void CollapseAllUncheck ()
575                 {
576                         CollapseUncheckRecursive (this);
577                 }
578
579                 private void CollapseRecursive (TreeNode node)
580                 {
581                         node.Collapse ();
582                         foreach (TreeNode child in node.Nodes) {
583                                 CollapseRecursive (child);
584                         }
585                 }
586
587                 private void CollapseUncheckRecursive (TreeNode node)
588                 {
589                         node.Collapse ();
590                         node.Checked = false;
591                         foreach (TreeNode child in node.Nodes) {
592                                 CollapseUncheckRecursive (child);
593                         }
594                 }
595
596                 internal void SetNodes (TreeNodeCollection nodes)
597                 {
598                         this.nodes = nodes;
599                 }
600
601                 private void GetNodeCountRecursive (TreeNode node, ref int count)
602                 {
603                         count += node.Nodes.Count;
604                         foreach (TreeNode child in node.Nodes) {
605                                 GetNodeCountRecursive (child, ref count);
606                         }
607                 }
608
609                 internal bool NeedsWidth {
610                         get { return bounds.Width == 0; }
611                 }
612
613                 internal void InvalidateWidth ()
614                 {
615                         bounds.Width = 0;
616                 }
617
618                 internal void SetWidth (int width)
619                 {
620                         bounds.Width = width;
621                 }
622
623                 internal void SetHeight (int height)
624                 {
625                         bounds.Height = height;
626                 }
627
628                 internal void SetPosition (int x, int y)
629                 {
630                         bounds.X = x;
631                         bounds.Y = y;
632                 }
633
634                 internal void SetAddedData (TreeView tree_view, TreeNode parent, int index)
635                 {
636                         this.tree_view = tree_view;
637                         this.parent = parent;
638                         this.index = index;
639                 }
640
641                 internal void SetIndex (int index)
642                 {
643                         this.index = index;
644                 }
645
646                 private bool IsInClippingRect
647                 {
648                         get {
649                                 if (TreeView == null)
650                                         return false;
651                                 if (bounds.Y < 0 && bounds.Y > tree_view.ClientRectangle.Height)
652                                         return false;
653                                 return true;
654                         }
655                 }
656                 #endregion      // Internal & Private Methods and Properties
657
658         }
659 }
660