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