1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/dcclient.cpp 
   3 // Purpose:     wxClientDC class 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // =========================================================================== 
  14 // =========================================================================== 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  27 #include "wx/dcclient.h" 
  28 #include "wx/msw/dcclient.h" 
  31     #include "wx/string.h" 
  33     #include "wx/window.h" 
  36 #include "wx/msw/private.h" 
  38 // ---------------------------------------------------------------------------- 
  40 // ---------------------------------------------------------------------------- 
  42 struct WXDLLEXPORT wxPaintDCInfo
 
  44     wxPaintDCInfo(wxWindow 
*win
, wxPaintDCImpl 
*dc
) 
  46         hwnd 
= win
->GetHWND(); 
  51     WXHWND    hwnd
;       // window for this DC 
  52     WXHDC     hdc
;        // the DC handle 
  53     size_t    count
;      // usage count 
  56 #include "wx/arrimpl.cpp" 
  58 WX_DEFINE_OBJARRAY(wxArrayDCInfo
) 
  60 // ---------------------------------------------------------------------------- 
  62 // ---------------------------------------------------------------------------- 
  64 // ---------------------------------------------------------------------------- 
  66 // ---------------------------------------------------------------------------- 
  68 static PAINTSTRUCT g_paintStruct
; 
  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. 
  78 // =========================================================================== 
  80 // =========================================================================== 
  82 // ---------------------------------------------------------------------------- 
  84 // ---------------------------------------------------------------------------- 
  86 IMPLEMENT_ABSTRACT_CLASS(wxWindowDCImpl
, wxMSWDCImpl
) 
  88 wxWindowDCImpl::wxWindowDCImpl( wxDC 
*owner 
) : 
  93 wxWindowDCImpl::wxWindowDCImpl( wxDC 
*owner
, wxWindow 
*window 
) : 
  96     wxCHECK_RET( window
, _T("invalid window in wxWindowDCImpl") ); 
  99     m_hDC 
= (WXHDC
) ::GetWindowDC(GetHwndOf(m_window
)); 
 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 
 106 void wxWindowDCImpl::InitDC() 
 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
); 
 112     // since we are a window dc we need to grab the palette from the window 
 118 void wxWindowDCImpl::DoGetSize(int *width
, int *height
) const 
 120     wxCHECK_RET( m_window
, _T("wxWindowDCImpl without a window?") ); 
 122     m_window
->GetSize(width
, height
); 
 125 // ---------------------------------------------------------------------------- 
 127 // ---------------------------------------------------------------------------- 
 129 IMPLEMENT_ABSTRACT_CLASS(wxClientDCImpl
, wxWindowDCImpl
) 
 131 wxClientDCImpl::wxClientDCImpl( wxDC 
*owner 
) : 
 132    wxWindowDCImpl( owner 
) 
 136 wxClientDCImpl::wxClientDCImpl( wxDC 
*owner
, wxWindow 
*window 
) : 
 137    wxWindowDCImpl( owner 
) 
 139     wxCHECK_RET( window
, _T("invalid window in wxClientDCImpl") ); 
 142     m_hDC 
= (WXHDC
)::GetDC(GetHwndOf(window
)); 
 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 
 150 void wxClientDCImpl::InitDC() 
 152     wxWindowDCImpl::InitDC(); 
 154     // in wxUniv build we must manually do some DC adjustments usually 
 155     // performed by Windows for us 
 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 
) 
 164         // no need to shift DC origin if shift is null 
 165         SetDeviceOrigin(ptOrigin
.x
, ptOrigin
.y
); 
 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__ 
 174 wxClientDCImpl::~wxClientDCImpl() 
 178 void wxClientDCImpl::DoGetSize(int *width
, int *height
) const 
 180     wxCHECK_RET( m_window
, _T("wxClientDCImpl without a window?") ); 
 182     m_window
->GetClientSize(width
, height
); 
 185 // ---------------------------------------------------------------------------- 
 187 // ---------------------------------------------------------------------------- 
 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. 
 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. 
 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. 
 205 IMPLEMENT_ABSTRACT_CLASS(wxPaintDCImpl
, wxClientDCImpl
) 
 207 wxArrayDCInfo 
