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