don't use m_backing_window, it can't be drawn on
[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/dcgraph.h"
37 #include "wx/gtk/dc.h"
38 #include "wx/gtk/private.h"
39
40 #include <gtk/gtk.h>
41
42 // ----------------------------------------------------------------------------
43 // wxRendererGTK: our wxRendererNative implementation
44 // ----------------------------------------------------------------------------
45
46 class WXDLLEXPORT wxRendererGTK : public wxDelegateRendererNative
47 {
48 public:
49 // draw the header control button (used by wxListCtrl)
50 virtual int DrawHeaderButton(wxWindow *win,
51 wxDC& dc,
52 const wxRect& rect,
53 int flags = 0,
54 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
55 wxHeaderButtonParams* params = NULL);
56
57 // draw the expanded/collapsed icon for a tree control item
58 virtual void DrawTreeItemButton(wxWindow *win,
59 wxDC& dc,
60 const wxRect& rect,
61 int flags = 0);
62
63 virtual void DrawSplitterBorder(wxWindow *win,
64 wxDC& dc,
65 const wxRect& rect,
66 int flags = 0);
67 virtual void DrawSplitterSash(wxWindow *win,
68 wxDC& dc,
69 const wxSize& size,
70 wxCoord position,
71 wxOrientation orient,
72 int flags = 0);
73
74 virtual void DrawComboBoxDropButton(wxWindow *win,
75 wxDC& dc,
76 const wxRect& rect,
77 int flags = 0);
78
79 virtual void DrawDropArrow(wxWindow *win,
80 wxDC& dc,
81 const wxRect& rect,
82 int flags = 0);
83
84 virtual void DrawCheckBox(wxWindow *win,
85 wxDC& dc,
86 const wxRect& rect,
87 int flags = 0);
88
89 virtual void DrawPushButton(wxWindow *win,
90 wxDC& dc,
91 const wxRect& rect,
92 int flags = 0);
93
94 virtual void DrawItemSelectionRect(wxWindow *win,
95 wxDC& dc,
96 const wxRect& rect,
97 int flags = 0);
98
99 virtual void DrawChoice(wxWindow* win,
100 wxDC& dc,
101 const wxRect& rect,
102 int flags=0);
103
104 virtual void DrawComboBox(wxWindow* win,
105 wxDC& dc,
106 const wxRect& rect,
107 int flags=0);
108
109 virtual void DrawTextCtrl(wxWindow* win,
110 wxDC& dc,
111 const wxRect& rect,
112 int flags=0);
113
114 virtual void DrawRadioButton(wxWindow* win,
115 wxDC& dc,
116 const wxRect& rect,
117 int flags=0);
118
119 virtual void DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags = 0);
120
121 virtual wxSize GetCheckBoxSize(wxWindow *win);
122
123 virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
124 };
125
126 // ============================================================================
127 // implementation
128 // ============================================================================
129
130 /* static */
131 wxRendererNative& wxRendererNative::GetDefault()
132 {
133 static wxRendererGTK s_rendererGTK;
134
135 return s_rendererGTK;
136 }
137
138 static GdkWindow* wxGetGdkWindowForDC(wxWindow* win, wxDC& dc)
139 {
140 GdkWindow* gdk_window = NULL;
141
142 #if wxUSE_GRAPHICS_CONTEXT
143 if ( dc.IsKindOf( CLASSINFO(wxGCDC) ) )
144 gdk_window = win->GTKGetDrawingWindow();
145 else
146 #endif
147 {
148 #if wxUSE_NEW_DC
149 wxDCImpl *impl = dc.GetImpl();
150 wxGTKDCImpl *gtk_impl = wxDynamicCast( impl, wxGTKDCImpl );
151 if (gtk_impl)
152 gdk_window = gtk_impl->GetGDKWindow();
153 #else
154 gdk_window = dc.GetGDKWindow();
155 #endif
156 }
157 return gdk_window;
158 }
159
160 // ----------------------------------------------------------------------------
161 // list/tree controls drawing
162 // ----------------------------------------------------------------------------
163
164 int
165 wxRendererGTK::DrawHeaderButton(wxWindow *win,
166 wxDC& dc,
167 const wxRect& rect,
168 int flags,
169 wxHeaderSortIconType sortArrow,
170 wxHeaderButtonParams* params)
171 {
172
173 GtkWidget *button = wxGTKPrivate::GetHeaderButtonWidget();
174
175 GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
176 wxASSERT_MSG( gdk_window,
177 wxT("cannot use wxRendererNative on wxDC of this type") );
178
179 int x_diff = 0;
180 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
181 x_diff = rect.width;
182
183 GtkStateType state = GTK_STATE_NORMAL;
184 if (flags & wxCONTROL_DISABLED)
185 state = GTK_STATE_INSENSITIVE;
186 else
187 {
188 if (flags & wxCONTROL_CURRENT)
189 state = GTK_STATE_PRELIGHT;
190 }
191
192 gtk_paint_box
193 (
194 button->style,
195 gdk_window,
196 state,
197 GTK_SHADOW_OUT,
198 NULL,
199 button,
200 "button",
201 dc.LogicalToDeviceX(rect.x) - x_diff, rect.y, rect.width, rect.height
202 );
203
204 return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params);
205 }
206
207 // draw a ">" or "v" button
208 void
209 wxRendererGTK::DrawTreeItemButton(wxWindow* win,
210 wxDC& dc, const wxRect& rect, int flags)
211 {
212 GtkWidget *tree = wxGTKPrivate::GetTreeWidget();
213
214 GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
215 wxASSERT_MSG( gdk_window,
216 wxT("cannot use wxRendererNative on wxDC of this type") );
217
218 GtkStateType state;
219 if ( flags & wxCONTROL_CURRENT )
220 state = GTK_STATE_PRELIGHT;
221 else
222 state = GTK_STATE_NORMAL;
223
224 int x_diff = 0;
225 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
226 x_diff = rect.width;
227
228 // VZ: I don't know how to get the size of the expander so as to centre it
229 // in the given rectangle, +2/3 below is just what looks good here...
230 gtk_paint_expander
231 (
232 tree->style,
233 gdk_window,
234 state,
235 NULL,
236 tree,
237 "treeview",
238 dc.LogicalToDeviceX(rect.x) + 6 - x_diff,
239 dc.LogicalToDeviceY(rect.y) + 3,
240 flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
241 : GTK_EXPANDER_COLLAPSED
242 );
243 }
244
245
246 // ----------------------------------------------------------------------------
247 // splitter sash drawing
248 // ----------------------------------------------------------------------------
249
250 static int GetGtkSplitterFullSize(GtkWidget* widget)
251 {
252 gint handle_size;
253 gtk_widget_style_get(widget, "handle_size", &handle_size, NULL);
254
255 return handle_size;
256 }
257
258 wxSplitterRenderParams
259 wxRendererGTK::GetSplitterParams(const wxWindow *WXUNUSED(win))
260 {
261 // we don't draw any border, hence 0 for the second field
262 return wxSplitterRenderParams
263 (
264 GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget()),
265 0,
266 true // hot sensitive
267 );
268 }
269
270 void
271 wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
272 wxDC& WXUNUSED(dc),
273 const wxRect& WXUNUSED(rect),
274 int WXUNUSED(flags))
275 {
276 // nothing to do
277 }
278
279 void
280 wxRendererGTK::DrawSplitterSash(wxWindow* win,
281 wxDC& dc,
282 const wxSize& size,
283 wxCoord position,
284 wxOrientation orient,
285 int flags)
286 {
287 if ( !win->m_wxwindow->window )
288 {
289 // window not realized yet
290 return;
291 }
292
293 GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
294 wxASSERT_MSG( gdk_window,
295 wxT("cannot use wxRendererNative on wxDC of this type") );
296
297 wxCoord full_size = GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget());
298
299 // are we drawing vertical or horizontal splitter?
300 const bool isVert = orient == wxVERTICAL;
301
302 GdkRectangle rect;
303
304 if ( isVert )
305 {
306 rect.x = position;
307 rect.y = 0;
308 rect.width = full_size;
309 rect.height = size.y;
310 }
311 else // horz
312 {
313 rect.x = 0;
314 rect.y = position;
315 rect.height = full_size;
316 rect.width = size.x;
317 }
318
319 int x_diff = 0;
320 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
321 x_diff = rect.width;
322
323 gtk_paint_handle
324 (
325 win->m_wxwindow->style,
326 gdk_window,
327 flags & wxCONTROL_CURRENT ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
328 GTK_SHADOW_NONE,
329 NULL /* no clipping */,
330 win->m_wxwindow,
331 "paned",
332 dc.LogicalToDeviceX(rect.x) - x_diff,
333 dc.LogicalToDeviceY(rect.y),
334 rect.width,
335 rect.height,
336 isVert ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL
337 );
338 }
339
340 void
341 wxRendererGTK::DrawDropArrow(wxWindow* win,
342 wxDC& dc,
343 const wxRect& rect,
344 int flags)
345 {
346 GtkWidget *button = wxGTKPrivate::GetButtonWidget();
347
348 // If we give WX_PIZZA(win->m_wxwindow)->bin_window as
349 // a window for gtk_paint_xxx function, then it won't
350 // work for wxMemoryDC. So that is why we assume wxDC
351 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
352 // are derived from it) and use its m_window.
353 GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
354 wxASSERT_MSG( gdk_window,
355 wxT("cannot use wxRendererNative on wxDC of this type") );
356
357 // draw arrow so that there is even space horizontally
358 // on both sides
359 int arrowX = rect.width/4 + 1;
360 int arrowWidth = rect.width - (arrowX*2);
361
362 // scale arrow's height accoording to the width
363 int arrowHeight = rect.width/3;
364 int arrowY = (rect.height-arrowHeight)/2 +
365 ((rect.height-arrowHeight) & 1);
366
367 GtkStateType state;
368
369 if ( flags & wxCONTROL_PRESSED )
370 state = GTK_STATE_ACTIVE;
371 else if ( flags & wxCONTROL_DISABLED )
372 state = GTK_STATE_INSENSITIVE;
373 else if ( flags & wxCONTROL_CURRENT )
374 state = GTK_STATE_PRELIGHT;
375 else
376 state = GTK_STATE_NORMAL;
377
378 // draw arrow on button
379 gtk_paint_arrow
380 (
381 button->style,
382 gdk_window,
383 state,
384 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
385 NULL,
386 button,
387 "arrow",
388 GTK_ARROW_DOWN,
389 FALSE,
390 rect.x + arrowX,
391 rect.y + arrowY,
392 arrowWidth,
393 arrowHeight
394 );
395 }
396
397 void
398 wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
399 wxDC& dc,
400 const wxRect& rect,
401 int flags)
402 {
403 DrawPushButton(win,dc,rect,flags);
404 DrawDropArrow(win,dc,rect);
405 }
406
407 wxSize
408 wxRendererGTK::GetCheckBoxSize(wxWindow *WXUNUSED(win))
409 {
410 gint indicator_size, indicator_spacing;
411 gtk_widget_style_get(wxGTKPrivate::GetCheckButtonWidget(),
412 "indicator_size", &indicator_size,
413 "indicator_spacing", &indicator_spacing,
414 NULL);
415
416 int size = indicator_size + indicator_spacing * 2;
417 return wxSize(size, size);
418 }
419
420 void
421 wxRendererGTK::DrawCheckBox(wxWindow* win,
422 wxDC& dc,
423 const wxRect& rect,
424 int flags )
425 {
426 GtkWidget *button = wxGTKPrivate::GetCheckButtonWidget();
427
428 GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
429 wxASSERT_MSG( gdk_window,
430 wxT("cannot use wxRendererNative on wxDC of this type") );
431
432 gint indicator_size, indicator_spacing;
433 gtk_widget_style_get(button,
434 "indicator_size", &indicator_size,
435 "indicator_spacing", &indicator_spacing,
436 NULL);
437
438 GtkStateType state;
439
440 if ( flags & wxCONTROL_PRESSED )
441 state = GTK_STATE_ACTIVE;
442 else if ( flags & wxCONTROL_DISABLED )
443 state = GTK_STATE_INSENSITIVE;
444 else if ( flags & wxCONTROL_CURRENT )
445 state = GTK_STATE_PRELIGHT;
446 else
447 state = GTK_STATE_NORMAL;
448
449 gtk_paint_check
450 (
451 button->style,
452 gdk_window,
453 state,
454 flags & wxCONTROL_CHECKED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
455 NULL,
456 button,
457 "cellcheck",
458 dc.LogicalToDeviceX(rect.x) + indicator_spacing,
459 dc.LogicalToDeviceY(rect.y) + indicator_spacing,
460 indicator_size, indicator_size
461 );
462 }
463
464 void
465 wxRendererGTK::DrawPushButton(wxWindow* win,
466 wxDC& dc,
467 const wxRect& rect,
468 int flags)
469 {
470 GtkWidget *button = wxGTKPrivate::GetButtonWidget();
471
472 GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
473 wxASSERT_MSG( gdk_window,
474 wxT("cannot use wxRendererNative on wxDC of this type") );
475
476 // draw button
477 GtkStateType state;
478
479 if ( flags & wxCONTROL_PRESSED )
480 state = GTK_STATE_ACTIVE;
481 else if ( flags & wxCONTROL_DISABLED )
482 state = GTK_STATE_INSENSITIVE;
483 else if ( flags & wxCONTROL_CURRENT )
484 state = GTK_STATE_PRELIGHT;
485 else
486 state = GTK_STATE_NORMAL;
487
488 gtk_paint_box
489 (
490 button->style,
491 gdk_window,
492 state,
493 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
494 NULL,
495 button,
496 "button",
497 dc.LogicalToDeviceX(rect.x),
498 dc.LogicalToDeviceY(rect.y),
499 rect.width,
500 rect.height
501 );
502 }
503
504 void
505 wxRendererGTK::DrawItemSelectionRect(wxWindow* win,
506 wxDC& dc,
507 const wxRect& rect,
508 int flags )
509 {
510 GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
511 wxASSERT_MSG( gdk_window,
512 wxT("cannot use wxRendererNative on wxDC of this type") );
513
514 int x_diff = 0;
515 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
516 x_diff = rect.width;
517
518 GtkStateType state;
519 if (flags & wxCONTROL_SELECTED)
520 {
521 // the wxCONTROL_FOCUSED state is deduced
522 // directly from the m_wxwindow by GTK+
523 state = GTK_STATE_SELECTED;
524
525 gtk_paint_flat_box( win->m_widget->style,
526 gdk_window,
527 state,
528 GTK_SHADOW_NONE,
529 NULL,
530 win->m_wxwindow,
531 "cell_even",
532 dc.LogicalToDeviceX(rect.x) - x_diff,
533 dc.LogicalToDeviceY(rect.y),
534 rect.width,
535 rect.height );
536 }
537 else // !wxCONTROL_SELECTED
538 {
539 state = GTK_STATE_NORMAL;
540 }
541
542 if ((flags & wxCONTROL_CURRENT) && (flags & wxCONTROL_FOCUSED))
543 {
544 gtk_paint_focus( win->m_widget->style,
545 gdk_window,
546 state,
547 NULL,
548 win->m_wxwindow,
549 // Detail "treeview" causes warning with GTK+ 2.12 Clearlooks theme:
550 // "... no property named `row-ending-details'"
551 // Using "treeview-middle" would fix the warning, but the right
552 // edge of the focus rect is not getting erased properly either.
553 // Better to not specify this detail unless the drawing is fixed.
554 NULL,
555 dc.LogicalToDeviceX(rect.x),
556 dc.LogicalToDeviceY(rect.y),
557 rect.width,
558 rect.height );
559 }
560 }
561
562 void wxRendererGTK::DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
563 {
564 GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
565 wxASSERT_MSG( gdk_window,
566 wxT("cannot use wxRendererNative on wxDC of this type") );
567
568 GtkStateType state;
569 if (flags & wxCONTROL_SELECTED)
570 state = GTK_STATE_SELECTED;
571 else
572 state = GTK_STATE_NORMAL;
573
574 gtk_paint_focus( win->m_widget->style,
575 gdk_window,
576 state,
577 NULL,
578 win->m_wxwindow,
579 NULL,
580 dc.LogicalToDeviceX(rect.x),
581 dc.LogicalToDeviceY(rect.y),
582 rect.width,
583 rect.height );
584 }
585
586 // Uses the theme to draw the border and fill for something like a wxTextCtrl
587 void wxRendererGTK::DrawTextCtrl(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
588 {
589 GtkWidget *entry = wxGTKPrivate::GetTextEntryWidget();
590
591 GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
592
593 GtkStateType state = GTK_STATE_NORMAL;
594 if ( flags & wxCONTROL_DISABLED )
595 state = GTK_STATE_INSENSITIVE;
596
597 if (flags & wxCONTROL_CURRENT )
598 GTK_WIDGET_SET_FLAGS( entry, GTK_HAS_FOCUS );
599 else
600 GTK_WIDGET_UNSET_FLAGS( entry, GTK_HAS_FOCUS );
601
602 gtk_paint_shadow
603 (
604 entry->style,
605 gdk_window,
606 state,
607 GTK_SHADOW_OUT,
608 NULL,
609 entry,
610 "entry",
611 dc.LogicalToDeviceX(rect.x),
612 dc.LogicalToDeviceY(rect.y),
613 rect.width,
614 rect.height
615 );
616 }
617
618 // Draw the equivallent of a wxComboBox
619 void wxRendererGTK::DrawComboBox(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
620 {
621 GtkWidget *combo = wxGTKPrivate::GetComboBoxWidget();
622
623 GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
624
625 GtkStateType state = GTK_STATE_NORMAL;
626 if ( flags & wxCONTROL_DISABLED )
627 state = GTK_STATE_INSENSITIVE;
628
629 if (flags & wxCONTROL_CURRENT )
630 GTK_WIDGET_SET_FLAGS( combo, GTK_HAS_FOCUS );
631 else
632 GTK_WIDGET_UNSET_FLAGS( combo, GTK_HAS_FOCUS );
633
634 gtk_paint_shadow
635 (
636 combo->style,
637 gdk_window,
638 state,
639 GTK_SHADOW_OUT,
640 NULL,
641 combo,
642 "combobox",
643 dc.LogicalToDeviceX(rect.x),
644 dc.LogicalToDeviceY(rect.y),
645 rect.width,
646 rect.height
647 );
648
649 wxRect r = rect;
650 int extent = rect.height / 2;
651 r.x += rect.width - extent - extent/2;
652 r.y += extent/2;
653 r.width = extent;
654 r.height = extent;
655
656 gtk_paint_arrow
657 (
658 combo->style,
659 gdk_window,
660 state,
661 GTK_SHADOW_OUT,
662 NULL,
663 combo,
664 "arrow",
665 GTK_ARROW_DOWN,
666 TRUE,
667 dc.LogicalToDeviceX(r.x),
668 dc.LogicalToDeviceY(r.y),
669 r.width,
670 r.height
671 );
672
673 r = rect;
674 r.x += rect.width - 2*extent;
675 r.width = 2;
676
677 gtk_paint_box
678 (
679 combo->style,
680 gdk_window,
681 state,
682 GTK_SHADOW_ETCHED_OUT,
683 NULL,
684 combo,
685 "vseparator",
686 dc.LogicalToDeviceX(r.x),
687 dc.LogicalToDeviceY(r.y+1),
688 r.width,
689 r.height-2
690 );
691 }
692
693
694 void wxRendererGTK::DrawChoice(wxWindow* win, wxDC& dc,
695 const wxRect& rect, int flags)
696 {
697 DrawComboBox( win, dc, rect, flags );
698 }
699
700
701 // Draw a themed radio button
702 void wxRendererGTK::DrawRadioButton(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
703 {
704 GtkWidget *button = wxGTKPrivate::GetRadioButtonWidget();
705
706 GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
707
708 GtkShadowType shadow_type = GTK_SHADOW_OUT;
709 if ( flags & wxCONTROL_CHECKED )
710 shadow_type = GTK_SHADOW_IN;
711 else if ( flags & wxCONTROL_UNDETERMINED )
712 shadow_type = GTK_SHADOW_ETCHED_IN;
713
714 GtkStateType state = GTK_STATE_NORMAL;
715 if ( flags & wxCONTROL_DISABLED )
716 state = GTK_STATE_INSENSITIVE;
717 if ( flags & wxCONTROL_PRESSED )
718 state = GTK_STATE_ACTIVE;
719 /*
720 Don't know when to set this
721 state_type = GTK_STATE_PRELIGHT;
722 */
723
724 gtk_paint_option
725 (
726 button->style,
727 gdk_window,
728 state,
729 shadow_type,
730 NULL,
731 button,
732 "radiobutton",
733 dc.LogicalToDeviceX(rect.x),
734 dc.LogicalToDeviceY(rect.y),
735 rect.width, rect.height
736 );
737 }