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