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