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