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