]> git.saurik.com Git - wxWidgets.git/blob - src/msw/bmpbuttn.cpp
Prevent crash in wxString::Mid under STL builds (fixes #1231725).
[wxWidgets.git] / src / msw / bmpbuttn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/bmpbuttn.cpp
3 // Purpose: wxBitmapButton
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "bmpbuttn.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #if wxUSE_BMPBUTTON
24
25 #ifndef WX_PRECOMP
26 #include "wx/bmpbuttn.h"
27 #include "wx/log.h"
28 #include "wx/dcmemory.h"
29 #endif
30
31 #include "wx/msw/private.h"
32 #include "wx/image.h"
33
34 // ----------------------------------------------------------------------------
35 // macros
36 // ----------------------------------------------------------------------------
37
38 #if wxUSE_EXTENDED_RTTI
39
40 WX_DEFINE_FLAGS( wxBitmapButtonStyle )
41
42 wxBEGIN_FLAGS( wxBitmapButtonStyle )
43 // new style border flags, we put them first to
44 // use them for streaming out
45 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
46 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
47 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
48 wxFLAGS_MEMBER(wxBORDER_RAISED)
49 wxFLAGS_MEMBER(wxBORDER_STATIC)
50 wxFLAGS_MEMBER(wxBORDER_NONE)
51
52 // old style border flags
53 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
54 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
55 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
56 wxFLAGS_MEMBER(wxRAISED_BORDER)
57 wxFLAGS_MEMBER(wxSTATIC_BORDER)
58 wxFLAGS_MEMBER(wxBORDER)
59
60 // standard window styles
61 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
62 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
63 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
64 wxFLAGS_MEMBER(wxWANTS_CHARS)
65 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
66 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
67 wxFLAGS_MEMBER(wxVSCROLL)
68 wxFLAGS_MEMBER(wxHSCROLL)
69
70 wxFLAGS_MEMBER(wxBU_AUTODRAW)
71 wxFLAGS_MEMBER(wxBU_LEFT)
72 wxFLAGS_MEMBER(wxBU_RIGHT)
73 wxFLAGS_MEMBER(wxBU_TOP)
74 wxFLAGS_MEMBER(wxBU_BOTTOM)
75 wxEND_FLAGS( wxBitmapButtonStyle )
76
77 IMPLEMENT_DYNAMIC_CLASS_XTI(wxBitmapButton, wxButton,"wx/bmpbuttn.h")
78
79 wxBEGIN_PROPERTIES_TABLE(wxBitmapButton)
80 wxPROPERTY_FLAGS( WindowStyle , wxBitmapButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
81 wxEND_PROPERTIES_TABLE()
82
83 wxBEGIN_HANDLERS_TABLE(wxBitmapButton)
84 wxEND_HANDLERS_TABLE()
85
86 wxCONSTRUCTOR_5( wxBitmapButton , wxWindow* , Parent , wxWindowID , Id , wxBitmap , Bitmap , wxPoint , Position , wxSize , Size )
87
88 #else
89 IMPLEMENT_DYNAMIC_CLASS(wxBitmapButton, wxButton)
90 #endif
91
92 BEGIN_EVENT_TABLE(wxBitmapButton, wxBitmapButtonBase)
93 EVT_SYS_COLOUR_CHANGED(wxBitmapButton::OnSysColourChanged)
94 END_EVENT_TABLE()
95
96 /*
97 TODO PROPERTIES :
98
99 long "style" , wxBU_AUTODRAW
100 bool "default" , 0
101 bitmap "selected" ,
102 bitmap "focus" ,
103 bitmap "disabled" ,
104 */
105
106 bool wxBitmapButton::Create(wxWindow *parent, wxWindowID id,
107 const wxBitmap& bitmap,
108 const wxPoint& pos,
109 const wxSize& size, long style,
110 const wxValidator& wxVALIDATOR_PARAM(validator),
111 const wxString& name)
112 {
113 m_bmpNormal = bitmap;
114 SetName(name);
115
116 #if wxUSE_VALIDATORS
117 SetValidator(validator);
118 #endif // wxUSE_VALIDATORS
119
120 parent->AddChild(this);
121
122 m_backgroundColour = parent->GetBackgroundColour();
123 m_foregroundColour = parent->GetForegroundColour();
124 m_windowStyle = style;
125
126 if ( style & wxBU_AUTODRAW )
127 {
128 m_marginX = wxDEFAULT_BUTTON_MARGIN;
129 m_marginY = wxDEFAULT_BUTTON_MARGIN;
130 }
131
132 if (id == wxID_ANY)
133 m_windowId = NewControlId();
134 else
135 m_windowId = id;
136
137 long msStyle = WS_VISIBLE | WS_TABSTOP | WS_CHILD | BS_OWNERDRAW ;
138
139 if ( m_windowStyle & wxCLIP_SIBLINGS )
140 msStyle |= WS_CLIPSIBLINGS;
141
142 #ifdef __WIN32__
143 if(m_windowStyle & wxBU_LEFT)
144 msStyle |= BS_LEFT;
145 if(m_windowStyle & wxBU_RIGHT)
146 msStyle |= BS_RIGHT;
147 if(m_windowStyle & wxBU_TOP)
148 msStyle |= BS_TOP;
149 if(m_windowStyle & wxBU_BOTTOM)
150 msStyle |= BS_BOTTOM;
151 #endif
152
153 m_hWnd = (WXHWND) CreateWindowEx(
154 0,
155 wxT("BUTTON"),
156 wxEmptyString,
157 msStyle,
158 0, 0, 0, 0,
159 GetWinHwnd(parent),
160 (HMENU)m_windowId,
161 wxGetInstance(),
162 NULL
163 );
164
165 // Subclass again for purposes of dialog editing mode
166 SubclassWin(m_hWnd);
167
168 SetPosition(pos);
169 SetBestSize(size);
170
171 return true;
172 }
173
174 bool wxBitmapButton::SetBackgroundColour(const wxColour& colour)
175 {
176 if ( !wxBitmapButtonBase::SetBackgroundColour(colour) )
177 {
178 // didn't change
179 return false;
180 }
181
182 // invalidate the brush, it will be recreated the next time it's needed
183 m_brushDisabled = wxNullBrush;
184
185 return true;
186 }
187
188 void wxBitmapButton::OnSysColourChanged(wxSysColourChangedEvent& event)
189 {
190 m_brushDisabled = wxNullBrush;
191
192 if ( !IsEnabled() )
193 {
194 // this change affects our current state
195 Refresh();
196 }
197
198 event.Skip();
199 }
200
201 // VZ: should be at the very least less than wxDEFAULT_BUTTON_MARGIN
202 #define FOCUS_MARGIN 3
203
204 bool wxBitmapButton::MSWOnDraw(WXDRAWITEMSTRUCT *item)
205 {
206 #ifndef __WXWINCE__
207 long style = GetWindowLong((HWND) GetHWND(), GWL_STYLE);
208 if (style & BS_BITMAP)
209 {
210 // Let default procedure draw the bitmap, which is defined
211 // in the Windows resource.
212 return false;
213 }
214 #endif
215
216 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT) item;
217 HDC hDC = lpDIS->hDC;
218 UINT state = lpDIS->itemState;
219 bool isSelected = (state & ODS_SELECTED) != 0;
220 bool autoDraw = (GetWindowStyleFlag() & wxBU_AUTODRAW) != 0;
221
222
223 // choose the bitmap to use depending on the button state
224 wxBitmap* bitmap;
225
226 if ( isSelected && m_bmpSelected.Ok() )
227 bitmap = &m_bmpSelected;
228 else if ((state & ODS_FOCUS) && m_bmpFocus.Ok())
229 bitmap = &m_bmpFocus;
230 else if ((state & ODS_DISABLED) && m_bmpDisabled.Ok())
231 bitmap = &m_bmpDisabled;
232 else
233 bitmap = &m_bmpNormal;
234
235 if ( !bitmap->Ok() )
236 return false;
237
238 // centre the bitmap in the control area
239 int x = lpDIS->rcItem.left;
240 int y = lpDIS->rcItem.top;
241 int width = lpDIS->rcItem.right - x;
242 int height = lpDIS->rcItem.bottom - y;
243 int wBmp = bitmap->GetWidth();
244 int hBmp = bitmap->GetHeight();
245
246 int x1,y1;
247
248 if(m_windowStyle & wxBU_LEFT)
249 x1 = x + (FOCUS_MARGIN+1);
250 else if(m_windowStyle & wxBU_RIGHT)
251 x1 = x + (width - wBmp) - (FOCUS_MARGIN+1);
252 else
253 x1 = x + (width - wBmp) / 2;
254
255 if(m_windowStyle & wxBU_TOP)
256 y1 = y + (FOCUS_MARGIN+1);
257 else if(m_windowStyle & wxBU_BOTTOM)
258 y1 = y + (height - hBmp) - (FOCUS_MARGIN+1);
259 else
260 y1 = y + (height - hBmp) / 2;
261
262 if ( isSelected && autoDraw )
263 {
264 x1++;
265 y1++;
266 }
267
268 // draw the face, if auto-drawing
269 if ( autoDraw )
270 {
271 DrawFace((WXHDC) hDC,
272 lpDIS->rcItem.left, lpDIS->rcItem.top,
273 lpDIS->rcItem.right, lpDIS->rcItem.bottom,
274 isSelected);
275 }
276
277 // draw the bitmap
278 wxDC dst;
279 dst.SetHDC((WXHDC) hDC, false);
280 dst.DrawBitmap(*bitmap, x1, y1, true);
281
282 // draw focus / disabled state, if auto-drawing
283 if ( (state & ODS_DISABLED) && autoDraw )
284 {
285 DrawButtonDisable((WXHDC) hDC,
286 lpDIS->rcItem.left, lpDIS->rcItem.top,
287 lpDIS->rcItem.right, lpDIS->rcItem.bottom,
288 true);
289 }
290 else if ( (state & ODS_FOCUS) && autoDraw )
291 {
292 DrawButtonFocus((WXHDC) hDC,
293 lpDIS->rcItem.left,
294 lpDIS->rcItem.top,
295 lpDIS->rcItem.right,
296 lpDIS->rcItem.bottom,
297 isSelected);
298 }
299
300 return true;
301 }
302
303 // GRG Feb/2000, support for bmp buttons with Win95/98 standard LNF
304
305 #if defined(__WIN95__)
306
307 void wxBitmapButton::DrawFace( WXHDC dc, int left, int top,
308 int right, int bottom, bool sel )
309 {
310 HPEN oldp;
311 HPEN penHiLight;
312 HPEN penLight;
313 HPEN penShadow;
314 HPEN penDkShadow;
315 HBRUSH brushFace;
316
317 // create needed pens and brush
318 penHiLight = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DHILIGHT));
319 penLight = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DLIGHT));
320 penShadow = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DSHADOW));
321 penDkShadow = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DDKSHADOW));
322 // brushFace = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
323 // Taking the background colour fits in better with
324 // Windows XP themes.
325 brushFace = CreateSolidBrush(m_backgroundColour.m_pixel);
326
327 // draw the rectangle
328 RECT rect;
329 rect.left = left;
330 rect.right = right;
331 rect.top = top;
332 rect.bottom = bottom;
333 FillRect((HDC) dc, &rect, brushFace);
334
335 // draw the border
336 oldp = (HPEN) SelectObject( (HDC) dc, sel? penDkShadow : penHiLight);
337
338 wxDrawLine((HDC) dc, left, top, right-1, top);
339 wxDrawLine((HDC) dc, left, top+1, left, bottom-1);
340
341 SelectObject( (HDC) dc, sel? penShadow : penLight);
342 wxDrawLine((HDC) dc, left+1, top+1, right-2, top+1);
343 wxDrawLine((HDC) dc, left+1, top+2, left+1, bottom-2);
344
345 SelectObject( (HDC) dc, sel? penLight : penShadow);
346 wxDrawLine((HDC) dc, left+1, bottom-2, right-1, bottom-2);
347 wxDrawLine((HDC) dc, right-2, bottom-3, right-2, top);
348
349 SelectObject( (HDC) dc, sel? penHiLight : penDkShadow);
350 wxDrawLine((HDC) dc, left, bottom-1, right+2, bottom-1);
351 wxDrawLine((HDC) dc, right-1, bottom-2, right-1, top-1);
352
353 // delete allocated resources
354 SelectObject((HDC) dc,oldp);
355 DeleteObject(penHiLight);
356 DeleteObject(penLight);
357 DeleteObject(penShadow);
358 DeleteObject(penDkShadow);
359 DeleteObject(brushFace);
360 }
361
362 #else
363
364 void wxBitmapButton::DrawFace( WXHDC dc, int left, int top,
365 int right, int bottom, bool sel )
366 {
367 HPEN oldp;
368 HPEN penBorder;
369 HPEN penLight;
370 HPEN penShadow;
371 HBRUSH brushFace;
372
373 // create needed pens and brush
374 penBorder = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_WINDOWFRAME));
375 penShadow = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_BTNSHADOW));
376 penLight = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_BTNHIGHLIGHT));
377 brushFace = CreateSolidBrush(COLOR_BTNFACE);
378
379 // draw the rectangle
380 RECT rect;
381 rect.left = left;
382 rect.right = right;
383 rect.top = top;
384 rect.bottom = bottom;
385 FillRect((HDC) dc, &rect, brushFace);
386
387 // draw the border
388 oldp = (HPEN) SelectObject( (HDC) dc, penBorder);
389 MoveToEx((HDC) dc,left+1,top,NULL);LineTo((HDC) dc,right-1,top);
390 MoveToEx((HDC) dc,left,top+1,NULL);LineTo((HDC) dc,left,bottom-1);
391 MoveToEx((HDC) dc,left+1,bottom-1,NULL);LineTo((HDC) dc,right-1,bottom-1);
392 MoveToEx((HDC) dc,right-1,top+1,NULL);LineTo((HDC) dc,right-1,bottom-1);
393
394 SelectObject( (HDC) dc, penShadow);
395 if (sel)
396 {
397 MoveToEx((HDC) dc,left+1 ,bottom-2 ,NULL);
398 LineTo((HDC) dc, left+1 ,top+1);
399 LineTo((HDC) dc, right-2 ,top+1);
400 }
401 else
402 {
403 MoveToEx((HDC) dc,left+1 ,bottom-2 ,NULL);
404 LineTo((HDC) dc, right-2 ,bottom-2);
405 LineTo((HDC) dc, right-2 ,top);
406
407 MoveToEx((HDC) dc,left+2 ,bottom-3 ,NULL);
408 LineTo((HDC) dc, right-3 ,bottom-3);
409 LineTo((HDC) dc, right-3 ,top+1);
410
411 SelectObject( (HDC) dc, penLight);
412
413 MoveToEx((HDC) dc,left+1 ,bottom-2 ,NULL);
414 LineTo((HDC) dc, left+1 ,top+1);
415 LineTo((HDC) dc, right-2 ,top+1);
416 }
417
418 // delete allocated resources
419 SelectObject((HDC) dc,oldp);
420 DeleteObject(penBorder);
421 DeleteObject(penLight);
422 DeleteObject(penShadow);
423 DeleteObject(brushFace);
424 }
425
426 #endif // defined(__WIN95__)
427
428
429 void wxBitmapButton::DrawButtonFocus( WXHDC dc, int left, int top, int right,
430 int bottom, bool WXUNUSED(sel) )
431 {
432 RECT rect;
433 rect.left = left;
434 rect.top = top;
435 rect.right = right;
436 rect.bottom = bottom;
437 InflateRect( &rect, - FOCUS_MARGIN, - FOCUS_MARGIN );
438
439 // GRG: the focus rectangle should not move when the button is pushed!
440 /*
441 if ( sel )
442 OffsetRect( &rect, 1, 1 );
443 */
444
445 DrawFocusRect( (HDC) dc, &rect );
446 }
447
448 void
449 wxBitmapButton::DrawButtonDisable( WXHDC dc,
450 int left, int top, int right, int bottom,
451 bool with_marg )
452 {
453 if ( !m_brushDisabled.Ok() )
454 {
455 // draw a bitmap with two black and two background colour pixels
456 wxBitmap bmp(2, 2);
457 wxMemoryDC dc;
458 dc.SelectObject(bmp);
459 dc.SetPen(*wxBLACK_PEN);
460 dc.DrawPoint(0, 0);
461 dc.DrawPoint(1, 1);
462 dc.SetPen(GetBackgroundColour());
463 dc.DrawPoint(0, 1);
464 dc.DrawPoint(1, 0);
465
466 m_brushDisabled = wxBrush(bmp);
467 }
468
469 SelectInHDC selectBrush((HDC)dc, GetHbrushOf(m_brushDisabled));
470
471 // ROP for "dest |= pattern" operation -- as it doesn't have a standard
472 // name, give it our own
473 static const DWORD PATTERNPAINT = 0xFA0089UL;
474
475 if ( with_marg )
476 {
477 left += m_marginX;
478 top += m_marginY;
479 right -= 2 * m_marginX;
480 bottom -= 2 * m_marginY;
481 }
482
483 ::PatBlt( (HDC) dc, left, top, right, bottom, PATTERNPAINT);
484 }
485
486 void wxBitmapButton::SetDefault()
487 {
488 wxButton::SetDefault();
489 }
490
491 wxSize wxBitmapButton::DoGetBestSize() const
492 {
493 if ( m_bmpNormal.Ok() )
494 {
495 wxSize best(m_bmpNormal.GetWidth() + 2*m_marginX,
496 m_bmpNormal.GetHeight() + 2*m_marginY);
497 CacheBestSize(best);
498 return best;
499 }
500
501 // no idea what our best size should be, defer to the base class
502 return wxBitmapButtonBase::DoGetBestSize();
503 }
504
505 #endif // wxUSE_BMPBUTTON
506