Added wxNonOwnedWindow::SetShape(wxGraphicsPath).
[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 // RCS-ID: $Id: wxhead.cpp,v 1.11 2010-04-22 12:44:51 zeitlin Exp $
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
27 #include "wx/nonownedwnd.h"
28 #include "wx/region.h"
29 #endif // WX_PRECOMP
30
31 #include "wx/gtk/private.h"
32
33 #include <gdk/gdk.h>
34
35 #include "wx/graphics.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_mask(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 wxScopedPtr<wxGraphicsContext> context(wxGraphicsContext::Create(dc));
178 context->SetBrush(*wxWHITE);
179 context->FillPath(path);
180
181 return bmp;
182 }
183
184 virtual bool DoSetShape(GdkWindow *window)
185 {
186 GdkBitmap* bitmap = m_mask.GetBitmap();
187 if ( !bitmap )
188 return false;
189
190 gdk_window_shape_combine_mask(window, bitmap, 0, 0);
191
192 return true;
193 }
194
195 // Draw a shaped window border.
196 void OnPaint(wxPaintEvent& event)
197 {
198 event.Skip();
199
200 wxPaintDC dc(m_win);
201 wxScopedPtr<wxGraphicsContext> context(wxGraphicsContext::Create(dc));
202 context->SetPen(wxPen(*wxLIGHT_GREY, 2));
203 context->StrokePath(m_path);
204 }
205
206 wxGraphicsPath m_path;
207 wxMask m_mask;
208 };
209
210 #endif // wxUSE_GRAPHICS_CONTEXT
211
212 // ============================================================================
213 // wxNonOwnedWindow implementation
214 // ============================================================================
215
216 wxNonOwnedWindow::~wxNonOwnedWindow()
217 {
218 delete m_shapeImpl;
219 }
220
221 void wxNonOwnedWindow::GTKHandleRealized()
222 {
223 wxNonOwnedWindowBase::GTKHandleRealized();
224
225 if ( m_shapeImpl )
226 {
227 m_shapeImpl->SetShape();
228
229 // We can destroy wxNonOwnedWindowShapeImplRegion immediately but need
230 // to keep wxNonOwnedWindowShapeImplPath around as it draws the border
231 // on every repaint.
232 if ( m_shapeImpl->CanBeDeleted() )
233 {
234 delete m_shapeImpl;
235 m_shapeImpl = NULL;
236 }
237 }
238 }
239
240 bool wxNonOwnedWindow::DoClearShape()
241 {
242 if ( !m_shapeImpl )
243 {
244 // Nothing to do, we don't have any custom shape.
245 return true;
246 }
247
248 if ( gtk_widget_get_realized(m_widget) )
249 {
250 // Reset the existing shape immediately.
251 wxNonOwnedWindowShapeImplNone data(this);
252 data.SetShape();
253 }
254 //else: just do nothing, deleting m_shapeImpl is enough to ensure that we
255 // don't set the custom shape later when we're realized.
256
257 delete m_shapeImpl;
258 m_shapeImpl = NULL;
259
260 return true;
261 }
262
263 bool wxNonOwnedWindow::DoSetRegionShape(const wxRegion& region)
264 {
265 // In any case get rid of the old data.
266 delete m_shapeImpl;
267 m_shapeImpl = NULL;
268
269 if ( gtk_widget_get_realized(m_widget) )
270 {
271 // We can avoid an unnecessary heap allocation and just set the shape
272 // immediately.
273 wxNonOwnedWindowShapeImplRegion data(this, region);
274 return data.SetShape();
275 }
276 else // Create an object that will set shape when we're realized.
277 {
278 m_shapeImpl = new wxNonOwnedWindowShapeImplRegion(this, region);
279
280 // In general we don't know whether we are going to succeed or not, so
281 // be optimistic.
282 return true;
283 }
284 }
285
286 #if wxUSE_GRAPHICS_CONTEXT
287
288 bool wxNonOwnedWindow::DoSetPathShape(const wxGraphicsPath& path)
289 {
290 // The logic here is simpler than above because we always create
291 // wxNonOwnedWindowShapeImplPath on the heap as we must keep it around,
292 // even if we're already realized
293
294 delete m_shapeImpl;
295 m_shapeImpl = new wxNonOwnedWindowShapeImplPath(this, path);
296
297 if ( gtk_widget_get_realized(m_widget) )
298 {
299 return m_shapeImpl->SetShape();
300 }
301 //else: will be done later from GTKHandleRealized().
302
303 return true;
304 }
305
306 #endif // wxUSE_GRAPHICS_CONTEXT