Don't use std::min(), it's not available everywhere.
[wxWidgets.git] / src / aui / tabartgtk.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/aui/tabartgtk.cpp
3 // Purpose: implementation of the wxAuiGTKTabArt
4 // Author: Jens Lody and Teodor Petrov
5 // Modified by:
6 // Created: 2012-03-23
7 // RCS-ID: $Id$
8 // Copyright: (c) 2012 Jens Lody <jens@codeblocks.org>
9 // and Teodor Petrov
10 // Licence: wxWindows licence
11 ///////////////////////////////////////////////////////////////////////////////
12
13 // ============================================================================
14 // declarations
15 // ============================================================================
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
23
24 #ifdef __BORLANDC__
25 #pragma hdrstop
26 #endif
27
28 #if wxUSE_AUI
29
30 #ifndef WX_PRECOMP
31 #include "wx/dc.h"
32 #include "wx/dcclient.h"
33 #include "wx/settings.h"
34 #include "wx/image.h"
35 #endif
36
37 #include "wx/gtk/dc.h"
38 #include "wx/gtk/private.h"
39
40 #include <gtk/gtk.h>
41
42 #include "wx/aui/auibook.h"
43 #include "wx/aui/tabartgtk.h"
44 #include "wx/renderer.h"
45
46 namespace
47 {
48
49 static int s_CloseIconSize = 16; // default size
50
51 }
52
53 wxAuiGtkTabArt::wxAuiGtkTabArt()
54
55 {
56 }
57
58 wxAuiTabArt* wxAuiGtkTabArt::Clone()
59 {
60 wxAuiGtkTabArt* clone = new wxAuiGtkTabArt();
61
62 clone->SetNormalFont(m_normalFont);
63 clone->SetSelectedFont(m_normalFont);
64 clone->SetMeasuringFont(m_normalFont);
65
66 return clone;
67 }
68
69 void wxAuiGtkTabArt::DrawBackground(wxDC& dc, wxWindow* WXUNUSED(wnd), const wxRect& rect)
70 {
71 wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
72 GdkWindow* window = impldc->GetGDKWindow();
73
74 gtk_style_apply_default_background(gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget()),
75 window,
76 true,
77 GTK_STATE_NORMAL,
78 NULL,
79 rect.x, rect.y, rect.width, rect.height);
80 }
81
82 void wxAuiGtkTabArt::DrawBorder(wxDC& WXUNUSED(dc), wxWindow* wnd, const wxRect& rect)
83 {
84 int generic_border_width = wxAuiGenericTabArt::GetBorderWidth(wnd);
85
86 if (!wnd) return;
87 if (!wnd->m_wxwindow) return;
88 if (!gtk_widget_is_drawable(wnd->m_wxwindow)) return;
89
90 GtkStyle *style_notebook = gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget());
91
92 gtk_paint_box(style_notebook, wnd->GTKGetDrawingWindow(), GTK_STATE_NORMAL, GTK_SHADOW_OUT,
93 NULL, wnd->m_wxwindow,
94 const_cast<char*>("notebook"),
95 rect.x + generic_border_width + 1, rect.y + generic_border_width + 1,
96 rect.width - (generic_border_width + 1), rect.height - (generic_border_width + 1));
97 }
98
99 void ButtonStateAndShadow(int button_state, GtkStateType &state, GtkShadowType &shadow)
100 {
101
102 if (button_state & wxAUI_BUTTON_STATE_DISABLED)
103 {
104 state = GTK_STATE_INSENSITIVE;
105 shadow = GTK_SHADOW_ETCHED_IN;
106 }
107 else if (button_state & wxAUI_BUTTON_STATE_HOVER)
108 {
109 state = GTK_STATE_PRELIGHT;
110 shadow = GTK_SHADOW_OUT;
111 }
112 else if (button_state & wxAUI_BUTTON_STATE_PRESSED)
113 {
114 state = GTK_STATE_ACTIVE;
115 shadow = GTK_SHADOW_IN;
116 }
117 else
118 {
119 state = GTK_STATE_NORMAL;
120 shadow = GTK_SHADOW_OUT;
121 }
122 }
123
124 wxRect DrawCloseButton(wxDC& dc,
125 GtkWidget *widget,
126 int button_state,
127 wxRect const &in_rect,
128 int orientation,
129 GdkRectangle* clipRect)
130 {
131 GtkStyle *style_button = gtk_widget_get_style(wxGTKPrivate::GetButtonWidget());
132 int xthickness = style_button->xthickness;
133 int ythickness = style_button->ythickness;
134
135 wxBitmap bmp(gtk_widget_render_icon(widget, GTK_STOCK_CLOSE, GTK_ICON_SIZE_SMALL_TOOLBAR, "tab"));
136
137 if(bmp.GetWidth() != s_CloseIconSize || bmp.GetHeight() != s_CloseIconSize)
138 {
139 wxImage img = bmp.ConvertToImage();
140 img.Rescale(s_CloseIconSize, s_CloseIconSize);
141 bmp = img;
142 }
143
144 int button_size = s_CloseIconSize + 2 * xthickness;
145
146 wxRect out_rect;
147
148 if (orientation == wxLEFT)
149 out_rect.x = in_rect.x - ythickness;
150 else
151 out_rect.x = in_rect.x + in_rect.width - button_size - ythickness;
152
153 out_rect.y = in_rect.y + (in_rect.height - button_size) / 2;
154 out_rect.width = button_size;
155 out_rect.height = button_size;
156
157 wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
158 GdkWindow* window = impldc->GetGDKWindow();
159
160 if (button_state == wxAUI_BUTTON_STATE_HOVER)
161 {
162 gtk_paint_box(style_button, window,
163 GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, clipRect, widget, "button",
164 out_rect.x, out_rect.y, out_rect.width, out_rect.height);
165 }
166 else if (button_state == wxAUI_BUTTON_STATE_PRESSED)
167 {
168 gtk_paint_box(style_button, window,
169 GTK_STATE_ACTIVE, GTK_SHADOW_IN, clipRect, widget, "button",
170 out_rect.x, out_rect.y, out_rect.width, out_rect.height);
171 }
172
173
174 dc.DrawBitmap(bmp, out_rect.x + xthickness, out_rect.y + ythickness, true);
175
176 return out_rect;
177 }
178
179 void wxAuiGtkTabArt::DrawTab(wxDC& dc, wxWindow* wnd, const wxAuiNotebookPage& page,
180 const wxRect& in_rect, int close_button_state, wxRect* out_tab_rect,
181 wxRect* out_button_rect, int* x_extent)
182 {
183 GtkWidget *widget = wnd->GetHandle();
184 GtkStyle *style_notebook = gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget());
185
186 wxRect const &window_rect = wnd->GetRect();
187
188 int focus_width = 0;
189
190 gtk_widget_style_get(wxGTKPrivate::GetNotebookWidget(),
191 "focus-line-width", &focus_width,
192 NULL);
193
194 int tab_pos;
195 if (m_flags &wxAUI_NB_BOTTOM)
196 tab_pos = wxAUI_NB_BOTTOM;
197 else //if (m_flags & wxAUI_NB_TOP) {}
198 tab_pos = wxAUI_NB_TOP;
199
200 // TODO: else if (m_flags &wxAUI_NB_LEFT) {}
201 // TODO: else if (m_flags &wxAUI_NB_RIGHT) {}
202
203 // figure out the size of the tab
204 wxSize tab_size = GetTabSize(dc, wnd, page.caption, page.bitmap,
205 page.active, close_button_state, x_extent);
206
207 wxRect tab_rect = in_rect;
208 tab_rect.width = tab_size.x;
209 tab_rect.height = tab_size.y;
210 tab_rect.y += 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
211
212 if (page.active)
213 tab_rect.height += 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
214
215 int gap_rect_height = 10 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
216 int gap_rect_x = 1, gap_start = 0, gap_width = 0;
217 int gap_rect_y = tab_rect.y - gap_rect_height;
218 int gap_rect_width = window_rect.width;
219
220 switch (tab_pos)
221 {
222 case wxAUI_NB_TOP:
223 tab_rect.y -= 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
224 if (!page.active)
225 tab_rect.y += 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
226 gap_rect_y = tab_rect.y + tab_rect.height - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder / 2;
227 // fall through
228 case wxAUI_NB_BOTTOM:
229 gap_start = tab_rect.x - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder / 2;
230 gap_width = tab_rect.width;
231 break;
232 // TODO: case wxAUI_NB_LEFT: break;
233 // TODO: case wxAUI_NB_RIGHT: break;
234 }
235 tab_rect.y += GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder / 2;
236 gap_rect_y += GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder / 2;
237
238 int padding = focus_width + GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
239
240 int clip_width = tab_rect.width;
241 if (tab_rect.x + tab_rect.width > in_rect.x + in_rect.width)
242 clip_width = (in_rect.x + in_rect.width) - tab_rect.x;
243
244 dc.SetClippingRegion(tab_rect.x, tab_rect.y - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder, clip_width, tab_rect.height + GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder);
245
246 GdkRectangle area;
247 area.x = tab_rect.x - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder;
248 area.y = tab_rect.y - 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
249 area.width = clip_width + GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder;
250 area.height = tab_rect.height + 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
251
252 wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
253 GdkWindow* window = impldc->GetGDKWindow();
254
255 // Before drawing the active tab itself, draw a box without border, because some themes
256 // have transparent gaps and a line would be visible at the bottom of the tab
257 if (page.active)
258 gtk_paint_box(style_notebook, window, GTK_STATE_NORMAL, GTK_SHADOW_NONE,
259 NULL, widget,
260 const_cast<char*>("notebook"),
261 gap_rect_x, gap_rect_y,
262 gap_rect_width, gap_rect_height);
263
264 if (tab_pos == wxAUI_NB_BOTTOM)
265 {
266 if (page.active)
267 {
268 gtk_paint_box_gap(style_notebook, window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
269 NULL, widget,
270 const_cast<char*>("notebook"),
271 gap_rect_x, gap_rect_y,
272 gap_rect_width, gap_rect_height,
273 GTK_POS_BOTTOM, gap_start , gap_width);
274 }
275 gtk_paint_extension(style_notebook, window,
276 page.active ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE, GTK_SHADOW_OUT,
277 &area, widget,
278 const_cast<char*>("tab"),
279 tab_rect.x, tab_rect.y,
280 tab_rect.width, tab_rect.height,
281 GTK_POS_TOP);
282 }
283 else
284 {
285 if (page.active)
286 {
287 gtk_paint_box_gap(style_notebook, window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
288 NULL, widget,
289 const_cast<char*>("notebook"),
290 gap_rect_x, gap_rect_y,
291 gap_rect_width, gap_rect_height,
292 GTK_POS_TOP, gap_start , gap_width);
293 }
294 gtk_paint_extension(style_notebook, window,
295 page.active ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE, GTK_SHADOW_OUT,
296 &area, widget,
297 const_cast<char*>("tab"),
298 tab_rect.x, tab_rect.y,
299 tab_rect.width, tab_rect.height,
300 GTK_POS_BOTTOM);
301 }
302
303 // After drawing the inactive tab itself, draw a box with the same dimensions as the gap-box,
304 // otherwise we don't get a gap-box, if the active tab is invisible
305 if (!page.active)
306 gtk_paint_box(style_notebook, window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
307 NULL, widget,
308 const_cast<char*>("notebook"),
309 gap_rect_x, gap_rect_y,
310 gap_rect_width, gap_rect_height);
311
312 wxCoord textX = tab_rect.x + padding + style_notebook->xthickness;
313
314 int bitmap_offset = 0;
315 if (page.bitmap.IsOk())
316 {
317 bitmap_offset = textX;
318
319 // draw bitmap
320 int bitmapY = tab_rect.y +(tab_rect.height - page.bitmap.GetHeight()) / 2;
321 if(!page.active)
322 {
323 if (tab_pos == wxAUI_NB_TOP)
324 bitmapY += style_notebook->ythickness / 2;
325 else
326 bitmapY -= style_notebook->ythickness / 2;
327 }
328 dc.DrawBitmap(page.bitmap,
329 bitmap_offset,
330 bitmapY,
331 true);
332
333 textX += page.bitmap.GetWidth() + padding;
334 }
335
336 wxCoord textW, textH, textY;
337
338 dc.SetFont(m_normalFont);
339 dc.GetTextExtent(page.caption, &textW, &textH);
340 textY = tab_rect.y + (tab_rect.height - textH) / 2;
341 if(!page.active)
342 {
343 if (tab_pos == wxAUI_NB_TOP)
344 textY += style_notebook->ythickness / 2;
345 else
346 textY -= style_notebook->ythickness / 2;
347 }
348
349 // draw tab text
350 GdkColor text_colour = page.active ? style_notebook->fg[GTK_STATE_NORMAL] : style_notebook->fg[GTK_STATE_ACTIVE];
351 dc.SetTextForeground(wxColor(text_colour));
352 GdkRectangle focus_area;
353
354 int padding_focus = padding - focus_width;
355 focus_area.x = tab_rect.x + padding_focus;
356 focus_area.y = textY - focus_width;
357 focus_area.width = tab_rect.width - 2 * padding_focus;
358 focus_area.height = textH + 2 * focus_width;
359
360 if(page.active && (wnd->FindFocus() == wnd) && focus_area.x <= (area.x + area.width))
361 {
362 // clipping seems not to work here, so we we have to recalc the focus-area manually
363 if((focus_area.x + focus_area.width) > (area.x + area.width))
364 focus_area.width = area.x + area.width - focus_area.x + focus_width - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder;
365 gtk_paint_focus (style_notebook, window,
366 GTK_STATE_ACTIVE, NULL, widget, "tab",
367 focus_area.x, focus_area.y, focus_area.width, focus_area.height);
368 }
369
370 dc.DrawText(page.caption, textX, textY);
371
372 // draw close-button on tab (if enabled)
373 if (close_button_state != wxAUI_BUTTON_STATE_HIDDEN)
374 {
375 wxRect rect(tab_rect.x, tab_rect.y, tab_rect.width - style_notebook->xthickness, tab_rect.height);
376 if(!page.active)
377 {
378 if (tab_pos == wxAUI_NB_TOP)
379 rect.y += style_notebook->ythickness / 2;
380 else
381 rect.y -= style_notebook->ythickness / 2;
382 }
383 *out_button_rect = DrawCloseButton(dc, widget, close_button_state, rect, wxRIGHT, &area);
384 }
385
386 if ( clip_width < tab_rect.width )
387 tab_rect.width = clip_width;
388 *out_tab_rect = tab_rect;
389
390 dc.DestroyClippingRegion();
391 }
392
393 wxRect DrawSimpleArrow(wxDC& dc,
394 GtkWidget *widget,
395 int button_state,
396 wxRect const &in_rect,
397 int orientation,
398 GtkArrowType arrow_type)
399 {
400 int scroll_arrow_hlength, scroll_arrow_vlength;
401 gtk_widget_style_get(widget,
402 "scroll-arrow-hlength", &scroll_arrow_hlength,
403 "scroll-arrow-vlength", &scroll_arrow_vlength,
404 NULL);
405
406 GtkStateType state;
407 GtkShadowType shadow;
408 ButtonStateAndShadow(button_state, state, shadow);
409
410 wxRect out_rect;
411
412 if (orientation == wxLEFT)
413 out_rect.x = in_rect.x;
414 else
415 out_rect.x = in_rect.x + in_rect.width - scroll_arrow_hlength;
416 out_rect.y = (in_rect.y + in_rect.height - 3 * gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget())->ythickness - scroll_arrow_vlength) / 2;
417 out_rect.width = scroll_arrow_hlength;
418 out_rect.height = scroll_arrow_vlength;
419
420 wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
421 GdkWindow* window = impldc->GetGDKWindow();
422 gtk_paint_arrow (gtk_widget_get_style(wxGTKPrivate::GetButtonWidget()), window, state, shadow, NULL, widget, "notebook",
423 arrow_type, TRUE, out_rect.x, out_rect.y, out_rect.width, out_rect.height);
424
425 return out_rect;
426 }
427
428 void wxAuiGtkTabArt::DrawButton(wxDC& dc, wxWindow* wnd,
429 const wxRect& in_rect,
430 int bitmap_id,
431 int button_state,
432 int orientation,
433 wxRect* out_rect)
434 {
435 GtkWidget *widget = wnd->GetHandle();
436 wxRect rect = in_rect;
437 if (m_flags &wxAUI_NB_BOTTOM)
438 rect.y += 2 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness;
439
440 switch (bitmap_id)
441 {
442 case wxAUI_BUTTON_CLOSE:
443 rect.y -= 2 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness;
444 rect = DrawCloseButton(dc, widget, button_state, rect, orientation, NULL);
445 break;
446
447 case wxAUI_BUTTON_LEFT:
448 rect = DrawSimpleArrow(dc, widget, button_state, rect, orientation, GTK_ARROW_LEFT);
449 break;
450
451 case wxAUI_BUTTON_RIGHT:
452 rect = DrawSimpleArrow(dc, widget, button_state, rect, orientation, GTK_ARROW_RIGHT);
453 break;
454
455 case wxAUI_BUTTON_WINDOWLIST:
456 {
457 rect.height -= 4 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness;
458 rect.width = rect.height;
459 rect.x = in_rect.x + in_rect.width - rect.width;
460
461 if (button_state == wxAUI_BUTTON_STATE_HOVER)
462 wxRendererNative::Get().DrawComboBoxDropButton(wnd, dc, rect, wxCONTROL_CURRENT);
463 else if (button_state == wxAUI_BUTTON_STATE_PRESSED)
464 wxRendererNative::Get().DrawComboBoxDropButton(wnd, dc, rect, wxCONTROL_PRESSED);
465 else
466 wxRendererNative::Get().DrawDropArrow(wnd, dc, rect);
467 }
468 break;
469 }
470
471 *out_rect = rect;
472 }
473
474
475 int wxAuiGtkTabArt::GetBestTabCtrlSize(wxWindow* wnd,
476 const wxAuiNotebookPageArray& pages,
477 const wxSize& required_bmp_size)
478 {
479 SetMeasuringFont(m_normalFont);
480 SetSelectedFont(m_normalFont);
481 int tab_height = 3 * gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget())->ythickness + wxAuiGenericTabArt::GetBestTabCtrlSize(wnd, pages, required_bmp_size);
482 return tab_height;
483 }
484
485 int wxAuiGtkTabArt::GetBorderWidth(wxWindow* wnd)
486 {
487 return wxAuiGenericTabArt::GetBorderWidth(wnd) + wxMax(GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder,
488 GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder);
489 }
490
491 int wxAuiGtkTabArt::GetAdditionalBorderSpace(wxWindow* wnd)
492 {
493 return 2 * GetBorderWidth(wnd);
494 }
495
496 wxSize wxAuiGtkTabArt::GetTabSize(wxDC& dc,
497 wxWindow* wnd,
498 const wxString& caption,
499 const wxBitmap& bitmap,
500 bool active,
501 int close_button_state,
502 int* x_extent)
503 {
504 wxSize s = wxAuiGenericTabArt::GetTabSize(dc, wnd, caption, bitmap, active, close_button_state, x_extent);
505
506 int overlap = 0;
507 gtk_widget_style_get (wnd->GetHandle(),
508 "focus-line-width", &overlap,
509 NULL);
510 *x_extent -= overlap;
511 return s;
512 }
513 #endif // wxUSE_AUI