]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk/renderer.cpp
Always invoke default action even if dclick is caught from wxListBox (as per wxMSW...
[wxWidgets.git] / src / gtk / renderer.cpp
... / ...
CommitLineData
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
44class WXDLLEXPORT wxRendererGTK : public wxDelegateRendererNative
45{
46public:
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
104private:
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
125class wxRendererGTK::Module: public wxModule
126{
127public:
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};
143IMPLEMENT_DYNAMIC_CLASS(wxRendererGTK::Module, wxModule)
144
145// ============================================================================
146// implementation
147// ============================================================================
148
149GtkWidget* wxRendererGTK::ms_container;
150
151/* static */
152wxRendererNative& wxRendererNative::GetDefault()
153{
154 static wxRendererGTK s_rendererGTK;
155
156 return s_rendererGTK;
157}
158
159// ----------------------------------------------------------------------------
160// helper functions
161// ----------------------------------------------------------------------------
162
163GtkContainer* 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
174GtkWidget *
175wxRendererGTK::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
189GtkWidget *
190wxRendererGTK::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
204GtkWidget *
205wxRendererGTK::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
220GtkWidget *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.
240GtkWidget *
241wxRendererGTK::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
258GtkWidget* 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
274int
275wxRendererGTK::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
326void
327wxRendererGTK::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
376static 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
384wxSplitterRenderParams
385wxRendererGTK::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
396void
397wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
398 wxDC& WXUNUSED(dc),
399 const wxRect& WXUNUSED(rect),
400 int WXUNUSED(flags))
401{
402 // nothing to do
403}
404
405void
406wxRendererGTK::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
474void
475wxRendererGTK::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
539void
540wxRendererGTK::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
549void
550wxRendererGTK::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
595void
596wxRendererGTK::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
640void
641wxRendererGTK::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
701void 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}