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