Merge pull request #980 from StephenMcConnel/bug-18638
[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                                 row.SetIndex(0);
265
266                                 // We can exit because the NewRow is always last
267                                 if (row.IsNewRow)
268                                         break;
269
270                                 row.SetDataGridView (null);
271                                 list.Remove (row);
272                         }
273
274                         DataGridView.OnRowsPostRemovedInternal (new DataGridViewRowsRemovedEventArgs (0, total));
275                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null));
276                 }
277
278                 internal void ClearInternal ()
279                 {
280                         list.Clear ();
281                 }
282
283                 bool IList.Contains (object value)
284                 {
285                         return Contains (value as DataGridViewRow);
286                 }
287
288                 public virtual bool Contains (DataGridViewRow dataGridViewRow)
289                 {
290                         return list.Contains (dataGridViewRow);
291                 }
292
293                 void ICollection.CopyTo (Array array, int index)
294                 {
295                         list.CopyTo (array, index);
296                 }
297
298                 public void CopyTo (DataGridViewRow[] array, int index)
299                 {
300                         list.CopyTo (array, index);
301                 }
302
303                 IEnumerator IEnumerable.GetEnumerator ()
304                 {
305                         return list.GetEnumerator ();
306                 }
307
308                 public int GetFirstRow (DataGridViewElementStates includeFilter)
309                 {
310                         for (int i = 0; i < list.Count; i++) {
311                                 DataGridViewRow row = (DataGridViewRow) list [i];
312                                 if ((row.State & includeFilter) != 0)
313                                         return i;
314                         }
315                         return -1;
316                 }
317
318                 public int GetFirstRow (DataGridViewElementStates includeFilter, DataGridViewElementStates excludeFilter)
319                 {
320                         for (int i = 0; i < list.Count; i++) {
321                                 DataGridViewRow row = (DataGridViewRow) list [i];
322                                 if (((row.State & includeFilter) != 0) && ((row.State & excludeFilter) == 0))
323                                         return i;
324                         }
325                         return -1;
326                 }
327
328                 public int GetLastRow (DataGridViewElementStates includeFilter)
329                 {
330                         for (int i = list.Count - 1; i >= 0; i--) {
331                                 DataGridViewRow row = (DataGridViewRow) list [i];
332                                 if ((row.State & includeFilter) != 0)
333                                         return i;
334                         }
335                         return -1;
336                 }
337
338                 public int GetNextRow (int indexStart, DataGridViewElementStates includeFilter)
339                 {
340                         for (int i = indexStart + 1; i < list.Count; i++) {
341                                 DataGridViewRow row = (DataGridViewRow) list [i];
342                                 if ((row.State & includeFilter) != 0)
343                                         return i;
344                         }
345                         return -1;
346                 }
347
348                 public int GetNextRow (int indexStart, DataGridViewElementStates includeFilter, DataGridViewElementStates excludeFilter)
349                 {
350                         for (int i = indexStart + 1; i < list.Count; i++) {
351                                 DataGridViewRow row = (DataGridViewRow) list [i];
352                                 if (((row.State & includeFilter) != 0) && ((row.State & excludeFilter) == 0))
353                                         return i;
354                         }
355                         return -1;
356                 }
357
358                 public int GetPreviousRow (int indexStart, DataGridViewElementStates includeFilter)
359                 {
360                         for (int i = indexStart - 1; i >= 0; i--) {
361                                 DataGridViewRow row = (DataGridViewRow) list [i];
362                                 if ((row.State & includeFilter) != 0)
363                                         return i;
364                         }
365                         return -1;
366                 }
367
368                 public int GetPreviousRow (int indexStart, DataGridViewElementStates includeFilter, DataGridViewElementStates excludeFilter)
369                 {
370                         for (int i = indexStart - 1; i >= 0; i--) {
371                                 DataGridViewRow row = (DataGridViewRow) list [i];
372                                 if (((row.State & includeFilter) != 0) && ((row.State & excludeFilter) == 0))
373                                         return i;
374                         }
375                         return -1;
376                 }
377
378                 public int GetRowCount (DataGridViewElementStates includeFilter)
379                 {
380                         int result = 0;
381                         foreach (DataGridViewRow row in list)
382                                 if ((row.State & includeFilter) != 0)
383                                         result ++;
384                         return result;
385                 }
386
387                 public int GetRowsHeight (DataGridViewElementStates includeFilter)
388                 {
389                         int result = 0;
390                         foreach (DataGridViewRow row in list)
391                                 if ((row.State & includeFilter) != 0)
392                                         result += row.Height;
393                         return result;
394                 }
395
396                 public virtual DataGridViewElementStates GetRowState (int rowIndex)
397                 {
398                         return (list [rowIndex] as DataGridViewRow).State;
399                 }
400
401                 int IList.IndexOf (object value)
402                 {
403                         return IndexOf (value as DataGridViewRow);
404                 }
405
406                 public int IndexOf (DataGridViewRow dataGridViewRow)
407                 {
408                         return list.IndexOf (dataGridViewRow);
409                 }
410
411                 void IList.Insert (int index, object value)
412                 {
413                         Insert (index, value as DataGridViewRow);
414                 }
415
416                 // FIXME: Do *not* allow insertation *after* the editing row!
417                 public virtual void Insert (int rowIndex, DataGridViewRow dataGridViewRow)
418                 {
419                         dataGridViewRow.SetIndex (rowIndex);
420                         dataGridViewRow.SetDataGridView (dataGridView);
421                         CompleteRowCells (dataGridViewRow);
422                         list.Insert (rowIndex, dataGridViewRow);
423                         ReIndex ();
424                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, dataGridViewRow));
425                         if (raiseEvent)
426                                 DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (rowIndex, 1));
427                 }
428
429                 public virtual void Insert (int rowIndex, int count)
430                 {
431                         int index = rowIndex;
432                         raiseEvent = false;
433                         for (int i = 0; i < count; i++)
434                                 Insert (index++, dataGridView.RowTemplateFull);
435                         DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (rowIndex, count));
436                         raiseEvent = true;
437                 }
438
439                 public virtual void Insert (int rowIndex, params object[] values)
440                 {
441                         if (values == null)
442                                 throw new ArgumentNullException ("Values is null.");
443                         if (dataGridView.VirtualMode || dataGridView.DataSource != null)
444                                 throw new InvalidOperationException ();
445                         DataGridViewRow row = dataGridView.RowTemplateFull;
446                         row.SetValues (values);
447                         Insert (rowIndex, row);
448                 }
449
450                 public virtual void InsertCopies (int indexSource, int indexDestination, int count)
451                 {
452                         raiseEvent = false;
453                         int index = indexDestination;
454                         for (int i = 0; i < count; i++)
455                                 InsertCopy (indexSource, index++);
456                         DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (indexDestination, count));
457                         raiseEvent = true;
458                 }
459
460                 public virtual void InsertCopy (int indexSource, int indexDestination)
461                 {
462                         Insert (indexDestination, (list [indexSource] as DataGridViewRow).Clone());
463                 }
464
465                 public virtual void InsertRange (int rowIndex, params DataGridViewRow [] dataGridViewRows)
466                 {
467                         raiseEvent = false;
468                         int index = rowIndex;
469                         int count = 0;
470                         foreach (DataGridViewRow row in dataGridViewRows) {
471                                 Insert (index++, row);
472                                 count++;
473                         }
474                         DataGridView.OnRowsAddedInternal (new DataGridViewRowsAddedEventArgs (rowIndex, count));
475                         raiseEvent = true;
476                 }
477
478                 void IList.Remove (object value)
479                 {
480                         Remove (value as DataGridViewRow);
481                 }
482
483                 public virtual void Remove (DataGridViewRow dataGridViewRow)
484                 {
485                         if (dataGridViewRow.IsNewRow)
486                                 throw new InvalidOperationException ("Cannot delete the new row");
487                                 
488                         DataGridView.OnRowsPreRemovedInternal (new DataGridViewRowsRemovedEventArgs (dataGridViewRow.Index, 1));
489                         dataGridViewRow.SetDataGridView (null);
490                         list.Remove (dataGridViewRow);
491                         ReIndex ();
492                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, dataGridViewRow));
493                         DataGridView.OnRowsPostRemovedInternal (new DataGridViewRowsRemovedEventArgs (dataGridViewRow.Index, 1));
494                 }
495
496                 internal virtual void RemoveInternal (DataGridViewRow dataGridViewRow)
497                 {
498                         DataGridView.OnRowsPreRemovedInternal (new DataGridViewRowsRemovedEventArgs (dataGridViewRow.Index, 1));
499                         dataGridViewRow.SetDataGridView (null);
500                         list.Remove (dataGridViewRow);
501                         ReIndex ();
502                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, dataGridViewRow));
503                         DataGridView.OnRowsPostRemovedInternal (new DataGridViewRowsRemovedEventArgs (dataGridViewRow.Index, 1));
504                 }
505                 
506                 public virtual void RemoveAt (int index)
507                 {
508                         DataGridViewRow row = this [index];
509                         
510                         Remove (row);
511                 }
512
513                 internal void RemoveAtInternal (int index)
514                 {
515                         DataGridViewRow row = this [index];
516                         
517                         RemoveInternal (row);
518                 }
519
520                 public DataGridViewRow SharedRow (int rowIndex)
521                 {
522                         return (DataGridViewRow) list [rowIndex];
523                 }
524
525                 internal int SharedRowIndexOf (DataGridViewRow row)
526                 {
527                         return list.IndexOf (row);
528                 }
529
530                 protected DataGridView DataGridView {
531                         get { return dataGridView; }
532                 }
533
534                 protected ArrayList List {
535                         get { return list; }
536                 }
537
538                 protected virtual void OnCollectionChanged (CollectionChangeEventArgs e)
539                 {
540                         if (CollectionChanged != null)
541                                 CollectionChanged (this, e);
542                 }
543
544                 internal void AddInternal (DataGridViewRow dataGridViewRow, bool sharable)
545                 {
546                         raiseEvent = false;
547                         AddCore (dataGridViewRow, sharable);
548                         raiseEvent = true;
549                 }
550
551                 internal ArrayList RowIndexSortedArrayList {
552                         get {
553                                 ArrayList result = (ArrayList) list.Clone();
554                                 result.Sort(new RowIndexComparator());
555                                 return result;
556                         }
557                 }
558                 
559                 internal void ReIndex ()
560                 {
561                         for (int i = 0; i < Count; i++)
562                                 (list[i] as DataGridViewRow).SetIndex (i);
563                 }
564                 
565                 internal void Sort (IComparer comparer)
566                 {
567                         // Note: you will probably want to call
568                         // invalidate after using this.
569                         if (DataGridView != null && DataGridView.EditingRow != null)
570                                 list.Sort (0, Count - 1, comparer);
571                         else
572                                 list.Sort (comparer);
573                         
574                         for (int i = 0; i < list.Count; i++)
575                                 (list[i] as DataGridViewRow).SetIndex (i);
576                 }
577
578                 private class RowIndexComparator : IComparer 
579                 {
580                         public int Compare (object o1, object o2)
581                         {
582                                 DataGridViewRow row1 = (DataGridViewRow) o1;
583                                 DataGridViewRow row2 = (DataGridViewRow) o2;
584                                 if (row1.Index < row2.Index) {
585                                         return -1;
586                                 } else if (row1.Index > row2.Index) {
587                                         return 1;
588                                 } else {
589                                         return 0;
590                                 }
591                         }
592                 }
593         }
594 }