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