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