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