Lots of OS/2 fixes mostly to do with painting
[wxWidgets.git] / src / os2 / dcclient.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dcclient.cpp
3 // Purpose: wxClientDC class
4 // Author: David Webster
5 // Modified by:
6 // Created: 09/21/99
7 // RCS-ID: $Id$
8 // Copyright: (c) David Webster
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 #include "wx/string.h"
24 #include "wx/log.h"
25 #include "wx/window.h"
26
27 #include "wx/os2/private.h"
28
29 #include "wx/dcclient.h"
30
31 // ----------------------------------------------------------------------------
32 // array/list types
33 // ----------------------------------------------------------------------------
34
35 struct WXDLLEXPORT wxPaintDCInfo
36 {
37 wxPaintDCInfo( wxWindow* pWin
38 ,wxDC* pDC
39 )
40 {
41 m_hWnd = pWin->GetHWND();
42 m_hDC = pDC->GetHDC();
43 m_nCount = 1;
44 }
45
46 WXHWND m_hWnd; // window for this DC
47 WXHDC m_hDC; // the DC handle
48 size_t m_nCount; // usage count
49 }; // end of wxPaintDCInfot
50
51 #include "wx/arrimpl.cpp"
52
53 WX_DEFINE_OBJARRAY(wxArrayDCInfo);
54
55 // ----------------------------------------------------------------------------
56 // macros
57 // ----------------------------------------------------------------------------
58
59 IMPLEMENT_DYNAMIC_CLASS(wxWindowDC, wxDC)
60 IMPLEMENT_DYNAMIC_CLASS(wxClientDC, wxWindowDC)
61 IMPLEMENT_DYNAMIC_CLASS(wxPaintDC, wxWindowDC)
62
63 // ----------------------------------------------------------------------------
64 // global variables
65 // ----------------------------------------------------------------------------
66
67 static RECT g_paintStruct;
68
69 #ifdef __WXDEBUG__
70 // a global variable which we check to verify that wxPaintDC are only
71 // created in resopnse to WM_PAINT message - doing this from elsewhere is a
72 // common programming error among wxWindows programmers and might lead to
73 // very subtle and difficult to debug refresh/repaint bugs.
74 int g_isPainting = 0;
75 #endif // __WXDEBUG__
76
77 // ===========================================================================
78 // implementation
79 // ===========================================================================
80
81 // ----------------------------------------------------------------------------
82 // wxWindowDC
83 // ----------------------------------------------------------------------------
84
85 wxWindowDC::wxWindowDC()
86 {
87 m_pCanvas = NULL;
88 }
89
90 wxWindowDC::wxWindowDC(
91 wxWindow* pTheCanvas
92 )
93 {
94 ERRORID vError;
95 wxString sError;
96
97 m_pCanvas = pTheCanvas;
98 m_hDC = (WXHDC) ::WinOpenWindowDC(GetWinHwnd(pTheCanvas) );
99 m_nDCCount++;
100 //
101 // default under PM is that Window and Client DC's are the same
102 // so we offer a separate Presentation Space to use for the
103 // entire window. Otherwise, calling BeginPaint will just create
104 // chached-micro client presentation space
105 //
106 m_hPS = GpiCreatePS( m_hab
107 ,m_hDC
108 ,&m_PageSize
109 ,PU_PELS | GPIF_LONG | GPIA_ASSOC
110 );
111 ::GpiAssociate(m_hPS, NULLHANDLE);
112 ::GpiAssociate(m_hPS, m_hDC);
113 // Set the wxWindows color table
114 if (!::GpiCreateLogColorTable( m_hPS
115 ,0L
116 ,LCOLF_CONSECRGB
117 ,0L
118 ,(LONG)wxTheColourDatabase->m_nSize
119 ,(PLONG)wxTheColourDatabase->m_palTable
120 ))
121 {
122 vError = ::WinGetLastError(vHabmain);
123 sError = wxPMErrorToStr(vError);
124 wxLogError("Unable to set current color table. Error: %s\n", sError);
125 }
126 SetBackground(wxBrush(m_pCanvas->GetBackgroundColour(), wxSOLID));
127 }
128
129 wxWindowDC::~wxWindowDC()
130 {
131 if (m_pCanvas && m_hDC)
132 {
133 SelectOldObjects(m_hDC);
134
135 //
136 // In PM one does not explicitly close or release an open WindowDC
137 // They automatically close with the window, unless explicitly detached
138 // but we need to destroy our PS
139 //
140 if(m_hPS)
141 {
142 ::GpiAssociate(m_hPS, NULLHANDLE);
143 ::GpiDestroyPS(m_hPS);
144 }
145 m_hPS = NULLHANDLE;
146 m_hDC = NULLHANDLE;
147 }
148 m_nDCCount--;
149 }
150
151 // ----------------------------------------------------------------------------
152 // wxClientDC
153 // ----------------------------------------------------------------------------
154
155 wxClientDC::wxClientDC()
156 {
157 m_pCanvas = NULL;
158 }
159
160 wxClientDC::wxClientDC(
161 wxWindow* pTheCanvas
162 )
163 {
164 SIZEL vSizl = { 0,0};
165 ERRORID vError;
166 wxString sError;
167
168 m_pCanvas = pTheCanvas;
169
170 //
171 // default under PM is that Window and Client DC's are the same
172 //
173 m_hDC = (WXHDC) ::WinOpenWindowDC(GetWinHwnd(pTheCanvas));
174 m_hPS = ::GpiCreatePS( wxGetInstance()
175 ,m_hDC
176 ,&vSizl
177 ,PU_PELS | GPIF_LONG | GPIA_ASSOC
178 );
179
180 // Set the wxWindows color table
181 if (!::GpiCreateLogColorTable( m_hPS
182 ,0L
183 ,LCOLF_CONSECRGB
184 ,0L
185 ,(LONG)wxTheColourDatabase->m_nSize
186 ,(PLONG)wxTheColourDatabase->m_palTable
187 ))
188 {
189 vError = ::WinGetLastError(vHabmain);
190 sError = wxPMErrorToStr(vError);
191 wxLogError("Unable to set current color table. Error: %s\n", sError);
192 }
193 //
194 // Default mode is BM_LEAVEALONE so we make no call Set the mix
195 //
196 SetBackground(wxBrush( m_pCanvas->GetBackgroundColour()
197 ,wxSOLID
198 )
199 );
200 } // end of wxClientDC::wxClientDC
201
202 wxClientDC::~wxClientDC()
203 {
204 if ( m_pCanvas && GetHdc() )
205 {
206 SelectOldObjects(m_hDC);
207
208 //
209 // We don't explicitly release Device contexts in PM and
210 // the cached micro PS is already gone
211 //
212 m_hDC = 0;
213 }
214 } // end of wxClientDC::~wxClientDC
215
216 // ----------------------------------------------------------------------------
217 // wxPaintDC
218 // ----------------------------------------------------------------------------
219
220 // VZ: initial implementation (by JACS) only remembered the last wxPaintDC
221 // created and tried to reuse - this was supposed to take care of a
222 // situation when a derived class OnPaint() calls base class OnPaint()
223 // because in this case ::BeginPaint() shouldn't be called second time.
224 //
225 // I'm not sure how useful this is, however we must remember the HWND
226 // associated with the last HDC as well - otherwise we may (and will!) try
227 // to reuse the HDC for another HWND which is a nice recipe for disaster.
228 //
229 // So we store a list of windows for which we already have the DC and not
230 // just one single hDC. This seems to work, but I'm really not sure about
231 // the usefullness of the whole idea - IMHO it's much better to not call
232 // base class OnPaint() at all, or, if we really want to allow it, add a
233 // "wxPaintDC *" parameter to wxPaintEvent which should be used if it's
234 // !NULL instead of creating a new DC.
235
236 wxArrayDCInfo wxPaintDC::ms_cache;
237
238 wxPaintDC::wxPaintDC()
239 {
240 m_pCanvas = NULL;
241 m_hDC = 0;
242 }
243
244 wxPaintDC::wxPaintDC(
245 wxWindow* pCanvas
246 )
247 {
248 wxCHECK_RET(pCanvas, wxT("NULL canvas in wxPaintDC ctor"));
249 RECTL vRect;
250
251 #ifdef __WXDEBUG__
252 if (g_isPainting <= 0)
253 {
254 wxFAIL_MSG( wxT("wxPaintDC may be created only in EVT_PAINT handler!") );
255 return;
256 }
257 #endif // __WXDEBUG__
258
259 m_pCanvas = pCanvas;
260
261 //
262 // Do we have a DC for this window in the cache?
263 //
264 wxPaintDCInfo* pInfo = FindInCache();
265
266 if (pInfo)
267 {
268 m_hDC = pInfo->m_hDC;
269 pInfo->m_nCount++;
270 }
271 else // not in cache, create a new one
272 {
273 HPS hPS;
274
275 hPS = ::WinBeginPaint( GetWinHwnd(m_pCanvas)
276 ,NULLHANDLE
277 ,&g_paintStruct
278 );
279 if(hPS)
280 {
281 POINTL vPoint[2];
282 LONG lControl;
283 LONG lColor;
284
285 m_hOldPS = m_hPS;
286 m_hPS = hPS;
287 ::GpiCreateLogColorTable( m_hPS
288 ,0L
289 ,LCOLF_CONSECRGB
290 ,0L
291 ,(LONG)wxTheColourDatabase->m_nSize
292 ,(PLONG)wxTheColourDatabase->m_palTable
293 );
294 ::GpiCreateLogColorTable( m_hPS
295 ,0L
296 ,LCOLF_RGB
297 ,0L
298 ,0L
299 ,NULL
300 );
301
302 ::WinFillRect(hPS, &g_paintStruct, m_pCanvas->GetBackgroundColour().GetPixel());
303 }
304
305 m_bIsPaintTime = TRUE;
306 m_hDC = (WXHDC) -1; // to satisfy those anonizmous efforts
307 m_vRclPaint = g_paintStruct;
308 ms_cache.Add(new wxPaintDCInfo(m_pCanvas, this));
309 }
310 SetBackground(wxBrush(m_pCanvas->GetBackgroundColour(), wxSOLID));
311 }
312
313 wxPaintDC::~wxPaintDC()
314 {
315 if ( m_hDC )
316 {
317 SelectOldObjects(m_hDC);
318
319 size_t nIndex;
320 wxPaintDCInfo* pInfo = FindInCache(&nIndex);
321
322 wxCHECK_RET( pInfo, wxT("existing DC should have a cache entry") );
323
324 if ( !--pInfo->m_nCount )
325 {
326 ::WinEndPaint(m_hPS);
327 m_hPS = m_hOldPS;
328 m_bIsPaintTime = FALSE;
329 ms_cache.Remove(nIndex);
330 }
331 //else: cached DC entry is still in use
332
333 // prevent the base class dtor from ReleaseDC()ing it again
334 m_hDC = 0;
335 }
336 }
337
338 wxPaintDCInfo* wxPaintDC::FindInCache(
339 size_t* pIndex
340 ) const
341 {
342 wxPaintDCInfo* pInfo = NULL;
343 size_t nCache = ms_cache.GetCount();
344
345 for (size_t n = 0; n < nCache; n++)
346 {
347 pInfo = &ms_cache[n];
348 if (pInfo->m_hWnd == m_pCanvas->GetHWND())
349 {
350 if (pIndex)
351 *pIndex = n;
352 break;
353 }
354 }
355 return pInfo;
356 } // end of wxPaintDC::FindInCache
357