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