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