2008-05-19 George Giolfan <georgegiolfan@yahoo.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms.Theming / Default / TabControlPainter.cs
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
8 //\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
11 //\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
19 //\r
20 // Copyright (c) 2007 Novell, Inc.\r
21 //\r
22 // Authors:\r
23 //      Andreia Gaita (avidigal@novell.com)\r
24 \r
25 using System;\r
26 using System.Drawing;\r
27 using System.Drawing.Text;\r
28 using System.Windows.Forms;\r
29 \r
30 namespace System.Windows.Forms.Theming.Default\r
31 {\r
32         /// <summary>\r
33         /// Summary description for TabControl.\r
34         /// </summary>\r
35         internal class TabControlPainter {\r
36                 protected SystemResPool ResPool { get { return ThemeEngine.Current.ResPool; } }\r
37 \r
38                 #region private\r
39 \r
40                 private Size    defaultItemSize;\r
41                 private Point   defaultPadding;\r
42                 private int             minimumTabWidth;\r
43                 private Rectangle selectedTabDelta;\r
44 \r
45                 private Point   tabPanelOffset;\r
46                 \r
47                 private int             selectedSpacing;\r
48 \r
49                 private Size    rowSpacingNormal;\r
50                 private Size    rowSpacingButtons;\r
51                 private Size    rowSpacingFlatButtons;\r
52                 private int             scrollerWidth;\r
53                 private Point   focusRectSpacing;\r
54                 private Rectangle tabPageSpacing;\r
55                 private int             colSpacing;\r
56                 private int             flatButtonSpacing;\r
57 \r
58                 private Point   imagePadding;\r
59 \r
60                 private StringFormat defaultFormatting;\r
61 \r
62                 private Rectangle borderThickness;\r
63 \r
64                 #endregion\r
65 \r
66                 #region Properties\r
67 \r
68                 public virtual Size DefaultItemSize {\r
69                         get { return defaultItemSize; }\r
70                         set { defaultItemSize = value; }\r
71                 }\r
72 \r
73                 public virtual Point DefaultPadding {\r
74                         get { return defaultPadding; }\r
75                         set { defaultPadding = value; }\r
76                 }\r
77 \r
78                 public virtual int MinimumTabWidth {\r
79                         get { return minimumTabWidth; }\r
80                         set { minimumTabWidth = value; }\r
81                 }\r
82 \r
83                 public virtual Rectangle SelectedTabDelta {\r
84                         get { return selectedTabDelta; }\r
85                         set { selectedTabDelta = value; }\r
86                 }\r
87 \r
88                 public virtual Point TabPanelOffset {\r
89                         get { return tabPanelOffset; }\r
90                         set { tabPanelOffset = value; }\r
91                 }\r
92 \r
93                 public virtual int SelectedSpacing {\r
94                         get { return selectedSpacing; }\r
95                         set { selectedSpacing = value; }\r
96                 }\r
97 \r
98                 public virtual Size RowSpacingNormal { \r
99                         get { return rowSpacingNormal; }\r
100                         set { rowSpacingNormal = value; }\r
101                 }\r
102 \r
103                 public virtual Size RowSpacingButtons { \r
104                         get { return rowSpacingButtons; }\r
105                         set { rowSpacingButtons = value; }\r
106                 }\r
107 \r
108                 public virtual Size RowSpacingFlatButtons { \r
109                         get { return rowSpacingFlatButtons; }\r
110                         set { rowSpacingFlatButtons = value; }\r
111                 }\r
112 \r
113                 public virtual Point FocusRectSpacing {\r
114                         get { return focusRectSpacing; }\r
115                         set { focusRectSpacing = value; }\r
116                 }\r
117 \r
118                 public virtual int ColSpacing { \r
119                         get { return colSpacing; }\r
120                         set { colSpacing = value; }\r
121                 }\r
122 \r
123                 public virtual int FlatButtonSpacing { \r
124                         get { return flatButtonSpacing; }\r
125                         set { flatButtonSpacing = value; }\r
126                 }\r
127 \r
128                 public virtual Rectangle TabPageSpacing {\r
129                         get { return tabPageSpacing; }\r
130                         set { tabPageSpacing = value; }\r
131                 }\r
132 \r
133                 public virtual Point ImagePadding {\r
134                         get { return imagePadding; }\r
135                         set { imagePadding = value; }\r
136                 }\r
137 \r
138                 public virtual StringFormat DefaultFormatting {\r
139                         get { return defaultFormatting; }\r
140                         set { defaultFormatting = value; }\r
141                 }\r
142 \r
143                 public virtual Rectangle BorderThickness {\r
144                         get { return borderThickness; }\r
145                         set { borderThickness = value; }\r
146                 }\r
147 \r
148                 public virtual int ScrollerWidth {\r
149                         get { return scrollerWidth; }\r
150                         set { scrollerWidth = value; }\r
151                 }\r
152 \r
153                 public virtual Size RowSpacing (System.Windows.Forms.TabControl tab) {\r
154                         switch (tab.Appearance) {\r
155                                 case TabAppearance.Normal:\r
156                                         return rowSpacingNormal;\r
157                                 case TabAppearance.Buttons:\r
158                                         return rowSpacingButtons;\r
159                                 case TabAppearance.FlatButtons:\r
160                                         return rowSpacingFlatButtons;\r
161                                 default:\r
162                                         throw new Exception ("Invalid Appearance value: " + tab.Appearance);\r
163                         }\r
164                 }\r
165                 #endregion\r
166 \r
167                 public TabControlPainter ()\r
168                 {\r
169                         defaultItemSize = new Size (42, 21);\r
170                         defaultPadding = new Point (6, 3);\r
171                         selectedTabDelta = new Rectangle (2, 2, 4, 3);\r
172                         selectedSpacing = 0;\r
173 \r
174                         rowSpacingNormal = new Size (0, 0);\r
175                         rowSpacingButtons = new Size (3, 3);\r
176                         rowSpacingFlatButtons = new Size (9, 3);\r
177                         \r
178                         colSpacing = 0;\r
179 \r
180                         minimumTabWidth = 42;\r
181                         scrollerWidth = 17;\r
182                         focusRectSpacing = new Point (2, 2);\r
183                         tabPanelOffset = new Point (4, 0);\r
184                         flatButtonSpacing = 8;\r
185                         tabPageSpacing = new Rectangle (4, 2, 3, 4);\r
186 \r
187                         imagePadding = new Point (2, 3);\r
188 \r
189                         defaultFormatting = new StringFormat();\r
190                         defaultFormatting.Alignment = StringAlignment.Near;\r
191                         defaultFormatting.LineAlignment = StringAlignment.Near;\r
192                         defaultFormatting.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip;\r
193                         defaultFormatting.HotkeyPrefix = HotkeyPrefix.Show;\r
194 \r
195                         borderThickness = new Rectangle (1, 1, 2, 2);\r
196                 }\r
197 \r
198                 public virtual Rectangle GetLeftScrollRect (System.Windows.Forms.TabControl tab)\r
199                 {\r
200                         switch (tab.Alignment) {\r
201                                 case TabAlignment.Top:\r
202                                         return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth * 2), tab.ClientRectangle.Top + 1, scrollerWidth, scrollerWidth);\r
203                                 default:\r
204                                         Rectangle panel_rect = GetTabPanelRect (tab);\r
205                                         return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth * 2), panel_rect.Bottom + 2, scrollerWidth, scrollerWidth);\r
206                         }\r
207                 }\r
208 \r
209                 public virtual Rectangle GetRightScrollRect (System.Windows.Forms.TabControl tab)\r
210                 {\r
211                         switch (tab.Alignment) {\r
212                                 case TabAlignment.Top:\r
213                                         return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth), tab.ClientRectangle.Top + 1, scrollerWidth, scrollerWidth);\r
214                                 default:\r
215                                         Rectangle panel_rect = GetTabPanelRect (tab);\r
216                                         return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth), panel_rect.Bottom + 2, scrollerWidth, scrollerWidth);\r
217                         }\r
218                 }\r
219 \r
220                 public Rectangle GetDisplayRectangle (System.Windows.Forms.TabControl tab)\r
221                 {\r
222                         Rectangle ext = GetTabPanelRect (tab);\r
223                         // Account for border size\r
224                         return new Rectangle (ext.Left + tabPageSpacing.X, ext.Top + tabPageSpacing.Y, \r
225                                 ext.Width - tabPageSpacing.X - tabPageSpacing.Width, ext.Height - tabPageSpacing.Y - tabPageSpacing.Height);\r
226                 }\r
227                 \r
228                 public Rectangle GetTabPanelRect (System.Windows.Forms.TabControl tab)\r
229                 {       \r
230                         // Offset the tab page (panel) from the top corner\r
231                         Rectangle res = new Rectangle (tab.ClientRectangle.X,\r
232                                 tab.ClientRectangle.Y,\r
233                                 tab.ClientRectangle.Width,\r
234                                 tab.ClientRectangle.Height);\r
235 \r
236                         if (tab.TabCount == 0)\r
237                                 return res;\r
238 \r
239                         int spacing = RowSpacing (tab).Height;\r
240                         int tabOffset = (tab.ItemSize.Height + spacing - (selectedTabDelta.Height - selectedTabDelta.Y)) * tab.RowCount;\r
241                         switch (tab.Alignment) {\r
242                                 case TabAlignment.Top:\r
243                                         res.Y += tabOffset;\r
244                                         res.Height -= tabOffset;\r
245                                         break;\r
246                                 case TabAlignment.Bottom:\r
247                                         res.Height -= tabOffset;\r
248                                         break;\r
249                                 case TabAlignment.Left:\r
250                                         res.X += tabOffset;\r
251                                         res.Width -= tabOffset;\r
252                                         break;\r
253                                 case TabAlignment.Right:\r
254                                         res.Width -= tabOffset;\r
255                                         break;\r
256                         }\r
257 \r
258                         return res;\r
259                 }\r
260 \r
261                 public virtual void Draw (Graphics dc, Rectangle area, TabControl tab)\r
262                 {\r
263                         DrawBackground (dc, area, tab);\r
264 \r
265                         int start = 0;\r
266                         int end = tab.TabPages.Count;\r
267                         int delta = 1;\r
268 \r
269                         if (tab.Alignment == TabAlignment.Top) {\r
270                                 start = end;\r
271                                 end = 0;\r
272                                 delta = -1;\r
273                         }\r
274                         int counter = start;\r
275                         for (; counter != end; counter += delta) {\r
276                                 for (int i = tab.SliderPos; i < tab.TabPages.Count; i++) {\r
277                                         if (i == tab.SelectedIndex)\r
278                                                 continue;\r
279                                         if (counter != tab.TabPages[i].Row)\r
280                                                 continue;\r
281                                         Rectangle rect = tab.GetTabRect (i);\r
282                                         if (!rect.IntersectsWith (area))\r
283                                                 continue;\r
284                                         DrawTab (dc, tab.TabPages[i], tab, rect, false);\r
285                                 }\r
286                         }\r
287 \r
288                         if (tab.SelectedIndex != -1 && tab.SelectedIndex >= tab.SliderPos) {\r
289                                 Rectangle rect = tab.GetTabRect (tab.SelectedIndex);\r
290                                 if (rect.IntersectsWith (area))\r
291                                         DrawTab (dc, tab.TabPages[tab.SelectedIndex], tab, rect, true);\r
292                         }\r
293 \r
294                         if (tab.ShowSlider) {\r
295                                 Rectangle right = GetRightScrollRect (tab);\r
296                                 Rectangle left = GetLeftScrollRect (tab);\r
297                                 ControlPaint.DrawScrollButton (dc, right, ScrollButton.Right, tab.RightSliderState);\r
298                                 ControlPaint.DrawScrollButton (dc, left, ScrollButton.Left, tab.LeftSliderState);\r
299                         }\r
300                 }\r
301 \r
302                 protected virtual void DrawBackground (Graphics dc, Rectangle area, TabControl tab)\r
303                 {\r
304                         Brush brush = SystemBrushes.Control;\r
305                         dc.FillRectangle (brush, area);\r
306                         Rectangle panel_rect = GetTabPanelRect (tab);\r
307 \r
308                         if (tab.Appearance == TabAppearance.Normal) {\r
309                                 ControlPaint.DrawBorder3D (dc, panel_rect, Border3DStyle.RaisedInner, Border3DSide.Left | Border3DSide.Top);\r
310                                 ControlPaint.DrawBorder3D (dc, panel_rect, Border3DStyle.Raised, Border3DSide.Right | Border3DSide.Bottom);\r
311                         }\r
312                 }\r
313 \r
314                 protected virtual int DrawTab (Graphics dc, System.Windows.Forms.TabPage page, System.Windows.Forms.TabControl tab, Rectangle bounds, bool is_selected)\r
315                 {\r
316                         Rectangle interior;\r
317                         int res = bounds.Width;\r
318 \r
319                         dc.FillRectangle (ResPool.GetSolidBrush (tab.BackColor), bounds);\r
320 \r
321                         if (tab.Appearance == TabAppearance.Buttons || tab.Appearance == TabAppearance.FlatButtons) {\r
322                                 // Separators\r
323                                 if (tab.Appearance == TabAppearance.FlatButtons) {\r
324                                         int width = bounds.Width;\r
325                                         bounds.Width += (flatButtonSpacing - 2);\r
326                                         res = bounds.Width;\r
327                                         if (tab.Alignment == TabAlignment.Top || tab.Alignment == TabAlignment.Bottom)\r
328                                                 ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Etched, Border3DSide.Right);\r
329                                         else\r
330                                                 ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Etched, Border3DSide.Top);\r
331                                         bounds.Width = width;\r
332                                 }\r
333 \r
334                                 if (is_selected) {\r
335                                         ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Sunken, Border3DSide.Left | Border3DSide.Right | Border3DSide.Top | Border3DSide.Bottom);\r
336                                 } else if (tab.Appearance != TabAppearance.FlatButtons) {\r
337                                         ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Raised, Border3DSide.Left | Border3DSide.Right | Border3DSide.Top | Border3DSide.Bottom);\r
338                                 }\r
339 \r
340 \r
341                         } else {\r
342                                 CPColor cpcolor = ResPool.GetCPColor (tab.BackColor);\r
343 \r
344                                 Pen light = ResPool.GetPen (cpcolor.LightLight);\r
345 \r
346                                 switch (tab.Alignment) {\r
347 \r
348                                         case TabAlignment.Top:\r
349 \r
350                                                 dc.DrawLine (light, bounds.Left, bounds.Bottom - 1, bounds.Left, bounds.Top + 3);\r
351                                                 dc.DrawLine (light, bounds.Left, bounds.Top + 3, bounds.Left + 2, bounds.Top);\r
352                                                 dc.DrawLine (light, bounds.Left + 2, bounds.Top, bounds.Right - 3, bounds.Top);\r
353 \r
354                                                 dc.DrawLine (SystemPens.ControlDark, bounds.Right - 2, bounds.Top + 1, bounds.Right - 2, bounds.Bottom - 1);\r
355                                                 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 2, bounds.Top + 1, bounds.Right - 1, bounds.Top + 2);\r
356                                                 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 1, bounds.Top + 2, bounds.Right - 1, bounds.Bottom - 1);\r
357                                                 break;\r
358 \r
359                                         case TabAlignment.Bottom:\r
360 \r
361                                                 dc.DrawLine (light, bounds.Left, bounds.Top, bounds.Left, bounds.Bottom - 2);\r
362                                                 dc.DrawLine (light, bounds.Left, bounds.Bottom - 2, bounds.Left + 3, bounds.Bottom);\r
363                                                 \r
364                                                 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Left + 3, bounds.Bottom, bounds.Right - 3, bounds.Bottom);\r
365                                                 dc.DrawLine (SystemPens.ControlDark, bounds.Left + 3, bounds.Bottom - 1, bounds.Right - 3, bounds.Bottom - 1);\r
366 \r
367                                                 dc.DrawLine (SystemPens.ControlDark, bounds.Right - 2, bounds.Bottom - 1, bounds.Right - 2, bounds.Top + 1);\r
368                                                 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 2, bounds.Bottom - 1, bounds.Right - 1, bounds.Bottom - 2);\r
369                                                 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 1, bounds.Bottom - 2, bounds.Right - 1, bounds.Top + 1);\r
370 \r
371                                                 break;\r
372 \r
373                                         case TabAlignment.Left:\r
374 \r
375                                                 dc.DrawLine (light, bounds.Left - 2, bounds.Top, bounds.Right, bounds.Top);\r
376                                                 dc.DrawLine (light, bounds.Left, bounds.Top + 2, bounds.Left - 2, bounds.Top);\r
377                                                 dc.DrawLine (light, bounds.Left, bounds.Top + 2, bounds.Left, bounds.Bottom - 2);\r
378 \r
379                                                 dc.DrawLine (SystemPens.ControlDark, bounds.Left, bounds.Bottom - 2, bounds.Left + 2, bounds.Bottom - 1);\r
380 \r
381                                                 dc.DrawLine (SystemPens.ControlDark, bounds.Left + 2, bounds.Bottom - 1, bounds.Right, bounds.Bottom - 1);\r
382                                                 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Left + 2, bounds.Bottom, bounds.Right, bounds.Bottom);\r
383 \r
384                                                 break;\r
385 \r
386                                         default: // TabAlignment.Right\r
387 \r
388                                                 dc.DrawLine (light, bounds.Left, bounds.Top, bounds.Right - 3, bounds.Top);\r
389                                                 dc.DrawLine (light, bounds.Right - 3, bounds.Top, bounds.Right, bounds.Top + 3);\r
390 \r
391                                                 dc.DrawLine (SystemPens.ControlDark, bounds.Right - 1, bounds.Top + 1, bounds.Right - 1, bounds.Bottom - 1);\r
392                                                 dc.DrawLine (SystemPens.ControlDark, bounds.Left, bounds.Bottom - 1, bounds.Right - 2, bounds.Bottom - 1);\r
393 \r
394                                                 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right, bounds.Top + 3, bounds.Right, bounds.Bottom - 3);\r
395                                                 dc.DrawLine (SystemPens.ControlDarkDark, bounds.Left, bounds.Bottom, bounds.Right - 3, bounds.Bottom);\r
396 \r
397                                                 break;\r
398                                 }\r
399                         }\r
400 \r
401                         interior = new Rectangle (bounds.Left + focusRectSpacing.X + borderThickness.Left, \r
402                                 bounds.Top + focusRectSpacing.Y +  + borderThickness.Top,\r
403                                 bounds.Width - (focusRectSpacing.X * 2) - borderThickness.Width, \r
404                                 bounds.Height - (focusRectSpacing.Y * 2) - borderThickness.Height);\r
405 \r
406                         if (tab.DrawMode == TabDrawMode.Normal && page.Text != null) {\r
407                                 if (tab.Alignment == TabAlignment.Left) {\r
408                                         dc.TranslateTransform (bounds.Left, bounds.Bottom);\r
409                                         dc.RotateTransform (-90);\r
410                                         dc.DrawString (page.Text, page.Font,\r
411                                                 SystemBrushes.ControlText, \r
412                                                 tab.Padding.X - 2, // drawstring adds some extra unwanted leading spaces, so trimming\r
413                                                 tab.Padding.Y,\r
414                                                 defaultFormatting);\r
415                                         dc.ResetTransform ();\r
416                                 } else if (tab.Alignment == TabAlignment.Right) {\r
417                                         dc.TranslateTransform (bounds.Right, bounds.Top);\r
418                                         dc.RotateTransform (90);\r
419                                         dc.DrawString (page.Text, page.Font,\r
420                                                 SystemBrushes.ControlText, \r
421                                                 tab.Padding.X - 2, // drawstring adds some extra unwanted leading spaces, so trimming\r
422                                                 tab.Padding.Y,\r
423                                                 defaultFormatting);\r
424                                         dc.ResetTransform ();\r
425                                 } else {\r
426                                         Rectangle str_rect = interior;\r
427 \r
428                                         if (tab.ImageList != null && page.ImageIndex >= 0 && page.ImageIndex < tab.ImageList.Images.Count) {\r
429                                                 int image_y = interior.Y + (interior.Height - tab.ImageList.ImageSize.Height) / 2;\r
430                                                 tab.ImageList.Draw (dc, new Point (interior.X, image_y), page.ImageIndex);\r
431                                                 str_rect.X += tab.ImageList.ImageSize.Width + 2;\r
432                                                 str_rect.Width -= tab.ImageList.ImageSize.Width + 2;\r
433                                         }\r
434                                         dc.DrawString (page.Text, page.Font,\r
435                                                 SystemBrushes.ControlText,\r
436                                                 str_rect, \r
437                                                 defaultFormatting);\r
438 \r
439                                 }\r
440                         } else if (page.Text != null) {\r
441                                 DrawItemState state = DrawItemState.None;\r
442                                 if (page == tab.SelectedTab)\r
443                                         state |= DrawItemState.Selected;\r
444                                 DrawItemEventArgs e = new DrawItemEventArgs (dc,\r
445                                         tab.Font, bounds, tab.IndexForTabPage (page),\r
446                                         state, page.ForeColor, page.BackColor);\r
447                                 tab.OnDrawItemInternal (e);\r
448                                 return res;\r
449                         }\r
450 \r
451                         if (page.Parent.Focused && is_selected) {\r
452                                 ThemeEngine.Current.CPDrawFocusRectangle (dc, interior, tab.ForeColor, tab.BackColor);\r
453                         }\r
454 \r
455                         return res;\r
456                 }\r
457         }\r
458 }\r