More support for drawing native column headers, adds more states
[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 gtk_paint_box
194 (
195 button->style,
196 // FIXME: I suppose GTK_PIZZA(win->m_wxwindow)->bin_window doesn't work with wxMemoryDC.
197 // Maybe use code similar as in DrawPushButton below?
198 GTK_PIZZA(win->m_wxwindow)->bin_window,
199 flags & wxCONTROL_DISABLED ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL,
200 GTK_SHADOW_OUT,
201 NULL,
202 button,
203 "button",
204 dc.LogicalToDeviceX(rect.x), rect.y, rect.width, rect.height
205 );
206
207 DrawHeaderButtonContents(win, dc, rect, flags, params);
208 }
209
210 // draw a ">" or "v" button
211 void
212 wxRendererGTK::DrawTreeItemButton(wxWindow* win,
213 wxDC& dc, const wxRect& rect, int flags)
214 {
215 GtkWidget *tree = GetTreeWidget();
216
217 GtkStateType state;
218 if ( flags & wxCONTROL_CURRENT )
219 state = GTK_STATE_PRELIGHT;
220 else
221 state = GTK_STATE_NORMAL;
222
223 int x_diff = 0;
224 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
225 x_diff = rect.width;
226
227 // VZ: I don't know how to get the size of the expander so as to centre it
228 // in the given rectangle, +2/3 below is just what looks good here...
229 gtk_paint_expander
230 (
231 tree->style,
232 GTK_PIZZA(win->m_wxwindow)->bin_window,
233 state,
234 NULL,
235 tree,
236 "treeview",
237 dc.LogicalToDeviceX(rect.x) + 2 - x_diff,
238 dc.LogicalToDeviceY(rect.y) + 3,
239 flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
240 : GTK_EXPANDER_COLLAPSED
241 );
242 }
243
244
245 // ----------------------------------------------------------------------------
246 // splitter sash drawing
247 // ----------------------------------------------------------------------------
248
249 static int GetGtkSplitterFullSize()
250 {
251 static GtkWidget *s_paned = NULL;
252 if (s_paned == NULL)
253 s_paned = gtk_vpaned_new();
254
255 gint handle_size;
256 gtk_widget_style_get (s_paned, "handle_size", &handle_size, NULL);
257
258 return handle_size;
259 }
260
261 wxSplitterRenderParams
262 wxRendererGTK::GetSplitterParams(const wxWindow *WXUNUSED(win))
263 {
264 // we don't draw any border, hence 0 for the second field
265 return wxSplitterRenderParams
266 (
267 GetGtkSplitterFullSize(),
268 0,
269 true // hot sensitive
270 );
271 }
272
273 void
274 wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
275 wxDC& WXUNUSED(dc),
276 const wxRect& WXUNUSED(rect),
277 int WXUNUSED(flags))
278 {
279 // nothing to do
280 }
281
282 void
283 wxRendererGTK::DrawSplitterSash(wxWindow *win,
284 wxDC& dc,
285 const wxSize& size,
286 wxCoord position,
287 wxOrientation orient,
288 int flags)
289 {
290 if ( !win->m_wxwindow->window )
291 {
292 // window not realized yet
293 return;
294 }
295
296 wxCoord full_size = GetGtkSplitterFullSize();
297
298 // are we drawing vertical or horizontal splitter?
299 const bool isVert = orient == wxVERTICAL;
300
301 GdkRectangle rect;
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 else // horz
313 {
314 int w = win->GetClientSize().GetWidth();
315
316 rect.x = 0;
317 rect.y = position;
318 rect.height = full_size;
319 rect.width = w;
320 }
321
322 int x_diff = 0;
323 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
324 x_diff = rect.width;
325
326 gtk_paint_handle
327 (
328 win->m_wxwindow->style,
329 GTK_PIZZA(win->m_wxwindow)->bin_window,
330 flags & wxCONTROL_CURRENT ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
331 GTK_SHADOW_NONE,
332 NULL /* no clipping */,
333 win->m_wxwindow,
334 "paned",
335 dc.LogicalToDeviceX(rect.x) - x_diff,
336 dc.LogicalToDeviceY(rect.y),
337 rect.width,
338 rect.height,
339 isVert ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL
340 );
341 }
342
343 void
344 wxRendererGTK::DrawDropArrow(wxWindow *win,
345 wxDC& dc,
346 const wxRect& rect,
347 int flags)
348 {
349 GtkWidget *button = GetButtonWidget();
350
351 // If we give GTK_PIZZA(win->m_wxwindow)->bin_window as
352 // a window for gtk_paint_xxx function, then it won't
353 // work for wxMemoryDC. So that is why we assume wxDC
354 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
355 // are derived from it) and use its m_window.
356 wxWindowDC& wdc = (wxWindowDC&)dc;
357
358 // only doing debug-time checking here (it should
359 // probably be enough)
360 wxASSERT ( wdc.IsKindOf(CLASSINFO(wxWindowDC)) );
361
362 // draw arrow so that there is even space horizontally
363 // on both sides
364 int arrowX = rect.width/4 + 1;
365 int arrowWidth = rect.width - (arrowX*2);
366
367 // scale arrow's height accoording to the width
368 int arrowHeight = rect.width/3;
369 int arrowY = (rect.height-arrowHeight)/2 +
370 ((rect.height-arrowHeight) & 1);
371
372 GtkStateType state;
373
374 if ( flags & wxCONTROL_PRESSED )
375 state = GTK_STATE_ACTIVE;
376 else if ( flags & wxCONTROL_DISABLED )
377 state = GTK_STATE_INSENSITIVE;
378 else if ( flags & wxCONTROL_CURRENT )
379 state = GTK_STATE_PRELIGHT;
380 else
381 state = GTK_STATE_NORMAL;
382
383 // draw arrow on button
384 gtk_paint_arrow
385 (
386 button->style,
387 wdc.m_window,
388 state,
389 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
390 NULL,
391 button,
392 "arrow",
393 GTK_ARROW_DOWN,
394 FALSE,
395 rect.x + arrowX,
396 rect.y + arrowY,
397 arrowWidth,
398 arrowHeight
399 );
400 }
401
402 void
403 wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
404 wxDC& dc,
405 const wxRect& rect,
406 int flags)
407 {
408 DrawPushButton(win,dc,rect,flags);
409 DrawDropArrow(win,dc,rect);
410 }
411
412 void
413 wxRendererGTK::DrawCheckBox(wxWindow *win,
414 wxDC& dc,
415 const wxRect& rect,
416 int flags )
417 {
418 GtkWidget *button = GetCheckButtonWidget();
419
420 // for reason why we do this, see DrawDropArrow
421 wxWindowDC& wdc = (wxWindowDC&)dc;
422 wxASSERT ( wdc.IsKindOf(CLASSINFO(wxWindowDC)) );
423
424 GtkStateType state;
425
426 if ( flags & wxCONTROL_PRESSED )
427 state = GTK_STATE_ACTIVE;
428 else if ( flags & wxCONTROL_DISABLED )
429 state = GTK_STATE_INSENSITIVE;
430 else if ( flags & wxCONTROL_CURRENT )
431 state = GTK_STATE_PRELIGHT;
432 else
433 state = GTK_STATE_NORMAL;
434
435 gtk_paint_check
436 (
437 button->style,
438 wdc.m_window,
439 state,
440 flags & wxCONTROL_CHECKED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
441 NULL,
442 button,
443 "cellcheck",
444 dc.LogicalToDeviceX(rect.x)+2,
445 dc.LogicalToDeviceY(rect.y)+3,
446 13, 13
447 );
448 }
449
450 void
451 wxRendererGTK::DrawPushButton(wxWindow *win,
452 wxDC& dc,
453 const wxRect& rect,
454 int flags)
455 {
456 GtkWidget *button = GetButtonWidget();
457
458 // for reason why we do this, see DrawDropArrow
459 wxWindowDC& wdc = (wxWindowDC&)dc;
460 wxASSERT ( wdc.IsKindOf(CLASSINFO(wxWindowDC)) );
461
462 // draw button
463 GtkStateType state;
464
465 if ( flags & wxCONTROL_PRESSED )
466 state = GTK_STATE_ACTIVE;
467 else if ( flags & wxCONTROL_DISABLED )
468 state = GTK_STATE_INSENSITIVE;
469 else if ( flags & wxCONTROL_CURRENT )
470 state = GTK_STATE_PRELIGHT;
471 else
472 state = GTK_STATE_NORMAL;
473
474 gtk_paint_box
475 (
476 button->style,
477 wdc.m_window,
478 state,
479 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
480 NULL,
481 button,
482 "button",
483 rect.x, rect.y, rect.width, rect.height
484 );
485 }
486
487 void
488 wxRendererGTK::DrawItemSelectionRect(wxWindow *win,
489 wxDC& dc,
490 const wxRect& rect,
491 int flags )
492 {
493 GtkStateType state;
494 if (flags & wxCONTROL_SELECTED)
495 {
496 if (flags & wxCONTROL_FOCUSED)
497 state = GTK_STATE_SELECTED;
498 else
499 state = GTK_STATE_INSENSITIVE;
500
501 gtk_paint_flat_box( win->m_wxwindow->style,
502 GTK_PIZZA(win->m_wxwindow)->bin_window,
503 state,
504 GTK_SHADOW_NONE,
505 NULL,
506 win->m_wxwindow,
507 "treeview",
508 dc.LogicalToDeviceX(rect.x),
509 dc.LogicalToDeviceY(rect.y),
510 rect.width,
511 rect.height );
512 }
513
514 if (flags & wxCONTROL_CURRENT)
515 {
516 dc.SetPen( *wxBLACK_PEN );
517 dc.SetBrush( *wxTRANSPARENT_BRUSH );
518 dc.DrawRectangle( rect );
519 }
520 }