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