]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/dcclient.cpp
preserve type when loaded image is rescaled, #11543
[wxWidgets.git] / src / msw / dcclient.cpp
... / ...
CommitLineData
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
42struct 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
58WX_DEFINE_OBJARRAY(wxArrayDCInfo)
59
60// ----------------------------------------------------------------------------
61// macros
62// ----------------------------------------------------------------------------
63
64// ----------------------------------------------------------------------------
65// global variables
66// ----------------------------------------------------------------------------
67
68static 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
86IMPLEMENT_ABSTRACT_CLASS(wxWindowDCImpl, wxMSWDCImpl)
87
88wxWindowDCImpl::wxWindowDCImpl( wxDC *owner ) :
89 wxMSWDCImpl( owner )
90{
91}
92
93wxWindowDCImpl::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
106void 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
118void 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
129IMPLEMENT_ABSTRACT_CLASS(wxClientDCImpl, wxWindowDCImpl)
130
131wxClientDCImpl::wxClientDCImpl( wxDC *owner ) :
132 wxWindowDCImpl( owner )
133{
134}
135
136wxClientDCImpl::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
150void 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
174wxClientDCImpl::~wxClientDCImpl()
175{
176}
177
178void 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
205IMPLEMENT_ABSTRACT_CLASS(wxPaintDCImpl, wxClientDCImpl)
206
207wxArrayDCInfo wxPaintDCImpl::ms_cache;
208
209wxPaintDCImpl::wxPaintDCImpl( wxDC *owner ) :
210 wxClientDCImpl( owner )
211{
212}
213
214wxPaintDCImpl::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 m_hDC = (WXHDC)::BeginPaint(GetHwndOf(m_window), &g_paintStruct);
240 if (m_hDC)
241 ms_cache.Add(new wxPaintDCInfo(m_window, this));
242 }
243
244 // Note: at this point m_hDC can be NULL under MicroWindows, when dragging.
245 if (!GetHDC())
246 return;
247
248 // (re)set the DC parameters.
249 InitDC();
250
251 // the HDC can have a clipping box (which we didn't set), make sure our
252 // DoGetClippingBox() checks for it
253 m_clipping = true;
254}
255
256wxPaintDCImpl::~wxPaintDCImpl()
257{
258 if ( m_hDC )
259 {
260 SelectOldObjects(m_hDC);
261
262 size_t index;
263 wxPaintDCInfo *info = FindInCache(&index);
264
265 wxCHECK_RET( info, wxT("existing DC should have a cache entry") );
266
267 if ( --info->count == 0 )
268 {
269 ::EndPaint(GetHwndOf(m_window), &g_paintStruct);
270
271 ms_cache.RemoveAt(index);
272
273 // Reduce the number of bogus reports of non-freed memory
274 // at app exit
275 if (ms_cache.IsEmpty())
276 ms_cache.Clear();
277 }
278 //else: cached DC entry is still in use
279
280 // prevent the base class dtor from ReleaseDC()ing it again
281 m_hDC = 0;
282 }
283}
284
285wxPaintDCInfo *wxPaintDCImpl::FindInCache(size_t *index) const
286{
287 wxPaintDCInfo *info = NULL;
288 size_t nCache = ms_cache.GetCount();
289 for ( size_t n = 0; n < nCache; n++ )
290 {
291 wxPaintDCInfo *info1 = &ms_cache[n];
292 if ( info1->hwnd == m_window->GetHWND() )
293 {
294 info = info1;
295 if ( index )
296 *index = n;
297 break;
298 }
299 }
300
301 return info;
302}
303
304// find the entry for this DC in the cache (keyed by the window)
305WXHDC wxPaintDCImpl::FindDCInCache(wxWindow* win)
306{
307 size_t nCache = ms_cache.GetCount();
308 for ( size_t n = 0; n < nCache; n++ )
309 {
310 wxPaintDCInfo *info = &ms_cache[n];
311 if ( info->hwnd == win->GetHWND() )
312 {
313 return info->hdc;
314 }
315 }
316 return 0;
317}
318
319/*
320 * wxPaintDCEx
321 */
322
323// TODO: don't duplicate wxPaintDCImpl code here!!
324
325class wxPaintDCExImpl: public wxPaintDCImpl
326{
327public:
328 wxPaintDCExImpl( wxDC *owner, wxWindow *window, WXHDC dc );
329 ~wxPaintDCExImpl();
330
331 int m_saveState;
332};
333
334
335IMPLEMENT_ABSTRACT_CLASS(wxPaintDCEx,wxPaintDC)
336
337wxPaintDCEx::wxPaintDCEx(wxWindow *window, WXHDC dc)
338 : wxPaintDC(new wxPaintDCExImpl(this, window, dc))
339{
340}
341
342wxPaintDCExImpl::wxPaintDCExImpl(wxDC *owner, wxWindow *window, WXHDC dc)
343 : wxPaintDCImpl( owner )
344{
345 wxCHECK_RET( dc, wxT("wxPaintDCEx requires an existing device context") );
346
347 m_saveState = 0;
348
349 m_window = window;
350
351 wxPaintDCInfo *info = FindInCache();
352 if ( info )
353 {
354 m_hDC = info->hdc;
355 info->count++;
356 }
357 else // not in cache, create a new one
358 {
359 m_hDC = dc;
360 ms_cache.Add(new wxPaintDCInfo(m_window, this));
361 m_saveState = SaveDC((HDC) dc);
362 }
363}
364
365wxPaintDCExImpl::~wxPaintDCExImpl()
366{
367 size_t index;
368 wxPaintDCInfo *info = FindInCache(&index);
369
370 wxCHECK_RET( info, wxT("existing DC should have a cache entry") );
371
372 if ( --info->count == 0 )
373 {
374 RestoreDC((HDC) m_hDC, m_saveState);
375 ms_cache.RemoveAt(index);
376
377 // Reduce the number of bogus reports of non-freed memory
378 // at app exit
379 if (ms_cache.IsEmpty())
380 ms_cache.Clear();
381 }
382 //else: cached DC entry is still in use
383
384 // prevent the base class dtor from ReleaseDC()ing it again
385 m_hDC = 0;
386}
387
388