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