2007-05-01 Frederik Carlier <frederik.carlier@carlier-online.be>
[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 #if NET_2_0
128                 public virtual TreeNode Add (string key, string text)
129                 {
130                         TreeNode node = new TreeNode (text);
131                         node.Name = key;
132                         Add (node);
133                         return node;
134                 }
135
136                 public virtual TreeNode Add (string key, string text, int imageIndex)
137                 {
138                         TreeNode node = Add (key, text);
139                         node.ImageIndex = imageIndex;
140                         return node;
141                 }
142
143                 public virtual TreeNode Add (string key, string text, string imageKey)
144                 {
145                         TreeNode node = Add (key, text);
146                         node.ImageKey = imageKey;
147                         return node;
148
149                 }
150
151                 public virtual TreeNode Add (string key, string text, int imageIndex, int selectedImageIndex)
152                 {
153                         TreeNode node = Add (key, text);
154                         node.ImageIndex = imageIndex;
155                         node.SelectedImageIndex = selectedImageIndex;
156                         return node;
157                 }
158
159                 public virtual TreeNode Add (string key, string text, string imageKey, string selectedImageKey)
160                 {
161                         TreeNode node = Add (key, text);
162                         node.ImageKey = imageKey;
163                         node.SelectedImageKey = selectedImageKey;
164                         return node;
165                 }
166
167
168 #endif
169
170                 public virtual void AddRange (TreeNode [] nodes)
171                 {
172                         if (nodes == null)
173                                 throw new ArgumentNullException("node");
174
175                         // We can't just use Array.Copy because the nodes also
176                         // need to have some properties set when they are added.
177                         for (int i = 0; i < nodes.Length; i++)
178                                 Add (nodes [i]);
179                 }
180
181                 public virtual void Clear ()
182                 {
183                         while (count > 0)
184                                 RemoveAt (0, false);
185                         
186                         Array.Clear (nodes, 0, count);
187                         count = 0;
188
189                         TreeView tree_view = null;
190                         if (owner != null) {
191                                 tree_view = owner.TreeView;
192                                 if (tree_view != null) {
193                                         tree_view.UpdateBelow (owner);
194                                         tree_view.RecalculateVisibleOrder (owner);
195                                         tree_view.UpdateScrollBars (false);
196                                 }
197                         }
198                 }
199
200                 public bool Contains (TreeNode node)
201                 {
202                         return (Array.BinarySearch (nodes, node) > 0);
203                 }
204 #if NET_2_0
205                 public bool ContainsKey (string key)
206                 {
207                         for (int i = 0; i < count; i++) {
208                                 if (string.Compare (nodes [i].Name, key, true, CultureInfo.InvariantCulture) == 0)
209                                         return true;
210                         }
211                         return false;
212                 }
213 #endif
214
215                 public void CopyTo (Array dest, int index)
216                 {
217                         Array.Copy (nodes, index, dest, index, count);
218                 }
219
220                 public IEnumerator GetEnumerator ()
221                 {
222                         return new TreeNodeEnumerator (this);
223                 }
224
225                 public int IndexOf (TreeNode node)
226                 {
227                         return Array.IndexOf (nodes, node);
228                 }
229
230 #if NET_2_0
231                 public int IndexOfKey (string key)
232                 {
233                         for (int i = 0; i < count; i++) {
234                                 if (string.Compare (nodes [i].Name, key, true, CultureInfo.InvariantCulture) == 0)
235                                         return i;
236                         }
237                         return -1;
238                 }
239 #endif
240
241                 public virtual void Insert (int index, TreeNode node)
242                 {
243                         if (count >= nodes.Length)
244                                 Grow ();
245
246                         Array.Copy (nodes, index, nodes, index + 1, count - index);
247                         nodes [index] = node;
248                         count++;
249
250                         SetupNode (node);
251                 }
252
253                 public void Remove (TreeNode node)
254                 {
255                         if (node == null)
256                                 throw new NullReferenceException ();
257
258                         int index = IndexOf (node);
259                         if (index != -1)
260                                 RemoveAt (index);
261 #if ONLY_1_1
262                         else
263                                 throw new NullReferenceException ();
264 #endif
265                 }
266
267                 public virtual void RemoveAt (int index)
268                 {
269                         RemoveAt (index, true);
270                 }
271
272                 private void RemoveAt (int index, bool update)
273                 {
274                         TreeNode removed = nodes [index];
275                         TreeNode prev = GetPrevNode (removed);
276                         TreeNode new_selected = null;
277                         bool visible = removed.IsVisible;
278
279                         TreeView tree_view = null;
280                         if (owner != null)
281                                 tree_view = owner.TreeView;
282
283                         if (tree_view != null) {
284                                 tree_view.RecalculateVisibleOrder (prev);
285
286                                 if (removed == tree_view.SelectedNode) {
287                                         OpenTreeNodeEnumerator oe = new OpenTreeNodeEnumerator (removed);
288                                         if (oe.MoveNext () && oe.MoveNext ()) {
289                                                 new_selected = oe.CurrentNode;
290                                         } else {
291                                                 oe = new OpenTreeNodeEnumerator (removed);
292                                                 oe.MovePrevious ();
293                                                 new_selected = oe.CurrentNode;
294                                         }
295                                 }
296                         }
297
298                         Array.Copy (nodes, index + 1, nodes, index, count - index);
299                         count--;
300                         if (nodes.Length > OrigSize && nodes.Length > (count * 2))
301                                 Shrink ();
302
303                         if (tree_view != null && new_selected != null) {
304                                 tree_view.SelectedNode = new_selected;
305                         }
306
307                         TreeNode parent = removed.parent;
308                         removed.parent = null;
309
310                         if (update && tree_view != null && visible) {
311                                 tree_view.RecalculateVisibleOrder (prev);
312                                 tree_view.UpdateScrollBars (false);
313                                 tree_view.UpdateBelow (parent);
314                         }
315                 }
316
317                 private TreeNode GetPrevNode (TreeNode node)
318                 {
319                         OpenTreeNodeEnumerator one = new OpenTreeNodeEnumerator (node);
320
321                         if (one.MovePrevious () && one.MovePrevious ())
322                                 return one.CurrentNode;
323                         return null;
324                 }
325
326                 private void SetupNode (TreeNode node)
327                 {
328                         // Remove it from any old parents
329                         node.Remove ();
330
331                         node.parent = owner;
332
333                         TreeView tree_view = null;
334                         if (owner != null)
335                                 tree_view = owner.TreeView;
336
337                         if (tree_view != null) {
338                                 TreeNode prev = GetPrevNode (node);
339
340                                 if (node.IsVisible)
341                                         tree_view.RecalculateVisibleOrder (prev);
342                                 if (owner == tree_view.root_node || node.Parent.IsVisible && node.Parent.IsExpanded)
343                                         tree_view.UpdateScrollBars (false);
344                         }
345
346                         if (owner != null && tree_view != null && (owner.IsExpanded || owner.IsRoot)) {
347                                 // tree_view.UpdateBelow (owner);
348                                 tree_view.UpdateNode (owner);
349                                 tree_view.UpdateNode (node);
350                         } else if (owner != null && tree_view != null) {
351                                 tree_view.UpdateBelow (owner);
352                         }
353                 }
354
355                 int IList.Add (object node)
356                 {
357                         return Add ((TreeNode) node);
358                 }
359
360                 bool IList.Contains (object node)
361                 {
362                         return Contains ((TreeNode) node);
363                 }
364                 
365                 int IList.IndexOf (object node)
366                 {
367                         return IndexOf ((TreeNode) node);
368                 }
369
370                 void IList.Insert (int index, object node)
371                 {
372                         Insert (index, (TreeNode) node);
373                 }
374
375                 void IList.Remove (object node)
376                 {
377                         Remove ((TreeNode) node);
378                 }
379
380                 private int AddSorted (TreeNode node)
381                 {
382                         if (count >= nodes.Length)
383                                 Grow ();
384
385                         CompareInfo compare = Application.CurrentCulture.CompareInfo;
386                         int pos = 0;
387                         bool found = false;
388                         for (int i = 0; i < count; i++) {
389                                 pos = i;
390                                 int comp = compare.Compare (node.Text, nodes [i].Text);
391                                 if (comp < 0) {
392                                         found = true;
393                                         break;
394                                 }
395                         }
396
397                         // Stick it at the end
398                         if (!found)
399                                 pos = count;
400
401                         // Move the nodes up and adjust their indices
402                         for (int i = count - 1; i >= pos; i--) {
403                                 nodes [i + 1] = nodes [i];
404                         }
405                         count++;
406                         nodes [pos] = node;
407
408                         return count;
409                 }
410
411                 // Would be nice to do this without running through the collection twice
412                 internal void Sort (IComparer sorter) {
413                         Array.Sort (nodes, 0, count, sorter == null ? new TreeNodeComparer (Application.CurrentCulture.CompareInfo) : sorter);
414
415                         for (int i = 0; i < count; i++) {
416                                 nodes [i].Nodes.Sort (sorter);
417                         }
418                 }
419
420                 private void Grow ()
421                 {
422                         TreeNode [] nn = new TreeNode [nodes.Length + 50];
423                         Array.Copy (nodes, nn, nodes.Length);
424                         nodes = nn;
425                 }
426
427                 private void Shrink ()
428                 {
429                         int len = (count + 1 > OrigSize ? count + 1 : OrigSize);
430                         TreeNode [] nn = new TreeNode [len];
431                         Array.Copy (nodes, nn, count);
432                         nodes = nn;
433                 }
434
435                 
436                 internal class TreeNodeEnumerator : IEnumerator {
437
438                         private TreeNodeCollection collection;
439                         private int index = -1;
440
441                         public TreeNodeEnumerator (TreeNodeCollection collection)
442                         {
443                                 this.collection = collection;
444                         }
445
446                         public object Current {
447                                 get {
448                                         if (index == -1)
449                                                 return null;
450                                         return collection [index];
451                                 }
452                         }
453
454                         public bool MoveNext ()
455                         {
456                                 if (index + 1 >= collection.Count)
457                                         return false;
458                                 index++;
459                                 return true;
460                         }
461
462                         public void Reset ()
463                         {
464                                 index = -1;
465                         }
466                 }
467
468                 private class TreeNodeComparer : IComparer {
469
470                         private CompareInfo compare;
471                 
472                         public TreeNodeComparer (CompareInfo compare)
473                         {
474                                 this.compare = compare;
475                         }
476                 
477                         public int Compare (object x, object y)
478                         {
479                                 TreeNode l = (TreeNode) x;
480                                 TreeNode r = (TreeNode) y;
481                                 int res = compare.Compare (l.Text, r.Text);
482
483                                 return (res == 0 ? l.Index - r.Index : res);
484                         }
485                 }
486         }
487 }
488