]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/renderer.cpp
Upport hint window improvement using custom GTK+ 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
37 // ----------------------------------------------------------------------------
38 // wxRendererGTK: our wxRendererNative implementation
39 // ----------------------------------------------------------------------------
40
41 class WXDLLEXPORT wxRendererGTK : public wxDelegateRendererNative
42 {
43 public:
44 // draw the header control button (used by wxListCtrl)
45 virtual int DrawHeaderButton(wxWindow *win,
46 wxDC& dc,
47 const wxRect& rect,
48 int flags = 0,
49 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
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 void DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags = 0);
95
96 virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
97
98 private:
99 // FIXME: shouldn't we destroy these windows somewhere?
100
101 // used by DrawPushButton and DrawDropArrow
102 static GtkWidget *GetButtonWidget();
103
104 // used by DrawTreeItemButton()
105 static GtkWidget *GetTreeWidget();
106
107 // used by DrawCheckBox()
108 static GtkWidget *GetCheckButtonWidget();
109
110 // Used by DrawHeaderButton
111 static GtkWidget *GetHeaderButtonWidget();
112 };
113
114 // ============================================================================
115 // implementation
116 // ============================================================================
117
118 /* static */
119 wxRendererNative& wxRendererNative::GetDefault()
120 {
121 static wxRendererGTK s_rendererGTK;
122
123 return s_rendererGTK;
124 }
125
126 // ----------------------------------------------------------------------------
127 // helper functions
128 // ----------------------------------------------------------------------------
129
130 GtkWidget *
131 wxRendererGTK::GetButtonWidget()
132 {
133 static GtkWidget *s_button = NULL;
134 static GtkWidget *s_window = NULL;
135
136 if ( !s_button )
137 {
138 s_window = gtk_window_new( GTK_WINDOW_POPUP );
139 gtk_widget_realize( s_window );
140 s_button = gtk_button_new();
141 gtk_container_add( GTK_CONTAINER(s_window), s_button );
142 gtk_widget_realize( s_button );
143 }
144
145 return s_button;
146 }
147
148 GtkWidget *
149 wxRendererGTK::GetCheckButtonWidget()
150 {
151 static GtkWidget *s_button = NULL;
152 static GtkWidget *s_window = NULL;
153
154 if ( !s_button )
155 {
156 s_window = gtk_window_new( GTK_WINDOW_POPUP );
157 gtk_widget_realize( s_window );
158 s_button = gtk_check_button_new();
159 gtk_container_add( GTK_CONTAINER(s_window), s_button );
160 gtk_widget_realize( s_button );
161 }
162
163 return s_button;
164 }
165
166 GtkWidget *
167 wxRendererGTK::GetTreeWidget()
168 {
169 static GtkWidget *s_tree = NULL;
170 static GtkWidget *s_window = NULL;
171
172 if ( !s_tree )
173 {
174 s_tree = gtk_tree_view_new();
175 s_window = gtk_window_new( GTK_WINDOW_POPUP );
176 gtk_widget_realize( s_window );
177 gtk_container_add( GTK_CONTAINER(s_window), s_tree );
178 gtk_widget_realize( s_tree );
179 }
180
181 return s_tree;
182 }
183
184
185 // This one just gets the button used by the column header. Although it's
186 // still a gtk_button the themes will typically differentiate and draw them
187 // differently if the button is in a treeview.
188 GtkWidget *
189 wxRendererGTK::GetHeaderButtonWidget()
190 {
191 static GtkWidget *s_button = NULL;
192
193 if ( !s_button )
194 {
195 // Get the dummy tree widget, give it a column, and then use the
196 // widget in the column header for the rendering code.
197 GtkWidget* treewidget = GetTreeWidget();
198 GtkTreeViewColumn* column = gtk_tree_view_column_new();
199 gtk_tree_view_append_column(GTK_TREE_VIEW(treewidget), column);
200 s_button = column->button;
201 }
202
203 return s_button;
204 }
205
206 // ----------------------------------------------------------------------------
207 // list/tree controls drawing
208 // ----------------------------------------------------------------------------
209
210 int
211 wxRendererGTK::DrawHeaderButton(wxWindow *win,
212 wxDC& dc,
213 const wxRect& rect,
214 int flags,
215 wxHeaderSortIconType sortArrow,
216 wxHeaderButtonParams* params)
217 {
218
219 GtkWidget *button = GetHeaderButtonWidget();
220
221 GdkWindow* gdk_window = NULL;
222 #if wxUSE_NEW_DC
223 wxImplDC *impl = dc.GetImpl();
224 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
225 if (gtk_impl)
226 gdk_window = gtk_impl->GetGDKWindow();
227 #else
228 gdk_window = dc.GetGDKWindow();
229 #endif
230 wxASSERT_MSG( gdk_window,
231 wxT("cannot use wxRendererNative on wxDC of this type") );
232
233 int x_diff = 0;
234 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
235 x_diff = rect.width;
236
237 GtkStateType state = GTK_STATE_NORMAL;
238 if (flags & wxCONTROL_DISABLED)
239 state = GTK_STATE_INSENSITIVE;
240 else
241 {
242 if (flags & wxCONTROL_CURRENT)
243 state = GTK_STATE_PRELIGHT;
244 }
245
246 gtk_paint_box
247 (
248 button->style,
249 gdk_window,
250 state,
251 GTK_SHADOW_OUT,
252 NULL,
253 button,
254 "button",
255 dc.LogicalToDeviceX(rect.x) - x_diff, rect.y, rect.width, rect.height
256 );
257
258 return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params);
259 }
260
261 // draw a ">" or "v" button
262 void
263 wxRendererGTK::DrawTreeItemButton(wxWindow* win,
264 wxDC& dc, const wxRect& rect, int flags)
265 {
266 GtkWidget *tree = GetTreeWidget();
267
268 GdkWindow* gdk_window = NULL;
269 #if wxUSE_NEW_DC
270 wxImplDC *impl = dc.GetImpl();
271 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
272 if (gtk_impl)
273 gdk_window = gtk_impl->GetGDKWindow();
274 #else
275 gdk_window = dc.GetGDKWindow();
276 #endif
277 wxASSERT_MSG( gdk_window,
278 wxT("cannot use wxRendererNative on wxDC of this type") );
279
280 GtkStateType state;
281 if ( flags & wxCONTROL_CURRENT )
282 state = GTK_STATE_PRELIGHT;
283 else
284 state = GTK_STATE_NORMAL;
285
286 int x_diff = 0;
287 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
288 x_diff = rect.width;
289
290 // VZ: I don't know how to get the size of the expander so as to centre it
291 // in the given rectangle, +2/3 below is just what looks good here...
292 gtk_paint_expander
293 (
294 tree->style,
295 gdk_window,
296 state,
297 NULL,
298 tree,
299 "treeview",
300 dc.LogicalToDeviceX(rect.x) + 6 - x_diff,
301 dc.LogicalToDeviceY(rect.y) + 3,
302 flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
303 : GTK_EXPANDER_COLLAPSED
304 );
305 }
306
307
308 // ----------------------------------------------------------------------------
309 // splitter sash drawing
310 // ----------------------------------------------------------------------------
311
312 static int GetGtkSplitterFullSize()
313 {
314 static GtkWidget *s_paned = NULL;
315 if (s_paned == NULL)
316 s_paned = gtk_vpaned_new();
317
318 gint handle_size;
319 gtk_widget_style_get (s_paned, "handle_size", &handle_size, NULL);
320
321 return handle_size;
322 }
323
324 wxSplitterRenderParams
325 wxRendererGTK::GetSplitterParams(const wxWindow *WXUNUSED(win))
326 {
327 // we don't draw any border, hence 0 for the second field
328 return wxSplitterRenderParams
329 (
330 GetGtkSplitterFullSize(),
331 0,
332 true // hot sensitive
333 );
334 }
335
336 void
337 wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
338 wxDC& WXUNUSED(dc),
339 const wxRect& WXUNUSED(rect),
340 int WXUNUSED(flags))
341 {
342 // nothing to do
343 }
344
345 void
346 wxRendererGTK::DrawSplitterSash(wxWindow *win,
347 wxDC& dc,
348 const wxSize& size,
349 wxCoord position,
350 wxOrientation orient,
351 int flags)
352 {
353 if ( !win->m_wxwindow->window )
354 {
355 // window not realized yet
356 return;
357 }
358
359 GdkWindow* gdk_window = NULL;
360 #if wxUSE_NEW_DC
361 wxImplDC *impl = dc.GetImpl();
362 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
363 if (gtk_impl)
364 gdk_window = gtk_impl->GetGDKWindow();
365 #else
366 gdk_window = dc.GetGDKWindow();
367 #endif
368 wxASSERT_MSG( gdk_window,
369 wxT("cannot use wxRendererNative on wxDC of this type") );
370
371 wxCoord full_size = GetGtkSplitterFullSize();
372
373 // are we drawing vertical or horizontal splitter?
374 const bool isVert = orient == wxVERTICAL;
375
376 GdkRectangle rect;
377
378 if ( isVert )
379 {
380 rect.x = position;
381 rect.y = 0;
382 rect.width = full_size;
383 rect.height = size.y;
384 }
385 else // horz
386 {
387 rect.x = 0;
388 rect.y = position;
389 rect.height = full_size;
390 rect.width = size.x;
391 }
392
393 int x_diff = 0;
394 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
395 x_diff = rect.width;
396
397 gtk_paint_handle
398 (
399 win->m_wxwindow->style,
400 gdk_window,
401 flags & wxCONTROL_CURRENT ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
402 GTK_SHADOW_NONE,
403 NULL /* no clipping */,
404 win->m_wxwindow,
405 "paned",
406 dc.LogicalToDeviceX(rect.x) - x_diff,
407 dc.LogicalToDeviceY(rect.y),
408 rect.width,
409 rect.height,
410 isVert ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL
411 );
412 }
413
414 void
415 wxRendererGTK::DrawDropArrow(wxWindow *WXUNUSED(win),
416 wxDC& dc,
417 const wxRect& rect,
418 int flags)
419 {
420 GtkWidget *button = GetButtonWidget();
421
422 // If we give GTK_PIZZA(win->m_wxwindow)->bin_window as
423 // a window for gtk_paint_xxx function, then it won't
424 // work for wxMemoryDC. So that is why we assume wxDC
425 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
426 // are derived from it) and use its m_window.
427 GdkWindow* gdk_window = NULL;
428 #if wxUSE_NEW_DC
429 wxImplDC *impl = dc.GetImpl();
430 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
431 if (gtk_impl)
432 gdk_window = gtk_impl->GetGDKWindow();
433 #else
434 gdk_window = dc.GetGDKWindow();
435 #endif
436 wxASSERT_MSG( gdk_window,
437 wxT("cannot use wxRendererNative on wxDC of this type") );
438
439 // draw arrow so that there is even space horizontally
440 // on both sides
441 int arrowX = rect.width/4 + 1;
442 int arrowWidth = rect.width - (arrowX*2);
443
444 // scale arrow's height accoording to the width
445 int arrowHeight = rect.width/3;
446 int arrowY = (rect.height-arrowHeight)/2 +
447 ((rect.height-arrowHeight) & 1);
448
449 GtkStateType state;
450
451 if ( flags & wxCONTROL_PRESSED )
452 state = GTK_STATE_ACTIVE;
453 else if ( flags & wxCONTROL_DISABLED )
454 state = GTK_STATE_INSENSITIVE;
455 else if ( flags & wxCONTROL_CURRENT )
456 state = GTK_STATE_PRELIGHT;
457 else
458 state = GTK_STATE_NORMAL;
459
460 // draw arrow on button
461 gtk_paint_arrow
462 (
463 button->style,
464 gdk_window,
465 state,
466 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
467 NULL,
468 button,
469 "arrow",
470 GTK_ARROW_DOWN,
471 FALSE,
472 rect.x + arrowX,
473 rect.y + arrowY,
474 arrowWidth,
475 arrowHeight
476 );
477 }
478
479 void
480 wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
481 wxDC& dc,
482 const wxRect& rect,
483 int flags)
484 {
485 DrawPushButton(win,dc,rect,flags);
486 DrawDropArrow(win,dc,rect);
487 }
488
489 void
490 wxRendererGTK::DrawCheckBox(wxWindow *WXUNUSED(win),
491 wxDC& dc,
492 const wxRect& rect,
493 int flags )
494 {
495 GtkWidget *button = GetCheckButtonWidget();
496
497 GdkWindow* gdk_window = NULL;
498 #if wxUSE_NEW_DC
499 wxImplDC *impl = dc.GetImpl();
500 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
501 if (gtk_impl)
502 gdk_window = gtk_impl->GetGDKWindow();
503 #else
504 gdk_window = dc.GetGDKWindow();
505 #endif
506 wxASSERT_MSG( gdk_window,
507 wxT("cannot use wxRendererNative on wxDC of this type") );
508
509 GtkStateType state;
510
511 if ( flags & wxCONTROL_PRESSED )
512 state = GTK_STATE_ACTIVE;
513 else if ( flags & wxCONTROL_DISABLED )
514 state = GTK_STATE_INSENSITIVE;
515 else if ( flags & wxCONTROL_CURRENT )
516 state = GTK_STATE_PRELIGHT;
517 else
518 state = GTK_STATE_NORMAL;
519
520 gtk_paint_check
521 (
522 button->style,
523 gdk_window,
524 state,
525 flags & wxCONTROL_CHECKED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
526 NULL,
527 button,
528 "cellcheck",
529 dc.LogicalToDeviceX(rect.x)+2,
530 dc.LogicalToDeviceY(rect.y)+3,
531 13, 13
532 );
533 }
534
535 void
536 wxRendererGTK::DrawPushButton(wxWindow *WXUNUSED(win),
537 wxDC& dc,
538 const wxRect& rect,
539 int flags)
540 {
541 GtkWidget *button = GetButtonWidget();
542
543 GdkWindow* gdk_window = NULL;
544 #if wxUSE_NEW_DC
545 wxImplDC *impl = dc.GetImpl();
546 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
547 if (gtk_impl)
548 gdk_window = gtk_impl->GetGDKWindow();
549 #else
550 gdk_window = dc.GetGDKWindow();
551 #endif
552 wxASSERT_MSG( gdk_window,
553 wxT("cannot use wxRendererNative on wxDC of this type") );
554
555 // draw button
556 GtkStateType state;
557
558 if ( flags & wxCONTROL_PRESSED )
559 state = GTK_STATE_ACTIVE;
560 else if ( flags & wxCONTROL_DISABLED )
561 state = GTK_STATE_INSENSITIVE;
562 else if ( flags & wxCONTROL_CURRENT )
563 state = GTK_STATE_PRELIGHT;
564 else
565 state = GTK_STATE_NORMAL;
566
567 gtk_paint_box
568 (
569 button->style,
570 gdk_window,
571 state,
572 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
573 NULL,
574 button,
575 "button",
576 rect.x, rect.y, rect.width, rect.height
577 );
578 }
579
580 void
581 wxRendererGTK::DrawItemSelectionRect(wxWindow *win,
582 wxDC& dc,
583 const wxRect& rect,
584 int flags )
585 {
586 GdkWindow* gdk_window = NULL;
587 #if wxUSE_NEW_DC
588 wxImplDC *impl = dc.GetImpl();
589 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
590 if (gtk_impl)
591 gdk_window = gtk_impl->GetGDKWindow();
592 #else
593 gdk_window = dc.GetGDKWindow();
594 #endif
595 wxASSERT_MSG( gdk_window,
596 wxT("cannot use wxRendererNative on wxDC of this type") );
597
598 int x_diff = 0;
599 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
600 x_diff = rect.width;
601
602 GtkStateType state;
603 if (flags & wxCONTROL_SELECTED)
604 {
605 // the wxCONTROL_FOCUSED state is deduced
606 // directly from the m_wxwindow by GTK+
607 state = GTK_STATE_SELECTED;
608
609 gtk_paint_flat_box( win->m_widget->style,
610 gdk_window,
611 state,
612 GTK_SHADOW_NONE,
613 NULL,
614 win->m_wxwindow,
615 "cell_even",
616 dc.LogicalToDeviceX(rect.x) - x_diff,
617 dc.LogicalToDeviceY(rect.y),
618 rect.width,
619 rect.height );
620 }
621 else // !wxCONTROL_SELECTED
622 {
623 state = GTK_STATE_NORMAL;
624 }
625
626 if ((flags & wxCONTROL_CURRENT) && (flags & wxCONTROL_FOCUSED))
627 {
628 gtk_paint_focus( win->m_widget->style,
629 gdk_window,
630 state,
631 NULL,
632 win->m_wxwindow,
633 "treeview",
634 dc.LogicalToDeviceX(rect.x),
635 dc.LogicalToDeviceY(rect.y),
636 rect.width,
637 rect.height );
638 }
639 }
640
641 void wxRendererGTK::DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
642 {
643 GdkWindow* gdk_window = NULL;
644 #if wxUSE_NEW_DC
645 wxImplDC *impl = dc.GetImpl();
646 wxGTKImplDC *gtk_impl = wxDynamicCast( impl, wxGTKImplDC );
647 if (gtk_impl)
648 gdk_window = gtk_impl->GetGDKWindow();
649 #else
650 gdk_window = dc.GetGDKWindow();
651 #endif
652 wxASSERT_MSG( gdk_window,
653 wxT("cannot use wxRendererNative on wxDC of this type") );
654
655 GtkStateType state;
656 if (flags & wxCONTROL_SELECTED)
657 state = GTK_STATE_SELECTED;
658 else
659 state = GTK_STATE_NORMAL;
660
661 gtk_paint_focus( win->m_widget->style,
662 gdk_window,
663 state,
664 NULL,
665 win->m_wxwindow,
666 NULL,
667 dc.LogicalToDeviceX(rect.x),
668 dc.LogicalToDeviceY(rect.y),
669 rect.width,
670 rect.height );
671 }