]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dcclient.cpp
Fix pulsing of bitmaps in focused buttons under Windows 7.
[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/hashmap.h"
33 #include "wx/log.h"
34 #include "wx/window.h"
35 #endif
36
37 #include "wx/msw/private.h"
38
39 // ----------------------------------------------------------------------------
40 // local data structures
41 // ----------------------------------------------------------------------------
42
43 // This is a base class for two concrete subclasses below and contains HDC
44 // cached for the duration of the WM_PAINT processing together with some
45 // bookkeeping information.
46 class wxPaintDCInfo
47 {
48 public:
49 wxPaintDCInfo(HDC hdc)
50 : m_hdc(hdc)
51 {
52 }
53
54 // The derived class must perform some cleanup.
55 virtual ~wxPaintDCInfo() = 0;
56
57 WXHDC GetHDC() const { return (WXHDC)m_hdc; }
58
59 protected:
60 const HDC m_hdc;
61
62 wxDECLARE_NO_COPY_CLASS(wxPaintDCInfo);
63 };
64
65 namespace
66 {
67
68 // This subclass contains information for the HDCs we create ourselves, i.e.
69 // those for which we call BeginPaint() -- and hence need to call EndPaint().
70 class wxPaintDCInfoOur : public wxPaintDCInfo
71 {
72 public:
73 wxPaintDCInfoOur(wxWindow* win)
74 : wxPaintDCInfo(::BeginPaint(GetHwndOf(win), GetPaintStructPtr(m_ps))),
75 m_hwnd(GetHwndOf(win))
76 {
77 }
78
79 virtual ~wxPaintDCInfoOur()
80 {
81 ::EndPaint(m_hwnd, &m_ps);
82 }
83
84 private:
85 // This helper is only needed in order to call it from the ctor initializer
86 // list.
87 static PAINTSTRUCT* GetPaintStructPtr(PAINTSTRUCT& ps)
88 {
89 wxZeroMemory(ps);
90 return &ps;
91 }
92
93 const HWND m_hwnd;
94 PAINTSTRUCT m_ps;
95
96 wxDECLARE_NO_COPY_CLASS(wxPaintDCInfoOur);
97 };
98
99 // This subclass contains information for the HDCs we receive from outside, as
100 // WPARAM of WM_PAINT itself.
101 class wxPaintDCInfoExternal : public wxPaintDCInfo
102 {
103 public:
104 wxPaintDCInfoExternal(HDC hdc)
105 : wxPaintDCInfo(hdc),
106 m_state(::SaveDC(hdc))
107 {
108 }
109
110 virtual ~wxPaintDCInfoExternal()
111 {
112 ::RestoreDC(m_hdc, m_state);
113 }
114
115 private:
116 const int m_state;
117
118 wxDECLARE_NO_COPY_CLASS(wxPaintDCInfoExternal);
119 };
120
121 // The global map containing HDC to use for the given window. The entries in
122 // this map only exist during WM_PAINT processing and are destroyed when it is
123 // over.
124 //
125 // It is needed because in some circumstances it can happen that more than one
126 // wxPaintDC is created for the same window during its WM_PAINT handling (and
127 // as this can happen implicitly, e.g. by calling a function in some library,
128 // this can be quite difficult to find) but we need to reuse the same HDC for
129 // all of them because we can't call BeginPaint() more than once. So we cache
130 // the first HDC created for the window in this map and then reuse it later if
131 // needed. And, of course, remove it from the map when the painting is done.
132 WX_DECLARE_HASH_MAP(wxWindow *, wxPaintDCInfo *,
133 wxPointerHash, wxPointerEqual,
134 PaintDCInfos);
135
136 PaintDCInfos gs_PaintDCInfos;
137
138 } // anonymous namespace
139
140 // ----------------------------------------------------------------------------
141 // global variables
142 // ----------------------------------------------------------------------------
143
144 #ifdef wxHAS_PAINT_DEBUG
145 // a global variable which we check to verify that wxPaintDC are only
146 // created in response to WM_PAINT message - doing this from elsewhere is a
147 // common programming error among wxWidgets programmers and might lead to
148 // very subtle and difficult to debug refresh/repaint bugs.
149 int g_isPainting = 0;
150 #endif // wxHAS_PAINT_DEBUG
151
152 // ===========================================================================
153 // implementation
154 // ===========================================================================
155
156 // ----------------------------------------------------------------------------
157 // wxMSWWindowDCImpl
158 // ----------------------------------------------------------------------------
159
160 IMPLEMENT_ABSTRACT_CLASS(wxWindowDCImpl, wxMSWDCImpl)
161
162 wxWindowDCImpl::wxWindowDCImpl( wxDC *owner ) :
163 wxMSWDCImpl( owner )
164 {
165 }
166
167 wxWindowDCImpl::wxWindowDCImpl( wxDC *owner, wxWindow *window ) :
168 wxMSWDCImpl( owner )
169 {
170 wxCHECK_RET( window, wxT("invalid window in wxWindowDCImpl") );
171
172 m_window = window;
173 m_hDC = (WXHDC) ::GetWindowDC(GetHwndOf(m_window));
174
175 // m_bOwnsDC was already set to false in the base class ctor, so the DC
176 // will be released (and not deleted) in ~wxDC
177 InitDC();
178 }
179
180 void wxWindowDCImpl::InitDC()
181 {
182 // the background mode is only used for text background and is set in
183 // DrawText() to OPAQUE as required, otherwise always TRANSPARENT,
184 ::SetBkMode(GetHdc(), TRANSPARENT);
185
186 // since we are a window dc we need to grab the palette from the window
187 #if wxUSE_PALETTE
188 InitializePalette();
189 #endif
190 }
191
192 void wxWindowDCImpl::DoGetSize(int *width, int *height) const
193 {
194 wxCHECK_RET( m_window, wxT("wxWindowDCImpl without a window?") );
195
196 m_window->GetSize(width, height);
197 }
198
199 // ----------------------------------------------------------------------------
200 // wxClientDCImpl
201 // ----------------------------------------------------------------------------
202
203 IMPLEMENT_ABSTRACT_CLASS(wxClientDCImpl, wxWindowDCImpl)
204
205 wxClientDCImpl::wxClientDCImpl( wxDC *owner ) :
206 wxWindowDCImpl( owner )
207 {
208 }
209
210 wxClientDCImpl::wxClientDCImpl( wxDC *owner, wxWindow *window ) :
211 wxWindowDCImpl( owner )
212 {
213 wxCHECK_RET( window, wxT("invalid window in wxClientDCImpl") );
214
215 m_window = window;
216 m_hDC = (WXHDC)::GetDC(GetHwndOf(window));
217
218 // m_bOwnsDC was already set to false in the base class ctor, so the DC
219 // will be released (and not deleted) in ~wxDC
220
221 InitDC();
222 }
223
224 void wxClientDCImpl::InitDC()
225 {
226 wxWindowDCImpl::InitDC();
227
228 // in wxUniv build we must manually do some DC adjustments usually
229 // performed by Windows for us
230 //
231 // we also need to take the menu/toolbar manually into account under
232 // Windows CE because they're just another control there, not anything
233 // special as usually under Windows
234 #if defined(__WXUNIVERSAL__) || defined(__WXWINCE__)
235 wxPoint ptOrigin = m_window->GetClientAreaOrigin();
236 if ( ptOrigin.x || ptOrigin.y )
237 {
238 // no need to shift DC origin if shift is null
239 SetDeviceOrigin(ptOrigin.x, ptOrigin.y);
240 }
241
242 // clip the DC to avoid overwriting the non client area
243 wxSize size = m_window->GetClientSize();
244 DoSetClippingRegion(0, 0, size.x, size.y);
245 #endif // __WXUNIVERSAL__ || __WXWINCE__
246 }
247
248 wxClientDCImpl::~wxClientDCImpl()
249 {
250 }
251
252 void wxClientDCImpl::DoGetSize(int *width, int *height) const
253 {
254 wxCHECK_RET( m_window, wxT("wxClientDCImpl without a window?") );
255
256 m_window->GetClientSize(width, height);
257 }
258
259 // ----------------------------------------------------------------------------
260 // wxPaintDCImpl
261 // ----------------------------------------------------------------------------
262
263 IMPLEMENT_ABSTRACT_CLASS(wxPaintDCImpl, wxClientDCImpl)
264
265 wxPaintDCImpl::wxPaintDCImpl( wxDC *owner ) :
266 wxClientDCImpl( owner )
267 {
268 }
269
270 wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *window ) :
271 wxClientDCImpl( owner )
272 {
273 wxCHECK_RET( window, wxT("NULL canvas in wxPaintDCImpl ctor") );
274
275 #ifdef wxHAS_PAINT_DEBUG
276 if ( g_isPainting <= 0 )
277 {
278 wxFAIL_MSG( wxT("wxPaintDCImpl may be created only in EVT_PAINT handler!") );
279
280 return;
281 }
282 #endif // wxHAS_PAINT_DEBUG
283
284 // see comments in src/msw/window.cpp where this is defined
285 extern bool wxDidCreatePaintDC;
286
287 wxDidCreatePaintDC = true;
288
289
290 m_window = window;
291
292 // do we have a DC for this window in the cache?
293 m_hDC = FindDCInCache(m_window);
294 if ( !m_hDC )
295 {
296 // not in cache, create a new one
297 wxPaintDCInfoOur* const info = new wxPaintDCInfoOur(m_window);
298 gs_PaintDCInfos[m_window] = info;
299 m_hDC = info->GetHDC();
300 }
301
302 // Note: at this point m_hDC can be NULL under MicroWindows, when dragging.
303 if (!GetHDC())
304 return;
305
306 // (re)set the DC parameters.
307 InitDC();
308
309 // the HDC can have a clipping box (which we didn't set), make sure our
310 // DoGetClippingBox() checks for it
311 m_clipping = true;
312 }
313
314 wxPaintDCImpl::~wxPaintDCImpl()
315 {
316 if ( m_hDC )
317 {
318 SelectOldObjects(m_hDC);
319 m_hDC = 0;
320 }
321 }
322
323
324 /* static */
325 wxPaintDCInfo *wxPaintDCImpl::FindInCache(wxWindow *win)
326 {
327 PaintDCInfos::const_iterator it = gs_PaintDCInfos.find( win );
328
329 return it != gs_PaintDCInfos.end() ? it->second : NULL;
330 }
331
332 /* static */
333 WXHDC wxPaintDCImpl::FindDCInCache(wxWindow* win)
334 {
335 wxPaintDCInfo* const info = FindInCache(win);
336
337 return info ? info->GetHDC() : 0;
338 }
339
340 /* static */
341 void wxPaintDCImpl::EndPaint(wxWindow *win)
342 {
343 wxPaintDCInfo *info = FindInCache(win);
344 if ( info )
345 {
346 gs_PaintDCInfos.erase(win);
347 delete info;
348 }
349 }
350
351 wxPaintDCInfo::~wxPaintDCInfo()
352 {
353 }
354
355 /*
356 * wxPaintDCEx
357 */
358
359 class wxPaintDCExImpl: public wxPaintDCImpl
360 {
361 public:
362 wxPaintDCExImpl( wxDC *owner, wxWindow *window, WXHDC dc );
363 ~wxPaintDCExImpl();
364 };
365
366
367 IMPLEMENT_ABSTRACT_CLASS(wxPaintDCEx,wxPaintDC)
368
369 wxPaintDCEx::wxPaintDCEx(wxWindow *window, WXHDC dc)
370 : wxPaintDC(new wxPaintDCExImpl(this, window, dc))
371 {
372 }
373
374 wxPaintDCExImpl::wxPaintDCExImpl(wxDC *owner, wxWindow *window, WXHDC dc)
375 : wxPaintDCImpl( owner )
376 {
377 wxCHECK_RET( dc, wxT("wxPaintDCEx requires an existing device context") );
378
379 m_window = window;
380
381 m_hDC = FindDCInCache(m_window);
382 if ( !m_hDC )
383 {
384 // not in cache, record it there
385 gs_PaintDCInfos[m_window] = new wxPaintDCInfoExternal(dc);
386
387 m_hDC = dc;
388 }
389 }
390
391 wxPaintDCExImpl::~wxPaintDCExImpl()
392 {
393 // prevent the base class dtor from ReleaseDC()ing it again
394 m_hDC = 0;
395 }