1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/button.cpp
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
29 #include "wx/button.h"
35 #include "wx/bmpbuttn.h"
36 #include "wx/settings.h"
37 #include "wx/dcscreen.h"
38 #include "wx/dcclient.h"
39 #include "wx/toplevel.h"
40 #include "wx/msw/wrapcctl.h"
41 #include "wx/msw/private.h"
42 #include "wx/msw/missing.h"
45 #include "wx/imaglist.h"
46 #include "wx/stockitem.h"
47 #include "wx/msw/private/button.h"
48 #include "wx/msw/private/dc.h"
49 #include "wx/private/window.h"
52 #include "wx/generic/private/markuptext.h"
53 #endif // wxUSE_MARKUP
55 // set the value for BCM_SETSHIELD (for the UAC shield) if it's not defined in
58 #define BCM_SETSHIELD 0x160c
61 // ----------------------------------------------------------------------------
63 // ----------------------------------------------------------------------------
65 // ============================================================================
67 // ============================================================================
69 // ----------------------------------------------------------------------------
70 // creation/destruction
71 // ----------------------------------------------------------------------------
73 bool wxButton::Create(wxWindow
*parent
,
79 const wxValidator
& validator
,
83 if (label
.empty() && wxIsStockID(id
))
85 // On Windows, some buttons aren't supposed to have mnemonics
86 label
= wxGetStockLabel
89 id
== wxID_OK
|| id
== wxID_CANCEL
|| id
== wxID_CLOSE
91 : wxSTOCK_WITH_MNEMONIC
95 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
99 WXDWORD msStyle
= MSWGetStyle(style
, &exstyle
);
101 // if the label contains several lines we must explicitly tell the button
102 // about it or it wouldn't draw it correctly ("\n"s would just appear as
105 // NB: we do it here and not in MSWGetStyle() because we need the label
106 // value and the label is not set yet when MSWGetStyle() is called
107 msStyle
|= wxMSWButton::GetMultilineStyle(label
);
109 return MSWCreateControl(wxT("BUTTON"), msStyle
, pos
, size
, label
, exstyle
);
112 wxButton::~wxButton()
114 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
115 if ( tlw
&& tlw
->GetTmpDefaultItem() == this )
121 // ----------------------------------------------------------------------------
123 // ----------------------------------------------------------------------------
125 WXDWORD
wxButton::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
127 // buttons never have an external border, they draw their own one
128 WXDWORD msStyle
= wxControl::MSWGetStyle
130 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
133 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
134 // each other in any resizable dialog which has more than one button in
136 msStyle
|= WS_CLIPSIBLINGS
;
138 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
139 // and wxBU_RIGHT to get BS_CENTER!
140 if ( style
& wxBU_LEFT
)
142 if ( style
& wxBU_RIGHT
)
144 if ( style
& wxBU_TOP
)
146 if ( style
& wxBU_BOTTOM
)
147 msStyle
|= BS_BOTTOM
;
150 if ( style
& wxNO_BORDER
)
152 #endif // __WXWINCE__
158 wxSize
wxButtonBase::GetDefaultSize()
160 static wxSize s_sizeBtn
;
162 if ( s_sizeBtn
.x
== 0 )
165 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
167 // The size of a standard button in the dialog units is 50x14,
168 // translate this to pixels.
170 // Windows' computes dialog units using average character width over
171 // upper- and lower-case ASCII alphabet and not using the average
172 // character width metadata stored in the font; see
173 // http://support.microsoft.com/default.aspx/kb/145994 for detailed
176 // NB: wxMulDivInt32() is used, because it correctly rounds the result
178 const wxSize base
= wxPrivate::GetAverageASCIILetterSize(dc
);
179 s_sizeBtn
.x
= wxMulDivInt32(50, base
.x
, 4);
180 s_sizeBtn
.y
= wxMulDivInt32(14, base
.y
, 8);
186 // ----------------------------------------------------------------------------
187 // default button handling
188 // ----------------------------------------------------------------------------
191 The comment below and all this code is probably due to not using WM_NEXTDLGCTL
192 message when changing focus (but just SetFocus() which is not enough), see
193 http://blogs.msdn.com/oldnewthing/archive/2004/08/02/205624.aspx for the
196 TODO: Do use WM_NEXTDLGCTL and get rid of all this code.
199 "Everything you ever wanted to know about the default buttons" or "Why do we
200 have to do all this?"
202 In MSW the default button should be activated when the user presses Enter
203 and the current control doesn't process Enter itself somehow. This is
204 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
205 Another aspect of "defaultness" is that the default button has different
206 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
207 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
208 be unset if our parent window is not active so it should be unset whenever
209 we lose activation and set back when we regain it.
211 Final complication is that when a button is active, it should be the default
212 one, i.e. pressing Enter on a button always activates it and not another
215 We handle this by maintaining a permanent and a temporary default items in
216 wxControlContainer (both may be NULL). When a button becomes the current
217 control (i.e. gets focus) it sets itself as the temporary default which
218 ensures that it has the right appearance and that Enter will be redirected
219 to it. When the button loses focus, it unsets the temporary default and so
220 the default item will be the permanent default -- that is the default button
221 if any had been set or none otherwise, which is just what we want.
223 NB: all this is quite complicated by now and the worst is that normally
224 it shouldn't be necessary at all as for the normal Windows programs
225 DefWindowProc() and IsDialogMessage() take care of all this
226 automatically -- however in wxWidgets programs this doesn't work for
227 nested hierarchies (i.e. a notebook inside a notebook) for unknown
228 reason and so we have to reproduce all this code ourselves. It would be
229 very nice if we could avoid doing it.
232 // set this button as the (permanently) default one in its panel
233 wxWindow
*wxButton::SetDefault()
235 // set this one as the default button both for wxWidgets ...
236 wxWindow
*winOldDefault
= wxButtonBase::SetDefault();
239 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
240 SetDefaultStyle(this, true);
242 return winOldDefault
;
245 // return the top level parent window if it's not being deleted yet, otherwise
247 static wxTopLevelWindow
*GetTLWParentIfNotBeingDeleted(wxWindow
*win
)
251 // IsTopLevel() will return false for a wxTLW being deleted, so we also
252 // need the parent test for this case
253 wxWindow
* const parent
= win
->GetParent();
254 if ( !parent
|| win
->IsTopLevel() )
256 if ( win
->IsBeingDeleted() )
265 wxASSERT_MSG( win
, wxT("button without top level parent?") );
267 wxTopLevelWindow
* const tlw
= wxDynamicCast(win
, wxTopLevelWindow
);
268 wxASSERT_MSG( tlw
, wxT("logic error in GetTLWParentIfNotBeingDeleted()") );
273 // set this button as being currently default
274 void wxButton::SetTmpDefault()
276 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
280 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
281 tlw
->SetTmpDefaultItem(this);
283 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
284 SetDefaultStyle(this, true);
287 // unset this button as currently default, it may still stay permanent default
288 void wxButton::UnsetTmpDefault()
290 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
294 tlw
->SetTmpDefaultItem(NULL
);
296 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
298 SetDefaultStyle(this, false);
299 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), true);
304 wxButton::SetDefaultStyle(wxButton
*btn
, bool on
)
306 // we may be called with NULL pointer -- simpler to do the check here than
307 // in the caller which does wxDynamicCast()
311 // first, let DefDlgProc() know about the new default button
314 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
315 // focus at all any more
316 if ( !wxTheApp
->IsActive() )
319 wxWindow
* const tlw
= wxGetTopLevelParent(btn
);
320 wxCHECK_RET( tlw
, wxT("button without top level window?") );
322 ::SendMessage(GetHwndOf(tlw
), DM_SETDEFID
, btn
->GetId(), 0L);
324 // sending DM_SETDEFID also changes the button style to
325 // BS_DEFPUSHBUTTON so there is nothing more to do
328 // then also change the style as needed
329 long style
= ::GetWindowLong(GetHwndOf(btn
), GWL_STYLE
);
330 if ( !(style
& BS_DEFPUSHBUTTON
) == on
)
332 // don't do it with the owner drawn buttons because it will
333 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
334 // BS_DEFPUSHBUTTON != 0)!
335 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
337 ::SendMessage(GetHwndOf(btn
), BM_SETSTYLE
,
338 on
? style
| BS_DEFPUSHBUTTON
339 : style
& ~BS_DEFPUSHBUTTON
,
344 // redraw the button - it will notice itself that it's
345 // [not] the default one [any longer]
349 //else: already has correct style
352 // ----------------------------------------------------------------------------
354 // ----------------------------------------------------------------------------
356 bool wxButton::SendClickEvent()
358 wxCommandEvent
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetId());
359 event
.SetEventObject(this);
361 return ProcessCommand(event
);
364 void wxButton::Command(wxCommandEvent
& event
)
366 ProcessCommand(event
);
369 // ----------------------------------------------------------------------------
370 // event/message handlers
371 // ----------------------------------------------------------------------------
373 bool wxButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
375 bool processed
= false;
378 // NOTE: Apparently older versions (NT 4?) of the common controls send
379 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
380 // buttons, so in order to send two EVT_BUTTON events we should
381 // catch both types. Currently (Feb 2003) up-to-date versions of
382 // win98, win2k and winXP all send two BN_CLICKED messages for
383 // all button types, so we don't catch BN_DOUBLECLICKED anymore
384 // in order to not get 3 EVT_BUTTON events. If this is a problem
385 // then we need to figure out which version of the comctl32 changed
386 // this behaviour and test for it.
388 case 1: // message came from an accelerator
389 case BN_CLICKED
: // normal buttons send this
390 processed
= SendClickEvent();
397 WXLRESULT
wxButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
399 // when we receive focus, we want to temporarily become the default button in
400 // our parent panel so that pressing "Enter" would activate us -- and when
401 // losing it we should restore the previous default button as well
402 if ( nMsg
== WM_SETFOCUS
)
406 // let the default processing take place too
408 else if ( nMsg
== WM_KILLFOCUS
)
413 // let the base class do all real processing
414 return wxAnyButton::MSWWindowProc(nMsg
, wParam
, lParam
);
417 // ----------------------------------------------------------------------------
418 // authentication needed handling
419 // ----------------------------------------------------------------------------
421 bool wxButton::DoGetAuthNeeded() const
426 void wxButton::DoSetAuthNeeded(bool show
)
428 // show/hide UAC symbol on Windows Vista and later
429 if ( wxGetWinVersion() >= wxWinVersion_6
)
432 ::SendMessage(GetHwnd(), BCM_SETSHIELD
, 0, show
);
433 InvalidateBestSize();
437 #endif // wxUSE_BUTTON