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