1 // Permission is hereby granted, free of charge, to any person obtaining
\r
2 // a copy of this software and associated documentation files (the
\r
3 // "Software"), to deal in the Software without restriction, including
\r
4 // without limitation the rights to use, copy, modify, merge, publish,
\r
5 // distribute, sublicense, and/or sell copies of the Software, and to
\r
6 // permit persons to whom the Software is furnished to do so, subject to
\r
7 // the following conditions:
\r
9 // The above copyright notice and this permission notice shall be
\r
10 // included in all copies or substantial portions of the Software.
\r
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
20 // Copyright (c) 2007 Novell, Inc.
\r
23 // Andreia Gaita (avidigal@novell.com)
\r
26 using System.Drawing;
\r
27 using System.Drawing.Text;
\r
28 using System.Windows.Forms;
\r
29 using System.Windows.Forms.VisualStyles;
\r
31 namespace System.Windows.Forms.Theming.Default
\r
34 /// Summary description for TabControl.
\r
36 internal class TabControlPainter {
\r
37 protected SystemResPool ResPool { get { return ThemeEngine.Current.ResPool; } }
\r
41 private Size defaultItemSize;
\r
42 private Point defaultPadding;
\r
43 private int minimumTabWidth;
\r
44 private Rectangle selectedTabDelta;
\r
46 private Point tabPanelOffset;
\r
48 private int selectedSpacing;
\r
50 private Size rowSpacingNormal;
\r
51 private Size rowSpacingButtons;
\r
52 private Size rowSpacingFlatButtons;
\r
53 private int scrollerWidth;
\r
54 private Point focusRectSpacing;
\r
55 private Rectangle tabPageSpacing;
\r
56 private int colSpacing;
\r
57 private int flatButtonSpacing;
\r
59 private Point imagePadding;
\r
61 private StringFormat defaultFormatting;
\r
63 private Rectangle borderThickness;
\r
69 public virtual Size DefaultItemSize {
\r
70 get { return defaultItemSize; }
\r
71 set { defaultItemSize = value; }
\r
74 public virtual Point DefaultPadding {
\r
75 get { return defaultPadding; }
\r
76 set { defaultPadding = value; }
\r
79 public virtual int MinimumTabWidth {
\r
80 get { return minimumTabWidth; }
\r
81 set { minimumTabWidth = value; }
\r
84 public virtual Rectangle SelectedTabDelta {
\r
85 get { return selectedTabDelta; }
\r
86 set { selectedTabDelta = value; }
\r
89 public virtual Point TabPanelOffset {
\r
90 get { return tabPanelOffset; }
\r
91 set { tabPanelOffset = value; }
\r
94 public virtual int SelectedSpacing {
\r
95 get { return selectedSpacing; }
\r
96 set { selectedSpacing = value; }
\r
99 public virtual Size RowSpacingNormal {
\r
100 get { return rowSpacingNormal; }
\r
101 set { rowSpacingNormal = value; }
\r
104 public virtual Size RowSpacingButtons {
\r
105 get { return rowSpacingButtons; }
\r
106 set { rowSpacingButtons = value; }
\r
109 public virtual Size RowSpacingFlatButtons {
\r
110 get { return rowSpacingFlatButtons; }
\r
111 set { rowSpacingFlatButtons = value; }
\r
114 public virtual Point FocusRectSpacing {
\r
115 get { return focusRectSpacing; }
\r
116 set { focusRectSpacing = value; }
\r
119 public virtual int ColSpacing {
\r
120 get { return colSpacing; }
\r
121 set { colSpacing = value; }
\r
124 public virtual int FlatButtonSpacing {
\r
125 get { return flatButtonSpacing; }
\r
126 set { flatButtonSpacing = value; }
\r
129 public virtual Rectangle TabPageSpacing {
\r
130 get { return tabPageSpacing; }
\r
131 set { tabPageSpacing = value; }
\r
134 public virtual Point ImagePadding {
\r
135 get { return imagePadding; }
\r
136 set { imagePadding = value; }
\r
139 public virtual StringFormat DefaultFormatting {
\r
140 get { return defaultFormatting; }
\r
141 set { defaultFormatting = value; }
\r
144 public virtual Rectangle BorderThickness {
\r
145 get { return borderThickness; }
\r
146 set { borderThickness = value; }
\r
149 public virtual int ScrollerWidth {
\r
150 get { return scrollerWidth; }
\r
151 set { scrollerWidth = value; }
\r
154 public virtual Size RowSpacing (System.Windows.Forms.TabControl tab) {
\r
155 switch (tab.Appearance) {
\r
156 case TabAppearance.Normal:
\r
157 return rowSpacingNormal;
\r
158 case TabAppearance.Buttons:
\r
159 return rowSpacingButtons;
\r
160 case TabAppearance.FlatButtons:
\r
161 return rowSpacingFlatButtons;
\r
163 throw new Exception ("Invalid Appearance value: " + tab.Appearance);
\r
168 public TabControlPainter ()
\r
170 defaultItemSize = new Size (42, 16);
\r
171 defaultPadding = new Point (6, 3);
\r
172 selectedTabDelta = new Rectangle (2, 2, 4, 3);
\r
173 selectedSpacing = 0;
\r
175 rowSpacingNormal = new Size (0, 0);
\r
176 rowSpacingButtons = new Size (3, 3);
\r
177 rowSpacingFlatButtons = new Size (9, 3);
\r
181 minimumTabWidth = 42;
\r
182 scrollerWidth = 17;
\r
183 focusRectSpacing = new Point (2, 2);
\r
184 tabPanelOffset = new Point (4, 0);
\r
185 flatButtonSpacing = 8;
\r
186 tabPageSpacing = new Rectangle (4, 2, 3, 4);
\r
188 imagePadding = new Point (2, 3);
\r
190 defaultFormatting = new StringFormat();
\r
191 // Horizontal Alignment is handled in the Draw method
\r
192 defaultFormatting.Alignment = StringAlignment.Near;
\r
193 defaultFormatting.LineAlignment = StringAlignment.Center;
\r
194 defaultFormatting.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip;
\r
195 defaultFormatting.HotkeyPrefix = HotkeyPrefix.Show;
\r
197 borderThickness = new Rectangle (1, 1, 2, 2);
\r
200 public virtual Rectangle GetLeftScrollRect (System.Windows.Forms.TabControl tab)
\r
202 switch (tab.Alignment) {
\r
203 case TabAlignment.Top:
\r
204 return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth * 2), tab.ClientRectangle.Top + 1, scrollerWidth, scrollerWidth);
\r
206 Rectangle panel_rect = GetTabPanelRect (tab);
\r
207 return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth * 2), panel_rect.Bottom + 2, scrollerWidth, scrollerWidth);
\r
211 public virtual Rectangle GetRightScrollRect (System.Windows.Forms.TabControl tab)
\r
213 switch (tab.Alignment) {
\r
214 case TabAlignment.Top:
\r
215 return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth), tab.ClientRectangle.Top + 1, scrollerWidth, scrollerWidth);
\r
217 Rectangle panel_rect = GetTabPanelRect (tab);
\r
218 return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth), panel_rect.Bottom + 2, scrollerWidth, scrollerWidth);
\r
222 public Rectangle GetDisplayRectangle (System.Windows.Forms.TabControl tab)
\r
224 Rectangle ext = GetTabPanelRect (tab);
\r
225 // Account for border size
\r
226 return new Rectangle (ext.Left + tabPageSpacing.X, ext.Top + tabPageSpacing.Y,
\r
227 ext.Width - tabPageSpacing.X - tabPageSpacing.Width, ext.Height - tabPageSpacing.Y - tabPageSpacing.Height);
\r
230 public Rectangle GetTabPanelRect (System.Windows.Forms.TabControl tab)
\r
232 // Offset the tab page (panel) from the top corner
\r
233 Rectangle res = new Rectangle (tab.ClientRectangle.X,
\r
234 tab.ClientRectangle.Y,
\r
235 tab.ClientRectangle.Width,
\r
236 tab.ClientRectangle.Height);
\r
238 if (tab.TabCount == 0)
\r
241 int spacing = RowSpacing (tab).Height;
\r
242 int tabOffset = (tab.ItemSize.Height + spacing - selectedTabDelta.Height) * tab.RowCount + selectedTabDelta.Y;
\r
243 switch (tab.Alignment) {
\r
244 case TabAlignment.Top:
\r
245 res.Y += tabOffset;
\r
246 res.Height -= tabOffset;
\r
248 case TabAlignment.Bottom:
\r
249 res.Height -= tabOffset;
\r
251 case TabAlignment.Left:
\r
252 res.X += tabOffset;
\r
253 res.Width -= tabOffset;
\r
255 case TabAlignment.Right:
\r
256 res.Width -= tabOffset;
\r
263 public virtual void Draw (Graphics dc, Rectangle area, TabControl tab)
\r
265 DrawBackground (dc, area, tab);
\r
268 int end = tab.TabPages.Count;
\r
271 if (tab.Alignment == TabAlignment.Top) {
\r
277 if (tab.SizeMode == TabSizeMode.Fixed)
\r
278 defaultFormatting.Alignment = StringAlignment.Center;
\r
280 defaultFormatting.Alignment = StringAlignment.Near;
\r
282 int counter = start;
\r
283 for (; counter != end; counter += delta) {
\r
284 for (int i = tab.SliderPos; i < tab.TabPages.Count; i++) {
\r
285 if (i == tab.SelectedIndex)
\r
287 if (counter != tab.TabPages[i].Row)
\r
289 Rectangle rect = tab.GetTabRect (i);
\r
290 if (!rect.IntersectsWith (area))
\r
292 DrawTab (dc, tab.TabPages[i], tab, rect, false);
\r
296 if (tab.SelectedIndex != -1 && tab.SelectedIndex >= tab.SliderPos) {
\r
297 Rectangle rect = tab.GetTabRect (tab.SelectedIndex);
\r
298 if (rect.IntersectsWith (area))
\r
299 DrawTab (dc, tab.TabPages[tab.SelectedIndex], tab, rect, true);
\r
302 if (tab.ShowSlider) {
\r
303 Rectangle right = GetRightScrollRect (tab);
\r
304 Rectangle left = GetLeftScrollRect (tab);
\r
305 DrawScrollButton (dc, right, area, ScrollButton.Right, tab.RightSliderState);
\r
306 DrawScrollButton (dc, left, area, ScrollButton.Left, tab.LeftSliderState);
\r
310 protected virtual void DrawScrollButton (Graphics dc, Rectangle bounds, Rectangle clippingArea, ScrollButton button, PushButtonState state)
\r
312 ControlPaint.DrawScrollButton (dc, bounds, button, GetButtonState (state));
\r
315 static ButtonState GetButtonState (PushButtonState state)
\r
318 case PushButtonState.Pressed:
\r
319 return ButtonState.Pushed;
\r
321 return ButtonState.Normal;
\r
325 protected virtual void DrawBackground (Graphics dc, Rectangle area, TabControl tab)
\r
327 Brush brush = SystemBrushes.Control;
\r
328 dc.FillRectangle (brush, area);
\r
329 Rectangle panel_rect = GetTabPanelRect (tab);
\r
331 if (tab.Appearance == TabAppearance.Normal) {
\r
332 ControlPaint.DrawBorder3D (dc, panel_rect, Border3DStyle.RaisedInner, Border3DSide.Left | Border3DSide.Top);
\r
333 ControlPaint.DrawBorder3D (dc, panel_rect, Border3DStyle.Raised, Border3DSide.Right | Border3DSide.Bottom);
\r
337 protected virtual int DrawTab (Graphics dc, System.Windows.Forms.TabPage page, System.Windows.Forms.TabControl tab, Rectangle bounds, bool is_selected)
\r
339 Rectangle interior;
\r
340 int res = bounds.Width;
\r
342 dc.FillRectangle (ResPool.GetSolidBrush (tab.BackColor), bounds);
\r
344 if (tab.Appearance == TabAppearance.Buttons || tab.Appearance == TabAppearance.FlatButtons) {
\r
346 if (tab.Appearance == TabAppearance.FlatButtons) {
\r
347 int width = bounds.Width;
\r
348 bounds.Width += (flatButtonSpacing - 2);
\r
349 res = bounds.Width;
\r
350 if (tab.Alignment == TabAlignment.Top || tab.Alignment == TabAlignment.Bottom)
\r
351 ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Etched, Border3DSide.Right);
\r
353 ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Etched, Border3DSide.Top);
\r
354 bounds.Width = width;
\r
358 ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Sunken, Border3DSide.Left | Border3DSide.Right | Border3DSide.Top | Border3DSide.Bottom);
\r
359 } else if (tab.Appearance != TabAppearance.FlatButtons) {
\r
360 ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Raised, Border3DSide.Left | Border3DSide.Right | Border3DSide.Top | Border3DSide.Bottom);
\r
365 CPColor cpcolor = ResPool.GetCPColor (tab.BackColor);
\r
367 Pen light = ResPool.GetPen (cpcolor.LightLight);
\r
369 switch (tab.Alignment) {
\r
371 case TabAlignment.Top:
\r
373 dc.DrawLine (light, bounds.Left, bounds.Bottom - 1, bounds.Left, bounds.Top + 3);
\r
374 dc.DrawLine (light, bounds.Left, bounds.Top + 3, bounds.Left + 2, bounds.Top);
\r
375 dc.DrawLine (light, bounds.Left + 2, bounds.Top, bounds.Right - 3, bounds.Top);
\r
377 dc.DrawLine (SystemPens.ControlDark, bounds.Right - 2, bounds.Top + 1, bounds.Right - 2, bounds.Bottom - 1);
\r
378 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 2, bounds.Top + 1, bounds.Right - 1, bounds.Top + 2);
\r
379 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 1, bounds.Top + 2, bounds.Right - 1, bounds.Bottom - 1);
\r
382 case TabAlignment.Bottom:
\r
384 dc.DrawLine (light, bounds.Left, bounds.Top, bounds.Left, bounds.Bottom - 2);
\r
385 dc.DrawLine (light, bounds.Left, bounds.Bottom - 2, bounds.Left + 3, bounds.Bottom);
\r
387 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Left + 3, bounds.Bottom, bounds.Right - 3, bounds.Bottom);
\r
388 dc.DrawLine (SystemPens.ControlDark, bounds.Left + 3, bounds.Bottom - 1, bounds.Right - 3, bounds.Bottom - 1);
\r
390 dc.DrawLine (SystemPens.ControlDark, bounds.Right - 2, bounds.Bottom - 1, bounds.Right - 2, bounds.Top + 1);
\r
391 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 2, bounds.Bottom - 1, bounds.Right - 1, bounds.Bottom - 2);
\r
392 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 1, bounds.Bottom - 2, bounds.Right - 1, bounds.Top + 1);
\r
396 case TabAlignment.Left:
\r
398 dc.DrawLine (light, bounds.Left - 2, bounds.Top, bounds.Right, bounds.Top);
\r
399 dc.DrawLine (light, bounds.Left, bounds.Top + 2, bounds.Left - 2, bounds.Top);
\r
400 dc.DrawLine (light, bounds.Left, bounds.Top + 2, bounds.Left, bounds.Bottom - 2);
\r
402 dc.DrawLine (SystemPens.ControlDark, bounds.Left, bounds.Bottom - 2, bounds.Left + 2, bounds.Bottom - 1);
\r
404 dc.DrawLine (SystemPens.ControlDark, bounds.Left + 2, bounds.Bottom - 1, bounds.Right, bounds.Bottom - 1);
\r
405 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Left + 2, bounds.Bottom, bounds.Right, bounds.Bottom);
\r
409 default: // TabAlignment.Right
\r
411 dc.DrawLine (light, bounds.Left, bounds.Top, bounds.Right - 3, bounds.Top);
\r
412 dc.DrawLine (light, bounds.Right - 3, bounds.Top, bounds.Right, bounds.Top + 3);
\r
414 dc.DrawLine (SystemPens.ControlDark, bounds.Right - 1, bounds.Top + 1, bounds.Right - 1, bounds.Bottom - 1);
\r
415 dc.DrawLine (SystemPens.ControlDark, bounds.Left, bounds.Bottom - 1, bounds.Right - 2, bounds.Bottom - 1);
\r
417 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right, bounds.Top + 3, bounds.Right, bounds.Bottom - 3);
\r
418 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Left, bounds.Bottom, bounds.Right - 3, bounds.Bottom);
\r
424 interior = new Rectangle (bounds.Left + focusRectSpacing.X + borderThickness.Left,
\r
425 bounds.Top + focusRectSpacing.Y + + borderThickness.Top,
\r
426 bounds.Width - (focusRectSpacing.X * 2) - borderThickness.Width + 1,
\r
427 bounds.Height - (focusRectSpacing.Y * 2) - borderThickness.Height);
\r
429 if (tab.DrawMode == TabDrawMode.Normal && page.Text != null) {
\r
430 if (tab.Alignment == TabAlignment.Left) {
\r
431 dc.TranslateTransform (bounds.Left, bounds.Bottom);
\r
432 dc.RotateTransform (-90);
\r
433 dc.DrawString (page.Text, page.Font,
\r
434 SystemBrushes.ControlText,
\r
435 tab.Padding.X - 2, // drawstring adds some extra unwanted leading spaces, so trimming
\r
437 defaultFormatting);
\r
438 dc.ResetTransform ();
\r
439 } else if (tab.Alignment == TabAlignment.Right) {
\r
440 dc.TranslateTransform (bounds.Right, bounds.Top);
\r
441 dc.RotateTransform (90);
\r
442 dc.DrawString (page.Text, page.Font,
\r
443 SystemBrushes.ControlText,
\r
444 tab.Padding.X - 2, // drawstring adds some extra unwanted leading spaces, so trimming
\r
446 defaultFormatting);
\r
447 dc.ResetTransform ();
\r
449 Rectangle str_rect = interior;
\r
451 if (tab.ImageList != null && page.ImageIndex >= 0 && page.ImageIndex < tab.ImageList.Images.Count) {
\r
452 int image_y = interior.Y + (interior.Height - tab.ImageList.ImageSize.Height) / 2;
\r
453 tab.ImageList.Draw (dc, new Point (interior.X, image_y), page.ImageIndex);
\r
454 str_rect.X += tab.ImageList.ImageSize.Width + 2;
\r
455 str_rect.Width -= tab.ImageList.ImageSize.Width + 2;
\r
457 dc.DrawString (page.Text, page.Font,
\r
458 SystemBrushes.ControlText,
\r
460 defaultFormatting);
\r
463 } else if (page.Text != null) {
\r
464 DrawItemState state = DrawItemState.None;
\r
465 if (page == tab.SelectedTab)
\r
466 state |= DrawItemState.Selected;
\r
467 DrawItemEventArgs e = new DrawItemEventArgs (dc,
\r
468 tab.Font, bounds, tab.IndexForTabPage (page),
\r
469 state, page.ForeColor, page.BackColor);
\r
470 tab.OnDrawItemInternal (e);
\r
474 if (page.Parent.Focused && is_selected && tab.ShowFocusCues) {
\r
475 interior.Width -= 1;
\r
476 ThemeEngine.Current.CPDrawFocusRectangle (dc, interior, tab.ForeColor, tab.BackColor);
\r
482 public virtual bool HasHotElementStyles (TabControl tabControl) {
\r