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