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