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