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