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
TableLayoutPanel()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)]
GetCellPosition(Control control)165 		public TableLayoutPanelCellPosition GetCellPosition (Control control)
166 		{
167 			return settings.GetCellPosition (control);
168 		}
169 
170 		[DisplayName ("Column")]
171 		[DefaultValue (-1)]
172 		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
GetColumn(Control control)173 		public int GetColumn (Control control)
174 		{
175 			return settings.GetColumn (control);
176 		}
177 
178 		[DisplayName ("ColumnSpan")]
179 		[DefaultValue (1)]
GetColumnSpan(Control control)180 		public int GetColumnSpan (Control control)
181 		{
182 			return settings.GetColumnSpan (control);
183 		}
184 
185 		[Browsable (false)]
186 		[EditorBrowsable (EditorBrowsableState.Never)]
GetColumnWidths()187 		public int[] GetColumnWidths ()
188 		{
189 			return this.column_widths;
190 		}
191 
GetControlFromPosition(int column, int row)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 
GetPositionFromControl(Control control)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)]
GetRow(Control control)219 		public int GetRow (Control control)
220 		{
221 			return settings.GetRow (control);
222 		}
223 
224 		[Browsable (false)]
225 		[EditorBrowsable (EditorBrowsableState.Never)]
GetRowHeights()226 		public int[] GetRowHeights ()
227 		{
228 			return this.row_heights;
229 		}
230 
231 		[DisplayName ("RowSpan")]
232 		[DefaultValue (1)]
GetRowSpan(Control control)233 		public int GetRowSpan (Control control)
234 		{
235 			return settings.GetRowSpan (control);
236 		}
237 
SetCellPosition(Control control, TableLayoutPanelCellPosition position)238 		public void SetCellPosition (Control control, TableLayoutPanelCellPosition position)
239 		{
240 			settings.SetCellPosition (control, position);
241 		}
242 
SetColumn(Control control, int column)243 		public void SetColumn (Control control, int column)
244 		{
245 			settings.SetColumn (control, column);
246 		}
247 
SetColumnSpan(Control control, int value)248 		public void SetColumnSpan (Control control, int value)
249 		{
250 			settings.SetColumnSpan (control, value);
251 		}
252 
SetRow(Control control, int row)253 		public void SetRow (Control control, int row)
254 		{
255 			settings.SetRow (control, row);
256 		}
257 
SetRowSpan(Control control, int value)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)]
CreateControlsInstance()266 		protected override ControlCollection CreateControlsInstance ()
267 		{
268 			return new TableLayoutControlCollection (this);
269 		}
270 
OnCellPaint(TableLayoutCellPaintEventArgs e)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)]
OnLayout(LayoutEventArgs levent)279 		protected override void OnLayout (LayoutEventArgs levent)
280 		{
281 			base.OnLayout (levent);
282 			Invalidate ();
283 		}
284 
OnPaintBackground(PaintEventArgs e)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 
ScaleControl(SizeF factor, BoundsSpecified specified)307 		protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
308 		{
309 			base.ScaleControl (factor, specified);
310 		}
311 
312 		[EditorBrowsable (EditorBrowsableState.Never)]
ScaleCore(float dx, float dy)313 		protected override void ScaleCore (float dx, float dy)
314 		{
315 			base.ScaleCore (dx, dy);
316 		}
317 		#endregion
318 
319 		#region Internal Methods
GetCellBorderWidth(TableLayoutPanelCellBorderStyle style)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 
DrawCellBorders(PaintEventArgs e)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 
DrawSingleBorder(Graphics g, Rectangle rect)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 
DrawInsetBorder(Graphics g, Rectangle rect)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 
DrawOutsetBorder(Graphics g, Rectangle rect)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 
DrawOutsetDoubleBorder(Graphics g, Rectangle rect)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 
DrawInsetDoubleBorder(Graphics g, Rectangle rect)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 
GetPreferredSizeCore(Size proposedSize)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>
AdjustWidthsForSpans(int[,] widths, int col, int span)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>
AdjustHeightsForSpans(int[,] heights, int row, int span)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
IExtenderProvider.CanExtend(object obj)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 }
788