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