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