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