1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk1/renderer.cpp
3 // Purpose:     implementation of wxRendererNative for wxGTK
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     20.07.2003
7 // Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence:     wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21 
22 
23 #include "wx/renderer.h"
24 
25 #ifndef WX_PRECOMP
26     #include "wx/window.h"
27     #include "wx/dc.h"
28 #endif
29 
30 #include <gtk/gtk.h>
31 #include "wx/gtk1/win_gtk.h"
32 #include "wx/gtk1/dcclient.h"
33 
34 // RR: After a correction to the orientation of the sash
35 //     this doesn't seem to be required anymore and it
36 //     seems to confuse some themes so USE_ERASE_RECT=0
37 #define USE_ERASE_RECT 0
38 
39 // ----------------------------------------------------------------------------
40 // wxRendererGTK: our wxRendererNative implementation
41 // ----------------------------------------------------------------------------
42 
43 class WXDLLEXPORT wxRendererGTK : public wxDelegateRendererNative
44 {
45 public:
46     // draw the header control button (used by wxListCtrl)
47     virtual int DrawHeaderButton(wxWindow *win,
48                                  wxDC& dc,
49                                  const wxRect& rect,
50                                  int flags = 0,
51                                  wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
52                                  wxHeaderButtonParams* params=NULL);
53 
54     virtual void DrawSplitterBorder(wxWindow *win,
55                                     wxDC& dc,
56                                     const wxRect& rect,
57                                     int flags = 0);
58     virtual void DrawSplitterSash(wxWindow *win,
59                                   wxDC& dc,
60                                   const wxSize& size,
61                                   wxCoord position,
62                                   wxOrientation orient,
63                                   int flags = 0);
64 
65     virtual void DrawComboBoxDropButton(wxWindow *win,
66                                         wxDC& dc,
67                                         const wxRect& rect,
68                                         int flags = 0);
69 
70     virtual void DrawDropArrow(wxWindow *win,
71                                wxDC& dc,
72                                const wxRect& rect,
73                                int flags = 0);
74 
75     virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
76 
77 private:
78     // FIXME: shouldn't we destroy these windows somewhere?
79 
80     // used by DrawHeaderButton and DrawComboBoxDropButton
81     static GtkWidget *GetButtonWidget();
82 };
83 
84 // ============================================================================
85 // implementation
86 // ============================================================================
87 
88 /* static */
GetDefault()89 wxRendererNative& wxRendererNative::GetDefault()
90 {
91     static wxRendererGTK s_rendererGTK;
92 
93     return s_rendererGTK;
94 }
95 
96 // ----------------------------------------------------------------------------
97 // helper functions
98 // ----------------------------------------------------------------------------
99 
100 GtkWidget *
GetButtonWidget()101 wxRendererGTK::GetButtonWidget()
102 {
103     static GtkWidget *s_button = NULL;
104     static GtkWidget *s_window = NULL;
105 
106     if ( !s_button )
107     {
108         s_window = gtk_window_new( GTK_WINDOW_POPUP );
109         gtk_widget_realize( s_window );
110         s_button = gtk_button_new();
111         gtk_container_add( GTK_CONTAINER(s_window), s_button );
112         gtk_widget_realize( s_button );
113     }
114 
115     return s_button;
116 }
117 
118 // ----------------------------------------------------------------------------
119 // list/tree controls drawing
120 // ----------------------------------------------------------------------------
121 
122 int
DrawHeaderButton(wxWindow * win,wxDC & dc,const wxRect & rect,int flags,wxHeaderSortIconType WXUNUSED (sortArrow),wxHeaderButtonParams * WXUNUSED (params))123 wxRendererGTK::DrawHeaderButton(wxWindow *win,
124                                 wxDC& dc,
125                                 const wxRect& rect,
126                                 int flags,
127                                 wxHeaderSortIconType WXUNUSED(sortArrow),
128                                 wxHeaderButtonParams* WXUNUSED(params))
129 {
130 
131     GtkWidget *button = GetButtonWidget();
132 
133     gtk_paint_box
134     (
135         button->style,
136         // FIXME: I suppose GTK_PIZZA(win->m_wxwindow)->bin_window doesn't work with wxMemoryDC.
137         //   Maybe use code similar as in DrawComboBoxDropButton below?
138         GTK_PIZZA(win->m_wxwindow)->bin_window,
139         flags & wxCONTROL_DISABLED ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL,
140         GTK_SHADOW_OUT,
141         NULL,
142         button,
143         "button",
144         dc.LogicalToDeviceX(rect.x) -1, rect.y -1, rect.width +2, rect.height +2
145     );
146 
147     return rect.width + 2;
148 }
149 
150 // ----------------------------------------------------------------------------
151 // splitter sash drawing
152 // ----------------------------------------------------------------------------
153 
154 // the full sash width (should be even)
155 static const wxCoord SASH_SIZE = 8;
156 
157 // margin around the sash
158 static const wxCoord SASH_MARGIN = 2;
159 
GetGtkSplitterFullSize()160 static int GetGtkSplitterFullSize()
161 {
162     return SASH_SIZE + SASH_MARGIN;
163 }
164 
165 wxSplitterRenderParams
GetSplitterParams(const wxWindow * WXUNUSED (win))166 wxRendererGTK::GetSplitterParams(const wxWindow *WXUNUSED(win))
167 {
168     // we don't draw any border, hence 0 for the second field
169     return wxSplitterRenderParams
170            (
171                GetGtkSplitterFullSize(),
172                0,
173                false    // not
174            );
175 }
176 
177 void
DrawSplitterBorder(wxWindow * WXUNUSED (win),wxDC & WXUNUSED (dc),const wxRect & WXUNUSED (rect),int WXUNUSED (flags))178 wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
179                                   wxDC& WXUNUSED(dc),
180                                   const wxRect& WXUNUSED(rect),
181                                   int WXUNUSED(flags))
182 {
183     // nothing to do
184 }
185 
186 void
DrawSplitterSash(wxWindow * win,wxDC & WXUNUSED (dc),const wxSize & size,wxCoord position,wxOrientation orient,int WXUNUSED (flags))187 wxRendererGTK::DrawSplitterSash(wxWindow *win,
188                                 wxDC& WXUNUSED(dc),
189                                 const wxSize& size,
190                                 wxCoord position,
191                                 wxOrientation orient,
192                                 int WXUNUSED(flags))
193 {
194     if ( !win->m_wxwindow->window )
195     {
196         // window not realized yet
197         return;
198     }
199 
200     wxCoord full_size = GetGtkSplitterFullSize();
201 
202     // are we drawing vertical or horizontal splitter?
203     const bool isVert = orient == wxVERTICAL;
204 
205     GdkRectangle rect;
206 #if USE_ERASE_RECT
207     GdkRectangle erase_rect;
208 #endif
209 
210     if ( isVert )
211     {
212         int h = win->GetClientSize().GetHeight();
213 
214         rect.x = position;
215         rect.y = 0;
216         rect.width = full_size;
217         rect.height = h;
218 
219 #if USE_ERASE_RECT
220         erase_rect.x = position;
221         erase_rect.y = 0;
222         erase_rect.width = full_size;
223         erase_rect.height = h;
224 #endif
225     }
226     else // horz
227     {
228         int w = win->GetClientSize().GetWidth();
229 
230         rect.x = 0;
231         rect.y = position;
232         rect.height = full_size;
233         rect.width = w;
234 
235 #if USE_ERASE_RECT
236         erase_rect.y = position;
237         erase_rect.x = 0;
238         erase_rect.height = full_size;
239         erase_rect.width = w;
240 #endif
241     }
242 
243 #if USE_ERASE_RECT
244     // we must erase everything first, otherwise the garbage
245     // from the old sash is left when dragging it
246     gtk_paint_flat_box
247     (
248         win->m_wxwindow->style,
249         GTK_PIZZA(win->m_wxwindow)->bin_window,
250         GTK_STATE_NORMAL,
251         GTK_SHADOW_NONE,
252         NULL,
253         win->m_wxwindow,
254         (char *)"viewportbin", // const_cast
255         erase_rect.x,
256         erase_rect.y,
257         erase_rect.width,
258         erase_rect.height
259     );
260 #endif
261 
262 
263     // leave some margin before sash itself
264     position += SASH_MARGIN / 2;
265 
266     // and finally draw it using GTK paint functions
267     typedef void (*GtkPaintLineFunc)(GtkStyle *, GdkWindow *,
268                                                 GtkStateType,
269                                                 GdkRectangle *, GtkWidget *,
270                                                 gchar *,
271                                                 gint, gint, gint);
272 
273     GtkPaintLineFunc func = isVert ? gtk_paint_vline : gtk_paint_hline;
274 
275     (*func)
276     (
277         win->m_wxwindow->style,
278         GTK_PIZZA(win->m_wxwindow)->bin_window,
279         GTK_STATE_NORMAL,
280         NULL,
281         win->m_wxwindow,
282         (char *)"paned", // const_cast
283         0, isVert ? size.y : size.x, position + SASH_SIZE / 2 - 1
284     );
285 
286     gtk_paint_box
287     (
288         win->m_wxwindow->style,
289         GTK_PIZZA(win->m_wxwindow)->bin_window,
290         GTK_STATE_NORMAL,
291         GTK_SHADOW_OUT,
292         NULL,
293         win->m_wxwindow,
294         (char *)"paned", // const_cast
295         isVert ? position : size.x - 2*SASH_SIZE,
296         isVert ? size.y - 2*SASH_SIZE : position,
297         SASH_SIZE, SASH_SIZE
298     );
299 }
300 
301 void
DrawDropArrow(wxWindow * WXUNUSED (win),wxDC & dc,const wxRect & rect,int flags)302 wxRendererGTK::DrawDropArrow(wxWindow *WXUNUSED(win),
303                              wxDC& dc,
304                              const wxRect& rect,
305                              int flags)
306 {
307     GtkWidget *button = GetButtonWidget();
308 
309     // If we give GTK_PIZZA(win->m_wxwindow)->bin_window as
310     // a window for gtk_paint_xxx function, then it won't
311     // work for wxMemoryDC. So that is why we assume wxDC
312     // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
313     // are derived from it) and use its m_window.
314     wxWindowDCImpl * const impl = wxDynamicCast(dc.GetImpl(), wxWindowDCImpl);
315     wxCHECK_RET( impl, "must have a window DC" );
316 
317     GdkWindow* gdk_window = impl->GetGDKWindow();
318 
319     // draw arrow so that there is even space horizontally
320     // on both sides
321     int arrowX = rect.width/4 + 1;
322     int arrowWidth = rect.width - (arrowX*2);
323 
324     // scale arrow's height accoording to the width
325     int arrowHeight = rect.width/3;
326     int arrowY = (rect.height-arrowHeight)/2 +
327                  ((rect.height-arrowHeight) & 1);
328 
329     GtkStateType state;
330 
331     if ( flags & wxCONTROL_PRESSED )
332         state = GTK_STATE_ACTIVE;
333     else if ( flags & wxCONTROL_DISABLED )
334         state = GTK_STATE_INSENSITIVE;
335     else if ( flags & wxCONTROL_CURRENT )
336         state = GTK_STATE_PRELIGHT;
337     else
338         state = GTK_STATE_NORMAL;
339 
340     // draw arrow on button
341     gtk_paint_arrow
342     (
343         button->style,
344         gdk_window,
345         state,
346         flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
347         NULL,
348         button,
349         "arrow",
350         GTK_ARROW_DOWN,
351         FALSE,
352         rect.x + arrowX,
353         rect.y + arrowY,
354         arrowWidth,
355         arrowHeight
356     );
357 }
358 
359 void
DrawComboBoxDropButton(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)360 wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
361                                       wxDC& dc,
362                                       const wxRect& rect,
363                                       int flags)
364 {
365     GtkWidget *button = GetButtonWidget();
366 
367     // for reason why we do this, see DrawDropArrow
368     wxWindowDCImpl * const impl = wxDynamicCast(dc.GetImpl(), wxWindowDCImpl);
369     wxCHECK_RET( impl, "must have a window DC" );
370 
371     GdkWindow* gdk_window = impl->GetGDKWindow();
372 
373     // draw button
374     GtkStateType state;
375 
376     if ( flags & wxCONTROL_PRESSED )
377         state = GTK_STATE_ACTIVE;
378     else if ( flags & wxCONTROL_DISABLED )
379         state = GTK_STATE_INSENSITIVE;
380     else if ( flags & wxCONTROL_CURRENT )
381         state = GTK_STATE_PRELIGHT;
382     else
383         state = GTK_STATE_NORMAL;
384 
385     gtk_paint_box
386     (
387         button->style,
388         gdk_window,
389         state,
390         flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
391         NULL,
392         button,
393         "button",
394         rect.x, rect.y, rect.width, rect.height
395     );
396 
397     // draw arrow on button
398     DrawDropArrow(win,dc,rect,flags);
399 
400 }
401