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