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: renderer.cpp 54792 2008-07-28 05:49:04Z PC $
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
42class WXDLLEXPORT wxRendererGTK : public wxDelegateRendererNative
43{
44public:
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 wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
96
97// private:
98    // FIXME: shouldn't we destroy these windows somewhere?
99
100    // used by DrawPushButton and DrawDropArrow
101    static GtkWidget *GetButtonWidget();
102
103    // used by DrawTreeItemButton()
104    static GtkWidget *GetTreeWidget();
105
106    // used by DrawCheckBox()
107    static GtkWidget *GetCheckButtonWidget();
108
109    // used by DrawRadioButton()
110    static GtkWidget *GetRadioButtonWidget();
111
112    // Used by DrawHeaderButton
113    static GtkWidget *GetHeaderButtonWidget();
114
115    // Used by DrawTextCtrl
116    static GtkWidget *GetTextEntryWidget();
117
118    // Used by DrawComboBox
119    static GtkWidget *GetComboBoxWidget();
120};
121
122GdkWindow* wxGetGdkWindowForDC(wxWindow* win, wxDC& dc)
123{
124    // The way to get a GdkWindow* from a wxWindow is to use
125    // GTK_PIZZA(win->m_wxwindow)->bin_window, but this approach
126    // won't work when drawing to a wxMemoryDC as it has its own
127    // GdkWindow* for its bitmap. wxWindowDC's GetGDKWindow() was
128    // designed to create a solution for all DCs, but we can't
129    // implement it with wxGCDC since it doesn't retain its wxWindow.
130    // So, to work around this, we use GetGDKWindow whenever possible
131    // and use bin_window for wxGCDC.
132    GdkWindow* gdk_window = NULL;
133#if wxUSE_GRAPHICS_CONTEXT
134    if ( dc.IsKindOf( CLASSINFO(wxGCDC) ) )
135        gdk_window = GTK_PIZZA(win->m_wxwindow)->bin_window;
136    else
137#endif
138       gdk_window = dc.GetGDKWindow();
139    wxASSERT_MSG( gdk_window,
140                  wxT("cannot use wxRendererNative on wxDC of this type") );
141
142    return gdk_window;
143}
144
145// ============================================================================
146// implementation
147// ============================================================================
148
149/* static */
150wxRendererNative& wxRendererNative::GetDefault()
151{
152    static wxRendererGTK s_rendererGTK;
153
154    return s_rendererGTK;
155}
156
157// ----------------------------------------------------------------------------
158// helper functions
159// ----------------------------------------------------------------------------
160
161GtkWidget *
162wxRendererGTK::GetButtonWidget()
163{
164    static GtkWidget *s_button = NULL;
165    static GtkWidget *s_window = NULL;
166
167    if ( !s_button )
168    {
169        s_window = gtk_window_new( GTK_WINDOW_POPUP );
170        gtk_widget_realize( s_window );
171        s_button = gtk_button_new();
172        gtk_container_add( GTK_CONTAINER(s_window), s_button );
173        gtk_widget_realize( s_button );
174    }
175
176    return s_button;
177}
178
179GtkWidget *
180wxRendererGTK::GetTextEntryWidget()
181{
182    static GtkWidget *s_button = NULL;
183    static GtkWidget *s_window = NULL;
184
185    if ( !s_button )
186    {
187        s_window = gtk_window_new( GTK_WINDOW_POPUP );
188        gtk_widget_realize( s_window );
189        s_button = gtk_entry_new();
190        gtk_container_add( GTK_CONTAINER(s_window), s_button );
191        gtk_widget_realize( s_button );
192    }
193
194    return s_button;
195}
196
197GtkWidget *
198wxRendererGTK::GetComboBoxWidget()
199{
200    static GtkWidget *s_button = NULL;
201    static GtkWidget *s_window = NULL;
202
203#ifdef __WXGTK24__
204    if (!gtk_check_version(2,4,0))
205    {
206        if ( !s_button )
207        {
208            s_window = gtk_window_new( GTK_WINDOW_POPUP );
209            gtk_widget_realize( s_window );
210            s_button = gtk_combo_box_new();
211            gtk_container_add( GTK_CONTAINER(s_window), s_button );
212            gtk_widget_realize( s_button );
213        }
214    }
215#endif
216    return s_button;
217}
218
219GtkWidget *
220wxRendererGTK::GetCheckButtonWidget()
221{
222    static GtkWidget *s_button = NULL;
223    static GtkWidget *s_window = NULL;
224
225    if ( !s_button )
226    {
227        s_window = gtk_window_new( GTK_WINDOW_POPUP );
228        gtk_widget_realize( s_window );
229        s_button = gtk_check_button_new();
230        gtk_container_add( GTK_CONTAINER(s_window), s_button );
231        gtk_widget_realize( s_button );
232    }
233
234    return s_button;
235}
236
237GtkWidget *
238wxRendererGTK::GetRadioButtonWidget()
239{
240    static GtkWidget *s_button = NULL;
241    static GtkWidget *s_window = NULL;
242
243    if ( !s_button )
244    {
245        s_window = gtk_window_new( GTK_WINDOW_POPUP );
246        gtk_widget_realize( s_window );
247        s_button = gtk_radio_button_new(NULL);
248        gtk_container_add( GTK_CONTAINER(s_window), s_button );
249        gtk_widget_realize( s_button );
250    }
251
252    return s_button;
253}
254
255GtkWidget *
256wxRendererGTK::GetTreeWidget()
257{
258    static GtkWidget *s_tree = NULL;
259    static GtkWidget *s_window = NULL;
260
261    if ( !s_tree )
262    {
263        s_tree = gtk_tree_view_new();
264        s_window = gtk_window_new( GTK_WINDOW_POPUP );
265        gtk_widget_realize( s_window );
266        gtk_container_add( GTK_CONTAINER(s_window), s_tree );
267        gtk_widget_realize( s_tree );
268    }
269
270    return s_tree;
271}
272
273
274// This one just gets the button used by the column header.  Although it's
275// still a gtk_button the themes will typically differentiate and draw them
276// differently if the button is in a treeview.
277GtkWidget *
278wxRendererGTK::GetHeaderButtonWidget()
279{
280    static GtkWidget *s_button = NULL;
281
282    if ( !s_button )
283    {
284        // Get the dummy tree widget, give it a column, and then use the
285        // widget in the column header for the rendering code.
286        GtkWidget* treewidget = GetTreeWidget();
287        GtkTreeViewColumn*  column = gtk_tree_view_column_new();
288        gtk_tree_view_append_column(GTK_TREE_VIEW(treewidget), column);
289        s_button = column->button;
290    }
291
292    return s_button;
293}
294
295// ----------------------------------------------------------------------------
296// list/tree controls drawing
297// ----------------------------------------------------------------------------
298
299int
300wxRendererGTK::DrawHeaderButton(wxWindow *win,
301                                wxDC& dc,
302                                const wxRect& rect,
303                                int flags,
304                                wxHeaderSortIconType sortArrow,
305                                wxHeaderButtonParams* params)
306{
307
308    GtkWidget *button = GetHeaderButtonWidget();
309
310    GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
311
312    int x_diff = 0;
313    if (win->GetLayoutDirection() == wxLayout_RightToLeft)
314        x_diff = rect.width;
315
316    gtk_paint_box
317    (
318        button->style,
319        gdk_window,
320        flags & wxCONTROL_DISABLED ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL,
321        GTK_SHADOW_OUT,
322        NULL,
323        button,
324        "button",
325        dc.LogicalToDeviceX(rect.x) - x_diff, rect.y, rect.width, rect.height
326    );
327
328    return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params);
329}
330
331// draw a ">" or "v" button
332void
333wxRendererGTK::DrawTreeItemButton(wxWindow* win,
334                                  wxDC& dc, const wxRect& rect, int flags)
335{
336    GtkWidget *tree = GetTreeWidget();
337
338    GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
339
340    GtkStateType state;
341    if ( flags & wxCONTROL_CURRENT )
342        state = GTK_STATE_PRELIGHT;
343    else
344        state = GTK_STATE_NORMAL;
345
346    int x_diff = 0;
347    if (win->GetLayoutDirection() == wxLayout_RightToLeft)
348        x_diff = rect.width;
349
350    // VZ: I don't know how to get the size of the expander so as to centre it
351    //     in the given rectangle, +2/3 below is just what looks good here...
352    gtk_paint_expander
353    (
354        tree->style,
355        gdk_window,
356        state,
357        NULL,
358        tree,
359        "treeview",
360        dc.LogicalToDeviceX(rect.x) + 6 - x_diff,
361        dc.LogicalToDeviceY(rect.y) + 3,
362        flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
363                                   : GTK_EXPANDER_COLLAPSED
364    );
365}
366
367
368// ----------------------------------------------------------------------------
369// splitter sash drawing
370// ----------------------------------------------------------------------------
371
372static int GetGtkSplitterFullSize()
373{
374    static GtkWidget *s_paned = NULL;
375    if (s_paned == NULL)
376        s_paned = gtk_vpaned_new();
377
378    gint handle_size;
379    gtk_widget_style_get (s_paned, "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(),
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 = wxGetGdkWindowForDC(win, dc);
420
421    wxCoord full_size = GetGtkSplitterFullSize();
422
423    // are we drawing vertical or horizontal splitter?
424    const bool isVert = orient == wxVERTICAL;
425
426    GdkRectangle rect;
427
428    if ( isVert )
429    {
430        int h = win->GetClientSize().GetHeight();
431
432        rect.x = position;
433        rect.y = 0;
434        rect.width = full_size;
435        rect.height = h;
436    }
437    else // horz
438    {
439        int w = win->GetClientSize().GetWidth();
440
441        rect.x = 0;
442        rect.y = position;
443        rect.height = full_size;
444        rect.width = w;
445    }
446
447    int x_diff = 0;
448    if (win->GetLayoutDirection() == wxLayout_RightToLeft)
449        x_diff = rect.width;
450
451    gtk_paint_handle
452    (
453        win->m_wxwindow->style,
454        gdk_window,
455        flags & wxCONTROL_CURRENT ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
456        GTK_SHADOW_NONE,
457        NULL /* no clipping */,
458        win->m_wxwindow,
459        "paned",
460        dc.LogicalToDeviceX(rect.x) - x_diff,
461        dc.LogicalToDeviceY(rect.y),
462        rect.width,
463        rect.height,
464        isVert ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL
465    );
466}
467
468void
469wxRendererGTK::DrawDropArrow(wxWindow *win,
470                             wxDC& dc,
471                             const wxRect& rect,
472                             int flags)
473{
474    GtkWidget *button = GetButtonWidget();
475
476    GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
477
478    // draw arrow so that there is even space horizontally
479    // on both sides
480    int arrowX = rect.width/4 + 1;
481    int arrowWidth = rect.width - (arrowX*2);
482
483    // scale arrow's height accoording to the width
484    int arrowHeight = rect.width/3;
485    int arrowY = (rect.height-arrowHeight)/2 +
486                 ((rect.height-arrowHeight) & 1);
487
488    GtkStateType state;
489
490    if ( flags & wxCONTROL_PRESSED )
491        state = GTK_STATE_ACTIVE;
492    else if ( flags & wxCONTROL_DISABLED )
493        state = GTK_STATE_INSENSITIVE;
494    else if ( flags & wxCONTROL_CURRENT )
495        state = GTK_STATE_PRELIGHT;
496    else
497        state = GTK_STATE_NORMAL;
498
499    // draw arrow on button
500    gtk_paint_arrow
501    (
502        button->style,
503        gdk_window,
504        state,
505        flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
506        NULL,
507        button,
508        "arrow",
509        GTK_ARROW_DOWN,
510        FALSE,
511        rect.x + arrowX,
512        rect.y + arrowY,
513        arrowWidth,
514        arrowHeight
515    );
516}
517
518void
519wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
520                                      wxDC& dc,
521                                      const wxRect& rect,
522                                      int flags)
523{
524    DrawPushButton(win,dc,rect,flags);
525    DrawDropArrow(win,dc,rect);
526}
527
528void
529wxRendererGTK::DrawCheckBox(wxWindow *win,
530                            wxDC& dc,
531                            const wxRect& rect,
532                            int flags )
533{
534    GtkWidget *button = GetCheckButtonWidget();
535
536    GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
537
538    GtkStateType state;
539
540    if ( flags & wxCONTROL_PRESSED )
541        state = GTK_STATE_ACTIVE;
542    else if ( flags & wxCONTROL_DISABLED )
543        state = GTK_STATE_INSENSITIVE;
544    else if ( flags & wxCONTROL_CURRENT )
545        state = GTK_STATE_PRELIGHT;
546    else
547        state = GTK_STATE_NORMAL;
548
549    gtk_paint_check
550    (
551        button->style,
552        gdk_window,
553        state,
554        flags & wxCONTROL_CHECKED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
555        NULL,
556        button,
557        "cellcheck",
558        dc.LogicalToDeviceX(rect.x)+2,
559        dc.LogicalToDeviceY(rect.y)+3,
560        13, 13
561    );
562}
563
564void
565wxRendererGTK::DrawPushButton(wxWindow* win,
566                              wxDC& dc,
567                              const wxRect& rect,
568                              int flags)
569{
570    GtkWidget *button = GetButtonWidget();
571
572    GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
573
574    // draw button
575    GtkStateType state;
576
577    if ( flags & wxCONTROL_PRESSED )
578        state = GTK_STATE_ACTIVE;
579    else if ( flags & wxCONTROL_DISABLED )
580        state = GTK_STATE_INSENSITIVE;
581    else if ( flags & wxCONTROL_CURRENT )
582        state = GTK_STATE_PRELIGHT;
583    else
584        state = GTK_STATE_NORMAL;
585
586    gtk_paint_box
587    (
588        button->style,
589        gdk_window,
590        state,
591        flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
592        NULL,
593        button,
594        "button",
595        dc.LogicalToDeviceX(rect.x),
596        dc.LogicalToDeviceY(rect.y),
597        rect.width,
598        rect.height
599    );
600}
601
602void
603wxRendererGTK::DrawItemSelectionRect(wxWindow *win,
604                                     wxDC& dc,
605                                     const wxRect& rect,
606                                     int flags )
607{
608    GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
609
610    int x_diff = 0;
611    if (win->GetLayoutDirection() == wxLayout_RightToLeft)
612        x_diff = rect.width;
613
614    GtkStateType state;
615    if (flags & wxCONTROL_SELECTED)
616    {
617        // the wxCONTROL_FOCUSED state is deduced
618        // directly from the m_wxwindow by GTK+
619        state = GTK_STATE_SELECTED;
620
621        gtk_paint_flat_box( win->m_widget->style,
622                        gdk_window,
623                        state,
624                        GTK_SHADOW_NONE,
625                        NULL,
626                        win->m_wxwindow,
627                        "cell_even",
628                        dc.LogicalToDeviceX(rect.x) - x_diff,
629                        dc.LogicalToDeviceY(rect.y),
630                        rect.width,
631                        rect.height );
632    }
633
634    if (flags & wxCONTROL_CURRENT)
635    {
636        gtk_paint_focus( win->m_widget->style,
637                         gdk_window,
638                         GTK_STATE_SELECTED,
639                         NULL,
640                         win->m_wxwindow,
641                         // Detail "treeview" causes warning with GTK+ 2.12 Clearlooks theme:
642                         // "... no property named `row-ending-details'"
643                         // Using "treeview-middle" would fix the warning, but the right
644                         // edge of the focus rect is not getting erased properly either.
645                         // Better to not specify this detail unless the drawing is fixed.
646                         NULL,
647                         dc.LogicalToDeviceX(rect.x),
648                         dc.LogicalToDeviceY(rect.y),
649                         rect.width,
650                         rect.height );
651    }
652}
653
654
655// ----------------------------------------------------------------------------
656// Other renderer functions to be merged in to wxRenderer class in 2.9, but
657// they are standalone functions here to protect the ABI.
658// ----------------------------------------------------------------------------
659
660// Uses the theme to draw the border and fill for something like a wxTextCtrl
661void wxRenderer_DrawTextCtrl(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
662{
663    GtkWidget *entry = wxRendererGTK::GetTextEntryWidget();
664
665    GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
666
667    GtkStateType state = GTK_STATE_NORMAL;
668    if ( flags & wxCONTROL_DISABLED )
669       state = GTK_STATE_INSENSITIVE;
670
671   if (flags & wxCONTROL_CURRENT )
672      GTK_WIDGET_SET_FLAGS( entry, GTK_HAS_FOCUS );
673   else
674      GTK_WIDGET_UNSET_FLAGS( entry, GTK_HAS_FOCUS );
675
676    gtk_paint_shadow
677    (
678      entry->style,
679      gdk_window,
680      state,
681      GTK_SHADOW_OUT,
682      NULL,
683      entry,
684      "entry",
685      dc.LogicalToDeviceX(rect.x),
686      dc.LogicalToDeviceY(rect.y),
687      rect.width,
688      rect.height
689   );
690}
691
692// Draw the equivallent of a wxComboBox
693void wxRenderer_DrawComboBox(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
694{
695    if (gtk_check_version(2,4,0))
696       return;
697
698    GtkWidget *combo = wxRendererGTK::GetComboBoxWidget();
699
700    GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
701
702    GtkStateType state = GTK_STATE_NORMAL;
703    if ( flags & wxCONTROL_DISABLED )
704       state = GTK_STATE_INSENSITIVE;
705
706   if (flags & wxCONTROL_CURRENT )
707      GTK_WIDGET_SET_FLAGS( combo, GTK_HAS_FOCUS );
708   else
709      GTK_WIDGET_UNSET_FLAGS( combo, GTK_HAS_FOCUS );
710
711   gtk_paint_shadow
712   (
713     combo->style,
714     gdk_window,
715     state,
716     GTK_SHADOW_OUT,
717     NULL,
718     combo,
719     "combobox",
720      dc.LogicalToDeviceX(rect.x),
721      dc.LogicalToDeviceY(rect.y),
722      rect.width,
723      rect.height
724   );
725
726   wxRect r = rect;
727   int extent = rect.height / 2;
728   r.x += rect.width - extent - extent/2;
729   r.y += extent/2;
730   r.width = extent;
731   r.height = extent;
732
733   gtk_paint_arrow
734   (
735     combo->style,
736     gdk_window,
737     state,
738     GTK_SHADOW_OUT,
739     NULL,
740     combo,
741     "arrow",
742     GTK_ARROW_DOWN,
743     TRUE,
744      dc.LogicalToDeviceX(r.x),
745      dc.LogicalToDeviceY(r.y),
746      r.width,
747      r.height
748   );
749
750   r = rect;
751   r.x += rect.width - 2*extent;
752   r.width = 2;
753
754   gtk_paint_box
755   (
756      combo->style,
757      gdk_window,
758      state,
759      GTK_SHADOW_ETCHED_OUT,
760      NULL,
761      combo,
762      "vseparator",
763      dc.LogicalToDeviceX(r.x),
764      dc.LogicalToDeviceY(r.y+1),
765      r.width,
766      r.height-2
767   );
768}
769
770
771void wxRenderer_DrawChoice(wxWindow* win, wxDC& dc,
772                           const wxRect& rect, int flags)
773{
774   wxRenderer_DrawComboBox( win, dc, rect, flags );
775}
776
777
778// Draw a themed radio button
779void wxRenderer_DrawRadioButton(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
780{
781    GtkWidget *button = wxRendererGTK::GetRadioButtonWidget();
782
783    GdkWindow* gdk_window = wxGetGdkWindowForDC(win, dc);
784
785    GtkShadowType shadow_type = GTK_SHADOW_OUT;
786    if ( flags & wxCONTROL_CHECKED )
787        shadow_type = GTK_SHADOW_IN;
788    else if ( flags & wxCONTROL_UNDETERMINED )
789        shadow_type = GTK_SHADOW_ETCHED_IN;
790
791    GtkStateType state = GTK_STATE_NORMAL;
792    if ( flags & wxCONTROL_DISABLED )
793       state = GTK_STATE_INSENSITIVE;
794    if ( flags & wxCONTROL_PRESSED )
795    state = GTK_STATE_ACTIVE;
796/*
797    Don't know when to set this
798       state_type = GTK_STATE_PRELIGHT;
799*/
800
801    gtk_paint_option
802    (
803        button->style,
804        gdk_window,
805        state,
806        shadow_type,
807        NULL,
808        button,
809        "radiobutton",
810        dc.LogicalToDeviceX(rect.x),
811        dc.LogicalToDeviceY(rect.y),
812        rect.width, rect.height
813    );
814}
815