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