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