Fix bug #395
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ToolStripSplitStackLayout.cs
1 //
2 // ToolStripSplitStackLayout.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.Windows.Forms.Layout;
32
33 namespace System.Windows.Forms
34 {
35         class ToolStripSplitStackLayout : LayoutEngine
36         {
37                 public override bool Layout (object container, LayoutEventArgs args)
38                 {
39                         if (container is ToolStrip)
40                         {
41                                 ToolStrip ts = (ToolStrip)container;
42
43                                 if (ts.Items == null)
44                                         return false;
45                                         
46                                 Rectangle ts_rect = ts.DisplayRectangle;
47                                         
48                                 if (ts.Orientation == Orientation.Horizontal)                                   
49                                         LayoutHorizontalToolStrip (ts, ts_rect);
50                                 else
51                                         LayoutVerticalToolStrip (ts, ts_rect);
52                                         
53                                 return false;
54                         } else  {
55                                 ToolStripContentPanel ts = (ToolStripContentPanel)container;
56                                 int x = ts.DisplayRectangle.Left;
57                                 int y = ts.DisplayRectangle.Top;
58
59                                 foreach (ToolStrip tsi in ts.Controls) {
60                                         Rectangle new_bounds = new Rectangle ();
61
62                                         x += tsi.Margin.Left;
63
64                                         new_bounds.Location = new Point (x, y + tsi.Margin.Top);
65                                         new_bounds.Height = ts.DisplayRectangle.Height - tsi.Margin.Vertical;
66                                         new_bounds.Width = tsi.GetToolStripPreferredSize (new Size (0, new_bounds.Height)).Width;
67
68                                         tsi.Width = new_bounds.Width + 12;
69
70                                         x += new_bounds.Width + tsi.Margin.Right;
71                                 }
72                         }
73
74                         return false;
75                 }
76                 
77                 private void LayoutHorizontalToolStrip (ToolStrip ts, Rectangle bounds)
78                 {
79                         //if (!ts.Visible) return;
80                         
81                         ToolStripItemOverflow[] overflow = new ToolStripItemOverflow[ts.Items.Count];
82                         ToolStripItemPlacement[] placement = new ToolStripItemPlacement[ts.Items.Count];
83                         Size proposedSize = new Size (0, bounds.Height);
84                         int[] widths = new int[ts.Items.Count];
85                         int total_width = 0;
86                         int toolstrip_width = bounds.Width;
87                         int i = 0;
88                         bool can_overflow = ts.CanOverflow & !(ts is MenuStrip) & !(ts is StatusStrip);
89                         bool need_overflow = false;
90                         
91                         foreach (ToolStripItem tsi in ts.Items) {
92                                 overflow[i] = tsi.Overflow;
93                                 placement[i] = tsi.Overflow == ToolStripItemOverflow.Always ? ToolStripItemPlacement.Overflow : ToolStripItemPlacement.Main;
94                                 widths[i] = tsi.GetPreferredSize (proposedSize).Width + tsi.Margin.Horizontal;
95                                 if (!tsi.Available)
96                                         placement[i] = ToolStripItemPlacement.None;
97                                 total_width += placement[i] == ToolStripItemPlacement.Main ? widths[i] : 0;
98                                 
99                                 if (placement[i] == ToolStripItemPlacement.Overflow)
100                                         need_overflow = true;
101                                 i++;
102                         }
103
104                         // This is needed for a button set to Overflow = Always
105                         if (need_overflow) {
106                                 ts.OverflowButton.Visible = true;
107                                 ts.OverflowButton.SetBounds (new Rectangle (ts.Width - 16, 0, 16, ts.Height));
108                                 toolstrip_width -= ts.OverflowButton.Width;
109                         } else
110                                 ts.OverflowButton.Visible = false;
111                         
112                         while (total_width > toolstrip_width) {
113                                 // If we can overflow, get our overflow button setup, and subtract it's width
114                                 // from our available width                             
115                                 if (can_overflow && !ts.OverflowButton.Visible) {
116                                         ts.OverflowButton.Visible = true;
117                                         ts.OverflowButton.SetBounds (new Rectangle (ts.Width - 16, 0, 16, ts.Height));
118                                         toolstrip_width -= ts.OverflowButton.Width;
119                                 }                                       
120                                 
121                                 bool removed_one = false;
122                                 
123                                 // Start at the right, removing Overflow.AsNeeded first
124                                 for (int j = widths.Length - 1; j >= 0; j--)
125                                         if (overflow[j] == ToolStripItemOverflow.AsNeeded && placement[j] == ToolStripItemPlacement.Main) {
126                                                 placement[j] = ToolStripItemPlacement.Overflow;
127                                                 total_width -= widths[j];
128                                                 removed_one = true;
129                                                 break;
130                                         }
131                         
132                                 // If we didn't remove any AsNeeded ones, we have to start removing Never ones
133                                 // These are not put on the Overflow, they are simply not shown
134                                 if (!removed_one)
135                                         for (int j = widths.Length - 1; j >= 0; j--)
136                                                 if (overflow[j] == ToolStripItemOverflow.Never && placement[j] == ToolStripItemPlacement.Main) {
137                                                         placement[j] = ToolStripItemPlacement.None;
138                                                         total_width -= widths[j];
139                                                         removed_one = true;
140                                                         break;
141                                                 }
142
143                                 // There's nothing left to remove, break or we will loop forever        
144                                 if (!removed_one)
145                                         break;
146                         }
147
148                         i = 0;
149                         Point start_layout_pointer = new Point (ts.DisplayRectangle.Left, ts.DisplayRectangle.Top);
150                         Point end_layout_pointer = new Point (ts.DisplayRectangle.Right, ts.DisplayRectangle.Top);
151                         int button_height = ts.DisplayRectangle.Height;
152                         
153                         // Now we should know where everything goes, so lay everything out
154                         foreach (ToolStripItem tsi in ts.Items) {
155                                 tsi.SetPlacement (placement[i]);
156                                 
157                                 if (placement[i] == ToolStripItemPlacement.Main) {
158                                         if (tsi.Alignment == ToolStripItemAlignment.Left) {
159                                                 tsi.SetBounds (new Rectangle (start_layout_pointer.X + tsi.Margin.Left, start_layout_pointer.Y + tsi.Margin.Top, widths[i] - tsi.Margin.Horizontal, button_height - tsi.Margin.Vertical));
160                                                 start_layout_pointer.X += widths[i];
161                                         } else {
162                                                 tsi.SetBounds (new Rectangle (end_layout_pointer.X - tsi.Margin.Right - tsi.Width, end_layout_pointer.Y + tsi.Margin.Top, widths[i] - tsi.Margin.Horizontal, button_height - tsi.Margin.Vertical));
163                                                 end_layout_pointer.X -= widths[i];
164                                         }                       
165                                 }
166                         
167                                 i++;
168                         }                       
169                 }
170
171                 private void LayoutVerticalToolStrip (ToolStrip ts, Rectangle bounds)
172                 {
173                         if (!ts.Visible) return;
174
175                         ToolStripItemOverflow[] overflow = new ToolStripItemOverflow[ts.Items.Count];
176                         ToolStripItemPlacement[] placement = new ToolStripItemPlacement[ts.Items.Count];
177                         Size proposedSize = new Size (bounds.Width, 0);
178                         int[] heights = new int[ts.Items.Count];
179                         int total_height = 0;
180                         int toolstrip_height = bounds.Height;
181                         int i = 0;
182                         bool can_overflow = ts.CanOverflow & !(ts is MenuStrip) & !(ts is StatusStrip);
183
184                         foreach (ToolStripItem tsi in ts.Items) {
185                                 overflow[i] = tsi.Overflow;
186                                 placement[i] = tsi.Overflow == ToolStripItemOverflow.Always ? ToolStripItemPlacement.Overflow : ToolStripItemPlacement.Main;
187                                 heights[i] = tsi.GetPreferredSize (proposedSize).Height + tsi.Margin.Vertical;
188                                 if (!tsi.Available)
189                                         placement[i] = ToolStripItemPlacement.None;
190                                 total_height += placement[i] == ToolStripItemPlacement.Main ? heights[i] : 0;
191                                 i++;
192                         }
193
194                         ts.OverflowButton.Visible = false;
195
196                         while (total_height > toolstrip_height) {
197                                 // If we can overflow, get our overflow button setup, and subtract it's width
198                                 // from our available width                             
199                                 if (can_overflow && !ts.OverflowButton.Visible) {
200                                         ts.OverflowButton.Visible = true;
201                                         ts.OverflowButton.SetBounds (new Rectangle (0, ts.Height - 16,  ts.Width, 16));
202                                         toolstrip_height -= ts.OverflowButton.Height;
203                                 }
204
205                                 bool removed_one = false;
206
207                                 // Start at the right, removing Overflow.AsNeeded first
208                                 for (int j = heights.Length - 1; j >= 0; j--)
209                                         if (overflow[j] == ToolStripItemOverflow.AsNeeded && placement[j] == ToolStripItemPlacement.Main) {
210                                                 placement[j] = ToolStripItemPlacement.Overflow;
211                                                 total_height -= heights[j];
212                                                 removed_one = true;
213                                                 break;
214                                         }
215
216                                 // If we didn't remove any AsNeeded ones, we have to start removing Never ones
217                                 // These are not put on the Overflow, they are simply not shown
218                                 if (!removed_one)
219                                         for (int j = heights.Length - 1; j >= 0; j--)
220                                                 if (overflow[j] == ToolStripItemOverflow.Never && placement[j] == ToolStripItemPlacement.Main) {
221                                                         placement[j] = ToolStripItemPlacement.None;
222                                                         total_height -= heights[j];
223                                                         removed_one = true;
224                                                         break;
225                                                 }
226                                         
227                                 // There's nothing left to remove, break or we will loop forever        
228                                 if (!removed_one)
229                                         break;
230                         }
231
232                         i = 0;
233                         Point start_layout_pointer = new Point (ts.DisplayRectangle.Left, ts.DisplayRectangle.Top);
234                         Point end_layout_pointer = new Point (ts.DisplayRectangle.Left, ts.DisplayRectangle.Bottom);
235                         int button_width = ts.DisplayRectangle.Width;
236
237                         // Now we should know where everything goes, so lay everything out
238                         foreach (ToolStripItem tsi in ts.Items) {
239                                 tsi.SetPlacement (placement[i]);
240
241                                 if (placement[i] == ToolStripItemPlacement.Main) {
242                                         if (tsi.Alignment == ToolStripItemAlignment.Left) {
243                                                 tsi.SetBounds (new Rectangle (start_layout_pointer.X + tsi.Margin.Left, start_layout_pointer.Y + tsi.Margin.Top, button_width - tsi.Margin.Horizontal, heights[i] - tsi.Margin.Vertical));
244                                                 start_layout_pointer.Y += heights[i];
245                                         } else {
246                                                 tsi.SetBounds (new Rectangle (end_layout_pointer.X + tsi.Margin.Left, end_layout_pointer.Y - tsi.Margin.Bottom - tsi.Height, button_width - tsi.Margin.Horizontal, heights[i] - tsi.Margin.Vertical));
247                                                 start_layout_pointer.Y += heights[i];
248                                         }
249                                 }
250
251                                 i++;
252                         }
253                 }
254         }
255 }