Fix bug #395
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / DataGridViewRowCollection.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) 2005 Novell, Inc. (http://www.novell.com)
21 //
22 // Author:
23 //      Pedro Martínez Juliá <pedromj@gmail.com>
24 //
25
26
27 using System.ComponentModel;
28 using System.Collections;
29 using System.ComponentModel.Design.Serialization;
30
31 namespace System.Windows.Forms
32         {
33         [DesignerSerializerAttribute ("System.Windows.Forms.Design.DataGridViewRowCollectionCodeDomSerializer, " + Consts.AssemblySystem_Design,
34                                       "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
35         [ListBindable (false)]
36         public class DataGridViewRowCollection : IList, ICollection, IEnumerable
37         {
38                 private ArrayList list;
39                 private DataGridView dataGridView;
40                 private bool raiseEvent = true;
41
42                 public DataGridViewRowCollection (DataGridView dataGridView)
43                 {
44                         this.dataGridView = dataGridView;
45                         list = new ArrayList ();
46                 }
47
48                 public int Count {
49                         get { return list.Count; }
50                 }
51
52                 int ICollection.Count {
53                         get { return Count; }
54                 }
55
56                 bool IList.IsFixedSize {
57                         get { return list.IsFixedSize; }
58                 }
59
60                 bool IList.IsReadOnly {
61                         get { return list.IsReadOnly; }
62                 }
63
64                 bool ICollection.IsSynchronized {
65                         get { return list.IsSynchronized; }
66                 }
67
68                 object IList.this [int index] {
69                         get {
70                                 return this[index];
71                         }
72                         set { list[index] = value as DataGridViewRow; }
73                 }
74
75                 public DataGridViewRow this [int index] {
76                         get {
77                                 // Accessing a System.Windows.Forms.DataGridViewRow with this indexer causes the row to become unshared. 
78                                 // To keep the row shared, use the System.Windows.Forms.DataGridViewRowCollection.SharedRow method. 
79                                 // For more information, see Best Practices for Scaling the Windows Forms DataGridView Control.
80                                 DataGridViewRow row = (DataGridViewRow) list [index];
81                                 if (row.Index == -1) {
82                                         row = (DataGridViewRow) row.Clone ();
83                                         row.SetIndex (index);
84                                         list [index] = row;
85                                 }
86                                 return row;
87                         }
88                 }
89
90                 object ICollection.SyncRoot {
91                         get { return list.SyncRoot; }
92                 }
93
94                 public event CollectionChangeEventHandler CollectionChanged;
95
96                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
97                 public virtual int Add ()
98                 {
99                         return Add (dataGridView.RowTemplateFull as DataGridViewRow);
100                 }
101
102                 int IList.Add (object value)
103                 {
104                         return Add (value as DataGridViewRow);
105                 }
106
107                 private int AddCore (DataGridViewRow dataGridViewRow, bool sharable)
108                 {
109                         if (dataGridView.Columns.Count == 0)
110                                 throw new InvalidOperationException ("DataGridView has no columns.");
111                         
112                         int result;
113                         
114                         dataGridViewRow.SetDataGridView (dataGridView);
115
116                         // 
117                         // Add the row just before the editing row (if there is an editing row).
118                         // 
119                         int editing_index = -1;
120                         if (DataGridView != null && DataGridView.EditingRow != null && DataGridView.EditingRow != dataGridViewRow) {
121                                 editing_index = list.Count - 1; // always the last row
122                                 DataGridView.EditingRow.SetIndex (list.Count);
123                         }
124                         
125                         if (editing_index >= 0) {
126                                 list.Insert (editing_index, dataGridViewRow);
127                                 result = editing_index;
128                         } else {
129                                 result = list.Add (dataGridViewRow);
130                         }
131                         
132                         if (sharable && CanBeShared (dataGridViewRow)) {
133                                 dataGridViewRow.SetIndex (-1);
134                         } else {
135                                 dataGridViewRow.SetIndex (result);
136                         }
137
138                         CompleteRowCells (dataGridViewRow);
139                         for (int i = 0; i < dataGridViewRow.Cells.Count; i++) {
140                                 dataGridViewRow.Cells [i].SetOwningColumn (dataGridView.Columns [i]);
141                         }
142
143                         if (raiseEvent) {
144                                 OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, dataGridViewRow));
145                                 DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (result, 1));
146                         }
147
148                         return result;
149                 }
150
151                 // Complete the rows if they are incomplete.
152                 private void CompleteRowCells (DataGridViewRow row)
153                 {
154                         if (row == null || DataGridView == null)
155                                 return;
156
157                         if (row.Cells.Count < DataGridView.ColumnCount) {
158                                 for (int i = row.Cells.Count; i < DataGridView.ColumnCount; i++)
159                                         row.Cells.Add ((DataGridViewCell) DataGridView.Columns[i].CellTemplate.Clone ());
160                         }
161                 }
162
163                 public virtual int Add (DataGridViewRow dataGridViewRow)
164                 {
165                         if (dataGridView.DataSource != null)
166                                 throw new InvalidOperationException ("DataSource of DataGridView is not null.");
167                         return AddCore (dataGridViewRow, true);
168                 }
169                 
170                 private bool CanBeShared (DataGridViewRow row)
171                 {
172                         // We don't currently support shared rows
173                         return false;
174                         
175                         //foreach (DataGridViewCell cell in row.Cells) {
176                         //        if (cell.Value != null)
177                         //                return false;
178                         //        if (cell.ToolTipText != string.Empty)
179                         //                return false;
180                         //        if (cell.ContextMenuStrip != null)
181                         //                return false;
182                         //}
183
184                         //return true;
185                 }
186                 
187
188                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
189                 public virtual int Add (int count)
190                 {
191                         if (count <= 0)
192                                 throw new ArgumentOutOfRangeException("Count is less than or equeal to 0.");
193                         if (dataGridView.DataSource != null)
194                                 throw new InvalidOperationException("DataSource of DataGridView is not null.");
195                         if (dataGridView.Columns.Count == 0)
196                                 throw new InvalidOperationException("DataGridView has no columns.");
197
198                         raiseEvent = false;
199                         int result = 0;
200                         for (int i = 0; i < count; i++)
201                                 result = Add (dataGridView.RowTemplateFull as DataGridViewRow);
202                         DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (result - count + 1, count));
203                         raiseEvent = true;
204                         return result;
205                 }
206
207                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
208                 public virtual int Add (params object[] values)
209                 {
210                         if (values == null)
211                                 throw new ArgumentNullException("values is null.");
212                         if (dataGridView.VirtualMode)
213                                 throw new InvalidOperationException("DataGridView is in virtual mode.");
214
215                         DataGridViewRow row = (DataGridViewRow)dataGridView.RowTemplateFull;
216                         int result = AddCore (row, false);
217                         row.SetValues(values);
218                         return result;
219                 }
220
221                 public virtual int AddCopies (int indexSource, int count)
222                 {
223                         raiseEvent = false;
224                         int lastIndex = 0;
225                         for (int i = 0; i < count; i++)
226                                 lastIndex = AddCopy(indexSource);
227                         DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (lastIndex - count + 1, count));
228                         raiseEvent = true;
229                         return lastIndex;
230                 }
231
232                 public virtual int AddCopy (int indexSource)
233                 {
234                         return Add ((list [indexSource] as DataGridViewRow).Clone () as DataGridViewRow);
235                 }
236
237                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
238                 public virtual void AddRange (params DataGridViewRow [] dataGridViewRows)
239                 {
240                         if (dataGridView.DataSource != null)
241                                 throw new InvalidOperationException ("DataSource of DataGridView is not null.");
242
243                         int count = 0;
244                         int lastIndex = -1;
245                         raiseEvent = false;
246                         foreach (DataGridViewRow row in dataGridViewRows) {
247                                 lastIndex = Add (row);
248                                 count++;
249                         }
250                         raiseEvent = true;
251
252                         DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (lastIndex - count + 1, count));
253                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, dataGridViewRows));
254                 }
255
256                 public virtual void Clear ()
257                 {
258                         int total = list.Count;
259                         
260                         DataGridView.OnRowsPreRemovedInternal (new DataGridViewRowsRemovedEventArgs (0, total));
261
262                         for (int i = 0; i < total; i++) {
263                                 DataGridViewRow row = (DataGridViewRow)list[0];
264                                 
265                                 // We can exit because the NewRow is always last
266                                 if (row.IsNewRow)
267                                         break;
268                                         
269                                 list.Remove (row);
270                                 ReIndex ();
271                         }
272
273                         DataGridView.OnRowsPostRemovedInternal (new DataGridViewRowsRemovedEventArgs (0, total));
274                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null));
275                 }
276
277                 internal void ClearInternal ()
278                 {
279                         list.Clear ();
280                 }
281
282                 bool IList.Contains (object value)
283                 {
284                         return Contains (value as DataGridViewRow);
285                 }
286
287                 public virtual bool Contains (DataGridViewRow dataGridViewRow)
288                 {
289                         return list.Contains (dataGridViewRow);
290                 }
291
292                 void ICollection.CopyTo (Array array, int index)
293                 {
294                         list.CopyTo (array, index);
295                 }
296
297                 public void CopyTo (DataGridViewRow[] array, int index)
298                 {
299                         list.CopyTo (array, index);
300                 }
301
302                 IEnumerator IEnumerable.GetEnumerator ()
303                 {
304                         return list.GetEnumerator ();
305                 }
306
307                 public int GetFirstRow (DataGridViewElementStates includeFilter)
308                 {
309                         for (int i = 0; i < list.Count; i++) {
310                                 DataGridViewRow row = (DataGridViewRow) list [i];
311                                 if ((row.State & includeFilter) != 0)
312                                         return i;
313                         }
314                         return -1;
315                 }
316
317                 public int GetFirstRow (DataGridViewElementStates includeFilter, DataGridViewElementStates excludeFilter)
318                 {
319                         for (int i = 0; i < list.Count; i++) {
320                                 DataGridViewRow row = (DataGridViewRow) list [i];
321                                 if (((row.State & includeFilter) != 0) && ((row.State & excludeFilter) == 0))
322                                         return i;
323                         }
324                         return -1;
325                 }
326
327                 public int GetLastRow (DataGridViewElementStates includeFilter)
328                 {
329                         for (int i = list.Count - 1; i >= 0; i--) {
330                                 DataGridViewRow row = (DataGridViewRow) list [i];
331                                 if ((row.State & includeFilter) != 0)
332                                         return i;
333                         }
334                         return -1;
335                 }
336
337                 public int GetNextRow (int indexStart, DataGridViewElementStates includeFilter)
338                 {
339                         for (int i = indexStart + 1; i < list.Count; i++) {
340                                 DataGridViewRow row = (DataGridViewRow) list [i];
341                                 if ((row.State & includeFilter) != 0)
342                                         return i;
343                         }
344                         return -1;
345                 }
346
347                 public int GetNextRow (int indexStart, DataGridViewElementStates includeFilter, DataGridViewElementStates excludeFilter)
348                 {
349                         for (int i = indexStart + 1; i < list.Count; i++) {
350                                 DataGridViewRow row = (DataGridViewRow) list [i];
351                                 if (((row.State & includeFilter) != 0) && ((row.State & excludeFilter) == 0))
352                                         return i;
353                         }
354                         return -1;
355                 }
356
357                 public int GetPreviousRow (int indexStart, DataGridViewElementStates includeFilter)
358                 {
359                         for (int i = indexStart - 1; i >= 0; i--) {
360                                 DataGridViewRow row = (DataGridViewRow) list [i];
361                                 if ((row.State & includeFilter) != 0)
362                                         return i;
363                         }
364                         return -1;
365                 }
366
367                 public int GetPreviousRow (int indexStart, DataGridViewElementStates includeFilter, DataGridViewElementStates excludeFilter)
368                 {
369                         for (int i = indexStart - 1; i >= 0; i--) {
370                                 DataGridViewRow row = (DataGridViewRow) list [i];
371                                 if (((row.State & includeFilter) != 0) && ((row.State & excludeFilter) == 0))
372                                         return i;
373                         }
374                         return -1;
375                 }
376
377                 public int GetRowCount (DataGridViewElementStates includeFilter)
378                 {
379                         int result = 0;
380                         foreach (DataGridViewRow row in list)
381                                 if ((row.State & includeFilter) != 0)
382                                         result ++;
383                         return result;
384                 }
385
386                 public int GetRowsHeight (DataGridViewElementStates includeFilter)
387                 {
388                         int result = 0;
389                         foreach (DataGridViewRow row in list)
390                                 if ((row.State & includeFilter) != 0)
391                                         result += row.Height;
392                         return result;
393                 }
394
395                 public virtual DataGridViewElementStates GetRowState (int rowIndex)
396                 {
397                         return (list [rowIndex] as DataGridViewRow).State;
398                 }
399
400                 int IList.IndexOf (object value)
401                 {
402                         return IndexOf (value as DataGridViewRow);
403                 }
404
405                 public int IndexOf (DataGridViewRow dataGridViewRow)
406                 {
407                         return list.IndexOf (dataGridViewRow);
408                 }
409
410                 void IList.Insert (int index, object value)
411                 {
412                         Insert (index, value as DataGridViewRow);
413                 }
414
415                 // FIXME: Do *not* allow insertation *after* the editing row!
416                 public virtual void Insert (int rowIndex, DataGridViewRow dataGridViewRow)
417                 {
418                         dataGridViewRow.SetIndex (rowIndex);
419                         dataGridViewRow.SetDataGridView (dataGridView);
420                         CompleteRowCells (dataGridViewRow);
421                         list.Insert (rowIndex, dataGridViewRow);
422                         ReIndex ();
423                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, dataGridViewRow));
424                         if (raiseEvent)
425                                 DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (rowIndex, 1));
426                 }
427
428                 public virtual void Insert (int rowIndex, int count)
429                 {
430                         int index = rowIndex;
431                         raiseEvent = false;
432                         for (int i = 0; i < count; i++)
433                                 Insert (index++, dataGridView.RowTemplateFull);
434                         DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (rowIndex, count));
435                         raiseEvent = true;
436                 }
437
438                 public virtual void Insert (int rowIndex, params object[] values)
439                 {
440                         if (values == null)
441                                 throw new ArgumentNullException ("Values is null.");
442                         if (dataGridView.VirtualMode || dataGridView.DataSource != null)
443                                 throw new InvalidOperationException ();
444                         DataGridViewRow row = dataGridView.RowTemplateFull;
445                         row.SetValues (values);
446                         Insert (rowIndex, row);
447                 }
448
449                 public virtual void InsertCopies (int indexSource, int indexDestination, int count)
450                 {
451                         raiseEvent = false;
452                         int index = indexDestination;
453                         for (int i = 0; i < count; i++)
454                                 InsertCopy (indexSource, index++);
455                         DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (indexDestination, count));
456                         raiseEvent = true;
457                 }
458
459                 public virtual void InsertCopy (int indexSource, int indexDestination)
460                 {
461                         Insert (indexDestination, (list [indexSource] as DataGridViewRow).Clone());
462                 }
463
464                 public virtual void InsertRange (int rowIndex, params DataGridViewRow [] dataGridViewRows)
465                 {
466                         raiseEvent = false;
467                         int index = rowIndex;
468                         int count = 0;
469                         foreach (DataGridViewRow row in dataGridViewRows) {
470                                 Insert (index++, row);
471                                 count++;
472                         }
473                         DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (rowIndex, count));
474                         raiseEvent = true;
475                 }
476
477                 void IList.Remove (object value)
478                 {
479                         Remove (value as DataGridViewRow);
480                 }
481
482                 public virtual void Remove (DataGridViewRow dataGridViewRow)
483                 {
484                         if (dataGridViewRow.IsNewRow)
485                                 throw new InvalidOperationException ("Cannot delete the new row");
486                                 
487                         DataGridView.OnRowsPreRemovedInternal (new DataGridViewRowsRemovedEventArgs (dataGridViewRow.Index, 1));
488                         list.Remove (dataGridViewRow);
489                         ReIndex ();
490                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, dataGridViewRow));
491                         DataGridView.OnRowsPostRemovedInternal (new DataGridViewRowsRemovedEventArgs (dataGridViewRow.Index, 1));
492                 }
493
494                 internal virtual void RemoveInternal (DataGridViewRow dataGridViewRow)
495                 {
496                         DataGridView.OnRowsPreRemovedInternal (new DataGridViewRowsRemovedEventArgs (dataGridViewRow.Index, 1));
497                         list.Remove (dataGridViewRow);
498                         ReIndex ();
499                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, dataGridViewRow));
500                         DataGridView.OnRowsPostRemovedInternal (new DataGridViewRowsRemovedEventArgs (dataGridViewRow.Index, 1));
501                 }
502                 
503                 public virtual void RemoveAt (int index)
504                 {
505                         DataGridViewRow row = this [index];
506                         
507                         Remove (row);
508                 }
509
510                 internal void RemoveAtInternal (int index)
511                 {
512                         DataGridViewRow row = this [index];
513                         
514                         RemoveInternal (row);
515                 }
516
517                 public DataGridViewRow SharedRow (int rowIndex)
518                 {
519                         return (DataGridViewRow) list [rowIndex];
520                 }
521
522                 internal int SharedRowIndexOf (DataGridViewRow row)
523                 {
524                         return list.IndexOf (row);
525                 }
526
527                 protected DataGridView DataGridView {
528                         get { return dataGridView; }
529                 }
530
531                 protected ArrayList List {
532                         get { return list; }
533                 }
534
535                 protected virtual void OnCollectionChanged (CollectionChangeEventArgs e)
536                 {
537                         if (CollectionChanged != null)
538                                 CollectionChanged (this, e);
539                 }
540
541                 internal void AddInternal (DataGridViewRow dataGridViewRow, bool sharable)
542                 {
543                         raiseEvent = false;
544                         AddCore (dataGridViewRow, sharable);
545                         raiseEvent = true;
546                 }
547
548                 internal ArrayList RowIndexSortedArrayList {
549                         get {
550                                 ArrayList result = (ArrayList) list.Clone();
551                                 result.Sort(new RowIndexComparator());
552                                 return result;
553                         }
554                 }
555                 
556                 internal void ReIndex ()
557                 {
558                         for (int i = 0; i < Count; i++)
559                                 (list[i] as DataGridViewRow).SetIndex (i);
560                 }
561                 
562                 internal void Sort (IComparer comparer)
563                 {
564                         // Note: you will probably want to call
565                         // invalidate after using this.
566                         if (DataGridView != null && DataGridView.EditingRow != null)
567                                 list.Sort (0, Count - 1, comparer);
568                         else
569                                 list.Sort (comparer);
570                         
571                         for (int i = 0; i < list.Count; i++)
572                                 (list[i] as DataGridViewRow).SetIndex (i);
573                 }
574
575                 private class RowIndexComparator : IComparer 
576                 {
577                         public int Compare (object o1, object o2)
578                         {
579                                 DataGridViewRow row1 = (DataGridViewRow) o1;
580                                 DataGridViewRow row2 = (DataGridViewRow) o2;
581                                 if (row1.Index < row2.Index) {
582                                         return -1;
583                                 } else if (row1.Index > row2.Index) {
584                                         return 1;
585                                 } else {
586                                         return 0;
587                                 }
588                         }
589                 }
590         }
591 }