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