Minor fix for owner drawn menu item.
[wxWidgets.git] / src / os2 / ownerdrw.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: msw/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 license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifndef WX_PRECOMP
20 #include "wx/window.h"
21 #include "wx/msw/private.h"
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
29 #if wxUSE_OWNER_DRAWN
30
31 #include "wx/ownerdrw.h"
32 #include "wx/menuitem.h"
33
34
35 // ============================================================================
36 // implementation of wxOwnerDrawn class
37 // ============================================================================
38
39 //
40 // ctor
41 // ----
42 //
43 wxOwnerDrawn::wxOwnerDrawn(
44 const wxString& rsStr
45 , bool bCheckable
46 , bool bMenuItem
47 )
48 : m_strName(rsStr)
49 {
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
57
58 size_t wxOwnerDrawn::ms_nDefaultMarginWidth = 15;
59
60 size_t wxOwnerDrawn::ms_nLastMarginWidth = ms_nDefaultMarginWidth;
61
62 //
63 // Drawing
64 // -------
65 //
66
67 bool wxOwnerDrawn::OnMeasureItem(
68 size_t* pWidth
69 , size_t* pHeight
70 )
71 {
72 wxMemoryDC vDC;
73
74 vDC.SetFont(GetFont());
75
76 wxString sStr = wxStripMenuCodes(m_strName);
77
78 vDC.GetTextExtent( sStr
79 ,(long *)pWidth
80 ,(long *)pHeight
81 );
82
83 (*pHeight) = (*pHeight) + 2;
84 m_nHeight = *pHeight; // remember height for use in OnDrawItem
85 return TRUE;
86 } // end of wxOwnerDrawn::OnMeasureItem
87
88 // draw the item
89 bool wxOwnerDrawn::OnDrawItem(
90 wxDC& rDC
91 , const wxRect& rRect
92 , wxODAction eAction
93 , wxODStatus eStatus
94 )
95 {
96 //
97 // We do nothing on focus change
98 //
99 if (eAction == wxODFocusChanged )
100 return TRUE;
101
102 //
103 // Select the font and draw the text
104 // ---------------------------------
105 //
106
107 CHARBUNDLE vCbnd;
108 HPS hPS= rDC.GetHPS();
109 wxColour vColBack;
110 wxColour vColText;
111 COLORREF vRef;
112 RECTL vRect = {rRect.x + 4, rRect.y + 1, rRect.x + (rRect.width - 2), rRect.y + rRect.height};
113
114 memset(&vCbnd, 0, sizeof(CHARBUNDLE));
115
116 //
117 // Use default font if no font set
118 //
119 if (m_font.Ok())
120 {
121 m_font.RealizeResource();
122 }
123 else
124 {
125 ::GpiSetCharSet(hPS, LCID_DEFAULT);
126 }
127
128 //
129 // Base on the status of the menu item pick the right colors
130 //
131 if (eStatus & wxODSelected)
132 {
133 wxColour vCol2("WHITE");
134 vColBack.Set( (unsigned char)0
135 ,(unsigned char)0
136 ,(unsigned char)160
137 ); // no dark blue in color table
138 vColText = vCol2;
139 }
140 else if (eStatus & wxODDisabled)
141 {
142 vRef = (ULONG)::WinQuerySysColor( HWND_DESKTOP
143 ,SYSCLR_MENU // Light gray
144 ,0L
145 );
146 vColBack.Set( GetRValue(vRef)
147 ,GetGValue(vRef)
148 ,GetBValue(vRef)
149 );
150 vRef = (ULONG)::WinQuerySysColor( HWND_DESKTOP
151 ,SYSCLR_MENUDISABLEDTEXT // dark gray
152 ,0L
153 );
154 vColText.Set( GetRValue(vRef)
155 ,GetGValue(vRef)
156 ,GetBValue(vRef)
157 );
158 }
159 else
160 {
161 //
162 // Fall back to default colors if none explicitly specified
163 //
164 vRef = ::WinQuerySysColor( HWND_DESKTOP
165 ,SYSCLR_MENU // we are using gray for all our window backgrounds in wxWindows
166 ,0L
167 );
168 vColBack.Set( GetRValue(vRef)
169 ,GetGValue(vRef)
170 ,GetBValue(vRef)
171 );
172 vRef = ::WinQuerySysColor( HWND_DESKTOP
173 ,SYSCLR_WINDOWTEXT // Black
174 ,0L
175 );
176 vColText.Set( GetRValue(vRef)
177 ,GetGValue(vRef)
178 ,GetBValue(vRef)
179 );
180 }
181
182 rDC.SetTextBackground(vColBack);
183 rDC.SetTextForeground(vColText);
184 rDC.SetBackgroundMode(wxTRANSPARENT);
185 vCbnd.lColor = vColText.GetPixel();
186 vCbnd.lBackColor = vColBack.GetPixel();
187 ::GpiSetAttrs( hPS
188 ,PRIM_CHAR
189 ,CBB_BACK_COLOR | CBB_COLOR
190 ,0
191 ,&vCbnd
192 );
193 ::GpiSetBackMix( hPS
194 ,BM_LEAVEALONE
195 );
196
197 //
198 // Paint the background
199 //
200 ::WinFillRect(hPS, &vRect, vColBack.GetPixel());
201
202 //
203 // Determine where to draw and leave space for a check-mark.
204 //
205 int nX = rRect.x + GetMarginWidth();
206
207 //
208 // Unfortunately, unlike Win32, PM has no owner drawn specific text
209 // drawing methods like ::DrawState that can cleanly handle accel
210 // pneumonics and deal, automatically, with various states, so we have
211 // to handle them ourselves. Notice Win32 can't handle \t in ownerdrawn
212 // strings either. We cannot handle mneumonics either. We display
213 // it, though, in hopes we can figure it out some day.
214 //
215
216 //
217 // Display main text and accel text separately to allign better
218 //
219 wxString sTgt = "\t";
220 wxString sFullString = m_strName; // need to save the original text
221 wxString sAccel;
222 size_t nIndex;
223 size_t nWidth;
224 size_t nCharWidth;
225 size_t nHeight;
226 bool bFoundMneumonic = FALSE;
227 bool bFoundAccel = FALSE;
228
229 //
230 // Deal with the tab, extracting the Accel text
231 //
232 nIndex = sFullString.Find(sTgt.c_str());
233 if (nIndex != -1)
234 {
235 bFoundAccel = TRUE;
236 sAccel = sFullString.Mid(nIndex + 1);
237 sFullString.Remove(nIndex);
238 }
239
240 //
241 // Deal with the mneumonic character
242 //
243 sTgt = "~";
244 nIndex = sFullString.Find(sTgt.c_str());
245 if (nIndex != -1)
246 {
247 wxString sTmp = sFullString;
248
249 bFoundMneumonic = TRUE;
250 sTmp.Remove(nIndex);
251 rDC.GetTextExtent( sTmp
252 ,(long *)&nWidth
253 ,(long *)&nHeight
254 );
255 sTmp = sFullString[nIndex + 1];
256 rDC.GetTextExtent( sTmp
257 ,(long *)&nCharWidth
258 ,(long *)&nHeight
259 );
260 sFullString.Replace(sTgt.c_str(), "", TRUE);
261 }
262
263 //
264 // Draw the main item text sans the accel text
265 //
266 POINTL vPntStart = {nX, rRect.y + 4};
267 ::GpiCharStringAt( rDC.GetHPS()
268 ,&vPntStart
269 ,sFullString.length()
270 ,(PCH)sFullString.c_str()
271 );
272 if (bFoundMneumonic)
273 {
274 //
275 // Underline the mneumonic -- still won't work, but at least it "looks" right
276 //
277 wxPen vPen;
278 POINTL vPntEnd = {nX + nWidth + nCharWidth - 3, rRect.y + 2}; //CharWidth is bit wide
279
280 vPntStart.x = nX + nWidth - 1;
281 vPntStart.y = rRect.y + 2; // Make it look pretty!
282 vPen = wxPen(vColText, 1, wxSOLID); // Assuming we are always black
283 rDC.SetPen(vPen);
284 ::GpiMove(hPS, &vPntStart);
285 ::GpiLine(hPS, &vPntEnd);
286 }
287
288 //
289 // Now draw the accel text
290 //
291 if (bFoundAccel)
292 {
293 size_t nWidth;
294 size_t nHeight;
295
296 rDC.GetTextExtent( sAccel
297 ,(long *)&nWidth
298 ,(long *)&nHeight
299 );
300 //
301 // Back off the starting position from the right edge
302 //
303 vPntStart.x = rRect.width - (nWidth + 7);
304 vPntStart.y = rRect.y + 4;
305 ::GpiCharStringAt( rDC.GetHPS()
306 ,&vPntStart
307 ,sAccel.length()
308 ,(PCH)sAccel.c_str()
309 );
310 }
311
312 //
313 // Draw the bitmap
314 // ---------------
315 //
316 if (IsCheckable() && !m_bmpChecked.Ok())
317 {
318 if (eStatus & wxODChecked)
319 {
320 RECTL vRect;
321 HBITMAP hBmpCheck = ::WinGetSysBitmap(HWND_DESKTOP, SBMP_MENUCHECK);
322
323 vRect.xLeft = rRect.x;
324 vRect.xRight = rRect.x + GetMarginWidth();
325 vRect.yBottom = rRect.y;
326 vRect.yTop = rRect.y + m_nHeight - 3;
327
328 ::WinDrawBitmap( hPS // PS for this menuitem
329 ,hBmpCheck // system checkmark
330 ,NULL // draw the whole bitmap
331 ,(PPOINTL)&vRect // destination -- bottom left corner of the menuitem area
332 ,0L // ignored
333 ,0L // draw a bitmap
334 ,DBM_NORMAL // draw normal size
335 );
336 }
337 }
338 else
339 {
340 //
341 // For uncheckable item we use only the 'checked' bitmap
342 //
343 wxBitmap vBmp(GetBitmap(IsCheckable() ? ((eStatus & wxODChecked) != 0) : TRUE));
344
345 if (vBmp.Ok())
346 {
347
348 wxMemoryDC vDCMem(&rDC);
349 wxMemoryDC* pOldDC = (wxMemoryDC*)vBmp.GetSelectedInto();
350
351 if(pOldDC != NULL)
352 {
353 vBmp.SetSelectedInto(NULL);
354 }
355 vDCMem.SelectObject(vBmp);
356
357 //
358 // Center bitmap
359 //
360 int nBmpWidth = vBmp.GetWidth();
361 int nBmpHeight = vBmp.GetHeight();
362
363 //
364 // There should be enough space!
365 //
366 wxASSERT((nBmpWidth <= rRect.width) && (nBmpHeight <= rRect.height));
367
368 //
369 //MT: blit with mask enabled.
370 //
371 rDC.Blit( rRect.x + (GetMarginWidth() - nBmpWidth) / 2
372 ,rRect.y + (m_nHeight - nBmpHeight) /2
373 ,nBmpWidth
374 ,nBmpHeight
375 ,&vDCMem
376 ,0
377 ,0
378 ,wxCOPY
379 ,TRUE
380 );
381
382 if (eStatus & wxODSelected)
383 {
384 RECT vRectBmp = { rRect.x
385 ,rRect.y
386 ,rRect.x + GetMarginWidth()
387 ,rRect.y + m_nHeight
388 };
389 POINTL vPnt1 = {rRect.x + 1, rRect.y + 3}; // Leave a little background border
390 POINTL vPnt2 = {rRect.x + GetMarginWidth(), rRect.y + m_nHeight - 3};
391
392 LINEBUNDLE vLine;
393
394 vLine.lColor = vColBack.GetPixel();
395 ::GpiSetAttrs( hPS
396 ,PRIM_LINE
397 ,LBB_COLOR
398 ,0
399 ,&vLine
400 );
401 ::GpiMove(hPS, &vPnt1);
402 char zMsg[128];
403 ::GpiBox( hPS
404 ,DRO_OUTLINE
405 ,&vPnt2
406 ,0L
407 ,0L
408 );
409 }
410 vBmp.SetSelectedInto(NULL);
411 }
412 }
413 return TRUE;
414 } // end of wxOwnerDrawn::OnDrawItem
415
416 #endif //wxUSE_OWNER_DRAWN