Merge pull request #980 from StephenMcConnel/bug-18638
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TableLayoutPanel.cs
1 //
2 // TableLayoutPanel.cs
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 // Copyright (c) 2006 Jonathan Pobst
24 //
25 // Authors:
26 //      Jonathan Pobst (monkey@jpobst.com)
27 //
28
29 using System;
30 using System.Drawing;
31 using System.Collections.Generic;
32 using System.ComponentModel;
33 using System.Runtime.InteropServices;
34 using System.Windows.Forms.Layout;
35 using System.ComponentModel.Design.Serialization;
36
37 namespace System.Windows.Forms
38 {
39         [ComVisible (true)]
40         [ClassInterface (ClassInterfaceType.AutoDispatch)]
41         [ProvideProperty ("CellPosition", typeof (Control))]
42         [ProvideProperty ("Column", typeof (Control))]
43         [ProvideProperty ("ColumnSpan", typeof (Control))]
44         [ProvideProperty ("Row", typeof (Control))]
45         [ProvideProperty ("RowSpan", typeof (Control))]
46         [DefaultProperty ("ColumnCount")]
47         [Docking (DockingBehavior.Never)]
48         [Designer ("System.Windows.Forms.Design.TableLayoutPanelDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
49         [DesignerSerializer ("System.Windows.Forms.Design.TableLayoutPanelCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
50         public class TableLayoutPanel : Panel, IExtenderProvider
51         {
52                 private TableLayoutSettings settings;
53                 private static TableLayout layout_engine = new TableLayout ();
54                 private TableLayoutPanelCellBorderStyle cell_border_style;
55
56                 // This is the row/column the Control actually got placed
57                 internal Control[,] actual_positions;
58                 
59                 // Widths and heights of each column/row
60                 internal int[] column_widths;
61                 internal int[] row_heights;
62
63                 #region Public Constructor
64                 public TableLayoutPanel ()
65                 {
66                         settings = new TableLayoutSettings(this);
67                         cell_border_style = TableLayoutPanelCellBorderStyle.None;
68                         column_widths = new int[0];
69                         row_heights = new int[0];
70                         CreateDockPadding ();
71                 }
72                 #endregion
73
74                 #region Public Properties
75                 [Localizable (true)]
76                 [Browsable (false)]
77                 [EditorBrowsable (EditorBrowsableState.Never)]
78                 new public BorderStyle BorderStyle {
79                         get { return base.BorderStyle; }
80                         set { base.BorderStyle = value; }
81                 }
82
83                 [Localizable (true)]
84                 [DefaultValue (TableLayoutPanelCellBorderStyle.None)]
85                 public TableLayoutPanelCellBorderStyle CellBorderStyle {
86                         get { return this.cell_border_style; }
87                         set { 
88                                 if (this.cell_border_style != value) {
89                                         this.cell_border_style = value;
90                                         this.PerformLayout (this, "CellBorderStyle");
91                                         this.Invalidate ();
92                                 }
93                         }
94                 }
95
96                 [Localizable (true)]
97                 [DefaultValue (0)]
98                 public int ColumnCount {
99                         get { return settings.ColumnCount; }
100                         set { settings.ColumnCount = value; }
101                 }
102
103                 [Browsable (false)]
104                 [DisplayName ("Columns")]
105                 [MergableProperty (false)]
106                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
107                 public TableLayoutColumnStyleCollection ColumnStyles {
108                         get { return settings.ColumnStyles; }
109                 }
110
111                 [Browsable (false)]
112                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
113                 new public TableLayoutControlCollection Controls {
114                         get { return (TableLayoutControlCollection) base.Controls; }
115                 }
116
117                 [DefaultValue (TableLayoutPanelGrowStyle.AddRows)]
118                 public TableLayoutPanelGrowStyle GrowStyle {
119                         get { return settings.GrowStyle; }
120                         set { settings.GrowStyle = value; }
121                 }
122
123                 public override System.Windows.Forms.Layout.LayoutEngine LayoutEngine {
124                         get { return TableLayoutPanel.layout_engine; }
125                 }
126
127                 [Browsable (false)]
128                 [EditorBrowsable (EditorBrowsableState.Never)]
129                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
130                 public TableLayoutSettings LayoutSettings {
131                         get { return this.settings; }
132                         set {
133                                 if (value.isSerialized) {
134                                         // Serialized version doesn't calculate these.
135                                         value.ColumnCount = value.ColumnStyles.Count;
136                                         value.RowCount = value.RowStyles.Count;
137                                         value.panel = this;
138                                         
139                                         this.settings = value;
140                                 } else
141                                         throw new NotSupportedException ("LayoutSettings value cannot be set directly.");
142                         }
143                 }
144
145                 [Localizable (true)]
146                 [DefaultValue (0)]
147                 public int RowCount {
148                         get { return settings.RowCount; }
149                         set { settings.RowCount = value; }
150                 }
151
152                 [Browsable (false)]
153                 [DisplayName ("Rows")]
154                 [MergableProperty (false)]
155                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
156                 public TableLayoutRowStyleCollection RowStyles {
157                         get { return settings.RowStyles; }
158                 }
159                 #endregion
160
161                 #region Public Methods
162                 [DefaultValue (-1)]
163                 [DisplayName ("Cell")]
164                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
165                 public TableLayoutPanelCellPosition GetCellPosition (Control control)
166                 {
167                         return settings.GetCellPosition (control);
168                 }
169
170                 [DisplayName ("Column")]
171                 [DefaultValue (-1)]
172                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
173                 public int GetColumn (Control control)
174                 {
175                         return settings.GetColumn (control);
176                 }
177
178                 [DisplayName ("ColumnSpan")]
179                 [DefaultValue (1)]
180                 public int GetColumnSpan (Control control)
181                 {
182                         return settings.GetColumnSpan (control);
183                 }
184
185                 [Browsable (false)]
186                 [EditorBrowsable (EditorBrowsableState.Never)]
187                 public int[] GetColumnWidths ()
188                 {
189                         return this.column_widths;
190                 }
191
192                 public Control GetControlFromPosition (int column, int row)
193                 {
194                         if (column < 0 || row < 0)
195                                 throw new ArgumentException ();
196
197                         TableLayoutPanelCellPosition pos = new TableLayoutPanelCellPosition (column, row);
198
199                         foreach (Control c in this.Controls)
200                                 if (settings.GetCellPosition (c) == pos)
201                                         return c;
202
203                         return null;
204                 }
205
206                 public TableLayoutPanelCellPosition GetPositionFromControl (Control control)
207                 {
208                         for (int x = 0; x < this.actual_positions.GetLength (0); x++)
209                                 for (int y = 0; y < this.actual_positions.GetLength (1); y++)
210                                         if (this.actual_positions[x, y] == control)
211                                                 return new TableLayoutPanelCellPosition (x, y);
212
213                         return new TableLayoutPanelCellPosition (-1, -1);
214                 }
215
216                 [DisplayName ("Row")]
217                 [DefaultValue ("-1")]
218                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
219                 public int GetRow (Control control)
220                 {
221                         return settings.GetRow (control);
222                 }
223
224                 [Browsable (false)]
225                 [EditorBrowsable (EditorBrowsableState.Never)]
226                 public int[] GetRowHeights ()
227                 {
228                         return this.row_heights;
229                 }
230
231                 [DisplayName ("RowSpan")]
232                 [DefaultValue (1)]
233                 public int GetRowSpan (Control control)
234                 {
235                         return settings.GetRowSpan (control);
236                 }
237
238                 public void SetCellPosition (Control control, TableLayoutPanelCellPosition position)
239                 {
240                         settings.SetCellPosition (control, position);
241                 }
242
243                 public void SetColumn (Control control, int column)
244                 {
245                         settings.SetColumn (control, column);
246                 }
247
248                 public void SetColumnSpan (Control control, int value)
249                 {
250                         settings.SetColumnSpan (control, value);
251                 }
252
253                 public void SetRow (Control control, int row)
254                 {
255                         settings.SetRow (control, row);
256                 }
257
258                 public void SetRowSpan (Control control, int value)
259                 {
260                         settings.SetRowSpan (control, value);
261                 }
262                 #endregion
263
264                 #region Protected Methods
265                 [EditorBrowsable (EditorBrowsableState.Advanced)]
266                 protected override ControlCollection CreateControlsInstance ()
267                 {
268                         return new TableLayoutControlCollection (this);
269                 }
270
271                 protected virtual void OnCellPaint (TableLayoutCellPaintEventArgs e)
272                 {
273                         TableLayoutCellPaintEventHandler eh = (TableLayoutCellPaintEventHandler)(Events [CellPaintEvent]);
274                         if (eh != null)
275                                 eh (this, e);
276                 }
277
278                 [EditorBrowsable (EditorBrowsableState.Advanced)]
279                 protected override void OnLayout (LayoutEventArgs levent)
280                 {
281                         base.OnLayout (levent);
282                         Invalidate ();
283                 }
284
285                 protected override void OnPaintBackground (PaintEventArgs e)
286                 {
287                         base.OnPaintBackground (e);
288
289                         DrawCellBorders (e);
290                         
291                         int border_width = GetCellBorderWidth (CellBorderStyle);
292
293                         int x = border_width;
294                         int y = border_width;
295                         
296                         for (int i = 0; i < column_widths.Length; i++) {
297                                 for (int j = 0; j < row_heights.Length; j++) {
298                                         this.OnCellPaint (new TableLayoutCellPaintEventArgs (e.Graphics, e.ClipRectangle, new Rectangle (x, y, column_widths[i] + border_width, row_heights[j] + border_width), i, j));
299                                         y += row_heights[j] + border_width;
300                                 }
301
302                                 x += column_widths[i] + border_width;
303                                 y = border_width;
304                         }
305                 }
306
307                 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
308                 {
309                         base.ScaleControl (factor, specified);
310                 }
311                 
312                 [EditorBrowsable (EditorBrowsableState.Never)]
313                 protected override void ScaleCore (float dx, float dy)
314                 {
315                         base.ScaleCore (dx, dy);
316                 }
317                 #endregion
318
319                 #region Internal Methods
320                 internal static int GetCellBorderWidth (TableLayoutPanelCellBorderStyle style)
321                 {
322                         switch (style) {
323                                 case TableLayoutPanelCellBorderStyle.Single:
324                                         return 1;
325                                 case TableLayoutPanelCellBorderStyle.Inset:
326                                 case TableLayoutPanelCellBorderStyle.Outset:
327                                         return 2;
328                                 case TableLayoutPanelCellBorderStyle.InsetDouble:
329                                 case TableLayoutPanelCellBorderStyle.OutsetPartial:
330                                 case TableLayoutPanelCellBorderStyle.OutsetDouble:
331                                         return 3;
332                         }
333                         
334                         return 0;
335                 }
336                 
337                 private void DrawCellBorders (PaintEventArgs e)
338                 {
339                         Rectangle paint_here = new Rectangle (Point.Empty, this.Size);
340
341                         switch (CellBorderStyle) {
342                                 case TableLayoutPanelCellBorderStyle.Single:
343                                         DrawSingleBorder (e.Graphics, paint_here);
344                                         break;
345                                 case TableLayoutPanelCellBorderStyle.Inset:
346                                         DrawInsetBorder (e.Graphics, paint_here);
347                                         break;
348                                 case TableLayoutPanelCellBorderStyle.InsetDouble:
349                                         DrawInsetDoubleBorder (e.Graphics, paint_here);
350                                         break;
351                                 case TableLayoutPanelCellBorderStyle.Outset:
352                                         DrawOutsetBorder (e.Graphics, paint_here);
353                                         break;
354                                 case TableLayoutPanelCellBorderStyle.OutsetDouble:
355                                 case TableLayoutPanelCellBorderStyle.OutsetPartial:
356                                         DrawOutsetDoubleBorder (e.Graphics, paint_here);
357                                         break;
358                         }
359                 }
360
361                 private void DrawSingleBorder (Graphics g, Rectangle rect)
362                 {
363                         ControlPaint.DrawBorder (g, rect, SystemColors.ControlDark, ButtonBorderStyle.Solid);
364
365                         int x = DisplayRectangle.X;
366                         int y = DisplayRectangle.Y;
367
368                         for (int i = 0; i < column_widths.Length - 1; i++) {
369                                 x += column_widths[i] + 1;
370
371                                 g.DrawLine (SystemPens.ControlDark, new Point (x, 1), new Point (x, Bottom - 2));
372                         }
373
374                         for (int j = 0; j < row_heights.Length - 1; j++) {
375                                 y += row_heights[j] + 1;
376
377                                 g.DrawLine (SystemPens.ControlDark, new Point (1, y), new Point (Right - 2, y));
378                         }
379                 }
380
381                 private void DrawInsetBorder (Graphics g, Rectangle rect)
382                 {
383                         ControlPaint.DrawBorder3D (g, rect, Border3DStyle.Etched);
384                         
385                         int x = DisplayRectangle.X;
386                         int y = DisplayRectangle.Y;
387
388                         for (int i = 0; i < column_widths.Length - 1; i++) {
389                                 x += column_widths[i] + 2;
390
391                                 g.DrawLine (SystemPens.ControlDark, new Point (x, 1), new Point (x, Bottom - 3));
392                                 g.DrawLine (Pens.White, new Point (x + 1, 1), new Point (x + 1, Bottom - 3));
393                         }
394
395                         for (int j = 0; j < row_heights.Length - 1; j++) {
396                                 y += row_heights[j] + 2;
397
398                                 g.DrawLine (SystemPens.ControlDark, new Point (1, y), new Point (Right - 3, y));
399                                 g.DrawLine (Pens.White, new Point (1, y + 1), new Point (Right - 3, y + 1));
400                         }
401                 }
402
403                 private void DrawOutsetBorder (Graphics g, Rectangle rect)
404                 {
405                         g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left + 1, rect.Top + 1, rect.Width - 2, rect.Height - 2));
406                         g.DrawRectangle (Pens.White, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2));
407
408                         int x = DisplayRectangle.X;
409                         int y = DisplayRectangle.Y;
410
411                         for (int i = 0; i < column_widths.Length - 1; i++) {
412                                 x += column_widths[i] + 2;
413
414                                 g.DrawLine (Pens.White, new Point (x, 1), new Point (x, Bottom - 3));
415                                 g.DrawLine (SystemPens.ControlDark, new Point (x + 1, 1), new Point (x + 1, Bottom - 3));
416                         }
417
418                         for (int j = 0; j < row_heights.Length - 1; j++) {
419                                 y += row_heights[j] + 2;
420
421                                 g.DrawLine (Pens.White, new Point (1, y), new Point (Right - 3, y));
422                                 g.DrawLine (SystemPens.ControlDark, new Point (1, y + 1), new Point (Right - 3, y + 1));
423                         }
424                 }
425
426                 private void DrawOutsetDoubleBorder (Graphics g, Rectangle rect)
427                 {
428                         rect.Width -= 1;
429                         rect.Height -= 1;
430                         
431                         g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left + 2, rect.Top + 2, rect.Width - 2, rect.Height - 2));
432                         g.DrawRectangle (Pens.White, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2));
433
434                         int x = DisplayRectangle.X;
435                         int y = DisplayRectangle.Y;
436
437                         for (int i = 0; i < column_widths.Length - 1; i++) {
438                                 x += column_widths[i] + 3;
439
440                                 g.DrawLine (Pens.White, new Point (x, 3), new Point (x, Bottom - 5));
441                                 g.DrawLine (SystemPens.ControlDark, new Point (x + 2, 3), new Point (x + 2, Bottom - 5));
442                         }
443
444                         for (int j = 0; j < row_heights.Length - 1; j++) {
445                                 y += row_heights[j] + 3;
446
447                                 g.DrawLine (Pens.White, new Point (3, y), new Point (Right - 4, y));
448                                 g.DrawLine (SystemPens.ControlDark, new Point (3, y + 2), new Point (Right - 4, y + 2));
449                         }
450
451                         x = DisplayRectangle.X;
452                         y = DisplayRectangle.Y;
453
454                         for (int i = 0; i < column_widths.Length - 1; i++) {
455                                 x += column_widths[i] + 3;
456
457                                 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (x + 1, 3), new Point (x + 1, Bottom - 5));
458                         }
459
460                         for (int j = 0; j < row_heights.Length - 1; j++) {
461                                 y += row_heights[j] + 3;
462
463                                 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (3, y + 1), new Point (Right - 4, y + 1));
464                         }
465                 }
466         
467                 private void DrawInsetDoubleBorder (Graphics g, Rectangle rect)
468                 {
469                         rect.Width -= 1;
470                         rect.Height -= 1;
471                         
472                         g.DrawRectangle (Pens.White, new Rectangle (rect.Left + 2, rect.Top + 2, rect.Width - 2, rect.Height - 2));
473                         g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2));
474
475                         int x = DisplayRectangle.X;
476                         int y = DisplayRectangle.Y;
477
478                         for (int i = 0; i < column_widths.Length - 1; i++) {
479                                 x += column_widths[i] + 3;
480
481                                 g.DrawLine (SystemPens.ControlDark, new Point (x, 3), new Point (x, Bottom - 5));
482                                 g.DrawLine (Pens.White, new Point (x + 2, 3), new Point (x + 2, Bottom - 5));
483                         }
484
485                         for (int j = 0; j < row_heights.Length - 1; j++) {
486                                 y += row_heights[j] + 3;
487
488                                 g.DrawLine (SystemPens.ControlDark, new Point (3, y), new Point (Right - 4, y));
489                                 g.DrawLine (Pens.White, new Point (3, y + 2), new Point (Right - 4, y + 2));
490                         }
491
492                         x = DisplayRectangle.X;
493                         y = DisplayRectangle.Y;
494                         
495                         for (int i = 0; i < column_widths.Length - 1; i++) {
496                                 x += column_widths[i] + 3;
497
498                                 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (x + 1, 3), new Point (x + 1, Bottom - 5));
499                         }
500
501                         for (int j = 0; j < row_heights.Length - 1; j++) {
502                                 y += row_heights[j] + 3;
503
504                                 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (3, y + 1), new Point (Right - 4, y + 1));
505                         }
506                 }
507
508                 internal override Size GetPreferredSizeCore (Size proposedSize)
509                 {
510                         // If the tablelayoutowner is autosize, we have to make sure it is big enough
511                         // to hold every non-autosize control
512                         actual_positions = (LayoutEngine as TableLayout).CalculateControlPositions (this, Math.Max (ColumnCount, 1), Math.Max (RowCount, 1));
513                         
514                         // Use actual row/column counts, not user set ones
515                         int actual_cols = actual_positions.GetLength (0);
516                         int actual_rows = actual_positions.GetLength (1);
517                         
518                         // Find the largest column-span/row-span values.  A table entry that spans more than one
519                         // column (row) should not be treated as though it's width (height) all belongs to the
520                         // first column (row), but should be spread out across all the columns (rows) that are
521                         // spanned.  So we need to keep track of the widths (heights) of spans as well as
522                         // individual columns (rows).
523                         int max_colspan = 1, max_rowspan = 1;
524                         foreach (Control c in Controls)
525                         {
526                                 max_colspan = Math.Max(max_colspan, GetColumnSpan(c));
527                                 max_rowspan = Math.Max(max_rowspan, GetRowSpan(c));
528                         }
529
530                         // Figure out how wide the owner needs to be
531                         int[] column_widths = new int[actual_cols];
532                         // Keep track of widths for spans as well as columns. column_span_widths[i,j] stores
533                         // the maximum width for items column i than have a span of j+1 (ie, covers columns
534                         // i through i+j).
535                         int[,] column_span_widths = new int[actual_cols, max_colspan];
536                         int[] biggest = new int[max_colspan];
537                         float total_column_percentage = 0f;
538                         
539                         // Figure out how wide each column wants to be
540                         for (int i = 0; i < actual_cols; i++) {
541                                 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent)
542                                         total_column_percentage += ColumnStyles[i].Width;
543                                 int absolute_width = -1;
544                                 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Absolute)
545                                         absolute_width = (int)ColumnStyles[i].Width;    // use the absolute width if it's absolute!
546
547                                 for (int s = 0; s < max_colspan; ++s)
548                                         biggest[s] = 0;
549
550                                 for (int j = 0; j < actual_rows; j++) {
551                                         Control c = actual_positions[i, j];
552
553                                         if (c != null) {
554                                                 int colspan = GetColumnSpan (c);
555                                                 if (colspan == 0)
556                                                         continue;
557                                                 if (colspan == 1 && absolute_width > -1)
558                                                         biggest[0] = absolute_width;    // use the absolute width if the column has absolute width assigned!
559                                                 else if (!c.AutoSize)
560                                                         biggest[colspan-1] = Math.Max (biggest[colspan-1], c.ExplicitBounds.Width + c.Margin.Horizontal + Padding.Horizontal);
561                                                 else
562                                                         biggest[colspan-1] = Math.Max (biggest[colspan-1], c.PreferredSize.Width + c.Margin.Horizontal + Padding.Horizontal);
563                                         }
564                                         else if (absolute_width > -1) {
565                                                 biggest[0] = absolute_width;
566                                         }
567                                 }
568
569                                 for (int s = 0; s < max_colspan; ++s)
570                                         column_span_widths[i,s] = biggest[s];
571                         }
572
573                         for (int i = 0; i < actual_cols; ++i) {
574                                 for (int s = 1; s < max_colspan; ++s) {
575                                         if (column_span_widths[i,s] > 0)
576                                                 AdjustWidthsForSpans (column_span_widths, i, s);
577                                 }
578                                 column_widths[i] = column_span_widths[i,0];
579                         }
580
581                         // Because percentage based rows divy up the remaining space,
582                         // we have to make the owner big enough so that all the rows
583                         // get bigger, even if we only need one to be bigger.
584                         int non_percent_total_width = 0;
585                         int percent_total_width = 0;
586
587                         for (int i = 0; i < actual_cols; i++) {
588                                 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent)
589                                         percent_total_width = Math.Max (percent_total_width, (int)(column_widths[i] / ((ColumnStyles[i].Width) / total_column_percentage)));
590                                 else
591                                         non_percent_total_width += column_widths[i];
592                         }
593
594                         int border_width = GetCellBorderWidth (CellBorderStyle);
595                         int needed_width = non_percent_total_width + percent_total_width + (border_width * (actual_cols + 1));
596
597                         // Figure out how tall the owner needs to be
598                         int[] row_heights = new int[actual_rows];
599                         int[,] row_span_heights = new int[actual_rows, max_rowspan];
600                         biggest = new int[max_rowspan];
601                         float total_row_percentage = 0f;
602                 
603                         // Figure out how tall each row wants to be
604                         for (int j = 0; j < actual_rows; j++) {
605                                 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent)
606                                         total_row_percentage += RowStyles[j].Height;
607                                 int absolute_height = -1;
608                                 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Absolute)
609                                         absolute_height = (int)RowStyles[j].Height;     // use the absolute height if it's absolute!
610                                         
611                                 for (int s = 0; s < max_rowspan; ++s)
612                                         biggest[s] = 0;
613                                 
614                                 for (int i = 0; i < actual_cols; i++) {
615                                         Control c = actual_positions[i, j];
616
617                                         if (c != null) {
618                                                 int rowspan = GetRowSpan (c);
619                                                 if (rowspan == 0)
620                                                         continue;
621                                                 if (rowspan == 1 && absolute_height > -1)
622                                                         biggest[0] = absolute_height;    // use the absolute height if the row has absolute height assigned!
623                                                 else if (!c.AutoSize)
624                                                         biggest[rowspan-1] = Math.Max (biggest[rowspan-1], c.ExplicitBounds.Height + c.Margin.Vertical + Padding.Vertical);
625                                                 else
626                                                         biggest[rowspan-1] = Math.Max (biggest[rowspan-1], c.PreferredSize.Height + c.Margin.Vertical + Padding.Vertical);
627                                         }
628                                         else if (absolute_height > -1) {
629                                                 biggest[0] = absolute_height;
630                                         }
631                                 }
632
633                                 for (int s = 0; s < max_rowspan; ++s)
634                                         row_span_heights[j,s] = biggest[s];
635                         }
636
637                         for (int j = 0; j < actual_rows; ++j) {
638                                 for (int s = 1; s < max_rowspan; ++s) {
639                                         if (row_span_heights[j,s] > 0)
640                                                 AdjustHeightsForSpans (row_span_heights, j, s);
641                                 }
642                                 row_heights[j] = row_span_heights[j,0];
643                         }
644                         
645                         // Because percentage based rows divy up the remaining space,
646                         // we have to make the owner big enough so that all the rows
647                         // get bigger, even if we only need one to be bigger.
648                         int non_percent_total_height = 0;
649                         int percent_total_height = 0;
650
651                         for (int j = 0; j < actual_rows; j++) {
652                                 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent)
653                                         percent_total_height = Math.Max (percent_total_height, (int)(row_heights[j] / ((RowStyles[j].Height) / total_row_percentage)));
654                                 else
655                                         non_percent_total_height += row_heights[j];
656                         }
657
658                         int needed_height = non_percent_total_height + percent_total_height + (border_width * (actual_rows + 1));
659
660                         return new Size (needed_width, needed_height);
661                 }
662
663                 /// <summary>
664                 /// Adjust the widths of the columns underlying a span if necessary.
665                 /// </summary>
666                 private void AdjustWidthsForSpans (int[,] widths, int col, int span)
667                 {
668                         // Get the combined width of the columns underlying the span.
669                         int existing_width = 0;
670                         for (int i = col; i <= col+span; ++i)
671                                 existing_width += widths[i,0];
672                         if (widths[col,span] > existing_width)
673                         {
674                                 // We need to expand one or more of the underlying columns to fit the span,
675                                 // preferably ones that are not Absolute style.
676                                 int excess = widths[col,span] - existing_width;
677                                 int remaining = excess;
678                                 List<int> adjusting = new List<int>();
679                                 List<float> adjusting_widths = new List<float>();
680                                 for (int i = col; i <= col+span; ++i) {
681                                         if (i < ColumnStyles.Count && ColumnStyles[i].SizeType != SizeType.Absolute) {
682                                                 adjusting.Add(i);
683                                                 adjusting_widths.Add((float)widths[i,0]);
684                                         }
685                                 }
686                                 if (adjusting.Count == 0) {
687                                         // if every column is Absolute, spread the gain across every column
688                                         for (int i = col; i <= col+span; ++i) {
689                                                 adjusting.Add(i);
690                                                 adjusting_widths.Add((float)widths[i,0]);
691                                         }
692                                 }
693                                 float original_total = 0f;
694                                 foreach (var w in adjusting_widths)
695                                         original_total += w;
696                                 // Divide up the needed additional width proportionally.
697                                 for (int i = 0; i < adjusting.Count; ++i) {
698                                         var idx = adjusting[i];
699                                         var percent = adjusting_widths[i] / original_total;
700                                         var adjust = (int)(percent * excess);
701                                         widths[idx,0] += adjust;
702                                         remaining -= adjust;
703                                 }
704                                 // Any remaining fragment (1 or 2 pixels?) is divided evenly.
705                                 while (remaining > 0) {
706                                         for (int i = 0; i < adjusting.Count && remaining > 0; ++i) {
707                                                 ++widths[adjusting[i],0];
708                                                 --remaining;
709                                         }
710                                 }
711                         }
712                 }
713
714                 /// <summary>
715                 /// Adjust the heights of the rows underlying a span if necessary.
716                 /// </summary>
717                 private void AdjustHeightsForSpans (int[,] heights, int row, int span)
718                 {
719                         // Get the combined height of the rows underlying the span.
720                         int existing_height = 0;
721                         for (int i = row; i <= row+span; ++i)
722                                 existing_height += heights[i,0];
723                         if (heights[row,span] > existing_height)
724                         {
725                                 // We need to expand one or more of the underlying rows to fit the span,
726                                 // preferably ones that are not Absolute style.
727                                 int excess = heights[row,span] - existing_height;
728                                 int remaining = excess;
729                                 List<int> adjusting = new List<int>();
730                                 List<float> adjusting_heights = new List<float>();
731                                 for (int i = row; i <= row+span; ++i) {
732                                         if (i < RowStyles.Count && RowStyles[i].SizeType != SizeType.Absolute) {
733                                                 adjusting.Add(i);
734                                                 adjusting_heights.Add((float)heights[i,0]);
735                                         }
736                                 }
737                                 if (adjusting.Count == 0) {
738                                         // if every row is Absolute, spread the gain across every row
739                                         for (int i = row; i <= row+span; ++i) {
740                                                 adjusting.Add(i);
741                                                 adjusting_heights.Add((float)heights[i,0]);
742                                         }
743                                 }
744                                 float original_total = 0f;
745                                 foreach (var w in adjusting_heights)
746                                         original_total += w;
747                                 // Divide up the needed additional height proportionally.
748                                 for (int i = 0; i < adjusting.Count; ++i) {
749                                         var idx = adjusting[i];
750                                         var percent = adjusting_heights[i] / original_total;
751                                         var adjust = (int)(percent * excess);
752                                         heights[idx,0] += adjust;
753                                         remaining -= adjust;
754                                 }
755                                 // Any remaining fragment (1 or 2 pixels?) is divided evenly.
756                                 while (remaining > 0) {
757                                         for (int i = 0; i < adjusting.Count && remaining > 0; ++i) {
758                                                 ++heights[adjusting[i],0];
759                                                 --remaining;
760                                         }
761                                 }
762                         }
763                 }
764                 #endregion
765                 
766                 #region Public Events
767                 static object CellPaintEvent = new object ();
768
769                 public event TableLayoutCellPaintEventHandler CellPaint {
770                         add { Events.AddHandler (CellPaintEvent, value); }
771                         remove { Events.RemoveHandler (CellPaintEvent, value); }
772                 }
773                 #endregion
774                 
775                 #region IExtenderProvider
776                 bool IExtenderProvider.CanExtend (object obj)
777                 {
778                         if (obj is Control)
779                                 if ((obj as Control).Parent == this)
780                                         return true;
781
782                         return false;
783                 }
784                 #endregion
785                 
786         }
787 }