2008-01-22 Jonathan Pobst <monkey@jpobst.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms.Layout / DefaultLayout.cs
1 //
2 // DefaultLayout.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 //      Stefan Noack (noackstefan@googlemail.com)
28 //
29
30 using System;
31 using System.Drawing;
32
33 namespace System.Windows.Forms.Layout
34 {
35         class DefaultLayout : LayoutEngine
36         {
37                 void LayoutDockedChildren (Control parent, Control[] controls)
38                 {
39                         Rectangle space = parent.DisplayRectangle;
40                         MdiClient mdi = null;
41                         
42                         // Deal with docking; go through in reverse, MS docs say that lowest Z-order is closest to edge
43                         for (int i = controls.Length - 1; i >= 0; i--) {
44                                 Control child = controls[i];
45                                 Size child_size = child.Size;
46
47 #if NET_2_0
48                                 if (child.AutoSize)
49                                         child_size = GetPreferredControlSize (child);
50 #endif
51
52                                 if (!child.VisibleInternal
53                                     || child.ControlLayoutType == Control.LayoutType.Anchor)
54                                         continue;
55
56                                 // MdiClient never fills the whole area like other controls, have to do it later
57                                 if (child is MdiClient) {
58                                         mdi = (MdiClient)child;
59                                         continue;
60                                 }
61                                 
62                                 switch (child.Dock) {
63                                 case DockStyle.None:
64                                         // Do nothing
65                                         break;
66
67                                 case DockStyle.Left:
68                                         child.SetBounds (space.Left, space.Y, child_size.Width, space.Height, BoundsSpecified.None);
69                                         space.X += child.Width;
70                                         space.Width -= child.Width;
71                                         break;
72
73                                 case DockStyle.Top:
74                                         child.SetBounds (space.Left, space.Y, space.Width, child_size.Height, BoundsSpecified.None);
75                                         space.Y += child.Height;
76                                         space.Height -= child.Height;
77                                         break;
78
79                                 case DockStyle.Right:
80                                         child.SetBounds (space.Right - child_size.Width, space.Y, child_size.Width, space.Height, BoundsSpecified.None);
81                                         space.Width -= child.Width;
82                                         break;
83
84                                 case DockStyle.Bottom:
85                                         child.SetBounds (space.Left, space.Bottom - child_size.Height, space.Width, child_size.Height, BoundsSpecified.None);
86                                         space.Height -= child.Height;
87                                         break;
88                                         
89                                 case DockStyle.Fill:
90                                         child.SetBounds (space.Left, space.Top, space.Width, space.Height, BoundsSpecified.None);
91                                         break;
92                                 }
93                         }
94
95                         // MdiClient gets whatever space is left
96                         if (mdi != null)
97                                 mdi.SetBounds (space.Left, space.Top, space.Width, space.Height, BoundsSpecified.None);
98                 }
99
100                 void LayoutAnchoredChildren (Control parent, Control[] controls)
101                 {
102                         Rectangle space = parent.ClientRectangle;
103
104                         for (int i = 0; i < controls.Length; i++) {
105                                 int left;
106                                 int top;
107                                 int width;
108                                 int height;
109
110                                 Control child = controls[i];
111
112                                 if (!child.VisibleInternal
113                                     || child.ControlLayoutType == Control.LayoutType.Dock)
114                                         continue;
115
116                                 AnchorStyles anchor = child.Anchor;
117
118                                 left = child.Left;
119                                 top = child.Top;
120                                 
121                                 width = child.Width;
122                                 height = child.Height;
123
124                                 if ((anchor & AnchorStyles.Right) != 0) {
125                                         if ((anchor & AnchorStyles.Left) != 0)
126                                                 width = space.Width - child.dist_right - left;
127                                         else
128                                                 left = space.Width - child.dist_right - width;
129                                 }
130                                 else if ((anchor & AnchorStyles.Left) == 0) {
131                                         // left+=diff_width/2 will introduce rounding errors (diff_width removed from svn after r51780)
132                                         // This calculates from scratch every time:
133                                         left = left + (space.Width - (left + width + child.dist_right)) / 2;
134                                         child.dist_right = space.Width - (left + width);
135                                 }
136
137                                 if ((anchor & AnchorStyles.Bottom) != 0) {
138                                         if ((anchor & AnchorStyles.Top) != 0)
139                                                 height = space.Height - child.dist_bottom - top;
140                                         else
141                                                 top = space.Height - child.dist_bottom - height;
142                                 }
143                                 else if ((anchor & AnchorStyles.Top) == 0) {
144                                         // top += diff_height/2 will introduce rounding errors (diff_height removed from after r51780)
145                                         // This calculates from scratch every time:
146                                         top = top + (space.Height - (top + height + child.dist_bottom)) / 2;
147                                         child.dist_bottom = space.Height - (top + height);
148                                 }
149
150                                 // Sanity
151                                 if (width < 0)
152                                         width = 0;
153
154                                 if (height < 0)
155                                         height = 0;
156
157                                 child.SetBounds (left, top, width, height, BoundsSpecified.None);
158                         }
159                 }
160                 
161 #if NET_2_0
162                 void LayoutAutoSizedChildren (Control parent, Control[] controls)
163                 {
164                         for (int i = 0; i < controls.Length; i++) {
165                                 int left;
166                                 int top;
167
168                                 Control child = controls[i];
169                                 if (!child.VisibleInternal
170                                     || child.ControlLayoutType == Control.LayoutType.Dock
171                                     || !child.AutoSize)
172                                         continue;
173
174                                 left = child.Left;
175                                 top = child.Top;
176                                 
177                                 Size preferredsize = GetPreferredControlSize (child);
178                                 
179                                 child.SetBounds (left, top, preferredsize.Width, preferredsize.Height, BoundsSpecified.None);
180                         }
181                 }
182
183                 void LayoutAutoSizeContainer (Control container)
184                 {
185                         int left;
186                         int top;
187                         int width;
188                         int height;
189
190                         if (!container.VisibleInternal || container.ControlLayoutType == Control.LayoutType.Dock || !container.AutoSize)
191                                 return;
192
193                         left = container.Left;
194                         top = container.Top;
195
196                         Size preferredsize = container.PreferredSize;
197
198                         if (container.GetAutoSizeMode () == AutoSizeMode.GrowAndShrink) {
199                                 width = preferredsize.Width;
200                                 height = preferredsize.Height;
201                         } else {
202                                 width = container.ExplicitBounds.Width;
203                                 height = container.ExplicitBounds.Height;
204                                 if (preferredsize.Width > width)
205                                         width = preferredsize.Width;
206                                 if (preferredsize.Height > height)
207                                         height = preferredsize.Height;
208                         }
209
210                         // Sanity
211                         if (width < container.MinimumSize.Width)
212                                 width = container.MinimumSize.Width;
213
214                         if (height < container.MinimumSize.Height)
215                                 height = container.MinimumSize.Height;
216
217                         if (container.MaximumSize.Width != 0 && width > container.MaximumSize.Width)
218                                 width = container.MaximumSize.Width;
219
220                         if (container.MaximumSize.Height != 0 && height > container.MaximumSize.Height)
221                                 height = container.MaximumSize.Height;
222
223                         container.SetBounds (left, top, width, height, BoundsSpecified.None);
224                 }
225 #endif
226
227                 public override bool Layout (object container, LayoutEventArgs args)
228                 {
229                         Control parent = container as Control;
230
231                         Control[] controls = parent.Controls.GetAllControls ();
232
233                         LayoutDockedChildren (parent, controls);
234                         LayoutAnchoredChildren (parent, controls);
235 #if NET_2_0
236                         LayoutAutoSizedChildren (parent, controls);
237                         if (parent is Form) LayoutAutoSizeContainer (parent);
238 #endif
239
240                         return false;
241                 }
242
243 #if NET_2_0
244                 private Size GetPreferredControlSize (Control child)
245                 {
246                         int width;
247                         int height;
248                         Size preferredsize = child.PreferredSize;
249
250                         if (child.GetAutoSizeMode () == AutoSizeMode.GrowAndShrink) {
251                                 width = preferredsize.Width;
252                                 height = preferredsize.Height;
253                         } else {
254                                 width = child.ExplicitBounds.Width;
255                                 height = child.ExplicitBounds.Height;
256                                 if (preferredsize.Width > width)
257                                         width = preferredsize.Width;
258                                 if (preferredsize.Height > height)
259                                         height = preferredsize.Height;
260                         }
261
262                         // Sanity
263                         if (width < child.MinimumSize.Width)
264                                 width = child.MinimumSize.Width;
265
266                         if (height < child.MinimumSize.Height)
267                                 height = child.MinimumSize.Height;
268
269                         if (child.MaximumSize.Width != 0 && width > child.MaximumSize.Width)
270                                 width = child.MaximumSize.Width;
271
272                         if (child.MaximumSize.Height != 0 && height > child.MaximumSize.Height)
273                                 height = child.MaximumSize.Height;
274                                 
275                         return new Size (width, height);
276                 }
277 #endif
278         }
279 }