merge r98600
[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 (tree_view != null && tree_view.Sorted) {
132                                 res = AddSorted (node);
133                         } else {
134                                 if (count >= nodes.Length)
135                                         Grow ();
136                                 nodes [count++] = node;
137                                 res = count;
138                         }
139
140                         SetupNode (node);
141
142                         return res;
143                 }
144
145 #if NET_2_0
146                 public virtual TreeNode Add (string key, string text)
147                 {
148                         TreeNode node = new TreeNode (text);
149                         node.Name = key;
150                         Add (node);
151                         return node;
152                 }
153
154                 public virtual TreeNode Add (string key, string text, int imageIndex)
155                 {
156                         TreeNode node = Add (key, text);
157                         node.ImageIndex = imageIndex;
158                         return node;
159                 }
160
161                 public virtual TreeNode Add (string key, string text, string imageKey)
162                 {
163                         TreeNode node = Add (key, text);
164                         node.ImageKey = imageKey;
165                         return node;
166
167                 }
168
169                 public virtual TreeNode Add (string key, string text, int imageIndex, int selectedImageIndex)
170                 {
171                         TreeNode node = Add (key, text);
172                         node.ImageIndex = imageIndex;
173                         node.SelectedImageIndex = selectedImageIndex;
174                         return node;
175                 }
176
177                 public virtual TreeNode Add (string key, string text, string imageKey, string selectedImageKey)
178                 {
179                         TreeNode node = Add (key, text);
180                         node.ImageKey = imageKey;
181                         node.SelectedImageKey = selectedImageKey;
182                         return node;
183                 }
184
185
186 #endif
187
188                 public virtual void AddRange (TreeNode [] nodes)
189                 {
190                         if (nodes == null)
191                                 throw new ArgumentNullException("nodes");
192
193                         // We can't just use Array.Copy because the nodes also
194                         // need to have some properties set when they are added.
195                         for (int i = 0; i < nodes.Length; i++)
196                                 Add (nodes [i]);
197                 }
198
199                 public virtual void Clear ()
200                 {
201                         while (count > 0)
202                                 RemoveAt (0, false);
203                         
204                         Array.Clear (nodes, 0, count);
205                         count = 0;
206
207                         TreeView tree_view = null;
208                         if (owner != null) {
209                                 tree_view = owner.TreeView;
210                                 if (tree_view != null) {
211                                         tree_view.highlighted_node = null;
212                                         tree_view.selected_node = null;
213                                         tree_view.UpdateBelow (owner);
214                                         tree_view.RecalculateVisibleOrder (owner);
215                                         tree_view.UpdateScrollBars (false);
216                                 }
217                         }
218                 }
219
220                 public bool Contains (TreeNode node)
221                 {
222                         return (Array.BinarySearch (nodes, node) > 0);
223                 }
224 #if NET_2_0
225                 public virtual bool ContainsKey (string key)
226                 {
227                         for (int i = 0; i < count; i++) {
228                                 if (string.Compare (nodes [i].Name, key, true, CultureInfo.InvariantCulture) == 0)
229                                         return true;
230                         }
231                         return false;
232                 }
233 #endif
234
235                 public void CopyTo (Array dest, int index)
236                 {
237                         Array.Copy (nodes, index, dest, index, count);
238                 }
239
240                 public IEnumerator GetEnumerator ()
241                 {
242                         return new TreeNodeEnumerator (this);
243                 }
244
245                 public int IndexOf (TreeNode node)
246                 {
247                         return Array.IndexOf (nodes, node);
248                 }
249
250 #if NET_2_0
251                 public virtual int IndexOfKey (string key)
252                 {
253                         for (int i = 0; i < count; i++) {
254                                 if (string.Compare (nodes [i].Name, key, true, CultureInfo.InvariantCulture) == 0)
255                                         return i;
256                         }
257                         return -1;
258                 }
259                 
260                 public virtual TreeNode Insert (int index, string text)
261                 {
262                         TreeNode node = new TreeNode (text);
263                         Insert (index, node);
264                         return node;
265                 }
266 #endif
267
268                 public virtual void Insert (int index, TreeNode node)
269                 {
270                         if (count >= nodes.Length)
271                                 Grow ();
272
273                         Array.Copy (nodes, index, nodes, index + 1, count - index);
274                         nodes [index] = node;
275                         count++;
276
277                         SetupNode (node);
278                 }
279
280 #if NET_2_0
281                 public virtual TreeNode Insert (int index, string key, string text)
282                 {
283                         TreeNode node = new TreeNode (text);
284                         node.Name = key;
285                         Insert (index, node);
286                         return node;
287                 }
288
289                 public virtual TreeNode Insert (int index, string key, string text, int imageIndex)
290                 {
291                         TreeNode node = new TreeNode (text);
292                         node.Name = key;
293                         node.ImageIndex = imageIndex;
294                         Insert (index, node);
295                         return node;
296                 }
297
298                 public virtual TreeNode Insert (int index, string key, string text, string imageKey)
299                 {
300                         TreeNode node = new TreeNode (text);
301                         node.Name = key;
302                         node.ImageKey = imageKey;
303                         Insert (index, node);
304                         return node;
305                 }
306
307                 public virtual TreeNode Insert (int index, string key, string text, int imageIndex, int selectedImageIndex)
308                 {
309                         TreeNode node = new TreeNode (text, imageIndex, selectedImageIndex);
310                         node.Name = key;
311                         Insert (index, node);
312                         return node;
313                 }
314
315                 public virtual TreeNode Insert (int index, string key, string text, string imageKey, string selectedImageKey)
316                 {
317                         TreeNode node = new TreeNode (text);
318                         node.Name = key;
319                         node.ImageKey = imageKey;
320                         node.SelectedImageKey = selectedImageKey;
321                         Insert (index, node);
322                         return node;
323                 }
324 #endif
325
326                 public void Remove (TreeNode node)
327                 {
328                         if (node == null)
329                                 throw new NullReferenceException ();
330
331                         int index = IndexOf (node);
332                         if (index != -1)
333                                 RemoveAt (index);
334 #if ONLY_1_1
335                         else
336                                 throw new NullReferenceException ();
337 #endif
338                 }
339
340                 public virtual void RemoveAt (int index)
341                 {
342                         RemoveAt (index, true);
343                 }
344
345                 private void RemoveAt (int index, bool update)
346                 {
347                         TreeNode removed = nodes [index];
348                         TreeNode prev = GetPrevNode (removed);
349                         TreeNode new_selected = null;
350                         bool visible = removed.IsVisible;
351
352                         TreeView tree_view = null;
353                         if (owner != null)
354                                 tree_view = owner.TreeView;
355
356                         if (tree_view != null) {
357                                 tree_view.RecalculateVisibleOrder (prev);
358
359                                 if (removed == tree_view.SelectedNode) {
360                                         OpenTreeNodeEnumerator oe = new OpenTreeNodeEnumerator (removed);
361                                         if (oe.MoveNext () && oe.MoveNext ()) {
362                                                 new_selected = oe.CurrentNode;
363                                         } else {
364                                                 oe = new OpenTreeNodeEnumerator (removed);
365                                                 oe.MovePrevious ();
366                                                 new_selected = oe.CurrentNode;
367                                         }
368                                 }
369                         }
370
371                         Array.Copy (nodes, index + 1, nodes, index, count - index);
372                         count--;
373                         if (nodes.Length > OrigSize && nodes.Length > (count * 2))
374                                 Shrink ();
375
376                         if (tree_view != null && new_selected != null) {
377                                 tree_view.SelectedNode = new_selected;
378                         }
379
380                         TreeNode parent = removed.parent;
381                         removed.parent = null;
382
383                         if (update && tree_view != null && visible) {
384                                 tree_view.RecalculateVisibleOrder (prev);
385                                 tree_view.UpdateScrollBars (false);
386                                 tree_view.UpdateBelow (parent);
387                         }
388                 }
389
390 #if NET_2_0
391                 public virtual void RemoveByKey (string key)
392                 {
393                         TreeNode node = this[key];
394                         
395                         if (node != null)
396                                 Remove (node);
397                 }
398 #endif
399
400                 private TreeNode GetPrevNode (TreeNode node)
401                 {
402                         OpenTreeNodeEnumerator one = new OpenTreeNodeEnumerator (node);
403
404                         if (one.MovePrevious () && one.MovePrevious ())
405                                 return one.CurrentNode;
406                         return null;
407                 }
408
409                 private void SetupNode (TreeNode node)
410                 {
411                         // Remove it from any old parents
412                         node.Remove ();
413
414                         node.parent = owner;
415
416                         TreeView tree_view = null;
417                         if (owner != null)
418                                 tree_view = owner.TreeView;
419
420                         if (tree_view != null) {
421                                 TreeNode prev = GetPrevNode (node);
422
423                                 if (tree_view.IsHandleCreated && node.ArePreviousNodesExpanded)
424                                         tree_view.RecalculateVisibleOrder (prev);
425                                 if (owner == tree_view.root_node || node.Parent.IsVisible && node.Parent.IsExpanded)
426                                         tree_view.UpdateScrollBars (false);
427                         }
428
429                         if (owner != null && tree_view != null && (owner.IsExpanded || owner.IsRoot)) {
430                                 // tree_view.UpdateBelow (owner);
431                                 tree_view.UpdateNode (owner);
432                                 tree_view.UpdateNode (node);
433                         } else if (owner != null && tree_view != null) {
434                                 tree_view.UpdateBelow (owner);
435                         }
436                 }
437
438                 int IList.Add (object node)
439                 {
440                         return Add ((TreeNode) node);
441                 }
442
443                 bool IList.Contains (object node)
444                 {
445                         return Contains ((TreeNode) node);
446                 }
447                 
448                 int IList.IndexOf (object node)
449                 {
450                         return IndexOf ((TreeNode) node);
451                 }
452
453                 void IList.Insert (int index, object node)
454                 {
455                         Insert (index, (TreeNode) node);
456                 }
457
458                 void IList.Remove (object node)
459                 {
460                         Remove ((TreeNode) node);
461                 }
462
463                 private int AddSorted (TreeNode node)
464                 {
465                         if (count >= nodes.Length)
466                                 Grow ();
467
468                         CompareInfo compare = Application.CurrentCulture.CompareInfo;
469                         int pos = 0;
470                         bool found = false;
471                         for (int i = 0; i < count; i++) {
472                                 pos = i;
473                                 int comp = compare.Compare (node.Text, nodes [i].Text);
474                                 if (comp < 0) {
475                                         found = true;
476                                         break;
477                                 }
478                         }
479
480                         // Stick it at the end
481                         if (!found)
482                                 pos = count;
483
484                         // Move the nodes up and adjust their indices
485                         for (int i = count - 1; i >= pos; i--) {
486                                 nodes [i + 1] = nodes [i];
487                         }
488                         count++;
489                         nodes [pos] = node;
490
491                         return count;
492                 }
493
494                 // Would be nice to do this without running through the collection twice
495                 internal void Sort (IComparer sorter) {
496                         Array.Sort (nodes, 0, count, sorter == null ? new TreeNodeComparer (Application.CurrentCulture.CompareInfo) : sorter);
497
498                         for (int i = 0; i < count; i++) {
499                                 nodes [i].Nodes.Sort (sorter);
500                         }
501                 }
502
503                 private void Grow ()
504                 {
505                         TreeNode [] nn = new TreeNode [nodes.Length + 50];
506                         Array.Copy (nodes, nn, nodes.Length);
507                         nodes = nn;
508                 }
509
510                 private void Shrink ()
511                 {
512                         int len = (count + 1 > OrigSize ? count + 1 : OrigSize);
513                         TreeNode [] nn = new TreeNode [len];
514                         Array.Copy (nodes, nn, count);
515                         nodes = nn;
516                 }
517
518 #if NET_2_0
519                 public TreeNode[] Find (string key, bool searchAllChildren)
520                 {
521                         List<TreeNode> results = new List<TreeNode> (0);
522                         Find (key, searchAllChildren, this, results);
523
524                         return results.ToArray ();             
525                 }
526                 
527                 private static void Find (string key, bool searchAllChildren, TreeNodeCollection nodes, List<TreeNode> results)
528                 {
529                         for (int i = 0; i < nodes.Count; i++) {
530                                 TreeNode thisNode = nodes [i];
531                                 
532                                 if (string.Compare (thisNode.Name, key, true, CultureInfo.InvariantCulture) == 0) 
533                                         results.Add (thisNode);
534
535                         }
536                         // Need to match the Microsoft order.
537
538                         if (searchAllChildren){
539                                 for (int i = 0; i < nodes.Count; i++){
540                                         TreeNodeCollection childNodes = nodes [i].Nodes;
541                                         if (childNodes.Count > 0) {
542                                                 Find (key, searchAllChildren, childNodes, results);
543                                         }
544                                 }
545                         }
546                 }
547 #endif
548                 internal class TreeNodeEnumerator : IEnumerator {
549
550                         private TreeNodeCollection collection;
551                         private int index = -1;
552
553                         public TreeNodeEnumerator (TreeNodeCollection collection)
554                         {
555                                 this.collection = collection;
556                         }
557
558                         public object Current {
559                                 get {
560                                         if (index == -1)
561                                                 return null;
562                                         return collection [index];
563                                 }
564                         }
565
566                         public bool MoveNext ()
567                         {
568                                 if (index + 1 >= collection.Count)
569                                         return false;
570                                 index++;
571                                 return true;
572                         }
573
574                         public void Reset ()
575                         {
576                                 index = -1;
577                         }
578                 }
579
580                 private class TreeNodeComparer : IComparer {
581
582                         private CompareInfo compare;
583                 
584                         public TreeNodeComparer (CompareInfo compare)
585                         {
586                                 this.compare = compare;
587                         }
588                 
589                         public int Compare (object x, object y)
590                         {
591                                 TreeNode l = (TreeNode) x;
592                                 TreeNode r = (TreeNode) y;
593                                 int res = compare.Compare (l.Text, r.Text);
594
595                                 return (res == 0 ? l.Index - r.Index : res);
596                         }
597                 }
598         }
599 }
600