]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/renderer.cpp
honour wx{CLOSE,MINIMIZE,MAXIMIZE}_BOX even for borderless windows; also always allow...
[wxWidgets.git] / src / gtk / renderer.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/renderer.cpp
3 // Purpose: implementation of wxRendererNative for wxGTK
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 20.07.2003
7 // RCS-ID: $Id$
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // License: 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 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #include "wx/renderer.h"
28
29 #ifndef WX_PRECOMP
30 #include "wx/window.h"
31 #include "wx/dcclient.h"
32 #include "wx/settings.h"
33 #include "wx/module.h"
34 #endif
35
36 #include "wx/gtk/dc.h"
37 #include "wx/gtk/private.h"
38
39 #include <gtk/gtk.h>
40
41 // ----------------------------------------------------------------------------
42 // wxRendererGTK: our wxRendererNative implementation
43 // ----------------------------------------------------------------------------
44
45 class WXDLLEXPORT wxRendererGTK : public wxDelegateRendererNative
46 {
47 public:
48 // draw the header control button (used by wxListCtrl)
49 virtual int DrawHeaderButton(wxWindow *win,
50 wxDC& dc,
51 const wxRect& rect,
52 int flags = 0,
53 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
54 wxHeaderButtonParams* params = NULL);
55
56 // draw the expanded/collapsed icon for a tree control item
57 virtual void DrawTreeItemButton(wxWindow *win,
58 wxDC& dc,
59 const wxRect& rect,
60 int flags = 0);
61
62 virtual void DrawSplitterBorder(wxWindow *win,
63 wxDC& dc,
64 const wxRect& rect,
65 int flags = 0);
66 virtual void DrawSplitterSash(wxWindow *win,
67 wxDC& dc,
68 const wxSize& size,
69 wxCoord position,
70 wxOrientation orient,
71 int flags = 0);
72
73 virtual void DrawComboBoxDropButton(wxWindow *win,
74 wxDC& dc,
75 const wxRect& rect,
76 int flags = 0);
77
78 virtual void DrawDropArrow(wxWindow *win,
79 wxDC& dc,
80 const wxRect& rect,
81 int flags = 0);
82
83 virtual void DrawCheckBox(wxWindow *win,
84 wxDC& dc,
85 const wxRect& rect,
86 int flags = 0);
87
88 virtual void DrawPushButton(wxWindow *win,
89 wxDC& dc,
90 const wxRect& rect,
91 int flags = 0);
92
93 virtual void DrawItemSelectionRect(wxWindow *win,
94 wxDC& dc,
95 const wxRect& rect,
96 int flags = 0);
97
98 virtual void DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags = 0);
99
100 virtual wxSize GetCheckBoxSize(wxWindow *win);
101
102 virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
103 };
104
105 // ============================================================================
106 // implementation
107 // ============================================================================
108
109 /* static */
110 wxRendererNative& wxRendererNative::GetDefault()
111 {
112 static wxRendererGTK s_rendererGTK;
113
114 return s_rendererGTK;
115 }
116
117 // ----------------------------------------------------------------------------
118 // list/tree controls drawing
119 // ----------------------------------------------------------------------------
120
121 int
122 wxRendererGTK::DrawHeaderButton(wxWindow *win,
123 wxDC& dc,
124 const wxRect& rect,
125 int flags,
126 wxHeaderSortIconType sortArrow,
127 wxHeaderButtonParams* params)
128 {
129
130 GtkWidget *button = wxGTKPrivate::GetHeaderButtonWidget();
131
132 GdkWindow* gdk_window = NULL;
133 #if wxUSE_NEW_DC
134 wxDCImpl *impl = dc.GetImpl();
135 wxGTKDCImpl *gtk_impl = wxDynamicCast( impl, wxGTKDCImpl );
136 if (gtk_impl)
137 gdk_window = gtk_impl->GetGDKWindow();
138 #else
139 gdk_window = dc.GetGDKWindow();
140 #endif
141 wxASSERT_MSG( gdk_window,
142 wxT("cannot use wxRendererNative on wxDC of this type") );
143
144 int x_diff = 0;
145 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
146 x_diff = rect.width;
147
148 GtkStateType state = GTK_STATE_NORMAL;
149 if (flags & wxCONTROL_DISABLED)
150 state = GTK_STATE_INSENSITIVE;
151 else
152 {
153 if (flags & wxCONTROL_CURRENT)
154 state = GTK_STATE_PRELIGHT;
155 }
156
157 gtk_paint_box
158 (
159 button->style,
160 gdk_window,
161 state,
162 GTK_SHADOW_OUT,
163 NULL,
164 button,
165 "button",
166 dc.LogicalToDeviceX(rect.x) - x_diff, rect.y, rect.width, rect.height
167 );
168
169 return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params);
170 }
171
172 // draw a ">" or "v" button
173 void
174 wxRendererGTK::DrawTreeItemButton(wxWindow* win,
175 wxDC& dc, const wxRect& rect, int flags)
176 {
177 GtkWidget *tree = wxGTKPrivate::GetTreeWidget();
178
179 GdkWindow* gdk_window = NULL;
180 #if wxUSE_NEW_DC
181 wxDCImpl *impl = dc.GetImpl();
182 wxGTKDCImpl *gtk_impl = wxDynamicCast( impl, wxGTKDCImpl );
183 if (gtk_impl)
184 gdk_window = gtk_impl->GetGDKWindow();
185 #else
186 gdk_window = dc.GetGDKWindow();
187 #endif
188 wxASSERT_MSG( gdk_window,
189 wxT("cannot use wxRendererNative on wxDC of this type") );
190
191 GtkStateType state;
192 if ( flags & wxCONTROL_CURRENT )
193 state = GTK_STATE_PRELIGHT;
194 else
195 state = GTK_STATE_NORMAL;
196
197 int x_diff = 0;
198 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
199 x_diff = rect.width;
200
201 // VZ: I don't know how to get the size of the expander so as to centre it
202 // in the given rectangle, +2/3 below is just what looks good here...
203 gtk_paint_expander
204 (
205 tree->style,
206 gdk_window,
207 state,
208 NULL,
209 tree,
210 "treeview",
211 dc.LogicalToDeviceX(rect.x) + 6 - x_diff,
212 dc.LogicalToDeviceY(rect.y) + 3,
213 flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
214 : GTK_EXPANDER_COLLAPSED
215 );
216 }
217
218
219 // ----------------------------------------------------------------------------
220 // splitter sash drawing
221 // ----------------------------------------------------------------------------
222
223 static int GetGtkSplitterFullSize(GtkWidget* widget)
224 {
225 gint handle_size;
226 gtk_widget_style_get(widget, "handle_size", &handle_size, NULL);
227
228 return handle_size;
229 }
230
231 wxSplitterRenderParams
232 wxRendererGTK::GetSplitterParams(const wxWindow *WXUNUSED(win))
233 {
234 // we don't draw any border, hence 0 for the second field
235 return wxSplitterRenderParams
236 (
237 GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget()),
238 0,
239 true // hot sensitive
240 );
241 }
242
243 void
244 wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
245 wxDC& WXUNUSED(dc),
246 const wxRect& WXUNUSED(rect),
247 int WXUNUSED(flags))
248 {
249 // nothing to do
250 }
251
252 void
253 wxRendererGTK::DrawSplitterSash(wxWindow *win,
254 wxDC& dc,
255 const wxSize& size,
256 wxCoord position,
257 wxOrientation orient,
258 int flags)
259 {
260 if ( !win->m_wxwindow->window )
261 {
262 // window not realized yet
263 return;
264 }
265
266 GdkWindow* gdk_window = NULL;
267 #if wxUSE_NEW_DC
268 wxDCImpl *impl = dc.GetImpl();
269 wxGTKDCImpl *gtk_impl = wxDynamicCast( impl, wxGTKDCImpl );
270 if (gtk_impl)
271 gdk_window = gtk_impl->GetGDKWindow();
272 #else
273 gdk_window = dc.GetGDKWindow();
274 #endif
275 wxASSERT_MSG( gdk_window,
276 wxT("cannot use wxRendererNative on wxDC of this type") );
277
278 wxCoord full_size = GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget());
279
280 // are we drawing vertical or horizontal splitter?
281 const bool isVert = orient == wxVERTICAL;
282
283 GdkRectangle rect;
284
285 if ( isVert )
286 {
287 rect.x = position;
288 rect.y = 0;
289 rect.width = full_size;
290 rect.height = size.y;
291 }
292 else // horz
293 {
294 rect.x = 0;
295 rect.y = position;
296 rect.height = full_size;
297 rect.width = size.x;
298 }
299
300 int x_diff = 0;
301 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
302 x_diff = rect.width;
303
304 gtk_paint_handle
305 (
306 win->m_wxwindow->style,
307 gdk_window,
308 flags & wxCONTROL_CURRENT ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
309 GTK_SHADOW_NONE,
310 NULL /* no clipping */,
311 win->m_wxwindow,
312 "paned",
313 dc.LogicalToDeviceX(rect.x) - x_diff,
314 dc.LogicalToDeviceY(rect.y),
315 rect.width,
316 rect.height,
317 isVert ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL
318 );
319 }
320
321 void
322 wxRendererGTK::DrawDropArrow(wxWindow *WXUNUSED(win),
323 wxDC& dc,
324 const wxRect& rect,
325 int flags)
326 {
327 GtkWidget *button = wxGTKPrivate::GetButtonWidget();
328
329 // If we give GTK_PIZZA(win->m_wxwindow)->bin_window as
330 // a window for gtk_paint_xxx function, then it won't
331 // work for wxMemoryDC. So that is why we assume wxDC
332 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
333 // are derived from it) and use its m_window.
334 GdkWindow* gdk_window = NULL;
335 #if wxUSE_NEW_DC
336 wxDCImpl *impl = dc.GetImpl();
337 wxGTKDCImpl *gtk_impl = wxDynamicCast( impl, wxGTKDCImpl );
338 if (gtk_impl)
339 gdk_window = gtk_impl->GetGDKWindow();
340 #else
341 gdk_window = dc.GetGDKWindow();
342 #endif
343 wxASSERT_MSG( gdk_window,
344 wxT("cannot use wxRendererNative on wxDC of this type") );
345
346 // draw arrow so that there is even space horizontally
347 // on both sides
348 int arrowX = rect.width/4 + 1;
349 int arrowWidth = rect.width - (arrowX*2);
350
351 // scale arrow's height accoording to the width
352 int arrowHeight = rect.width/3;
353 int arrowY = (rect.height-arrowHeight)/2 +
354 ((rect.height-arrowHeight) & 1);
355
356 GtkStateType state;
357
358 if ( flags & wxCONTROL_PRESSED )
359 state = GTK_STATE_ACTIVE;
360 else if ( flags & wxCONTROL_DISABLED )
361 state = GTK_STATE_INSENSITIVE;
362 else if ( flags & wxCONTROL_CURRENT )
363 state = GTK_STATE_PRELIGHT;
364 else
365 state = GTK_STATE_NORMAL;
366
367 // draw arrow on button
368 gtk_paint_arrow
369 (
370 button->style,
371 gdk_window,
372 state,
373 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
374 NULL,
375 button,
376 "arrow",
377 GTK_ARROW_DOWN,
378 FALSE,
379 rect.x + arrowX,
380 rect.y + arrowY,
381 arrowWidth,
382 arrowHeight
383 );
384 }
385
386 void
387 wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
388 wxDC& dc,
389 const wxRect& rect,
390 int flags)
391 {
392 DrawPushButton(win,dc,rect,flags);
393 DrawDropArrow(win,dc,rect);
394 }
395
396 wxSize
397 wxRendererGTK::GetCheckBoxSize(wxWindow *WXUNUSED(win))
398 {
399 gint indicator_size, indicator_spacing;
400 gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(),
401 "indicator_size", &indicator_size,
402 "indicator_spacing", &indicator_spacing,
403 NULL);
404
405 int size = indicator_size + indicator_spacing * 2;
406 return wxSize(size, size);
407 }
408
409 void
410 wxRendererGTK::DrawCheckBox(wxWindow *WXUNUSED(win),
411 wxDC& dc,
412 const wxRect& rect,
413 int flags )
414 {
415 GtkWidget *button = wxGTKPrivate::GetCheckButtonWidget();
416
417 GdkWindow* gdk_window = NULL;
418 #if wxUSE_NEW_DC
419 wxDCImpl *impl = dc.GetImpl();
420 wxGTKDCImpl *gtk_impl = wxDynamicCast( impl, wxGTKDCImpl );
421 if (gtk_impl)
422 gdk_window = gtk_impl->GetGDKWindow();
423 #else
424 gdk_window = dc.GetGDKWindow();
425 #endif
426 wxASSERT_MSG( gdk_window,
427 wxT("cannot use wxRendererNative on wxDC of this type") );
428
429 gint indicator_size, indicator_spacing;
430 gtk_widget_style_get(button,
431 "indicator_size", &indicator_size,
432 "indicator_spacing", &indicator_spacing,
433 NULL);
434
435 GtkStateType state;
436
437 if ( flags & wxCONTROL_PRESSED )
438 state = GTK_STATE_ACTIVE;
439 else if ( flags & wxCONTROL_DISABLED )
440 state = GTK_STATE_INSENSITIVE;
441 else if ( flags & wxCONTROL_CURRENT )
442 state = GTK_STATE_PRELIGHT;
443 else
444 state = GTK_STATE_NORMAL;
445
446 gtk_paint_check
447 (
448 button->style,
449 gdk_window,
450 state,
451 flags & wxCONTROL_CHECKED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
452 NULL,
453 button,
454 "cellcheck",
455 dc.LogicalToDeviceX(rect.x) + indicator_spacing,
456 dc.LogicalToDeviceY(rect.y) + indicator_spacing,
457 indicator_size, indicator_size
458 );
459 }
460
461 void
462 wxRendererGTK::DrawPushButton(wxWindow *WXUNUSED(win),
463 wxDC& dc,
464 const wxRect& rect,
465 int flags)
466 {
467 GtkWidget *button = wxGTKPrivate::GetButtonWidget();
468
469 GdkWindow* gdk_window = NULL;
470 #if wxUSE_NEW_DC
471 wxDCImpl *impl = dc.GetImpl();
472 wxGTKDCImpl *gtk_impl = wxDynamicCast( impl, wxGTKDCImpl );
473 if (gtk_impl)
474 gdk_window = gtk_impl->GetGDKWindow();
475 #else
476 gdk_window = dc.GetGDKWindow();
477 #endif
478 wxASSERT_MSG( gdk_window,
479 wxT("cannot use wxRendererNative on wxDC of this type") );
480
481 // draw button
482 GtkStateType state;
483
484 if ( flags & wxCONTROL_PRESSED )
485 state = GTK_STATE_ACTIVE;
486 else if ( flags & wxCONTROL_DISABLED )
487 state = GTK_STATE_INSENSITIVE;
488 else if ( flags & wxCONTROL_CURRENT )
489 state = GTK_STATE_PRELIGHT;
490 else
491 state = GTK_STATE_NORMAL;
492
493 gtk_paint_box
494 (
495 button->style,
496 gdk_window,
497 state,
498 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
499 NULL,
500 button,
501 "button",
502 rect.x, rect.y, rect.width, rect.height
503 );
504 }
505
506 void
507 wxRendererGTK::DrawItemSelectionRect(wxWindow *win,
508 wxDC& dc,
509 const wxRect& rect,
510 int flags )
511 {
512 GdkWindow* gdk_window = NULL;
513 #if wxUSE_NEW_DC
514 wxDCImpl *impl = dc.GetImpl();
515 wxGTKDCImpl *gtk_impl = wxDynamicCast( impl, wxGTKDCImpl );
516 if (gtk_impl)
517 gdk_window = gtk_impl->GetGDKWindow();
518 #else
519 gdk_window = dc.GetGDKWindow();
520 #endif
521 wxASSERT_MSG( gdk_window,
522 wxT("cannot use wxRendererNative on wxDC of this type") );
523
524 int x_diff = 0;
525 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
526 x_diff = rect.width;
527
528 GtkStateType state;
529 if (flags & wxCONTROL_SELECTED)
530 {
531 // the wxCONTROL_FOCUSED state is deduced
532 // directly from the m_wxwindow by GTK+
533 state = GTK_STATE_SELECTED;
534
535 gtk_paint_flat_box( win->m_widget->style,
536 gdk_window,
537 state,
538 GTK_SHADOW_NONE,
539 NULL,
540 win->m_wxwindow,
541 "cell_even",
542 dc.LogicalToDeviceX(rect.x) - x_diff,
543 dc.LogicalToDeviceY(rect.y),
544 rect.width,
545 rect.height );
546 }
547 else // !wxCONTROL_SELECTED
548 {
549 state = GTK_STATE_NORMAL;
550 }
551
552 if ((flags & wxCONTROL_CURRENT) && (flags & wxCONTROL_FOCUSED))
553 {
554 gtk_paint_focus( win->m_widget->style,
555 gdk_window,
556 state,
557 NULL,
558 win->m_wxwindow,
559 // Detail "treeview" causes warning with GTK+ 2.12 Clearlooks theme:
560 // "... no property named `row-ending-details'"
561 // Using "treeview-middle" would fix the warning, but the right
562 // edge of the focus rect is not getting erased properly either.
563 // Better to not specify this detail unless the drawing is fixed.
564 NULL,
565 dc.LogicalToDeviceX(rect.x),
566 dc.LogicalToDeviceY(rect.y),
567 rect.width,
568 rect.height );
569 }
570 }
571
572 void wxRendererGTK::DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
573 {
574 GdkWindow* gdk_window = NULL;
575 #if wxUSE_NEW_DC
576 wxDCImpl *impl = dc.GetImpl();
577 wxGTKDCImpl *gtk_impl = wxDynamicCast( impl, wxGTKDCImpl );
578 if (gtk_impl)
579 gdk_window = gtk_impl->GetGDKWindow();
580 #else
581 gdk_window = dc.GetGDKWindow();
582 #endif
583 wxASSERT_MSG( gdk_window,
584 wxT("cannot use wxRendererNative on wxDC of this type") );
585
586 GtkStateType state;
587 if (flags & wxCONTROL_SELECTED)
588 state = GTK_STATE_SELECTED;
589 else
590 state = GTK_STATE_NORMAL;
591
592 gtk_paint_focus( win->m_widget->style,
593 gdk_window,
594 state,
595 NULL,
596 win->m_wxwindow,
597 NULL,
598 dc.LogicalToDeviceX(rect.x),
599 dc.LogicalToDeviceY(rect.y),
600 rect.width,
601 rect.height );
602 }