**** Merged r36954 from MCS ****
[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 Novell, Inc.
21 //
22 // Authors:
23 //      Jackson Harper (jackson@ximian.com)
24
25 using System;
26 using System.Text;
27 using System.Drawing;
28 using System.Runtime.Serialization;
29
30 namespace System.Windows.Forms {
31
32         [Serializable]
33         public class TreeNode : MarshalByRefObject, ICloneable /*, ISerializable */ {
34
35                 private TreeView tree_view;
36                 internal TreeNode parent;
37                 private int index;
38
39                 private string text;
40                 private int image_index = -1;
41                 private int selected_image_index = -1;
42                 internal TreeNodeCollection nodes;
43                 
44                 private bool is_expanded = true;
45                 private Rectangle bounds = Rectangle.Empty;
46                 private bool check;
47                 internal OwnerDrawPropertyBag prop_bag;
48
49                 private object tag;
50                 private IntPtr handle;
51                 
52                 internal TreeNode (TreeView tree_view) : this ()
53                 {
54                         this.tree_view = tree_view;
55                 }
56
57                 public TreeNode ()
58                 {
59                         nodes = new TreeNodeCollection (this);
60                 }
61
62                 public TreeNode (string text) : this ()
63                 {
64                         Text = text;
65                 }
66
67                 public TreeNode (string text, TreeNode [] children) : this (text)
68                 {
69                         Nodes.AddRange (children);
70                 }
71
72                 public TreeNode (string text, int image_index, int selected_image_index) : this (text)
73                 {
74                         this.image_index = image_index;
75                         this.selected_image_index = selected_image_index;
76                 }
77
78                 public TreeNode (string text, int image_index, int selected_image_index,
79                                 TreeNode [] children) : this (text, image_index, selected_image_index)
80                 {
81                         Nodes.AddRange (children);
82                 }
83
84                 internal TreeView TreeView {
85                         get {
86                                 if (tree_view != null)
87                                         return tree_view;
88                                 TreeNode walk = parent;
89                                 while (walk != null) {
90                                         if (walk.TreeView != null)
91                                                 tree_view = walk.TreeView;
92                                         walk = walk.parent;
93                                 }
94                                 return tree_view;
95                         }
96                 }
97
98                 #region ICloneable Members
99
100                 public object Clone()
101                 {
102                         TreeNode tn = new TreeNode (text, image_index, selected_image_index);
103                         if (nodes != null) {
104                                 foreach (TreeNode child in nodes)
105                                         tn.Nodes.Add ((TreeNode)child.Clone ());
106                         }
107                         tn.Tag = tag;
108                         tn.Checked = Checked;
109                         if (prop_bag != null)
110                                 tn.prop_bag = OwnerDrawPropertyBag.Copy (prop_bag);
111                         return tn;
112                 }
113
114                 #endregion
115
116                 public TreeNode Parent {
117                         get {
118                                 if (tree_view != null && tree_view.root_node == parent)
119                                         return null;
120                                 return parent;
121                         }
122                 }
123
124                 public string Text {
125                         get {
126                                 if (text == null)
127                                         return String.Empty;
128                                 return text;
129                         }
130                         set {
131                                 if (text == value)
132                                         return;
133                                 text = value;
134                                 bounds.Width = 0;
135                         }
136                 }
137
138                 public Rectangle Bounds {
139                         get { return bounds; }
140                 }
141
142                 public bool Checked {
143                         get { return check; }
144                         set { check = value; }
145                 }
146
147                 public Color BackColor {
148                         get { 
149                                 return prop_bag == null ? Color.Empty : prop_bag.BackColor;
150                         }
151                         set { 
152                                 if (prop_bag == null)
153                                         prop_bag = new OwnerDrawPropertyBag ();
154                                 prop_bag.BackColor = value;
155                         }
156                 }
157
158                 public Color ForeColor {
159                         get { 
160                                 return prop_bag == null ? Color.Empty : prop_bag.ForeColor;
161                         }
162                         set {
163                                 if (prop_bag == null)
164                                         prop_bag = new OwnerDrawPropertyBag ();
165                                 prop_bag.ForeColor = value;
166                         }
167                 }
168
169                 public Font NodeFont {
170                         get {
171                                 return prop_bag == null ? null : prop_bag.Font;
172                         }
173                         set {
174                                 if (prop_bag == null)
175                                         prop_bag = new OwnerDrawPropertyBag (); 
176                                 prop_bag.Font = value;
177                         }
178                 }
179
180                 public TreeNodeCollection Nodes {
181                         get {
182                                 if (nodes == null)
183                                         nodes = new TreeNodeCollection (this);
184                                 return nodes;
185                         }
186                 }
187
188                 public TreeNode FirstNode {
189                         get {
190                                 if (nodes.Count > 0)
191                                         return nodes [0];
192                                 return null;
193                         }
194                 }
195
196                 public string FullPath {
197                         get {
198                                 if (tree_view == null)
199                                         throw new Exception ("No TreeView associated");
200
201                                 StringBuilder builder = new StringBuilder ();
202                                 BuildFullPath (builder);
203                                 return builder.ToString ();
204                         }
205                 }
206
207                 bool BuildFullPath (StringBuilder path)
208                 {
209                         if (parent == null)
210                                 return false;
211
212                         if (parent.BuildFullPath (path))
213                                 path.Append (tree_view.PathSeparator);
214
215                         path.Append (text);
216                         return true;
217                 }
218
219                 public bool IsExpanded {
220                         get { return is_expanded; }
221                 }
222
223                 public TreeNode NextNode {
224                         get {
225                                 if (parent == null)
226                                         return null;
227                                 if (parent.Nodes.Count > index + 1)
228                                         return parent.Nodes [index + 1];
229                                 return null;
230                         }
231                 }
232                 
233                 public TreeNode PrevNode {
234                         get {
235                                 if (parent == null)
236                                         return null;
237                                 if (index == 0 || index > parent.Nodes.Count)
238                                         return null;
239                                 return parent.Nodes [index - 1];
240                         }
241                 }
242
243                 public TreeNode NextVisibleNode {
244                         get {
245                                 OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (this);
246                                 if (!o.MoveNext ())
247                                         return null;
248                                 TreeNode c = (TreeNode) o.Current;
249                                 if (!c.IsInClippingRect)
250                                         return null;
251                                 return c;
252                         }
253                 }
254
255                 public TreeNode PrevVisibleNode {
256                         get {
257                                 OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (this);
258                                 if (!o.MovePrevious ())
259                                         return null;
260                                 TreeNode c = (TreeNode) o.Current;
261                                 if (!c.IsInClippingRect)
262                                         return null;
263                                 return c;
264                         }
265                 }
266
267                 public TreeNode LastNode {
268                         get {
269                                 return (nodes == null || nodes.Count == 0) ? null : nodes [nodes.Count - 1];
270                         }
271                 }
272
273                 public int Index {
274                         get { return index; }
275                 }
276
277                 public int ImageIndex {
278                         get { return image_index; }
279                         set { image_index = value; }
280                 }
281
282                 public int SelectedImageIndex {
283                         get { return selected_image_index; }
284                         set { selected_image_index = value; }
285                 }
286
287                 public object Tag {
288                         get { return tag; }
289                         set { tag = value; }
290                 }
291
292                 public void Expand ()
293                 {
294                         if (is_expanded)
295                                 return;
296
297                         bool cancel = false;
298                         if (TreeView != null) {
299                                 TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (this, false, TreeViewAction.Expand);
300                                 TreeView.OnBeforeCollapse (e);
301                                 cancel = e.Cancel;
302                         }
303
304                         if (!cancel) {
305                                 is_expanded = true;
306                                 if (TreeView != null)
307                                         TreeView.OnAfterCollapse (new TreeViewEventArgs (this));
308                                 if (IsNodeVisible () && TreeView != null)
309                                         TreeView.UpdateBelow (this);
310                         }
311                 }
312
313                 public void Collapse ()
314                 {
315                         if (!is_expanded)
316                                 return;
317
318                         bool cancel = false;
319                         if (TreeView != null) {
320                                 TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (this, false, TreeViewAction.Collapse);
321                                 TreeView.OnBeforeCollapse (e);
322                                 cancel = e.Cancel;
323                         }
324
325                         if (!cancel) {
326                                 is_expanded = false;
327                                 if (TreeView != null)
328                                         TreeView.OnAfterCollapse (new TreeViewEventArgs (this));
329                                 if (IsNodeVisible () && TreeView != null)
330                                         TreeView.UpdateBelow (this);
331                         }
332                 }
333
334                 public void Remove ()
335                 {
336                         if (parent == null)
337                                 return;
338                         parent.Nodes.RemoveAt (Index);
339                 }
340
341                 public void ExpandAll ()
342                 {
343                         ExpandRecursive (this);
344                 }
345
346                 private void ExpandRecursive (TreeNode node)
347                 {
348                         node.Expand ();
349                         foreach (TreeNode child in node.Nodes) {
350                                 ExpandRecursive (child);
351                         }
352                 }
353
354                 internal void CollapseAll ()
355                 {
356                         CollapseRecursive (this);
357                 }
358
359                 private void CollapseRecursive (TreeNode node)
360                 {
361                         node.Collapse ();
362                         foreach (TreeNode child in node.Nodes) {
363                                 CollapseRecursive (child);
364                         }
365                 }
366
367                 public int GetNodeCount (bool include_subtrees)
368                 {
369                         if (!include_subtrees)
370                                 return Nodes.Count;
371
372                         int count = 0;
373                         GetNodeCountRecursive (this, ref count);
374
375                         return count;
376                 }
377
378                 public void Toggle ()
379                 {
380                         if (is_expanded)
381                                 Collapse ();
382                         else
383                                 Expand ();
384
385                         if (TreeView != null)
386                                 TreeView.Refresh ();
387                 }
388
389                 internal void SetNodes (TreeNodeCollection nodes)
390                 {
391                         this.nodes = nodes;
392                 }
393
394                 private void GetNodeCountRecursive (TreeNode node, ref int count)
395                 {
396                         count += node.Nodes.Count;
397                         foreach (TreeNode child in node.Nodes) {
398                                 GetNodeCountRecursive (child, ref count);
399                         }
400                 }
401
402                 public override String ToString ()
403                 {
404                         return String.Concat ("TreeNode: ", Text);
405                 }
406
407                 internal void UpdateBounds (int x, int y, int width, int height)
408                 {
409                         bounds.X = x;
410                         bounds.Y = y;
411                         bounds.Width = width;
412                         bounds.Height = height;
413                 }
414
415                 internal void SetAddedData (TreeView tree_view, TreeNode parent, int index)
416                 {
417                         this.tree_view = tree_view;
418                         this.parent = parent;
419                         this.index = index;
420                 }
421
422                 private bool IsInClippingRect
423                 {
424                         get {
425                                 if (TreeView == null)
426                                         return false;
427                                 if (bounds.Y < 0 && bounds.Y > tree_view.ClientRectangle.Height)
428                                         return false;
429                                 return true;
430                         }
431                 }
432
433                 private bool IsNodeVisible ()
434                 {
435                         if (TreeView == null)
436                                 return false;
437
438                         if (bounds.Y < 0 && bounds.Y > TreeView.ClientRectangle.Height)
439                                 return false;
440
441                         TreeNode parent = Parent;
442                         while (parent != null) {
443                                 if (!parent.IsExpanded)
444                                         return false;
445                                 parent = parent.Parent;
446                         }
447                         return true;
448                 }
449         }
450 }
451