1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/nonownedwnd.cpp
3 // Purpose:     wxGTK implementation of wxNonOwnedWindow.
4 // Author:      Vadim Zeitlin
5 // Created:     2011-10-12
6 // Copyright:   (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 // ============================================================================
11 // declarations
12 // ============================================================================
13 
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17 
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20 
21 #ifdef __BORLANDC__
22     #pragma hdrstop
23 #endif
24 
25 #ifndef WX_PRECOMP
26     #include "wx/nonownedwnd.h"
27     #include "wx/dcclient.h"
28     #include "wx/dcmemory.h"
29     #include "wx/region.h"
30 #endif // WX_PRECOMP
31 
32 #include "wx/graphics.h"
33 
34 #include <gtk/gtk.h>
35 #include "wx/gtk/private/gtk2-compat.h"
36 
37 // ----------------------------------------------------------------------------
38 // wxNonOwnedWindowShapeImpl: base class for region and path-based classes.
39 // ----------------------------------------------------------------------------
40 
41 // This class provides behaviour common to both region and path-based
42 // implementations and defines SetShape() method and virtual dtor that can be
43 // called by wxNonOwnedWindow when it's realized leaving just the
44 // implementation of DoSetShape() to the derived classes.
45 class wxNonOwnedWindowShapeImpl : public wxEvtHandler
46 {
47 public:
wxNonOwnedWindowShapeImpl(wxWindow * win)48     wxNonOwnedWindowShapeImpl(wxWindow* win) : m_win(win)
49     {
50     }
51 
~wxNonOwnedWindowShapeImpl()52     virtual ~wxNonOwnedWindowShapeImpl() { }
53 
SetShape()54     bool SetShape()
55     {
56         if ( m_win->m_wxwindow )
57             SetShapeIfNonNull(gtk_widget_get_window(m_win->m_wxwindow));
58 
59         return SetShapeIfNonNull(gtk_widget_get_window(m_win->m_widget));
60     }
61 
62     // Must be overridden to indicate if the data object must stay around or if
63     // it can be deleted once SetShape() was called.
64     virtual bool CanBeDeleted() const = 0;
65 
66 protected:
67     wxWindow* const m_win;
68 
69 private:
70     // SetShape to the given GDK window by calling DoSetShape() if it's non-NULL.
SetShapeIfNonNull(GdkWindow * window)71     bool SetShapeIfNonNull(GdkWindow* window)
72     {
73         return window && DoSetShape(window);
74     }
75 
76     // SetShape the shape to the given GDK window which can be either the window
77     // of m_widget or m_wxwindow of the wxWindow we're used with.
78     virtual bool DoSetShape(GdkWindow* window) = 0;
79 
80     wxDECLARE_NO_COPY_CLASS(wxNonOwnedWindowShapeImpl);
81 };
82 
83 // Version not using any custom shape.
84 class wxNonOwnedWindowShapeImplNone : public wxNonOwnedWindowShapeImpl
85 {
86 public:
wxNonOwnedWindowShapeImplNone(wxWindow * win)87     wxNonOwnedWindowShapeImplNone(wxWindow* win) :
88         wxNonOwnedWindowShapeImpl(win)
89     {
90     }
91 
CanBeDeleted() const92     virtual bool CanBeDeleted() const { return true; }
93 
94 private:
DoSetShape(GdkWindow * window)95     virtual bool DoSetShape(GdkWindow* window)
96     {
97         gdk_window_shape_combine_region(window, NULL, 0, 0);
98 
99         return true;
100     }
101 };
102 
103 // Version using simple wxRegion.
104 class wxNonOwnedWindowShapeImplRegion : public wxNonOwnedWindowShapeImpl
105 {
106 public:
wxNonOwnedWindowShapeImplRegion(wxWindow * win,const wxRegion & region)107     wxNonOwnedWindowShapeImplRegion(wxWindow* win, const wxRegion& region) :
108         wxNonOwnedWindowShapeImpl(win),
109         m_region(region)
110     {
111     }
112 
CanBeDeleted() const113     virtual bool CanBeDeleted() const { return true; }
114 
115 private:
DoSetShape(GdkWindow * window)116     virtual bool DoSetShape(GdkWindow* window)
117     {
118         gdk_window_shape_combine_region(window, m_region.GetRegion(), 0, 0);
119 
120         return true;
121     }
122 
123     wxRegion m_region;
124 };
125 
126 #if wxUSE_GRAPHICS_CONTEXT
127 
128 // Version using more complex wxGraphicsPath.
129 class wxNonOwnedWindowShapeImplPath : public wxNonOwnedWindowShapeImpl
130 {
131 public:
wxNonOwnedWindowShapeImplPath(wxWindow * win,const wxGraphicsPath & path)132     wxNonOwnedWindowShapeImplPath(wxWindow* win, const wxGraphicsPath& path) :
133         wxNonOwnedWindowShapeImpl(win),
134         m_path(path),
135         m_mask(CreateShapeBitmap(path), *wxBLACK)
136     {
137 
138         m_win->Connect
139                (
140                 wxEVT_PAINT,
141                 wxPaintEventHandler(wxNonOwnedWindowShapeImplPath::OnPaint),
142                 NULL,
143                 this
144                );
145     }
146 
~wxNonOwnedWindowShapeImplPath()147     virtual ~wxNonOwnedWindowShapeImplPath()
148     {
149         m_win->Disconnect
150                (
151                 wxEVT_PAINT,
152                 wxPaintEventHandler(wxNonOwnedWindowShapeImplPath::OnPaint),
153                 NULL,
154                 this
155                );
156     }
157 
158     // Currently we always return false from here, if drawing the border
159     // becomes optional, we could return true if we don't need to draw it.
CanBeDeleted() const160     virtual bool CanBeDeleted() const { return false; }
161 
162 private:
CreateShapeBitmap(const wxGraphicsPath & path)163     wxBitmap CreateShapeBitmap(const wxGraphicsPath& path)
164     {
165         // Draw the path on a bitmap to get the mask we need.
166         //
167         // Notice that using monochrome bitmap here doesn't work because of an
168         // apparent wxGraphicsContext bug in wxGTK, so use a bitmap of screen
169         // depth even if this is wasteful.
170         wxBitmap bmp(m_win->GetSize());
171 
172         wxMemoryDC dc(bmp);
173 
174         dc.SetBackground(*wxBLACK);
175         dc.Clear();
176 
177 #ifdef __WXGTK3__
178         wxGraphicsContext* context = dc.GetGraphicsContext();
179 #else
180         wxScopedPtr<wxGraphicsContext> context(wxGraphicsContext::Create(dc));
181 #endif
182         context->SetBrush(*wxWHITE);
183         context->FillPath(path);
184 
185         return bmp;
186     }
187 
DoSetShape(GdkWindow * window)188     virtual bool DoSetShape(GdkWindow *window)
189     {
190         if (!m_mask)
191             return false;
192 
193 #ifdef __WXGTK3__
194         cairo_region_t* region = gdk_cairo_region_create_from_surface(m_mask);
195         gdk_window_shape_combine_region(window, region, 0, 0);
196         cairo_region_destroy(region);
197 #else
198         gdk_window_shape_combine_mask(window, m_mask, 0, 0);
199 #endif
200 
201         return true;
202     }
203 
204     // Draw a shaped window border.
OnPaint(wxPaintEvent & event)205     void OnPaint(wxPaintEvent& event)
206     {
207         event.Skip();
208 
209         wxPaintDC dc(m_win);
210 #ifdef __WXGTK3__
211         wxGraphicsContext* context = dc.GetGraphicsContext();
212 #else
213         wxScopedPtr<wxGraphicsContext> context(wxGraphicsContext::Create(dc));
214 #endif
215         context->SetPen(wxPen(*wxLIGHT_GREY, 2));
216         context->StrokePath(m_path);
217     }
218 
219     wxGraphicsPath m_path;
220     wxMask m_mask;
221 };
222 
223 #endif // wxUSE_GRAPHICS_CONTEXT
224 
225 // ============================================================================
226 // wxNonOwnedWindow implementation
227 // ============================================================================
228 
~wxNonOwnedWindow()229 wxNonOwnedWindow::~wxNonOwnedWindow()
230 {
231     delete m_shapeImpl;
232 }
233 
GTKHandleRealized()234 void wxNonOwnedWindow::GTKHandleRealized()
235 {
236     wxNonOwnedWindowBase::GTKHandleRealized();
237 
238     if ( m_shapeImpl )
239     {
240         m_shapeImpl->SetShape();
241 
242         // We can destroy wxNonOwnedWindowShapeImplRegion immediately but need
243         // to keep wxNonOwnedWindowShapeImplPath around as it draws the border
244         // on every repaint.
245         if ( m_shapeImpl->CanBeDeleted() )
246         {
247             delete m_shapeImpl;
248             m_shapeImpl = NULL;
249         }
250     }
251 }
252 
DoClearShape()253 bool wxNonOwnedWindow::DoClearShape()
254 {
255     if ( !m_shapeImpl )
256     {
257         // Nothing to do, we don't have any custom shape.
258         return true;
259     }
260 
261     if ( gtk_widget_get_realized(m_widget) )
262     {
263         // Reset the existing shape immediately.
264         wxNonOwnedWindowShapeImplNone data(this);
265         data.SetShape();
266     }
267     //else: just do nothing, deleting m_shapeImpl is enough to ensure that we
268     // don't set the custom shape later when we're realized.
269 
270     delete m_shapeImpl;
271     m_shapeImpl = NULL;
272 
273     return true;
274 }
275 
DoSetRegionShape(const wxRegion & region)276 bool wxNonOwnedWindow::DoSetRegionShape(const wxRegion& region)
277 {
278     // In any case get rid of the old data.
279     delete m_shapeImpl;
280     m_shapeImpl = NULL;
281 
282     if ( gtk_widget_get_realized(m_widget) )
283     {
284         // We can avoid an unnecessary heap allocation and just set the shape
285         // immediately.
286         wxNonOwnedWindowShapeImplRegion data(this, region);
287         return data.SetShape();
288     }
289     else // Create an object that will set shape when we're realized.
290     {
291         m_shapeImpl = new wxNonOwnedWindowShapeImplRegion(this, region);
292 
293         // In general we don't know whether we are going to succeed or not, so
294         // be optimistic.
295         return true;
296     }
297 }
298 
299 #if wxUSE_GRAPHICS_CONTEXT
300 
DoSetPathShape(const wxGraphicsPath & path)301 bool wxNonOwnedWindow::DoSetPathShape(const wxGraphicsPath& path)
302 {
303     // The logic here is simpler than above because we always create
304     // wxNonOwnedWindowShapeImplPath on the heap as we must keep it around,
305     // even if we're already realized
306 
307     delete m_shapeImpl;
308     m_shapeImpl = new wxNonOwnedWindowShapeImplPath(this, path);
309 
310     if ( gtk_widget_get_realized(m_widget) )
311     {
312         return m_shapeImpl->SetShape();
313     }
314     //else: will be done later from GTKHandleRealized().
315 
316     return true;
317 }
318 
319 #endif // wxUSE_GRAPHICS_CONTEXT
320