Converted wxGTK's basic DC classes to new DC code
[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 gtk_paint_box
239 (
240 button->style,
241 gdk_window,
242 flags & wxCONTROL_DISABLED ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL,
243 GTK_SHADOW_OUT,
244 NULL,
245 button,
246 "button",
247 dc.LogicalToDeviceX(rect.x) - x_diff, rect.y, rect.width, rect.height
248 );
249
250 return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params);
251 }
252
253 // draw a ">" or "v" button
254 void
255 wxRendererGTK::DrawTreeItemButton(wxWindow* win,
256 wxDC& dc, const wxRect& rect, int flags)
257 {
258 GtkWidget *tree = GetTreeWidget();
259
260 GdkWindow* gdk_window = NULL;
261 #if wxUSE_NEW_DC
262 wxImplDC *impl = dc.GetImpl();
263 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
264 if (gtk_impl)
265 gdk_window = gtk_impl->GetGDKWindow();
266 #else
267 gdk_window = dc.GetGDKWindow();
268 #endif
269 wxASSERT_MSG( gdk_window,
270 wxT("cannot use wxRendererNative on wxDC of this type") );
271
272 GtkStateType state;
273 if ( flags & wxCONTROL_CURRENT )
274 state = GTK_STATE_PRELIGHT;
275 else
276 state = GTK_STATE_NORMAL;
277
278 int x_diff = 0;
279 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
280 x_diff = rect.width;
281
282 // VZ: I don't know how to get the size of the expander so as to centre it
283 // in the given rectangle, +2/3 below is just what looks good here...
284 gtk_paint_expander
285 (
286 tree->style,
287 gdk_window,
288 state,
289 NULL,
290 tree,
291 "treeview",
292 dc.LogicalToDeviceX(rect.x) + 6 - x_diff,
293 dc.LogicalToDeviceY(rect.y) + 3,
294 flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
295 : GTK_EXPANDER_COLLAPSED
296 );
297 }
298
299
300 // ----------------------------------------------------------------------------
301 // splitter sash drawing
302 // ----------------------------------------------------------------------------
303
304 static int GetGtkSplitterFullSize()
305 {
306 static GtkWidget *s_paned = NULL;
307 if (s_paned == NULL)
308 s_paned = gtk_vpaned_new();
309
310 gint handle_size;
311 gtk_widget_style_get (s_paned, "handle_size", &handle_size, NULL);
312
313 return handle_size;
314 }
315
316 wxSplitterRenderParams
317 wxRendererGTK::GetSplitterParams(const wxWindow *WXUNUSED(win))
318 {
319 // we don't draw any border, hence 0 for the second field
320 return wxSplitterRenderParams
321 (
322 GetGtkSplitterFullSize(),
323 0,
324 true // hot sensitive
325 );
326 }
327
328 void
329 wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
330 wxDC& WXUNUSED(dc),
331 const wxRect& WXUNUSED(rect),
332 int WXUNUSED(flags))
333 {
334 // nothing to do
335 }
336
337 void
338 wxRendererGTK::DrawSplitterSash(wxWindow *win,
339 wxDC& dc,
340 const wxSize& size,
341 wxCoord position,
342 wxOrientation orient,
343 int flags)
344 {
345 if ( !win->m_wxwindow->window )
346 {
347 // window not realized yet
348 return;
349 }
350
351 GdkWindow* gdk_window = NULL;
352 #if wxUSE_NEW_DC
353 wxImplDC *impl = dc.GetImpl();
354 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
355 if (gtk_impl)
356 gdk_window = gtk_impl->GetGDKWindow();
357 #else
358 gdk_window = dc.GetGDKWindow();
359 #endif
360 wxASSERT_MSG( gdk_window,
361 wxT("cannot use wxRendererNative on wxDC of this type") );
362
363 wxCoord full_size = GetGtkSplitterFullSize();
364
365 // are we drawing vertical or horizontal splitter?
366 const bool isVert = orient == wxVERTICAL;
367
368 GdkRectangle rect;
369
370 if ( isVert )
371 {
372 int h = win->GetClientSize().GetHeight();
373
374 rect.x = position;
375 rect.y = 0;
376 rect.width = full_size;
377 rect.height = h;
378 }
379 else // horz
380 {
381 int w = win->GetClientSize().GetWidth();
382
383 rect.x = 0;
384 rect.y = position;
385 rect.height = full_size;
386 rect.width = w;
387 }
388
389 int x_diff = 0;
390 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
391 x_diff = rect.width;
392
393 gtk_paint_handle
394 (
395 win->m_wxwindow->style,
396 gdk_window,
397 flags & wxCONTROL_CURRENT ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
398 GTK_SHADOW_NONE,
399 NULL /* no clipping */,
400 win->m_wxwindow,
401 "paned",
402 dc.LogicalToDeviceX(rect.x) - x_diff,
403 dc.LogicalToDeviceY(rect.y),
404 rect.width,
405 rect.height,
406 isVert ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL
407 );
408 }
409
410 void
411 wxRendererGTK::DrawDropArrow(wxWindow *WXUNUSED(win),
412 wxDC& dc,
413 const wxRect& rect,
414 int flags)
415 {
416 GtkWidget *button = GetButtonWidget();
417
418 // If we give GTK_PIZZA(win->m_wxwindow)->bin_window as
419 // a window for gtk_paint_xxx function, then it won't
420 // work for wxMemoryDC. So that is why we assume wxDC
421 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
422 // are derived from it) and use its m_window.
423 GdkWindow* gdk_window = NULL;
424 #if wxUSE_NEW_DC
425 wxImplDC *impl = dc.GetImpl();
426 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
427 if (gtk_impl)
428 gdk_window = gtk_impl->GetGDKWindow();
429 #else
430 gdk_window = dc.GetGDKWindow();
431 #endif
432 wxASSERT_MSG( gdk_window,
433 wxT("cannot use wxRendererNative on wxDC of this type") );
434
435 // draw arrow so that there is even space horizontally
436 // on both sides
437 int arrowX = rect.width/4 + 1;
438 int arrowWidth = rect.width - (arrowX*2);
439
440 // scale arrow's height accoording to the width
441 int arrowHeight = rect.width/3;
442 int arrowY = (rect.height-arrowHeight)/2 +
443 ((rect.height-arrowHeight) & 1);
444
445 GtkStateType state;
446
447 if ( flags & wxCONTROL_PRESSED )
448 state = GTK_STATE_ACTIVE;
449 else if ( flags & wxCONTROL_DISABLED )
450 state = GTK_STATE_INSENSITIVE;
451 else if ( flags & wxCONTROL_CURRENT )
452 state = GTK_STATE_PRELIGHT;
453 else
454 state = GTK_STATE_NORMAL;
455
456 // draw arrow on button
457 gtk_paint_arrow
458 (
459 button->style,
460 gdk_window,
461 state,
462 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
463 NULL,
464 button,
465 "arrow",
466 GTK_ARROW_DOWN,
467 FALSE,
468 rect.x + arrowX,
469 rect.y + arrowY,
470 arrowWidth,
471 arrowHeight
472 );
473 }
474
475 void
476 wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
477 wxDC& dc,
478 const wxRect& rect,
479 int flags)
480 {
481 DrawPushButton(win,dc,rect,flags);
482 DrawDropArrow(win,dc,rect);
483 }
484
485 void
486 wxRendererGTK::DrawCheckBox(wxWindow *WXUNUSED(win),
487 wxDC& dc,
488 const wxRect& rect,
489 int flags )
490 {
491 GtkWidget *button = GetCheckButtonWidget();
492
493 GdkWindow* gdk_window = NULL;
494 #if wxUSE_NEW_DC
495 wxImplDC *impl = dc.GetImpl();
496 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
497 if (gtk_impl)
498 gdk_window = gtk_impl->GetGDKWindow();
499 #else
500 gdk_window = dc.GetGDKWindow();
501 #endif
502 wxASSERT_MSG( gdk_window,
503 wxT("cannot use wxRendererNative on wxDC of this type") );
504
505 GtkStateType state;
506
507 if ( flags & wxCONTROL_PRESSED )
508 state = GTK_STATE_ACTIVE;
509 else if ( flags & wxCONTROL_DISABLED )
510 state = GTK_STATE_INSENSITIVE;
511 else if ( flags & wxCONTROL_CURRENT )
512 state = GTK_STATE_PRELIGHT;
513 else
514 state = GTK_STATE_NORMAL;
515
516 gtk_paint_check
517 (
518 button->style,
519 gdk_window,
520 state,
521 flags & wxCONTROL_CHECKED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
522 NULL,
523 button,
524 "cellcheck",
525 dc.LogicalToDeviceX(rect.x)+2,
526 dc.LogicalToDeviceY(rect.y)+3,
527 13, 13
528 );
529 }
530
531 void
532 wxRendererGTK::DrawPushButton(wxWindow *WXUNUSED(win),
533 wxDC& dc,
534 const wxRect& rect,
535 int flags)
536 {
537 GtkWidget *button = GetButtonWidget();
538
539 GdkWindow* gdk_window = NULL;
540 #if wxUSE_NEW_DC
541 wxImplDC *impl = dc.GetImpl();
542 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
543 if (gtk_impl)
544 gdk_window = gtk_impl->GetGDKWindow();
545 #else
546 gdk_window = dc.GetGDKWindow();
547 #endif
548 wxASSERT_MSG( gdk_window,
549 wxT("cannot use wxRendererNative on wxDC of this type") );
550
551 // draw button
552 GtkStateType state;
553
554 if ( flags & wxCONTROL_PRESSED )
555 state = GTK_STATE_ACTIVE;
556 else if ( flags & wxCONTROL_DISABLED )
557 state = GTK_STATE_INSENSITIVE;
558 else if ( flags & wxCONTROL_CURRENT )
559 state = GTK_STATE_PRELIGHT;
560 else
561 state = GTK_STATE_NORMAL;
562
563 gtk_paint_box
564 (
565 button->style,
566 gdk_window,
567 state,
568 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
569 NULL,
570 button,
571 "button",
572 rect.x, rect.y, rect.width, rect.height
573 );
574 }
575
576 void
577 wxRendererGTK::DrawItemSelectionRect(wxWindow *win,
578 wxDC& dc,
579 const wxRect& rect,
580 int flags )
581 {
582 GdkWindow* gdk_window = NULL;
583 #if wxUSE_NEW_DC
584 wxImplDC *impl = dc.GetImpl();
585 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
586 if (gtk_impl)
587 gdk_window = gtk_impl->GetGDKWindow();
588 #else
589 gdk_window = dc.GetGDKWindow();
590 #endif
591 wxASSERT_MSG( gdk_window,
592 wxT("cannot use wxRendererNative on wxDC of this type") );
593
594 int x_diff = 0;
595 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
596 x_diff = rect.width;
597
598 GtkStateType state;
599 if (flags & wxCONTROL_SELECTED)
600 {
601 // the wxCONTROL_FOCUSED state is deduced
602 // directly from the m_wxwindow by GTK+
603 state = GTK_STATE_SELECTED;
604
605 gtk_paint_flat_box( win->m_widget->style,
606 gdk_window,
607 state,
608 GTK_SHADOW_NONE,
609 NULL,
610 win->m_wxwindow,
611 "cell_even",
612 dc.LogicalToDeviceX(rect.x) - x_diff,
613 dc.LogicalToDeviceY(rect.y),
614 rect.width,
615 rect.height );
616 }
617 else // !wxCONTROL_SELECTED
618 {
619 state = GTK_STATE_NORMAL;
620 }
621
622 if (flags & wxCONTROL_CURRENT)
623 {
624 gtk_paint_focus( win->m_widget->style,
625 gdk_window,
626 state,
627 NULL,
628 win->m_wxwindow,
629 "treeview",
630 dc.LogicalToDeviceX(rect.x),
631 dc.LogicalToDeviceY(rect.y),
632 rect.width,
633 rect.height );
634 }
635 }
636
637 void wxRendererGTK::DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
638 {
639 GdkWindow* gdk_window = NULL;
640 #if wxUSE_NEW_DC
641 wxImplDC *impl = dc.GetImpl();
642 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
643 if (gtk_impl)
644 gdk_window = gtk_impl->GetGDKWindow();
645 #else
646 gdk_window = dc.GetGDKWindow();
647 #endif
648 wxASSERT_MSG( gdk_window,
649 wxT("cannot use wxRendererNative on wxDC of this type") );
650
651 GtkStateType state;
652 if (flags & wxCONTROL_SELECTED)
653 state = GTK_STATE_SELECTED;
654 else
655 state = GTK_STATE_NORMAL;
656
657 gtk_paint_focus( win->m_widget->style,
658 gdk_window,
659 state,
660 NULL,
661 win->m_wxwindow,
662 NULL,
663 dc.LogicalToDeviceX(rect.x),
664 dc.LogicalToDeviceY(rect.y),
665 rect.width,
666 rect.height );
667 }