Ownerdrawn stuff. Text display done, image display next.
[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 ::GpiCreateLogColorTable( m_hPS
127 ,0L
128 ,LCOLF_RGB
129 ,0L
130 ,0L
131 ,NULL
132 );
133 SetBackground(wxBrush(m_pCanvas->GetBackgroundColour(), wxSOLID));
134 ::WinQueryWindowRect( GetWinHwnd(m_pCanvas)
135 ,&m_vRclPaint
136 );
137 }
138
139 wxWindowDC::~wxWindowDC()
140 {
141 if (m_pCanvas && m_hDC)
142 {
143 SelectOldObjects(m_hDC);
144
145 //
146 // In PM one does not explicitly close or release an open WindowDC
147 // They automatically close with the window, unless explicitly detached
148 // but we need to destroy our PS
149 //
150 if(m_hPS)
151 {
152 ::GpiAssociate(m_hPS, NULLHANDLE);
153 ::GpiDestroyPS(m_hPS);
154 }
155 m_hPS = NULLHANDLE;
156 m_hDC = NULLHANDLE;
157 }
158 m_nDCCount--;
159 }
160
161 // ----------------------------------------------------------------------------
162 // wxClientDC
163 // ----------------------------------------------------------------------------
164
165 wxClientDC::wxClientDC()
166 {
167 m_pCanvas = NULL;
168 }
169
170 wxClientDC::wxClientDC(
171 wxWindow* pTheCanvas
172 )
173 {
174 SIZEL vSizl = { 0,0};
175 ERRORID vError;
176 wxString sError;
177
178 m_pCanvas = pTheCanvas;
179
180 //
181 // default under PM is that Window and Client DC's are the same
182 //
183 m_hDC = (WXHDC) ::WinOpenWindowDC(GetWinHwnd(pTheCanvas));
184 m_hPS = ::GpiCreatePS( wxGetInstance()
185 ,m_hDC
186 ,&vSizl
187 ,PU_PELS | GPIF_LONG | GPIA_ASSOC
188 );
189
190 // Set the wxWindows color table
191 if (!::GpiCreateLogColorTable( m_hPS
192 ,0L
193 ,LCOLF_CONSECRGB
194 ,0L
195 ,(LONG)wxTheColourDatabase->m_nSize
196 ,(PLONG)wxTheColourDatabase->m_palTable
197 ))
198 {
199 vError = ::WinGetLastError(vHabmain);
200 sError = wxPMErrorToStr(vError);
201 wxLogError("Unable to set current color table. Error: %s\n", sError);
202 }
203 ::GpiCreateLogColorTable( m_hPS
204 ,0L
205 ,LCOLF_RGB
206 ,0L
207 ,0L
208 ,NULL
209 );
210 //
211 // Default mode is BM_LEAVEALONE so we make no call Set the mix
212 //
213 SetBackground(wxBrush( m_pCanvas->GetBackgroundColour()
214 ,wxSOLID
215 )
216 );
217 //
218 // Set the DC/PS rectangle
219 //
220 ::WinQueryWindowRect( GetWinHwnd(m_pCanvas)
221 ,&m_vRclPaint
222 );
223 } // end of wxClientDC::wxClientDC
224
225 wxClientDC::~wxClientDC()
226 {
227 if ( m_pCanvas && GetHdc() )
228 {
229 SelectOldObjects(m_hDC);
230
231 //
232 // We don't explicitly release Device contexts in PM and
233 // the cached micro PS is already gone
234 //
235 m_hDC = 0;
236 }
237 } // end of wxClientDC::~wxClientDC
238
239 // ----------------------------------------------------------------------------
240 // wxPaintDC
241 // ----------------------------------------------------------------------------
242
243 // VZ: initial implementation (by JACS) only remembered the last wxPaintDC
244 // created and tried to reuse - this was supposed to take care of a
245 // situation when a derived class OnPaint() calls base class OnPaint()
246 // because in this case ::BeginPaint() shouldn't be called second time.
247 //
248 // I'm not sure how useful this is, however we must remember the HWND
249 // associated with the last HDC as well - otherwise we may (and will!) try
250 // to reuse the HDC for another HWND which is a nice recipe for disaster.
251 //
252 // So we store a list of windows for which we already have the DC and not
253 // just one single hDC. This seems to work, but I'm really not sure about
254 // the usefullness of the whole idea - IMHO it's much better to not call
255 // base class OnPaint() at all, or, if we really want to allow it, add a
256 // "wxPaintDC *" parameter to wxPaintEvent which should be used if it's
257 // !NULL instead of creating a new DC.
258
259 wxArrayDCInfo wxPaintDC::ms_cache;
260
261 wxPaintDC::wxPaintDC()
262 {
263 m_pCanvas = NULL;
264 m_hDC = 0;
265 }
266
267 wxPaintDC::wxPaintDC(
268 wxWindow* pCanvas
269 )
270 {
271 wxCHECK_RET(pCanvas, wxT("NULL canvas in wxPaintDC ctor"));
272 RECTL vRect;
273
274 #ifdef __WXDEBUG__
275 if (g_isPainting <= 0)
276 {
277 wxFAIL_MSG( wxT("wxPaintDC may be created only in EVT_PAINT handler!") );
278 return;
279 }
280 #endif // __WXDEBUG__
281
282 m_pCanvas = pCanvas;
283
284 //
285 // Do we have a DC for this window in the cache?
286 //
287 wxPaintDCInfo* pInfo = FindInCache();
288
289 if (pInfo)
290 {
291 m_hDC = pInfo->m_hDC;
292 pInfo->m_nCount++;
293 }
294 else // not in cache, create a new one
295 {
296 HPS hPS;
297
298 hPS = ::WinBeginPaint( GetWinHwnd(m_pCanvas)
299 ,NULLHANDLE
300 ,&g_paintStruct
301 );
302 if(hPS)
303 {
304 POINTL vPoint[2];
305 LONG lControl;
306 LONG lColor;
307
308 m_hOldPS = m_hPS;
309 m_hPS = hPS;
310 ::GpiCreateLogColorTable( m_hPS
311 ,0L
312 ,LCOLF_CONSECRGB
313 ,0L
314 ,(LONG)wxTheColourDatabase->m_nSize
315 ,(PLONG)wxTheColourDatabase->m_palTable
316 );
317 ::GpiCreateLogColorTable( m_hPS
318 ,0L
319 ,LCOLF_RGB
320 ,0L
321 ,0L
322 ,NULL
323 );
324
325 ::WinFillRect(hPS, &g_paintStruct, m_pCanvas->GetBackgroundColour().GetPixel());
326 ::WinQueryWindowRect( GetWinHwnd(m_pCanvas)
327 ,&m_vRclPaint
328 );
329 }
330
331 m_bIsPaintTime = TRUE;
332 m_hDC = (WXHDC) -1; // to satisfy those anonizmous efforts
333 ms_cache.Add(new wxPaintDCInfo(m_pCanvas, this));
334 }
335 SetBackground(wxBrush(m_pCanvas->GetBackgroundColour(), wxSOLID));
336 }
337
338 wxPaintDC::~wxPaintDC()
339 {
340 if ( m_hDC )
341 {
342 SelectOldObjects(m_hDC);
343
344 size_t nIndex;
345 wxPaintDCInfo* pInfo = FindInCache(&nIndex);
346
347 wxCHECK_RET( pInfo, wxT("existing DC should have a cache entry") );
348
349 if ( !--pInfo->m_nCount )
350 {
351 ::WinEndPaint(m_hPS);
352 m_hPS = m_hOldPS;
353 m_bIsPaintTime = FALSE;
354 ms_cache.Remove(nIndex);
355 }
356 //else: cached DC entry is still in use
357
358 // prevent the base class dtor from ReleaseDC()ing it again
359 m_hDC = 0;
360 }
361 }
362
363 wxPaintDCInfo* wxPaintDC::FindInCache(
364 size_t* pIndex
365 ) const
366 {
367 wxPaintDCInfo* pInfo = NULL;
368 size_t nCache = ms_cache.GetCount();
369
370 for (size_t n = 0; n < nCache; n++)
371 {
372 pInfo = &ms_cache[n];
373 if (pInfo->m_hWnd == m_pCanvas->GetHWND())
374 {
375 if (pIndex)
376 *pIndex = n;
377 break;
378 }
379 }
380 return pInfo;
381 } // end of wxPaintDC::FindInCache
382