Don't hide the window in its dtor in wxGTK.
[wxWidgets.git] / src / gtk / nonownedwnd.cpp
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:
48 wxNonOwnedWindowShapeImpl(wxWindow* win) : m_win(win)
49 {
50 }
51
52 virtual ~wxNonOwnedWindowShapeImpl() { }
53
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.
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:
87 wxNonOwnedWindowShapeImplNone(wxWindow* win) :
88 wxNonOwnedWindowShapeImpl(win)
89 {
90 }
91
92 virtual bool CanBeDeleted() const { return true; }
93
94 private:
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:
107 wxNonOwnedWindowShapeImplRegion(wxWindow* win, const wxRegion& region) :
108 wxNonOwnedWindowShapeImpl(win),
109 m_region(region)
110 {
111 }
112
113 virtual bool CanBeDeleted() const { return true; }
114
115 private:
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:
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
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.
160 virtual bool CanBeDeleted() const { return false; }
161
162 private:
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
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.
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
229 wxNonOwnedWindow::~wxNonOwnedWindow()
230 {
231 delete m_shapeImpl;
232 }
233
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
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
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
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