1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/button.cpp
3 // Purpose: wxButton
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22
23 #if wxUSE_BUTTON
24
25 #include "wx/button.h"
26
27 #ifndef WX_PRECOMP
28 #include "wx/app.h"
29 #include "wx/brush.h"
30 #include "wx/panel.h"
31 #include "wx/bmpbuttn.h"
32 #include "wx/settings.h"
33 #include "wx/dcscreen.h"
34 #include "wx/dcclient.h"
35 #include "wx/toplevel.h"
36 #include "wx/msw/wrapcctl.h"
37 #include "wx/msw/private.h"
38 #include "wx/msw/missing.h"
39 #endif
40
41 #include "wx/imaglist.h"
42 #include "wx/stockitem.h"
43 #include "wx/msw/private/button.h"
44 #include "wx/msw/private/dc.h"
45 #include "wx/private/window.h"
46
47 #if wxUSE_MARKUP
48 #include "wx/generic/private/markuptext.h"
49 #endif // wxUSE_MARKUP
50
51 // set the value for BCM_SETSHIELD (for the UAC shield) if it's not defined in
52 // the header
53 #ifndef BCM_SETSHIELD
54 #define BCM_SETSHIELD 0x160c
55 #endif
56
57 // ----------------------------------------------------------------------------
58 // macros
59 // ----------------------------------------------------------------------------
60
wxBEGIN_EVENT_TABLE(wxButton,wxButtonBase)61 wxBEGIN_EVENT_TABLE(wxButton, wxButtonBase)
62 EVT_CHAR_HOOK(wxButton::OnCharHook)
63 wxEND_EVENT_TABLE()
64
65 // ============================================================================
66 // implementation
67 // ============================================================================
68
69 // ----------------------------------------------------------------------------
70 // creation/destruction
71 // ----------------------------------------------------------------------------
72
73 bool wxButton::Create(wxWindow *parent,
74 wxWindowID id,
75 const wxString& lbl,
76 const wxPoint& pos,
77 const wxSize& size,
78 long style,
79 const wxValidator& validator,
80 const wxString& name)
81 {
82 wxString label;
83 if ( !(style & wxBU_NOTEXT) )
84 {
85 label = lbl;
86 if (label.empty() && wxIsStockID(id))
87 {
88 // On Windows, some buttons aren't supposed to have mnemonics
89 label = wxGetStockLabel
90 (
91 id,
92 id == wxID_OK || id == wxID_CANCEL || id == wxID_CLOSE
93 ? wxSTOCK_NOFLAGS
94 : wxSTOCK_WITH_MNEMONIC
95 );
96 }
97 }
98
99 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
100 return false;
101
102 WXDWORD exstyle;
103 WXDWORD msStyle = MSWGetStyle(style, &exstyle);
104
105 // if the label contains several lines we must explicitly tell the button
106 // about it or it wouldn't draw it correctly ("\n"s would just appear as
107 // black boxes)
108 //
109 // NB: we do it here and not in MSWGetStyle() because we need the label
110 // value and the label is not set yet when MSWGetStyle() is called
111 msStyle |= wxMSWButton::GetMultilineStyle(label);
112
113 return MSWCreateControl(wxT("BUTTON"), msStyle, pos, size, label, exstyle);
114 }
115
~wxButton()116 wxButton::~wxButton()
117 {
118 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
119 if ( tlw && tlw->GetTmpDefaultItem() == this )
120 {
121 UnsetTmpDefault();
122 }
123 }
124
125 // ----------------------------------------------------------------------------
126 // flags
127 // ----------------------------------------------------------------------------
128
MSWGetStyle(long style,WXDWORD * exstyle) const129 WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const
130 {
131 // buttons never have an external border, they draw their own one
132 WXDWORD msStyle = wxControl::MSWGetStyle
133 (
134 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
135 );
136
137 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
138 // each other in any resizable dialog which has more than one button in
139 // the bottom
140 msStyle |= WS_CLIPSIBLINGS;
141
142 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
143 // and wxBU_RIGHT to get BS_CENTER!
144 if ( style & wxBU_LEFT )
145 msStyle |= BS_LEFT;
146 if ( style & wxBU_RIGHT )
147 msStyle |= BS_RIGHT;
148 if ( style & wxBU_TOP )
149 msStyle |= BS_TOP;
150 if ( style & wxBU_BOTTOM )
151 msStyle |= BS_BOTTOM;
152 // flat 2d buttons
153 if ( style & wxNO_BORDER )
154 msStyle |= BS_FLAT;
155
156 return msStyle;
157 }
158
159 /* static */
GetDefaultSize(wxWindow * win)160 wxSize wxButtonBase::GetDefaultSize(wxWindow* win)
161 {
162 static wxPrivate::DpiDependentValue<wxSize> s_sizeBtn;
163
164 if ( s_sizeBtn.HasChanged(win) )
165 {
166 wxSize base;
167 if ( win )
168 {
169 wxClientDC dc(win);
170 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
171 base = wxPrivate::GetAverageASCIILetterSize(dc);
172 }
173 else
174 {
175 wxScreenDC dc;
176 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
177 base = wxPrivate::GetAverageASCIILetterSize(dc);
178 }
179
180 // The size of a standard button in the dialog units is 50x14,
181 // translate this to pixels.
182 //
183 // Windows' computes dialog units using average character width over
184 // upper- and lower-case ASCII alphabet and not using the average
185 // character width metadata stored in the font; see
186 // http://support.microsoft.com/default.aspx/kb/145994 for detailed
187 // discussion.
188 //
189 // NB: wxMulDivInt32() is used, because it correctly rounds the result
190
191 s_sizeBtn.SetAtNewDPI(wxSize(wxMulDivInt32(50, base.x, 4),
192 wxMulDivInt32(14, base.y, 8)));
193 }
194
195 return s_sizeBtn.Get();
196 }
197
198 // ----------------------------------------------------------------------------
199 // default button handling
200 // ----------------------------------------------------------------------------
201
202 /*
203 In normal Windows programs there is no need to handle default button
204 manually because this is taken care by the system provided you use
205 WM_NEXTDLGCTL and not just SetFocus() to switch focus betweeh the controls
206 (see http://blogs.msdn.com/oldnewthing/archive/2004/08/02/205624.aspx for
207 the full explanation why just calling SetFocus() is not enough).
208
209 However this only works if the window is a dialog, i.e. uses DefDlgProc(),
210 but not with plain windows using DefWindowProc() and we do want to have
211 default buttons inside frames as well, so we're forced to reimplement all
212 this logic ourselves. It would be great to avoid having to do this but using
213 DefDlgProc() for all the windows would almost certainly result in more
214 problems, we'd need to carefully filter messages and pass some of them to
215 DefWindowProc() and some of them to DefDlgProc() which looks dangerous (what
216 if the handling of some message changes in some Windows version?), so doing
217 this ourselves is probably a lesser evil.
218
219 Read the rest to learn everything you ever wanted to know about the default
220 buttons but were afraid to ask.
221
222
223 In MSW the default button should be activated when the user presses Enter
224 and the current control doesn't process Enter itself somehow. This is
225 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
226 Another aspect of "defaultness" is that the default button has different
227 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
228 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
229 be unset if our parent window is not active so it should be unset whenever
230 we lose activation and set back when we regain it.
231
232 Final complication is that when a button is active, it should be the default
233 one, i.e. pressing Enter on a button always activates it and not another
234 one.
235
236 We handle this by maintaining a permanent and a temporary default items in
237 wxControlContainer (both may be NULL). When a button becomes the current
238 control (i.e. gets focus) it sets itself as the temporary default which
239 ensures that it has the right appearance and that Enter will be redirected
240 to it. When the button loses focus, it unsets the temporary default and so
241 the default item will be the permanent default -- that is the default button
242 if any had been set or none otherwise, which is just what we want.
243 */
244
245 // set this button as the (permanently) default one in its panel
SetDefault()246 wxWindow *wxButton::SetDefault()
247 {
248 // set this one as the default button both for wxWidgets ...
249 wxWindow *winOldDefault = wxButtonBase::SetDefault();
250
251 // ... and Windows
252 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false);
253 SetDefaultStyle(this, true);
254
255 return winOldDefault;
256 }
257
258 // return the top level parent window if it's not being deleted yet, otherwise
259 // return NULL
GetTLWParentIfNotBeingDeleted(wxWindow * win)260 static wxTopLevelWindow *GetTLWParentIfNotBeingDeleted(wxWindow *win)
261 {
262 for ( ;; )
263 {
264 // IsTopLevel() will return false for a wxTLW being deleted, so we also
265 // need the parent test for this case
266 wxWindow * const parent = win->GetParent();
267 if ( !parent || win->IsTopLevel() )
268 {
269 if ( win->IsBeingDeleted() )
270 return NULL;
271
272 break;
273 }
274
275 win = parent;
276 }
277
278 wxASSERT_MSG( win, wxT("button without top level parent?") );
279
280 // Note that this may still return null for a button inside wxPopupWindow.
281 return wxDynamicCast(win, wxTopLevelWindow);
282 }
283
284 // set this button as being currently default
SetTmpDefault()285 void wxButton::SetTmpDefault()
286 {
287 wxTopLevelWindow * const tlw = GetTLWParentIfNotBeingDeleted(this);
288 if ( !tlw )
289 return;
290
291 wxWindow *winOldDefault = tlw->GetDefaultItem();
292
293 tlw->SetTmpDefaultItem(this);
294
295 // Notice that the order of these statements is important, the old button
296 // is not reset if we do it the other way round, probably because of
297 // something done by the default DM_SETDEFID handler.
298 SetDefaultStyle(this, true);
299 if ( winOldDefault != this )
300 {
301 // But we mustn't reset the default style on this button itself if it
302 // had already been the default.
303 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false);
304 }
305 }
306
307 // unset this button as currently default, it may still stay permanent default
UnsetTmpDefault()308 void wxButton::UnsetTmpDefault()
309 {
310 wxTopLevelWindow * const tlw = GetTLWParentIfNotBeingDeleted(this);
311 if ( !tlw )
312 return;
313
314 tlw->SetTmpDefaultItem(NULL);
315
316 wxWindow *winOldDefault = tlw->GetDefaultItem();
317
318 // Just as in SetTmpDefault() above, the order is important here.
319 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), true);
320 if ( winOldDefault != this )
321 {
322 SetDefaultStyle(this, false);
323 }
324 }
325
326 /* static */
327 void
SetDefaultStyle(wxButton * btn,bool on)328 wxButton::SetDefaultStyle(wxButton *btn, bool on)
329 {
330 // we may be called with NULL pointer -- simpler to do the check here than
331 // in the caller which does wxDynamicCast()
332 if ( !btn )
333 return;
334
335 // first, let DefDlgProc() know about the new default button
336 if ( on )
337 {
338 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
339 // focus at all any more
340 if ( !wxTheApp->IsActive() )
341 return;
342
343 wxWindow * const tlw = wxGetTopLevelParent(btn);
344 wxCHECK_RET( tlw, wxT("button without top level window?") );
345
346 ::SendMessage(GetHwndOf(tlw), DM_SETDEFID, btn->GetId(), 0L);
347
348 // sending DM_SETDEFID also changes the button style to
349 // BS_DEFPUSHBUTTON so there is nothing more to do
350 }
351
352 // then also change the style as needed
353 long style = ::GetWindowLong(GetHwndOf(btn), GWL_STYLE);
354 if ( !(style & BS_DEFPUSHBUTTON) == on )
355 {
356 // don't do it with the owner drawn buttons because it will
357 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
358 // BS_DEFPUSHBUTTON != 0)!
359 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
360 {
361 ::SendMessage(GetHwndOf(btn), BM_SETSTYLE,
362 on ? style | BS_DEFPUSHBUTTON
363 : style & ~BS_DEFPUSHBUTTON,
364 1L /* redraw */);
365 }
366 else // owner drawn
367 {
368 // redraw the button - it will notice itself that it's
369 // [not] the default one [any longer]
370 btn->Refresh();
371 }
372 }
373 //else: already has correct style
374 }
375
376 // ----------------------------------------------------------------------------
377 // helpers
378 // ----------------------------------------------------------------------------
379
SendClickEvent()380 bool wxButton::SendClickEvent()
381 {
382 wxCommandEvent event(wxEVT_BUTTON, GetId());
383 event.SetEventObject(this);
384
385 return ProcessCommand(event);
386 }
387
Command(wxCommandEvent & event)388 void wxButton::Command(wxCommandEvent & event)
389 {
390 ProcessCommand(event);
391 }
392
393 // ----------------------------------------------------------------------------
394 // event/message handlers
395 // ----------------------------------------------------------------------------
396
OnCharHook(wxKeyEvent & event)397 void wxButton::OnCharHook(wxKeyEvent& event)
398 {
399 // We want to ensure that the button always processes Enter key events
400 // itself, even if it's inside some control that normally takes over them
401 // (this happens when the button is part of an in-place editor control for
402 // example).
403 if ( event.GetKeyCode() == WXK_RETURN )
404 {
405 // We should ensure that subsequent key events are still generated even
406 // if we did handle EVT_CHAR_HOOK (normally this would suppress their
407 // generation).
408 event.DoAllowNextEvent();
409 }
410 else
411 {
412 event.Skip();
413 }
414 }
415
MSWCommand(WXUINT param,WXWORD WXUNUSED (id))416 bool wxButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
417 {
418 bool processed = false;
419 switch ( param )
420 {
421 // NOTE: Currently all versions of Windows send two BN_CLICKED messages
422 // for all button types, so we don't catch BN_DOUBLECLICKED
423 // in order to not get 3 EVT_BUTTON events. If this is a problem
424 // then we need to figure out which version of the comctl32 changed
425 // this behaviour and test for it.
426
427 case 1: // message came from an accelerator
428 case BN_CLICKED: // normal buttons send this
429 processed = SendClickEvent();
430 break;
431 }
432
433 return processed;
434 }
435
MSWWindowProc(WXUINT nMsg,WXWPARAM wParam,WXLPARAM lParam)436 WXLRESULT wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
437 {
438 // when we receive focus, we want to temporarily become the default button in
439 // our parent panel so that pressing "Enter" would activate us -- and when
440 // losing it we should restore the previous default button as well
441 if ( nMsg == WM_SETFOCUS )
442 {
443 SetTmpDefault();
444
445 // let the default processing take place too
446 }
447 else if ( nMsg == WM_KILLFOCUS )
448 {
449 UnsetTmpDefault();
450 }
451
452 // let the base class do all real processing
453 return wxAnyButton::MSWWindowProc(nMsg, wParam, lParam);
454 }
455
456 // ----------------------------------------------------------------------------
457 // authentication needed handling
458 // ----------------------------------------------------------------------------
459
DoGetAuthNeeded() const460 bool wxButton::DoGetAuthNeeded() const
461 {
462 return m_authNeeded;
463 }
464
DoSetAuthNeeded(bool show)465 void wxButton::DoSetAuthNeeded(bool show)
466 {
467 // show/hide UAC symbol on Windows Vista and later
468 if ( wxGetWinVersion() >= wxWinVersion_6 )
469 {
470 m_authNeeded = show;
471 ::SendMessage(GetHwnd(), BCM_SETSHIELD, 0, show);
472 InvalidateBestSize();
473 }
474 }
475
476 #endif // wxUSE_BUTTON
477
478