]> git.saurik.com Git - wxWidgets.git/blob - src/os2/ownerdrw.cpp
fixed double deletion (patch #945491)
[wxWidgets.git] / src / os2 / ownerdrw.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: msw/ownerdrw.cpp
3 // Purpose: implementation of wxOwnerDrawn class
4 // Author: David Webster
5 // Modified by:
6 // Created: 10/12/99
7 // RCS-ID: $Id$
8 // Copyright: (c) David Webster
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifndef WX_PRECOMP
20 #include "wx/window.h"
21 #include "wx/os2/private.h"
22 #include "wx/font.h"
23 #include "wx/bitmap.h"
24 #include "wx/dcmemory.h"
25 #include "wx/menu.h"
26 #include "wx/utils.h"
27 #endif
28
29 #if wxUSE_OWNER_DRAWN
30
31 #include "wx/settings.h"
32 #include "wx/ownerdrw.h"
33 #include "wx/menuitem.h"
34
35
36 // ============================================================================
37 // implementation of wxOwnerDrawn class
38 // ============================================================================
39
40 //
41 // ctor
42 // ----
43 //
44 wxOwnerDrawn::wxOwnerDrawn(
45 const wxString& rsStr
46 , bool bCheckable
47 , bool bMenuItem
48 )
49 : m_strName(rsStr)
50 {
51 m_bCheckable = bCheckable;
52 m_bOwnerDrawn = FALSE;
53 m_nHeight = 0;
54 m_nMarginWidth = ms_nLastMarginWidth;
55 if (wxNORMAL_FONT)
56 m_font = *wxNORMAL_FONT;
57 } // end of wxOwnerDrawn::wxOwnerDrawn
58
59 size_t wxOwnerDrawn::ms_nDefaultMarginWidth = 15;
60
61 size_t wxOwnerDrawn::ms_nLastMarginWidth = ms_nDefaultMarginWidth;
62
63 //
64 // Drawing
65 // -------
66 //
67
68 bool wxOwnerDrawn::OnMeasureItem(
69 size_t* pWidth
70 , size_t* pHeight
71 )
72 {
73 wxMemoryDC vDC;
74
75
76 wxString sStr = wxStripMenuCodes(m_strName);
77
78 //
79 // If we have a valid accel string, then pad out
80 // the menu string so the menu and accel string are not
81 // placed ontop of eachother.
82 if (!m_strAccel.empty() )
83 {
84 sStr.Pad(sStr.Length()%8);
85 sStr += m_strAccel;
86 }
87 vDC.SetFont(GetFont());
88 vDC.GetTextExtent( sStr
89 ,(long *)pWidth
90 ,(long *)pHeight
91 );
92 if (!m_strAccel.IsEmpty())
93 {
94 //
95 // Measure the accelerator string, and add it's width to
96 // the total item width, plus 16 (Accelerators are right justified,
97 // with the right edge of the text rectangle 16 pixels left of
98 // the right edge of the menu)
99 //
100 int nAccelWidth;
101 int nAccelHeight;
102
103 vDC.GetTextExtent( m_strAccel
104 ,&nAccelWidth
105 ,&nAccelHeight
106 );
107 *pWidth += nAccelWidth;
108 }
109
110 //
111 // Add space at the end of the menu for the submenu expansion arrow
112 // this will also allow offsetting the accel string from the right edge
113 //
114 *pWidth = (size_t)(*pWidth + GetDefaultMarginWidth() * 1.5);
115
116 //
117 // JACS: items still look too tightly packed, so adding 5 pixels.
118 //
119 (*pHeight) += 5;
120
121 //
122 // Ray Gilbert's changes - Corrects the problem of a BMP
123 // being placed next to text in a menu item, and the BMP does
124 // not match the size expected by the system. This will
125 // resize the space so the BMP will fit. Without this, BMPs
126 // must be no larger or smaller than 16x16.
127 //
128 if (m_bmpChecked.Ok())
129 {
130 //
131 // Is BMP height larger then text height?
132 //
133 size_t nAdjustedHeight = m_bmpChecked.GetHeight() +
134 wxSystemSettings::GetMetric(wxSYS_EDGE_Y);
135 if (*pHeight < nAdjustedHeight)
136 *pHeight = nAdjustedHeight;
137
138 //
139 // Does BMP encroach on default check menu position?
140 //
141 size_t nAdjustedWidth = m_bmpChecked.GetWidth() +
142 (wxSystemSettings::GetMetric(wxSYS_EDGE_X) * 2);
143
144 //
145 // Do we need to widen margin to fit BMP?
146 //
147 if ((size_t)GetMarginWidth() < nAdjustedWidth)
148 SetMarginWidth(nAdjustedWidth);
149
150 //
151 // Add the size of the bitmap to our total size...
152 //
153 *pWidth += GetMarginWidth();
154 }
155
156 //
157 // Add the size of the bitmap to our total size - even if we don't have
158 // a bitmap we leave room for one...
159 //
160 *pWidth += GetMarginWidth();
161
162 //
163 // Make sure that this item is at least as
164 // tall as the user's system settings specify
165 //
166 if (*pHeight < m_nMinHeight)
167 *pHeight = m_nMinHeight;
168 m_nHeight = *pHeight; // remember height for use in OnDrawItem
169 return TRUE;
170 } // end of wxOwnerDrawn::OnMeasureItem
171
172 // draw the item
173 bool wxOwnerDrawn::OnDrawItem(
174 wxDC& rDC
175 , const wxRect& rRect
176 , wxODAction eAction
177 , wxODStatus eStatus
178 )
179 {
180 //
181 // We do nothing on focus change
182 //
183 if (eAction == wxODFocusChanged )
184 return TRUE;
185
186 //
187 // Select the font and draw the text
188 // ---------------------------------
189 //
190
191 CHARBUNDLE vCbnd;
192 HPS hPS= rDC.GetHPS();
193 wxColour vColBack;
194 wxColour vColText;
195 COLORREF vRef;
196 RECTL vRect = {rRect.x + 4, rRect.y + 1, rRect.x + (rRect.width - 2), rRect.y + rRect.height};
197
198 memset(&vCbnd, 0, sizeof(CHARBUNDLE));
199
200 //
201 // Use default font if no font set
202 //
203 if (m_font.Ok())
204 {
205 m_font.RealizeResource();
206 }
207 else
208 {
209 ::GpiSetCharSet(hPS, LCID_DEFAULT);
210 }
211
212 //
213 // Base on the status of the menu item pick the right colors
214 //
215 if (eStatus & wxODSelected)
216 {
217 wxColour vCol2("WHITE");
218 vColBack.Set( (unsigned char)0
219 ,(unsigned char)0
220 ,(unsigned char)160
221 ); // no dark blue in color table
222 vColText = vCol2;
223 }
224 else if (eStatus & wxODDisabled)
225 {
226 vRef = (ULONG)::WinQuerySysColor( HWND_DESKTOP
227 ,SYSCLR_MENU // Light gray
228 ,0L
229 );
230 vColBack.Set( GetRValue(vRef)
231 ,GetGValue(vRef)
232 ,GetBValue(vRef)
233 );
234 vRef = (ULONG)::WinQuerySysColor( HWND_DESKTOP
235 ,SYSCLR_MENUDISABLEDTEXT // dark gray
236 ,0L
237 );
238 vColText.Set( GetRValue(vRef)
239 ,GetGValue(vRef)
240 ,GetBValue(vRef)
241 );
242 }
243 else
244 {
245 //
246 // Fall back to default colors if none explicitly specified
247 //
248 vRef = ::WinQuerySysColor( HWND_DESKTOP
249 ,SYSCLR_MENU // we are using gray for all our window backgrounds in wxWidgets
250 ,0L
251 );
252 vColBack.Set( GetRValue(vRef)
253 ,GetGValue(vRef)
254 ,GetBValue(vRef)
255 );
256 vRef = ::WinQuerySysColor( HWND_DESKTOP
257 ,SYSCLR_WINDOWTEXT // Black
258 ,0L
259 );
260 vColText.Set( GetRValue(vRef)
261 ,GetGValue(vRef)
262 ,GetBValue(vRef)
263 );
264 }
265
266 rDC.SetTextBackground(vColBack);
267 rDC.SetTextForeground(vColText);
268 rDC.SetBackgroundMode(wxTRANSPARENT);
269 vCbnd.lColor = vColText.GetPixel();
270 vCbnd.lBackColor = vColBack.GetPixel();
271 ::GpiSetAttrs( hPS
272 ,PRIM_CHAR
273 ,CBB_BACK_COLOR | CBB_COLOR
274 ,0
275 ,&vCbnd
276 );
277 ::GpiSetBackMix( hPS
278 ,BM_LEAVEALONE
279 );
280
281 //
282 // Paint the background
283 //
284 ::WinFillRect(hPS, &vRect, vColBack.GetPixel());
285
286 //
287 // Determine where to draw and leave space for a check-mark.
288 //
289 int nX = rRect.x + GetMarginWidth();
290
291 //
292 // Unfortunately, unlike Win32, PM has no owner drawn specific text
293 // drawing methods like ::DrawState that can cleanly handle accel
294 // pneumonics and deal, automatically, with various states, so we have
295 // to handle them ourselves. Notice Win32 can't handle \t in ownerdrawn
296 // strings either. We cannot handle mneumonics either. We display
297 // it, though, in hopes we can figure it out some day.
298 //
299
300 //
301 // Display main text and accel text separately to allign better
302 //
303 wxString sTgt = "\t";
304 wxString sFullString = m_strName; // need to save the original text
305 wxString sAccel;
306 int nIndex;
307 size_t nWidth;
308 size_t nCharWidth;
309 size_t nHeight;
310 bool bFoundMneumonic = FALSE;
311 bool bFoundAccel = FALSE;
312
313 //
314 // Deal with the tab, extracting the Accel text
315 //
316 nIndex = sFullString.Find(sTgt.c_str());
317 if (nIndex != -1)
318 {
319 bFoundAccel = TRUE;
320 sAccel = sFullString.Mid(nIndex + 1);
321 sFullString.Remove(nIndex);
322 }
323
324 //
325 // Deal with the mneumonic character
326 //
327 sTgt = "~";
328 nIndex = sFullString.Find(sTgt.c_str());
329 if (nIndex != -1)
330 {
331 wxString sTmp = sFullString;
332
333 bFoundMneumonic = TRUE;
334 sTmp.Remove(nIndex);
335 rDC.GetTextExtent( sTmp
336 ,(long *)&nWidth
337 ,(long *)&nHeight
338 );
339 sTmp = sFullString[(size_t)(nIndex + 1)];
340 rDC.GetTextExtent( sTmp
341 ,(long *)&nCharWidth
342 ,(long *)&nHeight
343 );
344 sFullString.Replace(sTgt.c_str(), "", TRUE);
345 }
346
347 //
348 // Draw the main item text sans the accel text
349 //
350 POINTL vPntStart = {nX, rRect.y + 4};
351 ::GpiCharStringAt( rDC.GetHPS()
352 ,&vPntStart
353 ,sFullString.length()
354 ,(PCH)sFullString.c_str()
355 );
356 if (bFoundMneumonic)
357 {
358 //
359 // Underline the mneumonic -- still won't work, but at least it "looks" right
360 //
361 wxPen vPen;
362 POINTL vPntEnd = {nX + nWidth + nCharWidth - 3, rRect.y + 2}; //CharWidth is bit wide
363
364 vPntStart.x = nX + nWidth - 1;
365 vPntStart.y = rRect.y + 2; // Make it look pretty!
366 vPen = wxPen(vColText, 1, wxSOLID); // Assuming we are always black
367 rDC.SetPen(vPen);
368 ::GpiMove(hPS, &vPntStart);
369 ::GpiLine(hPS, &vPntEnd);
370 }
371
372 //
373 // Now draw the accel text
374 //
375 if (bFoundAccel)
376 {
377 size_t nWidth;
378 size_t nHeight;
379
380 rDC.GetTextExtent( sAccel
381 ,(long *)&nWidth
382 ,(long *)&nHeight
383 );
384 //
385 // Back off the starting position from the right edge
386 //
387 vPntStart.x = rRect.width - (nWidth + 7);
388 vPntStart.y = rRect.y + 4;
389 ::GpiCharStringAt( rDC.GetHPS()
390 ,&vPntStart
391 ,sAccel.length()
392 ,(PCH)sAccel.c_str()
393 );
394 }
395
396 //
397 // Draw the bitmap
398 // ---------------
399 //
400 if (IsCheckable() && !m_bmpChecked.Ok())
401 {
402 if (eStatus & wxODChecked)
403 {
404 RECTL vRect;
405 HBITMAP hBmpCheck = ::WinGetSysBitmap(HWND_DESKTOP, SBMP_MENUCHECK);
406
407 vRect.xLeft = rRect.x;
408 vRect.xRight = rRect.x + GetMarginWidth();
409 vRect.yBottom = rRect.y;
410 vRect.yTop = rRect.y + m_nHeight - 3;
411
412 ::WinDrawBitmap( hPS // PS for this menuitem
413 ,hBmpCheck // system checkmark
414 ,NULL // draw the whole bitmap
415 ,(PPOINTL)&vRect // destination -- bottom left corner of the menuitem area
416 ,0L // ignored
417 ,0L // draw a bitmap
418 ,DBM_NORMAL // draw normal size
419 );
420 }
421 }
422 else
423 {
424 //
425 // For uncheckable item we use only the 'checked' bitmap
426 //
427 wxBitmap vBmp(GetBitmap(IsCheckable() ? ((eStatus & wxODChecked) != 0) : TRUE));
428
429 if (vBmp.Ok())
430 {
431
432 wxMemoryDC vDCMem(&rDC);
433 wxMemoryDC* pOldDC = (wxMemoryDC*)vBmp.GetSelectedInto();
434
435 if(pOldDC != NULL)
436 {
437 vBmp.SetSelectedInto(NULL);
438 }
439 vDCMem.SelectObject(vBmp);
440
441 //
442 // Center bitmap
443 //
444 int nBmpWidth = vBmp.GetWidth();
445 int nBmpHeight = vBmp.GetHeight();
446
447 //
448 // There should be enough space!
449 //
450 wxASSERT((nBmpWidth <= rRect.width) && (nBmpHeight <= rRect.height));
451
452 int nHeightDiff = m_nHeight - nBmpHeight;
453
454 rDC.Blit( rRect.x + (GetMarginWidth() - nBmpWidth) / 2
455 ,rRect.y + nHeightDiff / 2
456 ,nBmpWidth
457 ,nBmpHeight
458 ,&vDCMem
459 ,0
460 ,0
461 ,wxCOPY
462 ,TRUE
463 );
464
465 if (eStatus & wxODSelected)
466 {
467 POINTL vPnt1 = {rRect.x + 1, rRect.y + 3}; // Leave a little background border
468 POINTL vPnt2 = {rRect.x + GetMarginWidth(), rRect.y + m_nHeight - 3};
469
470 LINEBUNDLE vLine;
471
472 vLine.lColor = vColBack.GetPixel();
473 ::GpiSetAttrs( hPS
474 ,PRIM_LINE
475 ,LBB_COLOR
476 ,0
477 ,&vLine
478 );
479 ::GpiMove(hPS, &vPnt1);
480 ::GpiBox( hPS
481 ,DRO_OUTLINE
482 ,&vPnt2
483 ,0L
484 ,0L
485 );
486 }
487 vBmp.SetSelectedInto(NULL);
488 }
489 }
490 return TRUE;
491 } // end of wxOwnerDrawn::OnDrawItem
492
493 #endif //wxUSE_OWNER_DRAWN