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