2008-07-14 Jonathan Pobst <monkey@jpobst.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TreeNodeCollection.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-2006 Novell, Inc.
21 //
22 // Authors:
23 //      Jackson Harper (jackson@ximian.com)
24
25
26 using System;
27 using System.Collections;
28 using System.ComponentModel;
29 using System.Globalization;
30
31 #if NET_2_0
32 using System.Collections.Generic;
33 #endif
34
35 namespace System.Windows.Forms {
36         [Editor("System.Windows.Forms.Design.TreeNodeCollectionEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
37         public class TreeNodeCollection : IList, ICollection, IEnumerable {
38
39                 private static readonly int OrigSize = 50;
40
41                 private TreeNode owner;
42                 private int count;
43                 private TreeNode [] nodes;
44
45                 private TreeNodeCollection ()
46                 {
47                 }
48
49                 internal TreeNodeCollection (TreeNode owner)
50                 {
51                         this.owner = owner;
52                         nodes = new TreeNode [OrigSize];
53                 }
54
55 #if !NET_2_0
56                 [EditorBrowsable(EditorBrowsableState.Advanced)]
57 #endif
58                 [Browsable(false)]
59                 public int Count {
60                         get { return count; }
61                 }
62
63                 public bool IsReadOnly {
64                         get { return false; }
65                 }
66
67                 bool ICollection.IsSynchronized {
68                         get { return false; }
69                 }
70
71                 object ICollection.SyncRoot {
72                         get { return this; }
73                 }
74
75                 bool IList.IsFixedSize {
76                         get { return false; }
77                 }
78
79                 object IList.this [int index] {
80                         get {
81                                 return this [index];
82                         }
83                         set {
84                                 if (!(value is TreeNode))
85                                         throw new ArgumentException ("Parameter must be of type TreeNode.", "value");
86                                 this [index] = (TreeNode) value;
87                         }
88                 }
89
90                 public virtual TreeNode this [int index] {
91                         get {
92                                 if (index < 0 || index >= Count)
93                                         throw new ArgumentOutOfRangeException ("index");
94                                 return nodes [index];
95                         }
96                         set {
97                                 if (index < 0 || index >= Count)
98                                         throw new ArgumentOutOfRangeException ("index");
99                                 SetupNode (value);
100                                 nodes [index] = value;
101                         }
102                 }
103
104 #if NET_2_0
105                 public virtual TreeNode this [string key] {
106                         get {
107                                 for (int i = 0; i < count; i++)
108                                         if (string.Compare (key, nodes[i].Name, true) == 0)
109                                                 return nodes[i];
110                                                 
111                                 return null;
112                         }
113                 }
114 #endif
115
116                 public virtual TreeNode Add (string text)
117                 {
118                         TreeNode res = new TreeNode (text);
119                         Add (res);
120                         return res;
121                 }
122
123                 public virtual int Add (TreeNode node)
124                 {
125                         if (node == null)
126                                 throw new ArgumentNullException("node");
127
128                         int res;
129                         TreeView tree_view = null;
130
131                         if (owner != null)
132                                 tree_view = owner.TreeView;
133                                 
134                         if (tree_view != null && tree_view.Sorted) {
135                                 res = AddSorted (node);
136                         } else {
137                                 if (count >= nodes.Length)
138                                         Grow ();
139                                 nodes[count] = node;
140                                 res = count;
141                                 count++;
142                         }
143
144                         SetupNode (node);
145
146                         return res;
147                 }
148
149 #if NET_2_0
150                 public virtual TreeNode Add (string key, string text)
151                 {
152                         TreeNode node = new TreeNode (text);
153                         node.Name = key;
154                         Add (node);
155                         return node;
156                 }
157
158                 public virtual TreeNode Add (string key, string text, int imageIndex)
159                 {
160                         TreeNode node = Add (key, text);
161                         node.ImageIndex = imageIndex;
162                         return node;
163                 }
164
165                 public virtual TreeNode Add (string key, string text, string imageKey)
166                 {
167                         TreeNode node = Add (key, text);
168                         node.ImageKey = imageKey;
169                         return node;
170
171                 }
172
173                 public virtual TreeNode Add (string key, string text, int imageIndex, int selectedImageIndex)
174                 {
175                         TreeNode node = Add (key, text);
176                         node.ImageIndex = imageIndex;
177                         node.SelectedImageIndex = selectedImageIndex;
178                         return node;
179                 }
180
181                 public virtual TreeNode Add (string key, string text, string imageKey, string selectedImageKey)
182                 {
183                         TreeNode node = Add (key, text);
184                         node.ImageKey = imageKey;
185                         node.SelectedImageKey = selectedImageKey;
186                         return node;
187                 }
188
189
190 #endif
191
192                 public virtual void AddRange (TreeNode [] nodes)
193                 {
194                         if (nodes == null)
195                                 throw new ArgumentNullException("nodes");
196
197                         // We can't just use Array.Copy because the nodes also
198                         // need to have some properties set when they are added.
199                         for (int i = 0; i < nodes.Length; i++)
200                                 Add (nodes [i]);
201                 }
202
203                 public virtual void Clear ()
204                 {
205                         while (count > 0)
206                                 RemoveAt (0, false);
207                         
208                         Array.Clear (nodes, 0, count);
209                         count = 0;
210
211                         TreeView tree_view = null;
212                         if (owner != null) {
213                                 tree_view = owner.TreeView;
214                                 if (tree_view != null) {
215                                         tree_view.highlighted_node = null;
216                                         tree_view.selected_node = null;
217                                         tree_view.UpdateBelow (owner);
218                                         tree_view.RecalculateVisibleOrder (owner);
219                                         tree_view.UpdateScrollBars (false);
220                                 }
221                         }
222                 }
223
224                 public bool Contains (TreeNode node)
225                 {
226                         return (Array.BinarySearch (nodes, node) > 0);
227                 }
228 #if NET_2_0
229                 public virtual bool ContainsKey (string key)
230                 {
231                         for (int i = 0; i < count; i++) {
232                                 if (string.Compare (nodes [i].Name, key, true, CultureInfo.InvariantCulture) == 0)
233                                         return true;
234                         }
235                         return false;
236                 }
237 #endif
238
239                 public void CopyTo (Array dest, int index)
240                 {
241                         Array.Copy (nodes, index, dest, index, count);
242                 }
243
244                 public IEnumerator GetEnumerator ()
245                 {
246                         return new TreeNodeEnumerator (this);
247                 }
248
249                 public int IndexOf (TreeNode node)
250                 {
251                         return Array.IndexOf (nodes, node);
252                 }
253
254 #if NET_2_0
255                 public virtual int IndexOfKey (string key)
256                 {
257                         for (int i = 0; i < count; i++) {
258                                 if (string.Compare (nodes [i].Name, key, true, CultureInfo.InvariantCulture) == 0)
259                                         return i;
260                         }
261                         return -1;
262                 }
263                 
264                 public virtual TreeNode Insert (int index, string text)
265                 {
266                         TreeNode node = new TreeNode (text);
267                         Insert (index, node);
268                         return node;
269                 }
270 #endif
271
272                 public virtual void Insert (int index, TreeNode node)
273                 {
274                         if (count >= nodes.Length)
275                                 Grow ();
276
277                         Array.Copy (nodes, index, nodes, index + 1, count - index);
278                         nodes [index] = node;
279                         count++;
280
281                         SetupNode (node);
282                 }
283
284 #if NET_2_0
285                 public virtual TreeNode Insert (int index, string key, string text)
286                 {
287                         TreeNode node = new TreeNode (text);
288                         node.Name = key;
289                         Insert (index, node);
290                         return node;
291                 }
292
293                 public virtual TreeNode Insert (int index, string key, string text, int imageIndex)
294                 {
295                         TreeNode node = new TreeNode (text);
296                         node.Name = key;
297                         node.ImageIndex = imageIndex;
298                         Insert (index, node);
299                         return node;
300                 }
301
302                 public virtual TreeNode Insert (int index, string key, string text, string imageKey)
303                 {
304                         TreeNode node = new TreeNode (text);
305                         node.Name = key;
306                         node.ImageKey = imageKey;
307                         Insert (index, node);
308                         return node;
309                 }
310
311                 public virtual TreeNode Insert (int index, string key, string text, int imageIndex, int selectedImageIndex)
312                 {
313                         TreeNode node = new TreeNode (text, imageIndex, selectedImageIndex);
314                         node.Name = key;
315                         Insert (index, node);
316                         return node;
317                 }
318
319                 public virtual TreeNode Insert (int index, string key, string text, string imageKey, string selectedImageKey)
320                 {
321                         TreeNode node = new TreeNode (text);
322                         node.Name = key;
323                         node.ImageKey = imageKey;
324                         node.SelectedImageKey = selectedImageKey;
325                         Insert (index, node);
326                         return node;
327                 }
328 #endif
329
330                 public void Remove (TreeNode node)
331                 {
332                         if (node == null)
333                                 throw new NullReferenceException ();
334
335                         int index = IndexOf (node);
336                         if (index != -1)
337                                 RemoveAt (index);
338 #if ONLY_1_1
339                         else
340                                 throw new NullReferenceException ();
341 #endif
342                 }
343
344                 public virtual void RemoveAt (int index)
345                 {
346                         RemoveAt (index, true);
347                 }
348
349                 private void RemoveAt (int index, bool update)
350                 {
351                         TreeNode removed = nodes [index];
352                         TreeNode prev = GetPrevNode (removed);
353                         TreeNode new_selected = null;
354                         bool visible = removed.IsVisible;
355
356                         TreeView tree_view = null;
357                         if (owner != null)
358                                 tree_view = owner.TreeView;
359
360                         if (tree_view != null) {
361                                 tree_view.RecalculateVisibleOrder (prev);
362
363                                 if (removed == tree_view.SelectedNode) {
364                                         OpenTreeNodeEnumerator oe = new OpenTreeNodeEnumerator (removed);
365                                         if (oe.MoveNext () && oe.MoveNext ()) {
366                                                 new_selected = oe.CurrentNode;
367                                         } else {
368                                                 oe = new OpenTreeNodeEnumerator (removed);
369                                                 oe.MovePrevious ();
370                                                 new_selected = oe.CurrentNode;
371                                         }
372                                 }
373                         }
374
375                         Array.Copy (nodes, index + 1, nodes, index, count - index - 1);
376                         count--;
377                         
378                         nodes[count] = null;
379                         
380                         if (nodes.Length > OrigSize && nodes.Length > (count * 2))
381                                 Shrink ();
382
383                         if (tree_view != null && new_selected != null) {
384                                 tree_view.SelectedNode = new_selected;
385                         }
386
387                         TreeNode parent = removed.parent;
388                         removed.parent = null;
389
390                         if (update && tree_view != null && visible) {
391                                 tree_view.RecalculateVisibleOrder (prev);
392                                 tree_view.UpdateScrollBars (false);
393                                 tree_view.UpdateBelow (parent);
394                         }
395                 }
396
397 #if NET_2_0
398                 public virtual void RemoveByKey (string key)
399                 {
400                         TreeNode node = this[key];
401                         
402                         if (node != null)
403                                 Remove (node);
404                 }
405 #endif
406
407                 private TreeNode GetPrevNode (TreeNode node)
408                 {
409                         OpenTreeNodeEnumerator one = new OpenTreeNodeEnumerator (node);
410
411                         if (one.MovePrevious () && one.MovePrevious ())
412                                 return one.CurrentNode;
413                         return null;
414                 }
415
416                 private void SetupNode (TreeNode node)
417                 {
418                         // Remove it from any old parents
419                         node.Remove ();
420
421                         node.parent = owner;
422
423                         TreeView tree_view = null;
424                         if (owner != null)
425                                 tree_view = owner.TreeView;
426
427                         if (tree_view != null) {
428                                 TreeNode prev = GetPrevNode (node);
429
430                                 if (tree_view.IsHandleCreated && node.ArePreviousNodesExpanded)
431                                         tree_view.RecalculateVisibleOrder (prev);
432                                 if (owner == tree_view.root_node || node.Parent.IsVisible && node.Parent.IsExpanded)
433                                         tree_view.UpdateScrollBars (false);
434                         }
435
436                         if (owner != null && tree_view != null && (owner.IsExpanded || owner.IsRoot)) {
437                                 // tree_view.UpdateBelow (owner);
438                                 tree_view.UpdateNode (owner);
439                                 tree_view.UpdateNode (node);
440                         } else if (owner != null && tree_view != null) {
441                                 tree_view.UpdateBelow (owner);
442                         }
443                 }
444
445                 int IList.Add (object node)
446                 {
447                         return Add ((TreeNode) node);
448                 }
449
450                 bool IList.Contains (object node)
451                 {
452                         return Contains ((TreeNode) node);
453                 }
454                 
455                 int IList.IndexOf (object node)
456                 {
457                         return IndexOf ((TreeNode) node);
458                 }
459
460                 void IList.Insert (int index, object node)
461                 {
462                         Insert (index, (TreeNode) node);
463                 }
464
465                 void IList.Remove (object node)
466                 {
467                         Remove ((TreeNode) node);
468                 }
469
470                 private int AddSorted (TreeNode node)
471                 {
472                         if (count >= nodes.Length)
473                                 Grow ();
474
475                         CompareInfo compare = Application.CurrentCulture.CompareInfo;
476                         int pos = 0;
477                         bool found = false;
478                         for (int i = 0; i < count; i++) {
479                                 pos = i;
480                                 int comp = compare.Compare (node.Text, nodes [i].Text);
481                                 if (comp < 0) {
482                                         found = true;
483                                         break;
484                                 }
485                         }
486
487                         // Stick it at the end
488                         if (!found)
489                                 pos = count;
490
491                         // Move the nodes up and adjust their indices
492                         for (int i = count - 1; i >= pos; i--) {
493                                 nodes [i + 1] = nodes [i];
494                         }
495                         count++;
496                         nodes [pos] = node;
497
498                         return count;
499                 }
500
501                 // Would be nice to do this without running through the collection twice
502                 internal void Sort (IComparer sorter) {
503                         Array.Sort (nodes, 0, count, sorter == null ? new TreeNodeComparer (Application.CurrentCulture.CompareInfo) : sorter);
504
505                         for (int i = 0; i < count; i++) {
506                                 nodes [i].Nodes.Sort (sorter);
507                         }
508                 }
509
510                 private void Grow ()
511                 {
512                         TreeNode [] nn = new TreeNode [nodes.Length + 50];
513                         Array.Copy (nodes, nn, nodes.Length);
514                         nodes = nn;
515                 }
516
517                 private void Shrink ()
518                 {
519                         int len = (count + 1 > OrigSize ? count + 1 : OrigSize);
520                         TreeNode [] nn = new TreeNode [len];
521                         Array.Copy (nodes, nn, count);
522                         nodes = nn;
523                 }
524
525 #if NET_2_0
526                 public TreeNode[] Find (string key, bool searchAllChildren)
527                 {
528                         List<TreeNode> results = new List<TreeNode> (0);
529                         Find (key, searchAllChildren, this, results);
530
531                         return results.ToArray ();             
532                 }
533                 
534                 private static void Find (string key, bool searchAllChildren, TreeNodeCollection nodes, List<TreeNode> results)
535                 {
536                         for (int i = 0; i < nodes.Count; i++) {
537                                 TreeNode thisNode = nodes [i];
538                                 
539                                 if (string.Compare (thisNode.Name, key, true, CultureInfo.InvariantCulture) == 0) 
540                                         results.Add (thisNode);
541
542                         }
543                         // Need to match the Microsoft order.
544
545                         if (searchAllChildren){
546                                 for (int i = 0; i < nodes.Count; i++){
547                                         TreeNodeCollection childNodes = nodes [i].Nodes;
548                                         if (childNodes.Count > 0) {
549                                                 Find (key, searchAllChildren, childNodes, results);
550                                         }
551                                 }
552                         }
553                 }
554 #endif
555                 internal class TreeNodeEnumerator : IEnumerator {
556
557                         private TreeNodeCollection collection;
558                         private int index = -1;
559
560                         public TreeNodeEnumerator (TreeNodeCollection collection)
561                         {
562                                 this.collection = collection;
563                         }
564
565                         public object Current {
566                                 get {
567                                         if (index == -1)
568                                                 return null;
569                                         return collection [index];
570                                 }
571                         }
572
573                         public bool MoveNext ()
574                         {
575                                 if (index + 1 >= collection.Count)
576                                         return false;
577                                 index++;
578                                 return true;
579                         }
580
581                         public void Reset ()
582                         {
583                                 index = -1;
584                         }
585                 }
586
587                 private class TreeNodeComparer : IComparer {
588
589                         private CompareInfo compare;
590                 
591                         public TreeNodeComparer (CompareInfo compare)
592                         {
593                                 this.compare = compare;
594                         }
595                 
596                         public int Compare (object x, object y)
597                         {
598                                 TreeNode l = (TreeNode) x;
599                                 TreeNode r = (TreeNode) y;
600                                 int res = compare.Compare (l.Text, r.Text);
601
602                                 return (res == 0 ? l.Index - r.Index : res);
603                         }
604                 }
605         }
606 }
607