wxPaintDCImpl::ms_cache
; 
 209 wxPaintDCImpl::wxPaintDCImpl( wxDC 
*owner 
) : 
 210    wxClientDCImpl( owner 
) 
 214 wxPaintDCImpl::wxPaintDCImpl( wxDC 
*owner
, wxWindow 
*window 
) : 
 215    wxClientDCImpl( owner 
) 
 217     wxCHECK_RET( window
, wxT("NULL canvas in wxPaintDCImpl ctor") ); 
 220     if ( g_isPainting 
<= 0 ) 
 222         wxFAIL_MSG( wxT("wxPaintDCImpl may be created only in EVT_PAINT handler!") ); 
 226 #endif // __WXDEBUG__ 
 230     // do we have a DC for this window in the cache? 
 231     wxPaintDCInfo 
*info 
= FindInCache(); 
 237     else // not in cache, create a new one 
 239         m_hDC 
= (WXHDC
)::BeginPaint(GetHwndOf(m_window
), &g_paintStruct
); 
 241             ms_cache
.Add(new wxPaintDCInfo(m_window
, this)); 
 244     // Note: at this point m_hDC can be NULL under MicroWindows, when dragging. 
 248     // (re)set the DC parameters. 
 251     // the HDC can have a clipping box (which we didn't set), make sure our 
 252     // DoGetClippingBox() checks for it 
 256 wxPaintDCImpl::~wxPaintDCImpl() 
 260         SelectOldObjects(m_hDC
); 
 263         wxPaintDCInfo 
*info 
= FindInCache(&index
); 
 265         wxCHECK_RET( info
, wxT("existing DC should have a cache entry") ); 
 267         if ( --info
->count 
== 0 ) 
 269             ::EndPaint(GetHwndOf(m_window
), &g_paintStruct
); 
 271             ms_cache
.RemoveAt(index
); 
 273             // Reduce the number of bogus reports of non-freed memory 
 275             if (ms_cache
.IsEmpty()) 
 278         //else: cached DC entry is still in use 
 280         // prevent the base class dtor from ReleaseDC()ing it again 
 285 wxPaintDCInfo 
*wxPaintDCImpl::FindInCache(size_t *index
) const 
 287     wxPaintDCInfo 
*info 
= NULL
; 
 288     size_t nCache 
= ms_cache
.GetCount(); 
 289     for ( size_t n 
= 0; n 
< nCache
; n
++ ) 
 291         wxPaintDCInfo 
*info1 
= &ms_cache
[n
]; 
 292         if ( info1
->hwnd 
== m_window
->GetHWND() ) 
 304 // find the entry for this DC in the cache (keyed by the window) 
 305 WXHDC 
wxPaintDCImpl::FindDCInCache(wxWindow
* win
) 
 307     size_t nCache 
= ms_cache
.GetCount(); 
 308     for ( size_t n 
= 0; n 
< nCache
; n
++ ) 
 310         wxPaintDCInfo 
*info 
= &ms_cache
[n
]; 
 311         if ( info
->hwnd 
== win
->GetHWND() ) 
 323 // TODO: don't duplicate wxPaintDCImpl code here!! 
 325 class wxPaintDCExImpl
: public wxPaintDCImpl
 
 328     wxPaintDCExImpl( wxDC 
*owner
, wxWindow 
*window
, WXHDC dc 
); 
 335 IMPLEMENT_ABSTRACT_CLASS(wxPaintDCEx
,wxPaintDC
) 
 337 wxPaintDCEx::wxPaintDCEx(wxWindow 
*window
, WXHDC dc
) 
 338            : wxPaintDC(new wxPaintDCExImpl(this, window
, dc
)) 
 342 wxPaintDCExImpl::wxPaintDCExImpl(wxDC 
*owner
, wxWindow 
*window
, WXHDC dc
) 
 343                : wxPaintDCImpl( owner 
) 
 345         wxCHECK_RET( dc
, wxT("wxPaintDCEx requires an existing device context") ); 
 351         wxPaintDCInfo 
*info 
= FindInCache(); 
 357         else // not in cache, create a new one 
 360                 ms_cache
.Add(new wxPaintDCInfo(m_window
, this)); 
 361                 m_saveState 
= SaveDC((HDC
) dc
); 
 365 wxPaintDCExImpl::~wxPaintDCExImpl() 
 368         wxPaintDCInfo 
*info 
= FindInCache(&index
); 
 370         wxCHECK_RET( info
, wxT("existing DC should have a cache entry") ); 
 372         if ( --info
->count 
== 0 ) 
 374                 RestoreDC((HDC
) m_hDC
, m_saveState
); 
 375                 ms_cache
.RemoveAt(index
); 
 377                 // Reduce the number of bogus reports of non-freed memory 
 379                 if (ms_cache
.IsEmpty()) 
 382         //else: cached DC entry is still in use 
 384         // prevent the base class dtor from ReleaseDC()ing it again