]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dcclient.cpp
Allow to not create wxPaintDC in EVT_PAINT handler in wxMSW.
[wxWidgets.git] / src / msw / dcclient.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/dcclient.cpp
3 // Purpose: wxClientDC class
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #include "wx/dcclient.h"
28 #include "wx/msw/dcclient.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/string.h"
32 #include "wx/log.h"
33 #include "wx/window.h"
34 #endif
35
36 #include "wx/msw/private.h"
37
38 // ----------------------------------------------------------------------------
39 // array/list types
40 // ----------------------------------------------------------------------------
41
42 struct WXDLLEXPORT wxPaintDCInfo
43 {
44 wxPaintDCInfo(wxWindow *win, wxPaintDCImpl *dc)
45 {
46 hwnd = win->GetHWND();
47 hdc = dc->GetHDC();
48 count = 1;
49 }
50
51 WXHWND hwnd; // window for this DC
52 WXHDC hdc; // the DC handle
53 size_t count; // usage count
54 };
55
56 #include "wx/arrimpl.cpp"
57
58 WX_DEFINE_OBJARRAY(wxArrayDCInfo)
59
60 // ----------------------------------------------------------------------------
61 // macros
62 // ----------------------------------------------------------------------------
63
64 // ----------------------------------------------------------------------------
65 // global variables
66 // ----------------------------------------------------------------------------
67
68 static PAINTSTRUCT g_paintStruct;
69
70 #ifdef wxHAS_PAINT_DEBUG
71 // a global variable which we check to verify that wxPaintDC are only
72 // created in response to WM_PAINT message - doing this from elsewhere is a
73 // common programming error among wxWidgets programmers and might lead to
74 // very subtle and difficult to debug refresh/repaint bugs.
75 int g_isPainting = 0;
76 #endif // wxHAS_PAINT_DEBUG
77
78 // ===========================================================================
79 // implementation
80 // ===========================================================================
81
82 // ----------------------------------------------------------------------------
83 // wxMSWWindowDCImpl
84 // ----------------------------------------------------------------------------
85
86 IMPLEMENT_ABSTRACT_CLASS(wxWindowDCImpl, wxMSWDCImpl)
87
88 wxWindowDCImpl::wxWindowDCImpl( wxDC *owner ) :
89 wxMSWDCImpl( owner )
90 {
91 }
92
93 wxWindowDCImpl::wxWindowDCImpl( wxDC *owner, wxWindow *window ) :
94 wxMSWDCImpl( owner )
95 {
96 wxCHECK_RET( window, wxT("invalid window in wxWindowDCImpl") );
97
98 m_window = window;
99 m_hDC = (WXHDC) ::GetWindowDC(GetHwndOf(m_window));
100
101 // m_bOwnsDC was already set to false in the base class ctor, so the DC
102 // will be released (and not deleted) in ~wxDC
103 InitDC();
104 }
105
106 void wxWindowDCImpl::InitDC()
107 {
108 // the background mode is only used for text background and is set in
109 // DrawText() to OPAQUE as required, otherwise always TRANSPARENT,
110 ::SetBkMode(GetHdc(), TRANSPARENT);
111
112 // since we are a window dc we need to grab the palette from the window
113 #if wxUSE_PALETTE
114 InitializePalette();
115 #endif
116 }
117
118 void wxWindowDCImpl::DoGetSize(int *width, int *height) const
119 {
120 wxCHECK_RET( m_window, wxT("wxWindowDCImpl without a window?") );
121
122 m_window->GetSize(width, height);
123 }
124
125 // ----------------------------------------------------------------------------
126 // wxClientDCImpl
127 // ----------------------------------------------------------------------------
128
129 IMPLEMENT_ABSTRACT_CLASS(wxClientDCImpl, wxWindowDCImpl)
130
131 wxClientDCImpl::wxClientDCImpl( wxDC *owner ) :
132 wxWindowDCImpl( owner )
133 {
134 }
135
136 wxClientDCImpl::wxClientDCImpl( wxDC *owner, wxWindow *window ) :
137 wxWindowDCImpl( owner )
138 {
139 wxCHECK_RET( window, wxT("invalid window in wxClientDCImpl") );
140
141 m_window = window;
142 m_hDC = (WXHDC)::GetDC(GetHwndOf(window));
143
144 // m_bOwnsDC was already set to false in the base class ctor, so the DC
145 // will be released (and not deleted) in ~wxDC
146
147 InitDC();
148 }
149
150 void wxClientDCImpl::InitDC()
151 {
152 wxWindowDCImpl::InitDC();
153
154 // in wxUniv build we must manually do some DC adjustments usually
155 // performed by Windows for us
156 //
157 // we also need to take the menu/toolbar manually into account under
158 // Windows CE because they're just another control there, not anything
159 // special as usually under Windows
160 #if defined(__WXUNIVERSAL__) || defined(__WXWINCE__)
161 wxPoint ptOrigin = m_window->GetClientAreaOrigin();
162 if ( ptOrigin.x || ptOrigin.y )
163 {
164 // no need to shift DC origin if shift is null
165 SetDeviceOrigin(ptOrigin.x, ptOrigin.y);
166 }
167
168 // clip the DC to avoid overwriting the non client area
169 wxSize size = m_window->GetClientSize();
170 DoSetClippingRegion(0, 0, size.x, size.y);
171 #endif // __WXUNIVERSAL__ || __WXWINCE__
172 }
173
174 wxClientDCImpl::~wxClientDCImpl()
175 {
176 }
177
178 void wxClientDCImpl::DoGetSize(int *width, int *height) const
179 {
180 wxCHECK_RET( m_window, wxT("wxClientDCImpl without a window?") );
181
182 m_window->GetClientSize(width, height);
183 }
184
185 // ----------------------------------------------------------------------------
186 // wxPaintDCImpl
187 // ----------------------------------------------------------------------------
188
189 // VZ: initial implementation (by JACS) only remembered the last wxPaintDC
190 // created and tried to reuse it - this was supposed to take care of a
191 // situation when a derived class OnPaint() calls base class OnPaint()
192 // because in this case ::BeginPaint() shouldn't be called second time.
193 //
194 // I'm not sure how useful this is, however we must remember the HWND
195 // associated with the last HDC as well - otherwise we may (and will!) try
196 // to reuse the HDC for another HWND which is a nice recipe for disaster.
197 //
198 // So we store a list of windows for which we already have the DC and not
199 // just one single hDC. This seems to work, but I'm really not sure about
200 // the usefullness of the whole idea - IMHO it's much better to not call
201 // base class OnPaint() at all, or, if we really want to allow it, add a
202 // "wxPaintDC *" parameter to wxPaintEvent which should be used if it's
203 // !NULL instead of creating a new DC.
204
205 IMPLEMENT_ABSTRACT_CLASS(wxPaintDCImpl, wxClientDCImpl)
206
207 wxArrayDCInfo wxPaintDCImpl::ms_cache;
208
209 wxPaintDCImpl::wxPaintDCImpl( wxDC *owner ) :
210 wxClientDCImpl( owner )
211 {
212 }
213
214 wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *window ) :
215 wxClientDCImpl( owner )
216 {
217 wxCHECK_RET( window, wxT("NULL canvas in wxPaintDCImpl ctor") );
218
219 #ifdef wxHAS_PAINT_DEBUG
220 if ( g_isPainting <= 0 )
221 {
222 wxFAIL_MSG( wxT("wxPaintDCImpl may be created only in EVT_PAINT handler!") );
223
224 return;
225 }
226 #endif // wxHAS_PAINT_DEBUG
227
228 m_window = window;
229
230 // do we have a DC for this window in the cache?
231 wxPaintDCInfo *info = FindInCache();
232 if ( info )
233 {
234 m_hDC = info->hdc;
235 info->count++;
236 }
237 else // not in cache, create a new one
238 {
239 // see comments in src/msw/window.cpp where this is defined
240 extern bool wxDidCreatePaintDC;
241
242 wxDidCreatePaintDC = true;
243
244 m_hDC = (WXHDC)::BeginPaint(GetHwndOf(m_window), &g_paintStruct);
245 if (m_hDC)
246 ms_cache.Add(new wxPaintDCInfo(m_window, this));
247 }
248
249 // Note: at this point m_hDC can be NULL under MicroWindows, when dragging.
250 if (!GetHDC())
251 return;
252
253 // (re)set the DC parameters.
254 InitDC();
255
256 // the HDC can have a clipping box (which we didn't set), make sure our
257 // DoGetClippingBox() checks for it
258 m_clipping = true;
259 }
260
261 wxPaintDCImpl::~wxPaintDCImpl()
262 {
263 if ( m_hDC )
264 {
265 SelectOldObjects(m_hDC);
266
267 size_t index;
268 wxPaintDCInfo *info = FindInCache(&index);
269
270 wxCHECK_RET( info, wxT("existing DC should have a cache entry") );
271
272 if ( --info->count == 0 )
273 {
274 ::EndPaint(GetHwndOf(m_window), &g_paintStruct);
275
276 ms_cache.RemoveAt(index);
277
278 // Reduce the number of bogus reports of non-freed memory
279 // at app exit
280 if (ms_cache.IsEmpty())
281 ms_cache.Clear();
282 }
283 //else: cached DC entry is still in use
284
285 // prevent the base class dtor from ReleaseDC()ing it again
286 m_hDC = 0;
287 }
288 }
289
290 wxPaintDCInfo *wxPaintDCImpl::FindInCache(size_t *index) const
291 {
292 wxPaintDCInfo *info = NULL;
293 size_t nCache = ms_cache.GetCount();
294 for ( size_t n = 0; n < nCache; n++ )
295 {
296 wxPaintDCInfo *info1 = &ms_cache[n];
297 if ( info1->hwnd == m_window->GetHWND() )
298 {
299 info = info1;
300 if ( index )
301 *index = n;
302 break;
303 }
304 }
305
306 return info;
307 }
308
309 // find the entry for this DC in the cache (keyed by the window)
310 WXHDC wxPaintDCImpl::FindDCInCache(wxWindow* win)
311 {
312 size_t nCache = ms_cache.GetCount();
313 for ( size_t n = 0; n < nCache; n++ )
314 {
315 wxPaintDCInfo *info = &ms_cache[n];
316 if ( info->hwnd == win->GetHWND() )
317 {
318 return info->hdc;
319 }
320 }
321 return 0;
322 }
323
324 /*
325 * wxPaintDCEx
326 */
327
328 // TODO: don't duplicate wxPaintDCImpl code here!!
329
330 class wxPaintDCExImpl: public wxPaintDCImpl
331 {
332 public:
333 wxPaintDCExImpl( wxDC *owner, wxWindow *window, WXHDC dc );
334 ~wxPaintDCExImpl();
335
336 int m_saveState;
337 };
338
339
340 IMPLEMENT_ABSTRACT_CLASS(wxPaintDCEx,wxPaintDC)
341
342 wxPaintDCEx::wxPaintDCEx(wxWindow *window, WXHDC dc)
343 : wxPaintDC(new wxPaintDCExImpl(this, window, dc))
344 {
345 }
346
347 wxPaintDCExImpl::wxPaintDCExImpl(wxDC *owner, wxWindow *window, WXHDC dc)
348 : wxPaintDCImpl( owner )
349 {
350 wxCHECK_RET( dc, wxT("wxPaintDCEx requires an existing device context") );
351
352 m_saveState = 0;
353
354 m_window = window;
355
356 wxPaintDCInfo *info = FindInCache();
357 if ( info )
358 {
359 m_hDC = info->hdc;
360 info->count++;
361 }
362 else // not in cache, create a new one
363 {
364 m_hDC = dc;
365 ms_cache.Add(new wxPaintDCInfo(m_window, this));
366 m_saveState = SaveDC((HDC) dc);
367 }
368 }
369
370 wxPaintDCExImpl::~wxPaintDCExImpl()
371 {
372 size_t index;
373 wxPaintDCInfo *info = FindInCache(&index);
374
375 wxCHECK_RET( info, wxT("existing DC should have a cache entry") );
376
377 if ( --info->count == 0 )
378 {
379 RestoreDC((HDC) m_hDC, m_saveState);
380 ms_cache.RemoveAt(index);
381
382 // Reduce the number of bogus reports of non-freed memory
383 // at app exit
384 if (ms_cache.IsEmpty())
385 ms_cache.Clear();
386 }
387 //else: cached DC entry is still in use
388
389 // prevent the base class dtor from ReleaseDC()ing it again
390 m_hDC = 0;
391 }
392
393