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