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