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