New test.
[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 namespace System.Windows.Forms {
32         [Editor("System.Windows.Forms.Design.TreeNodeCollectionEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
33         public class TreeNodeCollection : IList, ICollection, IEnumerable {
34
35                 private static readonly int OrigSize = 50;
36
37                 private TreeNode owner;
38                 private int count;
39                 private TreeNode [] nodes;
40
41                 private TreeNodeCollection ()
42                 {
43                 }
44
45                 internal TreeNodeCollection (TreeNode owner)
46                 {
47                         this.owner = owner;
48                         nodes = new TreeNode [OrigSize];
49                 }
50
51                 [Browsable(false)]
52                 [EditorBrowsable(EditorBrowsableState.Advanced)]
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 ("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 Add (string text)
99                 {
100                         TreeNode res = new TreeNode (text);
101                         Add (res);
102                         return res;
103                 }
104
105                 public virtual int Add (TreeNode node)
106                 {
107                         if (node == null)
108                                 throw new ArgumentNullException("node");
109
110                         int res;
111                         TreeView tree_view = null;
112
113                         if (tree_view != null && tree_view.Sorted) {
114                                 res = AddSorted (node);
115                         } else {
116                                 if (count >= nodes.Length)
117                                         Grow ();
118                                 nodes [count++] = node;
119                                 res = count;
120                         }
121
122                         SetupNode (node);
123
124                         return res;
125                 }
126
127                 public virtual void AddRange (TreeNode [] nodes)
128                 {
129                         if (nodes == null)
130                                 throw new ArgumentNullException("node");
131
132                         // We can't just use Array.Copy because the nodes also
133                         // need to have some properties set when they are added.
134                         for (int i = 0; i < nodes.Length; i++)
135                                 Add (nodes [i]);
136                 }
137
138                 public virtual void Clear ()
139                 {
140                         while (count > 0)
141                                 RemoveAt (0, false);
142                         
143                         Array.Clear (nodes, 0, count);
144                         count = 0;
145
146                         TreeView tree_view = null;
147                         if (owner != null) {
148                                 tree_view = owner.TreeView;
149                                 if (owner.IsRoot)
150                                         tree_view.top_node = null;
151                                 if (tree_view != null) {
152                                         tree_view.UpdateBelow (owner);
153                                         tree_view.RecalculateVisibleOrder (owner);
154                                         tree_view.UpdateScrollBars ();
155                                 }
156                         }
157                 }
158
159                 public bool Contains (TreeNode node)
160                 {
161                         return (Array.BinarySearch (nodes, node) > 0);
162                 }
163
164                 public void CopyTo (Array dest, int index)
165                 {
166                         Array.Copy (nodes, index, dest, index, count);
167                 }
168
169                 public IEnumerator GetEnumerator ()
170                 {
171                         return new TreeNodeEnumerator (this);
172                 }
173
174                 public int IndexOf (TreeNode node)
175                 {
176                         return Array.IndexOf (nodes, node);
177                 }
178
179                 public virtual void Insert (int index, TreeNode node)
180                 {
181                         if (count >= nodes.Length)
182                                 Grow ();
183
184                         Array.Copy (nodes, index, nodes, index + 1, count - index);
185                         nodes [index] = node;
186                         count++;
187
188                         SetupNode (node);
189                 }
190
191                 public void Remove (TreeNode node)
192                 {
193                         if (node == null)
194                                 throw new NullReferenceException ();
195
196                         int index = IndexOf (node);
197                         if (index != -1)
198                                 RemoveAt (index);
199 #if ONLY_1_1
200                         else
201                                 throw new NullReferenceException ();
202 #endif
203                 }
204
205                 public virtual void RemoveAt (int index)
206                 {
207                         RemoveAt (index, true);
208                 }
209
210                 private void RemoveAt (int index, bool update)
211                 {
212                         TreeNode removed = nodes [index];
213                         TreeNode prev = GetPrevNode (removed);
214                         TreeNode new_selected = null;
215                         bool visible = removed.IsVisible;
216
217                         TreeView tree_view = null;
218                         if (owner != null)
219                                 tree_view = owner.TreeView;
220
221                         if (tree_view != null) {
222                                 tree_view.RecalculateVisibleOrder (prev);
223                                 if (removed == tree_view.top_node) {
224
225                                         if (removed.IsRoot) {
226                                                 tree_view.top_node = null;
227                                         } else {
228                                                 OpenTreeNodeEnumerator oe = new OpenTreeNodeEnumerator (removed);
229                                                 if (oe.MovePrevious () && oe.MovePrevious ()) {
230                                                         tree_view.top_node = oe.CurrentNode;
231                                                 } else {
232                                                         removed.is_expanded = false;
233                                                         oe = new OpenTreeNodeEnumerator (removed);
234                                                         if (oe.MoveNext () && oe.MoveNext ()) {
235                                                                 tree_view.top_node = oe.CurrentNode;
236                                                         } else {
237                                                                 tree_view.top_node = null;
238                                                         }
239                                                 }
240                                         }
241                                 }
242                                 if (removed == tree_view.selected_node) {
243                                         OpenTreeNodeEnumerator oe = new OpenTreeNodeEnumerator (removed);
244                                         if (oe.MoveNext () && oe.MoveNext ()) {
245                                                 new_selected = oe.CurrentNode;
246                                         } else {
247                                                 oe = new OpenTreeNodeEnumerator (removed);
248                                                 oe.MovePrevious ();
249                                                 new_selected = oe.CurrentNode;
250                                         }
251                                 }
252                         }
253
254                         Array.Copy (nodes, index + 1, nodes, index, count - index);
255                         count--;
256                         if (nodes.Length > OrigSize && nodes.Length > (count * 2))
257                                 Shrink ();
258
259                         if (tree_view != null && new_selected != null) {
260                                 tree_view.SelectedNode = new_selected;
261                         }
262
263                         TreeNode parent = removed.parent;
264                         removed.parent = null;
265
266                         if (update && tree_view != null && visible) {
267                                 tree_view.RecalculateVisibleOrder (prev);
268                                 tree_view.UpdateScrollBars ();
269                                 tree_view.UpdateBelow (parent);
270                         }
271                 }
272
273                 private TreeNode GetPrevNode (TreeNode node)
274                 {
275                         OpenTreeNodeEnumerator one = new OpenTreeNodeEnumerator (node);
276
277                         if (one.MovePrevious () && one.MovePrevious ())
278                                 return one.CurrentNode;
279                         return null;
280                 }
281
282                 private void SetupNode (TreeNode node)
283                 {
284                         // Remove it from any old parents
285                         node.Remove ();
286
287                         node.parent = owner;
288
289                         TreeView tree_view = null;
290                         if (owner != null)
291                                 tree_view = owner.TreeView;
292
293                         if (tree_view != null) {
294                                 TreeNode prev = GetPrevNode (node);
295
296                                 if (tree_view.top_node == null)
297                                         tree_view.top_node = node;
298
299                                 if (node.IsVisible)
300                                         tree_view.RecalculateVisibleOrder (prev);
301                                 if (owner == tree_view.root_node || node.Parent.IsVisible && node.Parent.IsExpanded)
302                                         tree_view.UpdateScrollBars ();
303                         }
304
305                         if (owner != null && tree_view != null && (owner.IsExpanded || owner.IsRoot)) {
306                                 // tree_view.UpdateBelow (owner);
307                                 tree_view.UpdateNode (owner);
308                                 tree_view.UpdateNode (node);
309                         } else if (owner != null && tree_view != null) {
310                                 tree_view.UpdateBelow (owner);
311                         }
312                 }
313
314                 int IList.Add (object node)
315                 {
316                         return Add ((TreeNode) node);
317                 }
318
319                 bool IList.Contains (object node)
320                 {
321                         return Contains ((TreeNode) node);
322                 }
323                 
324                 int IList.IndexOf (object node)
325                 {
326                         return IndexOf ((TreeNode) node);
327                 }
328
329                 void IList.Insert (int index, object node)
330                 {
331                         Insert (index, (TreeNode) node);
332                 }
333
334                 void IList.Remove (object node)
335                 {
336                         Remove ((TreeNode) node);
337                 }
338
339                 private int AddSorted (TreeNode node)
340                 {
341                         if (count >= nodes.Length)
342                                 Grow ();
343
344                         CompareInfo compare = Application.CurrentCulture.CompareInfo;
345                         int pos = 0;
346                         bool found = false;
347                         for (int i = 0; i < count; i++) {
348                                 pos = i;
349                                 int comp = compare.Compare (node.Text, nodes [i].Text);
350                                 if (comp < 0) {
351                                         found = true;
352                                         break;
353                                 }
354                         }
355
356                         // Stick it at the end
357                         if (!found)
358                                 pos = count;
359
360                         // Move the nodes up and adjust their indices
361                         for (int i = count - 1; i >= pos; i--) {
362                                 nodes [i + 1] = nodes [i];
363                         }
364                         count++;
365                         nodes [pos] = node;
366
367                         return count;
368                 }
369
370                 // Would be nice to do this without running through the collection twice
371                 internal void Sort () {
372                         Array.Sort (nodes, 0, count, new TreeNodeComparer (Application.CurrentCulture.CompareInfo));
373
374                         for (int i = 0; i < count; i++) {
375                                 nodes [i].Nodes.Sort ();
376                         }
377                 }
378
379                 private void Grow ()
380                 {
381                         TreeNode [] nn = new TreeNode [nodes.Length + 50];
382                         Array.Copy (nodes, nn, nodes.Length);
383                         nodes = nn;
384                 }
385
386                 private void Shrink ()
387                 {
388                         int len = (count + 1 > OrigSize ? count + 1 : OrigSize);
389                         TreeNode [] nn = new TreeNode [len];
390                         Array.Copy (nodes, nn, count);
391                         nodes = nn;
392                 }
393
394                 
395                 internal class TreeNodeEnumerator : IEnumerator {
396
397                         private TreeNodeCollection collection;
398                         private int index = -1;
399
400                         public TreeNodeEnumerator (TreeNodeCollection collection)
401                         {
402                                 this.collection = collection;
403                         }
404
405                         public object Current {
406                                 get {
407                                         if (index == -1)
408                                                 return null;
409                                         return collection [index];
410                                 }
411                         }
412
413                         public bool MoveNext ()
414                         {
415                                 if (index + 1 >= collection.Count)
416                                         return false;
417                                 index++;
418                                 return true;
419                         }
420
421                         public void Reset ()
422                         {
423                                 index = -1;
424                         }
425                 }
426
427                 private class TreeNodeComparer : IComparer {
428
429                         private CompareInfo compare;
430                 
431                         public TreeNodeComparer (CompareInfo compare)
432                         {
433                                 this.compare = compare;
434                         }
435                 
436                         public int Compare (object x, object y)
437                         {
438                                 TreeNode l = (TreeNode) x;
439                                 TreeNode r = (TreeNode) y;
440                                 int res = compare.Compare (l.Text, r.Text);
441
442                                 return (res == 0 ? l.Index - r.Index : res);
443                         }
444                 }
445         }
446 }
447