draw drop down button using native calls (patch 1144389)
[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
34 #ifdef __WXGTK20__
35 #include "wx/settings.h"
36 #endif // GTK 2.0
37
38 #ifdef __WXGTK20__
39 #define WXUNUSED_IN_GTK1(arg) arg
40 #else
41 #define WXUNUSED_IN_GTK1(arg)
42 #endif
43
44 // ----------------------------------------------------------------------------
45 // wxRendererGTK: our wxRendererNative implementation
46 // ----------------------------------------------------------------------------
47
48 class WXDLLEXPORT wxRendererGTK : public wxDelegateRendererNative
49 {
50 public:
51
52 // used by DrawHeaderButton and DrawComboBoxDropButton
53 void PrepareButtonDraw();
54
55 // draw the header control button (used by wxListCtrl)
56 virtual void DrawHeaderButton(wxWindow *win,
57 wxDC& dc,
58 const wxRect& rect,
59 int flags = 0);
60
61 #ifdef __WXGTK20__
62 // draw the expanded/collapsed icon for a tree control item
63 virtual void DrawTreeItemButton(wxWindow *win,
64 wxDC& dc,
65 const wxRect& rect,
66 int flags = 0);
67 #endif // GTK 2.0
68
69 virtual void DrawSplitterBorder(wxWindow *win,
70 wxDC& dc,
71 const wxRect& rect,
72 int flags = 0);
73 virtual void DrawSplitterSash(wxWindow *win,
74 wxDC& dc,
75 const wxSize& size,
76 wxCoord position,
77 wxOrientation orient,
78 int flags = 0);
79
80 virtual void DrawComboBoxDropButton(wxWindow *win,
81 wxDC& dc,
82 const wxRect& rect,
83 int flags = 0);
84
85 virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
86 };
87
88 // ============================================================================
89 // implementation
90 // ============================================================================
91
92 /* static */
93 wxRendererNative& wxRendererNative::GetDefault()
94 {
95 static wxRendererGTK s_rendererGTK;
96
97 return s_rendererGTK;
98 }
99
100 // ----------------------------------------------------------------------------
101 // common code
102 // ----------------------------------------------------------------------------
103
104 static GtkWidget *gs_button = NULL;
105 static GtkWidget *gs_window = NULL;
106
107 void
108 wxRendererGTK::PrepareButtonDraw()
109 {
110 // prepares gs_button and gs_window which are used when
111 // drawing button based elements.
112 wxASSERT ( !gs_button );
113
114 gs_window = gtk_window_new( GTK_WINDOW_POPUP );
115 gtk_widget_realize( gs_window );
116 gs_button = gtk_button_new();
117 gtk_container_add( GTK_CONTAINER(gs_window), gs_button );
118 gtk_widget_realize( gs_button );
119 }
120
121 // ----------------------------------------------------------------------------
122 // list/tree controls drawing
123 // ----------------------------------------------------------------------------
124
125 void
126 wxRendererGTK::DrawHeaderButton(wxWindow *win,
127 wxDC& dc,
128 const wxRect& rect,
129 int flags)
130 {
131
132 if (gs_button == NULL)
133 PrepareButtonDraw();
134
135 gtk_paint_box
136 (
137 gs_button->style,
138 // FIXME: I suppose GTK_PIZZA(win->m_wxwindow)->bin_window doesn't work with wxMemoryDC.
139 // Maybe use code similar as in DrawComboBoxDropButton below?
140 GTK_PIZZA(win->m_wxwindow)->bin_window,
141 flags & wxCONTROL_DISABLED ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL,
142 GTK_SHADOW_OUT,
143 NULL,
144 gs_button,
145 "button",
146 dc.XLOG2DEV(rect.x) -1, rect.y -1, rect.width +2, rect.height +2
147 );
148 }
149
150 #ifdef __WXGTK20__
151
152 // draw a ">" or "v" button
153 //
154 // TODO: replace the code below with gtk_paint_expander()
155 void
156 wxRendererGTK::DrawTreeItemButton(wxWindow* win,
157 wxDC& dc, const wxRect& rect, int flags)
158 {
159 #define PM_SIZE 8
160
161 GtkPizza *pizza = GTK_PIZZA( win->m_wxwindow );
162 GtkStyle *style = win->m_widget->style;
163 int x = rect.x;
164 int y = rect.y;
165 y = dc.LogicalToDeviceY( y );
166 x = dc.LogicalToDeviceX( x );
167
168 // This draws the GTK+ 2.2.4 triangle
169 x--;
170 GdkPoint points[3];
171
172 if ( flags & wxCONTROL_EXPANDED )
173 {
174 points[0].x = x;
175 points[0].y = y + (PM_SIZE + 2) / 6;
176 points[1].x = points[0].x + (PM_SIZE + 2);
177 points[1].y = points[0].y;
178 points[2].x = (points[0].x + (PM_SIZE + 2) / 2);
179 points[2].y = y + 2 * (PM_SIZE + 2) / 3;
180 }
181 else
182 {
183 points[0].x = x + ((PM_SIZE + 2) / 6 + 2);
184 points[0].y = y - 1;
185 points[1].x = points[0].x;
186 points[1].y = points[0].y + (PM_SIZE + 2);
187 points[2].x = (points[0].x +
188 (2 * (PM_SIZE + 2) / 3 - 1));
189 points[2].y = points[0].y + (PM_SIZE + 2) / 2;
190 }
191
192 if ( flags & wxCONTROL_CURRENT )
193 gdk_draw_polygon( pizza->bin_window, style->fg_gc[GTK_STATE_PRELIGHT], TRUE, points, 3);
194 else
195 gdk_draw_polygon( pizza->bin_window, style->base_gc[GTK_STATE_NORMAL], TRUE, points, 3);
196 gdk_draw_polygon( pizza->bin_window, style->fg_gc[GTK_STATE_NORMAL], FALSE, points, 3 );
197 }
198
199 #endif // GTK 2.0
200
201 // ----------------------------------------------------------------------------
202 // splitter sash drawing
203 // ----------------------------------------------------------------------------
204
205 // all this should probably be read from the current theme settings somehow?
206 #ifdef __WXGTK20__
207 // the full sash size
208 static const wxCoord SASH_FULL_SIZE = 5;
209 #else // GTK+ 1.x
210 // the full sash width (should be even)
211 static const wxCoord SASH_SIZE = 8;
212
213 // margin around the sash
214 static const wxCoord SASH_MARGIN = 2;
215
216 // the full sash size
217 static const wxCoord SASH_FULL_SIZE = SASH_SIZE + SASH_MARGIN;
218 #endif // GTK+ 2.x/1.x
219
220 wxSplitterRenderParams
221 wxRendererGTK::GetSplitterParams(const wxWindow * WXUNUSED(win))
222 {
223 // we don't draw any border, hence 0 for the second field
224 return wxSplitterRenderParams
225 (
226 SASH_FULL_SIZE,
227 0,
228 #ifdef __WXGTK20__
229 true // hot sensitive
230 #else // GTK+ 1.x
231 false // not
232 #endif // GTK+ 2.x/1.x
233 );
234 }
235
236 void
237 wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
238 wxDC& WXUNUSED(dc),
239 const wxRect& WXUNUSED(rect),
240 int WXUNUSED(flags))
241 {
242 // nothing to do
243 }
244
245 void
246 wxRendererGTK::DrawSplitterSash(wxWindow *win,
247 wxDC& dc,
248 const wxSize& size,
249 wxCoord position,
250 wxOrientation orient,
251 int WXUNUSED_IN_GTK1(flags))
252 {
253 if ( !win->m_wxwindow->window )
254 {
255 // window not realized yet
256 return;
257 }
258
259 // are we drawing vertical or horizontal splitter?
260 const bool isVert = orient == wxVERTICAL;
261
262 GdkRectangle rect;
263 GdkRectangle erase_rect;
264 if ( isVert )
265 {
266 int h = win->GetClientSize().GetHeight();
267
268 rect.x = position;
269 rect.y = 0;
270 rect.width = SASH_FULL_SIZE;
271 rect.height = h;
272
273 erase_rect.x = position;
274 erase_rect.y = 0;
275 erase_rect.width = SASH_FULL_SIZE;
276 erase_rect.height = h;
277 }
278 else // horz
279 {
280 int w = win->GetClientSize().GetWidth();
281
282 rect.x = 0;
283 rect.y = position;
284 rect.height = SASH_FULL_SIZE;
285 rect.width = w;
286
287 erase_rect.y = position;
288 erase_rect.x = 0;
289 erase_rect.height = SASH_FULL_SIZE;
290 erase_rect.width = w;
291 }
292
293 // we must erase everything first, otherwise the garbage from the old sash
294 // is left when dragging it
295 //
296 // TODO: is this the right way to draw themed background?
297 gtk_paint_flat_box
298 (
299 win->m_wxwindow->style,
300 GTK_PIZZA(win->m_wxwindow)->bin_window,
301 GTK_STATE_NORMAL,
302 GTK_SHADOW_NONE,
303 NULL,
304 win->m_wxwindow,
305 (char *)"base", // const_cast
306 erase_rect.x,
307 erase_rect.y,
308 erase_rect.width,
309 erase_rect.height
310 );
311
312 #ifdef __WXGTK20__
313 gtk_paint_handle
314 (
315 win->m_wxwindow->style,
316 GTK_PIZZA(win->m_wxwindow)->bin_window,
317 flags & wxCONTROL_CURRENT ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
318 GTK_SHADOW_NONE,
319 NULL /* no clipping */,
320 win->m_wxwindow,
321 "paned",
322 rect.x,
323 rect.y,
324 rect.width,
325 rect.height,
326 !isVert ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL
327 );
328 #else // GTK+ 1.x
329
330 // leave some margin before sash itself
331 position += SASH_MARGIN / 2;
332
333 // and finally draw it using GTK paint functions
334 typedef void (*GtkPaintLineFunc)(GtkStyle *, GdkWindow *,
335 GtkStateType,
336 GdkRectangle *, GtkWidget *,
337 gchar *,
338 gint, gint, gint);
339
340 GtkPaintLineFunc func = isVert ? gtk_paint_vline : gtk_paint_hline;
341
342 (*func)
343 (
344 win->m_wxwindow->style,
345 GTK_PIZZA(win->m_wxwindow)->bin_window,
346 GTK_STATE_NORMAL,
347 NULL,
348 win->m_wxwindow,
349 (char *)"paned", // const_cast
350 0, isVert ? size.y : size.x, position + SASH_SIZE / 2 - 1
351 );
352
353 gtk_paint_box
354 (
355 win->m_wxwindow->style,
356 GTK_PIZZA(win->m_wxwindow)->bin_window,
357 GTK_STATE_NORMAL,
358 GTK_SHADOW_OUT,
359 (GdkRectangle*) NULL,
360 win->m_wxwindow,
361 (char *)"paned", // const_cast
362 isVert ? position : size.x - 2*SASH_SIZE,
363 isVert ? size.y - 2*SASH_SIZE : position,
364 SASH_SIZE, SASH_SIZE
365 );
366 #endif // GTK+ 2.x/1.x
367 }
368
369 void wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
370 wxDC& dc,
371 const wxRect& rect,
372 int flags)
373 {
374 if (gs_button == NULL)
375 PrepareButtonDraw();
376
377 // device context must inherit from wxWindowDC
378 // (so it must be wxClientDC, wxMemoryDC or wxPaintDC)
379 wxWindowDC& wdc = (wxWindowDC&)dc;
380
381 // only doing debug-time checking here (it should probably be enough)
382 wxASSERT ( wdc.IsKindOf(CLASSINFO(wxWindowDC)) );
383
384 GtkStateType state = GTK_STATE_NORMAL;
385 GtkShadowType shadow = GTK_SHADOW_OUT;
386
387 if ( flags & wxCONTROL_PRESSED )
388 shadow = GTK_SHADOW_IN;
389 else if ( flags & wxCONTROL_CURRENT )
390 state = GTK_STATE_PRELIGHT;
391 else if ( flags & wxCONTROL_DISABLED )
392 state = GTK_STATE_INSENSITIVE;
393
394 gtk_paint_box
395 (
396 gs_button->style,
397 //GTK_PIZZA(wdc->m_window)->bin_window,
398 wdc.m_window,
399 state,
400 shadow,
401 NULL,
402 gs_button,
403 "button",
404 dc.XLOG2DEV(rect.x), rect.y, rect.width, rect.height
405 );
406
407 // draw arrow on button
408
409 int arr_wid = rect.width/2;
410 int arr_hei = rect.height/2;
411 arr_wid += arr_wid & 1;
412 arr_hei += arr_hei & 1;
413
414 gtk_paint_arrow
415 (
416 gs_button->style,
417 //GTK_PIZZA(wdc->m_window)->bin_window,
418 wdc.m_window,
419 state,
420 shadow,
421 NULL,
422 gs_button,
423 "arrow",
424 GTK_ARROW_DOWN,
425 TRUE,
426 dc.XLOG2DEV(rect.x) + (rect.width/2-arr_wid/2) + 1,
427 rect.y + (rect.height/2-arr_hei/2) + 1,
428 arr_wid,
429 arr_hei
430 );
431
432 }
433