]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/renderer.cpp
correction to last commit: don't test unsetenv() return value, it's void under Darwin
[wxWidgets.git] / src / gtk / renderer.cpp
CommitLineData
9c7f49f5 1///////////////////////////////////////////////////////////////////////////////
90b903c2 2// Name: src/gtk/renderer.cpp
38c4cb6a 3// Purpose: implementation of wxRendererNative for wxGTK
9c7f49f5
VZ
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>
65571936 9// License: wxWindows licence
9c7f49f5
VZ
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
e1bf3ad3 27#include "wx/renderer.h"
cdccdfab
WS
28
29#ifndef WX_PRECOMP
30 #include "wx/window.h"
ed4b0fdc 31 #include "wx/dcclient.h"
9eddec69 32 #include "wx/settings.h"
cdccdfab
WS
33#endif
34
9c7f49f5
VZ
35#include <gtk/gtk.h>
36#include "wx/gtk/win_gtk.h"
37
9c7f49f5 38// ----------------------------------------------------------------------------
38c4cb6a 39// wxRendererGTK: our wxRendererNative implementation
9c7f49f5
VZ
40// ----------------------------------------------------------------------------
41
38c4cb6a 42class WXDLLEXPORT wxRendererGTK : public wxDelegateRendererNative
9c7f49f5
VZ
43{
44public:
45 // draw the header control button (used by wxListCtrl)
c97c9952 46 virtual int DrawHeaderButton(wxWindow *win,
9c7f49f5
VZ
47 wxDC& dc,
48 const wxRect& rect,
4b94ddc4 49 int flags = 0,
80752b57 50 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
4b94ddc4 51 wxHeaderButtonParams* params = NULL);
9c7f49f5 52
9c7f49f5
VZ
53 // draw the expanded/collapsed icon for a tree control item
54 virtual void DrawTreeItemButton(wxWindow *win,
55 wxDC& dc,
56 const wxRect& rect,
57 int flags = 0);
9c7f49f5 58
d16cf3cd
VZ
59 virtual void DrawSplitterBorder(wxWindow *win,
60 wxDC& dc,
af99040c
VZ
61 const wxRect& rect,
62 int flags = 0);
95155e75
VZ
63 virtual void DrawSplitterSash(wxWindow *win,
64 wxDC& dc,
65 const wxSize& size,
d16cf3cd 66 wxCoord position,
af99040c
VZ
67 wxOrientation orient,
68 int flags = 0);
d16cf3cd 69
38511687
VZ
70 virtual void DrawComboBoxDropButton(wxWindow *win,
71 wxDC& dc,
72 const wxRect& rect,
73 int flags = 0);
74
4c85ab75
VZ
75 virtual void DrawDropArrow(wxWindow *win,
76 wxDC& dc,
77 const wxRect& rect,
78 int flags = 0);
79
90b903c2
WS
80 virtual void DrawCheckBox(wxWindow *win,
81 wxDC& dc,
82 const wxRect& rect,
83 int flags = 0);
2209baae
RR
84
85 virtual void DrawPushButton(wxWindow *win,
86 wxDC& dc,
87 const wxRect& rect,
88 int flags = 0);
89
daebb44c
RR
90 virtual void DrawItemSelectionRect(wxWindow *win,
91 wxDC& dc,
92 const wxRect& rect,
93 int flags = 0);
90b903c2 94
af99040c 95 virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
e1befae3
VZ
96
97private:
bc13e772
VZ
98 // FIXME: shouldn't we destroy these windows somewhere?
99
621ed8af 100 // used by DrawPushButton and DrawDropArrow
bc13e772
VZ
101 static GtkWidget *GetButtonWidget();
102
bc13e772
VZ
103 // used by DrawTreeItemButton()
104 static GtkWidget *GetTreeWidget();
90b903c2
WS
105
106 // used by DrawCheckBox()
862d8041 107 static GtkWidget *GetCheckButtonWidget();
621ed8af
RD
108
109 // Used by DrawHeaderButton
110 static GtkWidget *GetHeaderButtonWidget();
9c7f49f5
VZ
111};
112
113// ============================================================================
114// implementation
115// ============================================================================
116
117/* static */
f0244295 118wxRendererNative& wxRendererNative::GetDefault()
9c7f49f5
VZ
119{
120 static wxRendererGTK s_rendererGTK;
121
122 return s_rendererGTK;
123}
124
a4622f29 125// ----------------------------------------------------------------------------
bc13e772 126// helper functions
a4622f29
VZ
127// ----------------------------------------------------------------------------
128
bc13e772
VZ
129GtkWidget *
130wxRendererGTK::GetButtonWidget()
131{
132 static GtkWidget *s_button = NULL;
133 static GtkWidget *s_window = NULL;
a4622f29 134
bc13e772
VZ
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
862d8041
RR
147GtkWidget *
148wxRendererGTK::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
bc13e772
VZ
165GtkWidget *
166wxRendererGTK::GetTreeWidget()
a4622f29 167{
bc13e772
VZ
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;
a4622f29
VZ
181}
182
621ed8af 183
c7a757aa
RD
184// This one just gets the button used by the column header. Although it's
185// still a gtk_button the themes will typically differentiate and draw them
186// differently if the button is in a treeview.
621ed8af
RD
187GtkWidget *
188wxRendererGTK::GetHeaderButtonWidget()
189{
190 static GtkWidget *s_button = NULL;
c7a757aa 191
621ed8af
RD
192 if ( !s_button )
193 {
c7a757aa
RD
194 // Get the dummy tree widget, give it a column, and then use the
195 // widget in the column header for the rendering code.
621ed8af 196 GtkWidget* treewidget = GetTreeWidget();
c7a757aa
RD
197 GtkTreeViewColumn* column = gtk_tree_view_column_new();
198 gtk_tree_view_append_column(GTK_TREE_VIEW(treewidget), column);
199 s_button = column->button;
621ed8af
RD
200 }
201
202 return s_button;
203}
204
d16cf3cd
VZ
205// ----------------------------------------------------------------------------
206// list/tree controls drawing
207// ----------------------------------------------------------------------------
208
c97c9952 209int
9c7f49f5
VZ
210wxRendererGTK::DrawHeaderButton(wxWindow *win,
211 wxDC& dc,
212 const wxRect& rect,
4b94ddc4 213 int flags,
80752b57 214 wxHeaderSortIconType sortArrow,
4b94ddc4 215 wxHeaderButtonParams* params)
9c7f49f5 216{
9b311923 217
621ed8af
RD
218 GtkWidget *button = GetHeaderButtonWidget();
219
2e992e06
VZ
220 GdkWindow* gdk_window = dc.GetGDKWindow();
221 wxASSERT_MSG( gdk_window,
222 wxT("cannot use wxRendererNative on wxDC of this type") );
223
5eefe029
RR
224 int x_diff = 0;
225 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
226 x_diff = rect.width;
f4322df6 227
9c7f49f5
VZ
228 gtk_paint_box
229 (
bc13e772 230 button->style,
2e992e06 231 gdk_window,
9c7f49f5
VZ
232 flags & wxCONTROL_DISABLED ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL,
233 GTK_SHADOW_OUT,
38511687 234 NULL,
bc13e772 235 button,
9b311923 236 "button",
5eefe029 237 dc.LogicalToDeviceX(rect.x) - x_diff, rect.y, rect.width, rect.height
9c7f49f5 238 );
4b94ddc4 239
c97c9952 240 return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params);
9c7f49f5
VZ
241}
242
9c7f49f5 243// draw a ">" or "v" button
9c7f49f5 244void
f8b043e7 245wxRendererGTK::DrawTreeItemButton(wxWindow* win,
9a0b7e33 246 wxDC& dc, const wxRect& rect, int flags)
9c7f49f5 247{
bc13e772 248 GtkWidget *tree = GetTreeWidget();
f8b043e7 249
2e992e06
VZ
250 GdkWindow* gdk_window = dc.GetGDKWindow();
251 wxASSERT_MSG( gdk_window,
252 wxT("cannot use wxRendererNative on wxDC of this type") );
253
885dd597
RR
254 GtkStateType state;
255 if ( flags & wxCONTROL_CURRENT )
256 state = GTK_STATE_PRELIGHT;
257 else
258 state = GTK_STATE_NORMAL;
91af0895 259
428f4657
RR
260 int x_diff = 0;
261 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
262 x_diff = rect.width;
2e992e06 263
bc13e772
VZ
264 // VZ: I don't know how to get the size of the expander so as to centre it
265 // in the given rectangle, +2/3 below is just what looks good here...
266 gtk_paint_expander
267 (
268 tree->style,
2e992e06 269 gdk_window,
885dd597 270 state,
bc13e772
VZ
271 NULL,
272 tree,
273 "treeview",
11012f47 274 dc.LogicalToDeviceX(rect.x) + 6 - x_diff,
bc13e772
VZ
275 dc.LogicalToDeviceY(rect.y) + 3,
276 flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
277 : GTK_EXPANDER_COLLAPSED
278 );
9c7f49f5
VZ
279}
280
9c7f49f5 281
d16cf3cd
VZ
282// ----------------------------------------------------------------------------
283// splitter sash drawing
284// ----------------------------------------------------------------------------
285
38418827
RR
286static int GetGtkSplitterFullSize()
287{
38418827
RR
288 static GtkWidget *s_paned = NULL;
289 if (s_paned == NULL)
290 s_paned = gtk_vpaned_new();
291
292 gint handle_size;
293 gtk_widget_style_get (s_paned, "handle_size", &handle_size, NULL);
91af0895 294
38418827 295 return handle_size;
38418827
RR
296}
297
af99040c 298wxSplitterRenderParams
38418827 299wxRendererGTK::GetSplitterParams(const wxWindow *WXUNUSED(win))
d16cf3cd 300{
af99040c
VZ
301 // we don't draw any border, hence 0 for the second field
302 return wxSplitterRenderParams
303 (
38418827 304 GetGtkSplitterFullSize(),
af99040c 305 0,
af99040c 306 true // hot sensitive
af99040c 307 );
d16cf3cd
VZ
308}
309
310void
311wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
312 wxDC& WXUNUSED(dc),
af99040c
VZ
313 const wxRect& WXUNUSED(rect),
314 int WXUNUSED(flags))
d16cf3cd
VZ
315{
316 // nothing to do
317}
95155e75 318
95155e75
VZ
319void
320wxRendererGTK::DrawSplitterSash(wxWindow *win,
321 wxDC& dc,
322 const wxSize& size,
d16cf3cd 323 wxCoord position,
af99040c 324 wxOrientation orient,
68567a96 325 int flags)
95155e75
VZ
326{
327 if ( !win->m_wxwindow->window )
328 {
0100b858 329 // window not realized yet
95155e75
VZ
330 return;
331 }
91af0895 332
2e992e06
VZ
333 GdkWindow* gdk_window = dc.GetGDKWindow();
334 wxASSERT_MSG( gdk_window,
335 wxT("cannot use wxRendererNative on wxDC of this type") );
336
38418827 337 wxCoord full_size = GetGtkSplitterFullSize();
95155e75 338
d16cf3cd
VZ
339 // are we drawing vertical or horizontal splitter?
340 const bool isVert = orient == wxVERTICAL;
341
d16cf3cd 342 GdkRectangle rect;
91af0895 343
d16cf3cd
VZ
344 if ( isVert )
345 {
0100b858 346 int h = win->GetClientSize().GetHeight();
e1befae3 347
d16cf3cd 348 rect.x = position;
0100b858 349 rect.y = 0;
38418827 350 rect.width = full_size;
0100b858 351 rect.height = h;
d16cf3cd
VZ
352 }
353 else // horz
354 {
0100b858 355 int w = win->GetClientSize().GetWidth();
e1befae3 356
0100b858 357 rect.x = 0;
d16cf3cd 358 rect.y = position;
38418827 359 rect.height = full_size;
0100b858 360 rect.width = w;
d16cf3cd 361 }
f4322df6 362
847dfdb4
RR
363 int x_diff = 0;
364 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
365 x_diff = rect.width;
35468934 366
af99040c
VZ
367 gtk_paint_handle
368 (
369 win->m_wxwindow->style,
2e992e06 370 gdk_window,
af99040c
VZ
371 flags & wxCONTROL_CURRENT ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
372 GTK_SHADOW_NONE,
373 NULL /* no clipping */,
374 win->m_wxwindow,
375 "paned",
847dfdb4
RR
376 dc.LogicalToDeviceX(rect.x) - x_diff,
377 dc.LogicalToDeviceY(rect.y),
af99040c
VZ
378 rect.width,
379 rect.height,
38418827 380 isVert ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL
af99040c 381 );
95155e75
VZ
382}
383
4c85ab75 384void
2e992e06 385wxRendererGTK::DrawDropArrow(wxWindow *WXUNUSED(win),
4c85ab75
VZ
386 wxDC& dc,
387 const wxRect& rect,
388 int flags)
38511687 389{
bc13e772 390 GtkWidget *button = GetButtonWidget();
38511687 391
4c85ab75
VZ
392 // If we give GTK_PIZZA(win->m_wxwindow)->bin_window as
393 // a window for gtk_paint_xxx function, then it won't
394 // work for wxMemoryDC. So that is why we assume wxDC
395 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
396 // are derived from it) and use its m_window.
2e992e06
VZ
397 GdkWindow* gdk_window = dc.GetGDKWindow();
398 wxASSERT_MSG( gdk_window,
399 wxT("cannot use wxRendererNative on wxDC of this type") );
a4622f29 400
4c85ab75
VZ
401 // draw arrow so that there is even space horizontally
402 // on both sides
403 int arrowX = rect.width/4 + 1;
404 int arrowWidth = rect.width - (arrowX*2);
405
406 // scale arrow's height accoording to the width
407 int arrowHeight = rect.width/3;
408 int arrowY = (rect.height-arrowHeight)/2 +
409 ((rect.height-arrowHeight) & 1);
410
e1befae3 411 GtkStateType state;
a4622f29 412
3203621a
JS
413 if ( flags & wxCONTROL_PRESSED )
414 state = GTK_STATE_ACTIVE;
a4622f29
VZ
415 else if ( flags & wxCONTROL_DISABLED )
416 state = GTK_STATE_INSENSITIVE;
3203621a
JS
417 else if ( flags & wxCONTROL_CURRENT )
418 state = GTK_STATE_PRELIGHT;
e1befae3
VZ
419 else
420 state = GTK_STATE_NORMAL;
a4622f29 421
a4622f29 422 // draw arrow on button
a4622f29
VZ
423 gtk_paint_arrow
424 (
bc13e772 425 button->style,
2e992e06 426 gdk_window,
a4622f29 427 state,
e1befae3 428 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
a4622f29 429 NULL,
bc13e772 430 button,
a4622f29
VZ
431 "arrow",
432 GTK_ARROW_DOWN,
a8ac548e 433 FALSE,
4c85ab75
VZ
434 rect.x + arrowX,
435 rect.y + arrowY,
436 arrowWidth,
437 arrowHeight
a4622f29 438 );
38511687
VZ
439}
440
4c85ab75
VZ
441void
442wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
443 wxDC& dc,
444 const wxRect& rect,
445 int flags)
446{
2209baae
RR
447 DrawPushButton(win,dc,rect,flags);
448 DrawDropArrow(win,dc,rect);
449}
450
cdccdfab 451void
2e992e06 452wxRendererGTK::DrawCheckBox(wxWindow *WXUNUSED(win),
90b903c2
WS
453 wxDC& dc,
454 const wxRect& rect,
455 int flags )
2209baae
RR
456{
457 GtkWidget *button = GetCheckButtonWidget();
4c85ab75
VZ
458
459 // for reason why we do this, see DrawDropArrow
2e992e06
VZ
460 GdkWindow* gdk_window = dc.GetGDKWindow();
461 wxASSERT_MSG( gdk_window,
462 wxT("cannot use wxRendererNative on wxDC of this type") );
90b903c2 463
4c85ab75
VZ
464 GtkStateType state;
465
3203621a
JS
466 if ( flags & wxCONTROL_PRESSED )
467 state = GTK_STATE_ACTIVE;
4c85ab75
VZ
468 else if ( flags & wxCONTROL_DISABLED )
469 state = GTK_STATE_INSENSITIVE;
3203621a
JS
470 else if ( flags & wxCONTROL_CURRENT )
471 state = GTK_STATE_PRELIGHT;
4c85ab75
VZ
472 else
473 state = GTK_STATE_NORMAL;
90b903c2 474
2209baae 475 gtk_paint_check
4c85ab75
VZ
476 (
477 button->style,
2e992e06 478 gdk_window,
4c85ab75 479 state,
2209baae 480 flags & wxCONTROL_CHECKED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
4c85ab75
VZ
481 NULL,
482 button,
2209baae 483 "cellcheck",
cdccdfab
WS
484 dc.LogicalToDeviceX(rect.x)+2,
485 dc.LogicalToDeviceY(rect.y)+3,
506d54a3 486 13, 13
4c85ab75 487 );
4c85ab75
VZ
488}
489
2209baae 490void
2e992e06 491wxRendererGTK::DrawPushButton(wxWindow *WXUNUSED(win),
2209baae
RR
492 wxDC& dc,
493 const wxRect& rect,
494 int flags)
862d8041 495{
2209baae 496 GtkWidget *button = GetButtonWidget();
862d8041
RR
497
498 // for reason why we do this, see DrawDropArrow
2e992e06
VZ
499 GdkWindow* gdk_window = dc.GetGDKWindow();
500 wxASSERT_MSG( gdk_window,
501 wxT("cannot use wxRendererNative on wxDC of this type") );
2209baae
RR
502
503 // draw button
862d8041
RR
504 GtkStateType state;
505
506 if ( flags & wxCONTROL_PRESSED )
507 state = GTK_STATE_ACTIVE;
508 else if ( flags & wxCONTROL_DISABLED )
509 state = GTK_STATE_INSENSITIVE;
510 else if ( flags & wxCONTROL_CURRENT )
511 state = GTK_STATE_PRELIGHT;
512 else
513 state = GTK_STATE_NORMAL;
2209baae
RR
514
515 gtk_paint_box
862d8041
RR
516 (
517 button->style,
2e992e06 518 gdk_window,
862d8041 519 state,
2209baae 520 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
862d8041
RR
521 NULL,
522 button,
2209baae
RR
523 "button",
524 rect.x, rect.y, rect.width, rect.height
862d8041
RR
525 );
526}
daebb44c 527
cdccdfab 528void
daebb44c 529wxRendererGTK::DrawItemSelectionRect(wxWindow *win,
cdccdfab
WS
530 wxDC& dc,
531 const wxRect& rect,
532 int flags )
daebb44c 533{
2e992e06
VZ
534 GdkWindow* gdk_window = dc.GetGDKWindow();
535 wxASSERT_MSG( gdk_window,
536 wxT("cannot use wxRendererNative on wxDC of this type") );
537
08f57d21
RR
538 int x_diff = 0;
539 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
540 x_diff = rect.width;
541
daebb44c 542 GtkStateType state;
90b903c2 543 if (flags & wxCONTROL_SELECTED)
daebb44c 544 {
05d97538
RR
545 // the wxCONTROL_FOCUSED state is deduced
546 // directly from the m_wxwindow by GTK+
547 state = GTK_STATE_SELECTED;
daebb44c 548
05d97538 549 gtk_paint_flat_box( win->m_widget->style,
2e992e06 550 gdk_window,
daebb44c
RR
551 state,
552 GTK_SHADOW_NONE,
cdccdfab 553 NULL,
daebb44c 554 win->m_wxwindow,
05d97538 555 "cell_even",
08f57d21 556 dc.LogicalToDeviceX(rect.x) - x_diff,
daebb44c
RR
557 dc.LogicalToDeviceY(rect.y),
558 rect.width,
559 rect.height );
560 }
72be9a3a
VZ
561 else // !wxCONTROL_SELECTED
562 {
563 state = GTK_STATE_NORMAL;
564 }
90b903c2 565
daebb44c
RR
566 if (flags & wxCONTROL_CURRENT)
567 {
f4322df6 568 gtk_paint_focus( win->m_widget->style,
05d97538 569 gdk_window,
72be9a3a 570 state,
05d97538
RR
571 NULL,
572 win->m_wxwindow,
573 "treeview",
574 dc.LogicalToDeviceX(rect.x),
575 dc.LogicalToDeviceY(rect.y),
576 rect.width,
577 rect.height );
daebb44c
RR
578 }
579}