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