Add wxRenderer::DrawCheckButton for use inside
[wxWidgets.git] / src / gtk / renderer.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #include <gtk/gtk.h>
29 #include "wx/gtk/win_gtk.h"
30
31 #include "wx/window.h"
32 #include "wx/dc.h"
33 #include "wx/dcclient.h"
34 #include "wx/settings.h"
35
36 // RR: After a correction to the orientation of the sash
37 // this doesn't seem to be required anymore and it
38 // seems to confuse some themes so USE_ERASE_RECT=0
39 #define USE_ERASE_RECT 0
40
41 // ----------------------------------------------------------------------------
42 // wxRendererGTK: our wxRendererNative implementation
43 // ----------------------------------------------------------------------------
44
45 class WXDLLEXPORT wxRendererGTK : public wxDelegateRendererNative
46 {
47 public:
48 // draw the header control button (used by wxListCtrl)
49 virtual void DrawHeaderButton(wxWindow *win,
50 wxDC& dc,
51 const wxRect& rect,
52 int flags = 0);
53
54 // draw the expanded/collapsed icon for a tree control item
55 virtual void DrawTreeItemButton(wxWindow *win,
56 wxDC& dc,
57 const wxRect& rect,
58 int flags = 0);
59
60 virtual void DrawSplitterBorder(wxWindow *win,
61 wxDC& dc,
62 const wxRect& rect,
63 int flags = 0);
64 virtual void DrawSplitterSash(wxWindow *win,
65 wxDC& dc,
66 const wxSize& size,
67 wxCoord position,
68 wxOrientation orient,
69 int flags = 0);
70
71 virtual void DrawComboBoxDropButton(wxWindow *win,
72 wxDC& dc,
73 const wxRect& rect,
74 int flags = 0);
75
76 virtual void DrawDropArrow(wxWindow *win,
77 wxDC& dc,
78 const wxRect& rect,
79 int flags = 0);
80
81 virtual void DrawCheckButton(wxWindow *win,
82 wxDC& dc,
83 const wxRect& rect,
84 int flags = 0);
85
86 virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
87
88 private:
89 // FIXME: shouldn't we destroy these windows somewhere?
90
91 // used by DrawHeaderButton and DrawComboBoxDropButton
92 static GtkWidget *GetButtonWidget();
93
94 // used by DrawTreeItemButton()
95 static GtkWidget *GetTreeWidget();
96
97 // used by DrawCheckButton()
98 static GtkWidget *GetCheckButtonWidget();
99 };
100
101 // ============================================================================
102 // implementation
103 // ============================================================================
104
105 /* static */
106 wxRendererNative& wxRendererNative::GetDefault()
107 {
108 static wxRendererGTK s_rendererGTK;
109
110 return s_rendererGTK;
111 }
112
113 // ----------------------------------------------------------------------------
114 // helper functions
115 // ----------------------------------------------------------------------------
116
117 GtkWidget *
118 wxRendererGTK::GetButtonWidget()
119 {
120 static GtkWidget *s_button = NULL;
121 static GtkWidget *s_window = NULL;
122
123 if ( !s_button )
124 {
125 s_window = gtk_window_new( GTK_WINDOW_POPUP );
126 gtk_widget_realize( s_window );
127 s_button = gtk_button_new();
128 gtk_container_add( GTK_CONTAINER(s_window), s_button );
129 gtk_widget_realize( s_button );
130 }
131
132 return s_button;
133 }
134
135 GtkWidget *
136 wxRendererGTK::GetCheckButtonWidget()
137 {
138 static GtkWidget *s_button = NULL;
139 static GtkWidget *s_window = NULL;
140
141 if ( !s_button )
142 {
143 s_window = gtk_window_new( GTK_WINDOW_POPUP );
144 gtk_widget_realize( s_window );
145 s_button = gtk_check_button_new();
146 gtk_container_add( GTK_CONTAINER(s_window), s_button );
147 gtk_widget_realize( s_button );
148 }
149
150 return s_button;
151 }
152
153 GtkWidget *
154 wxRendererGTK::GetTreeWidget()
155 {
156 static GtkWidget *s_tree = NULL;
157 static GtkWidget *s_window = NULL;
158
159 if ( !s_tree )
160 {
161 s_tree = gtk_tree_view_new();
162 s_window = gtk_window_new( GTK_WINDOW_POPUP );
163 gtk_widget_realize( s_window );
164 gtk_container_add( GTK_CONTAINER(s_window), s_tree );
165 gtk_widget_realize( s_tree );
166 }
167
168 return s_tree;
169 }
170
171 // ----------------------------------------------------------------------------
172 // list/tree controls drawing
173 // ----------------------------------------------------------------------------
174
175 void
176 wxRendererGTK::DrawHeaderButton(wxWindow *win,
177 wxDC& dc,
178 const wxRect& rect,
179 int flags)
180 {
181
182 GtkWidget *button = GetButtonWidget();
183
184 gtk_paint_box
185 (
186 button->style,
187 // FIXME: I suppose GTK_PIZZA(win->m_wxwindow)->bin_window doesn't work with wxMemoryDC.
188 // Maybe use code similar as in DrawComboBoxDropButton below?
189 GTK_PIZZA(win->m_wxwindow)->bin_window,
190 flags & wxCONTROL_DISABLED ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL,
191 GTK_SHADOW_OUT,
192 NULL,
193 button,
194 "button",
195 dc.XLOG2DEV(rect.x) -1, rect.y -1, rect.width +2, rect.height +2
196 );
197 }
198
199 // draw a ">" or "v" button
200 void
201 wxRendererGTK::DrawTreeItemButton(wxWindow* win,
202 wxDC& dc, const wxRect& rect, int flags)
203 {
204 GtkWidget *tree = GetTreeWidget();
205
206 GtkStateType state;
207 if ( flags & wxCONTROL_CURRENT )
208 state = GTK_STATE_PRELIGHT;
209 else
210 state = GTK_STATE_NORMAL;
211
212 // VZ: I don't know how to get the size of the expander so as to centre it
213 // in the given rectangle, +2/3 below is just what looks good here...
214 gtk_paint_expander
215 (
216 tree->style,
217 GTK_PIZZA(win->m_wxwindow)->bin_window,
218 state,
219 NULL,
220 tree,
221 "treeview",
222 dc.LogicalToDeviceX(rect.x) + 2,
223 dc.LogicalToDeviceY(rect.y) + 3,
224 flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
225 : GTK_EXPANDER_COLLAPSED
226 );
227 }
228
229
230 // ----------------------------------------------------------------------------
231 // splitter sash drawing
232 // ----------------------------------------------------------------------------
233
234 static int GetGtkSplitterFullSize()
235 {
236 static GtkWidget *s_paned = NULL;
237 if (s_paned == NULL)
238 s_paned = gtk_vpaned_new();
239
240 gint handle_size;
241 gtk_widget_style_get (s_paned, "handle_size", &handle_size, NULL);
242
243 return handle_size;
244 }
245
246 wxSplitterRenderParams
247 wxRendererGTK::GetSplitterParams(const wxWindow *WXUNUSED(win))
248 {
249 // we don't draw any border, hence 0 for the second field
250 return wxSplitterRenderParams
251 (
252 GetGtkSplitterFullSize(),
253 0,
254 true // hot sensitive
255 );
256 }
257
258 void
259 wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
260 wxDC& WXUNUSED(dc),
261 const wxRect& WXUNUSED(rect),
262 int WXUNUSED(flags))
263 {
264 // nothing to do
265 }
266
267 void
268 wxRendererGTK::DrawSplitterSash(wxWindow *win,
269 wxDC& dc,
270 const wxSize& size,
271 wxCoord position,
272 wxOrientation orient,
273 int flags)
274 {
275 if ( !win->m_wxwindow->window )
276 {
277 // window not realized yet
278 return;
279 }
280
281 wxCoord full_size = GetGtkSplitterFullSize();
282
283 // are we drawing vertical or horizontal splitter?
284 const bool isVert = orient == wxVERTICAL;
285
286 GdkRectangle rect;
287 #if USE_ERASE_RECT
288 GdkRectangle erase_rect;
289 #endif
290
291 if ( isVert )
292 {
293 int h = win->GetClientSize().GetHeight();
294
295 rect.x = position;
296 rect.y = 0;
297 rect.width = full_size;
298 rect.height = h;
299
300 #if USE_ERASE_RECT
301 erase_rect.x = position;
302 erase_rect.y = 0;
303 erase_rect.width = full_size;
304 erase_rect.height = h;
305 #endif
306 }
307 else // horz
308 {
309 int w = win->GetClientSize().GetWidth();
310
311 rect.x = 0;
312 rect.y = position;
313 rect.height = full_size;
314 rect.width = w;
315
316 #if USE_ERASE_RECT
317 erase_rect.y = position;
318 erase_rect.x = 0;
319 erase_rect.height = full_size;
320 erase_rect.width = w;
321 #endif
322 }
323
324 #if USE_ERASE_RECT
325 // we must erase everything first, otherwise the garbage
326 // from the old sash is left when dragging it
327 gtk_paint_flat_box
328 (
329 win->m_wxwindow->style,
330 GTK_PIZZA(win->m_wxwindow)->bin_window,
331 GTK_STATE_NORMAL,
332 GTK_SHADOW_NONE,
333 NULL,
334 win->m_wxwindow,
335 (char *)"viewportbin", // const_cast
336 erase_rect.x,
337 erase_rect.y,
338 erase_rect.width,
339 erase_rect.height
340 );
341 #endif
342
343 gtk_paint_handle
344 (
345 win->m_wxwindow->style,
346 GTK_PIZZA(win->m_wxwindow)->bin_window,
347 flags & wxCONTROL_CURRENT ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
348 GTK_SHADOW_NONE,
349 NULL /* no clipping */,
350 win->m_wxwindow,
351 "paned",
352 rect.x,
353 rect.y,
354 rect.width,
355 rect.height,
356 isVert ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL
357 );
358 }
359
360 void
361 wxRendererGTK::DrawDropArrow(wxWindow *win,
362 wxDC& dc,
363 const wxRect& rect,
364 int flags)
365 {
366 GtkWidget *button = GetButtonWidget();
367
368 // If we give GTK_PIZZA(win->m_wxwindow)->bin_window as
369 // a window for gtk_paint_xxx function, then it won't
370 // work for wxMemoryDC. So that is why we assume wxDC
371 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
372 // are derived from it) and use its m_window.
373 wxWindowDC& wdc = (wxWindowDC&)dc;
374
375 // only doing debug-time checking here (it should
376 // probably be enough)
377 wxASSERT ( wdc.IsKindOf(CLASSINFO(wxWindowDC)) );
378
379 // draw arrow so that there is even space horizontally
380 // on both sides
381 int arrowX = rect.width/4 + 1;
382 int arrowWidth = rect.width - (arrowX*2);
383
384 // scale arrow's height accoording to the width
385 int arrowHeight = rect.width/3;
386 int arrowY = (rect.height-arrowHeight)/2 +
387 ((rect.height-arrowHeight) & 1);
388
389 GtkStateType state;
390
391 if ( flags & wxCONTROL_PRESSED )
392 state = GTK_STATE_ACTIVE;
393 else if ( flags & wxCONTROL_DISABLED )
394 state = GTK_STATE_INSENSITIVE;
395 else if ( flags & wxCONTROL_CURRENT )
396 state = GTK_STATE_PRELIGHT;
397 else
398 state = GTK_STATE_NORMAL;
399
400 // draw arrow on button
401 gtk_paint_arrow
402 (
403 button->style,
404 wdc.m_window,
405 state,
406 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
407 NULL,
408 button,
409 "arrow",
410 GTK_ARROW_DOWN,
411 FALSE,
412 rect.x + arrowX,
413 rect.y + arrowY,
414 arrowWidth,
415 arrowHeight
416 );
417 }
418
419 void
420 wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
421 wxDC& dc,
422 const wxRect& rect,
423 int flags)
424 {
425 GtkWidget *button = GetButtonWidget();
426
427 // for reason why we do this, see DrawDropArrow
428 wxWindowDC& wdc = (wxWindowDC&)dc;
429 wxASSERT ( wdc.IsKindOf(CLASSINFO(wxWindowDC)) );
430
431 // draw button
432 GtkStateType state;
433
434 if ( flags & wxCONTROL_PRESSED )
435 state = GTK_STATE_ACTIVE;
436 else if ( flags & wxCONTROL_DISABLED )
437 state = GTK_STATE_INSENSITIVE;
438 else if ( flags & wxCONTROL_CURRENT )
439 state = GTK_STATE_PRELIGHT;
440 else
441 state = GTK_STATE_NORMAL;
442
443 gtk_paint_box
444 (
445 button->style,
446 wdc.m_window,
447 state,
448 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
449 NULL,
450 button,
451 "button",
452 rect.x, rect.y, rect.width, rect.height
453 );
454
455 // draw arrow on button
456 DrawDropArrow(win,dc,rect,flags);
457
458 }
459
460 void
461 wxRendererGTK::DrawCheckButton(wxWindow *win,
462 wxDC& dc,
463 const wxRect& rect,
464 int flags )
465 {
466 GtkWidget *button = GetCheckButtonWidget();
467
468 // for reason why we do this, see DrawDropArrow
469 wxWindowDC& wdc = (wxWindowDC&)dc;
470 wxASSERT ( wdc.IsKindOf(CLASSINFO(wxWindowDC)) );
471
472 GtkStateType state;
473
474 if ( flags & wxCONTROL_PRESSED )
475 state = GTK_STATE_ACTIVE;
476 else if ( flags & wxCONTROL_DISABLED )
477 state = GTK_STATE_INSENSITIVE;
478 else if ( flags & wxCONTROL_CURRENT )
479 state = GTK_STATE_PRELIGHT;
480 else
481 state = GTK_STATE_NORMAL;
482
483 gtk_paint_check
484 (
485 button->style,
486 wdc.m_window,
487 state,
488 flags & wxCONTROL_CHECKED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
489 NULL,
490 button,
491 "cellcheck",
492 rect.x, rect.y, 13, 13
493 );
494 }