move default button handling code from wxControlContainer to wxTLW (patch 1524441)
[wxWidgets.git] / src / os2 / button.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/os2/button.cpp
3 // Purpose: wxButton
4 // Author: David Webster
5 // Modified by:
6 // Created: 10/13/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 #include "wx/button.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/app.h"
19 #include "wx/brush.h"
20 #include "wx/panel.h"
21 #include "wx/bmpbuttn.h"
22 #include "wx/settings.h"
23 #include "wx/dcscreen.h"
24 #include "wx/scrolwin.h"
25 #endif
26
27 #include "wx/stockitem.h"
28 #include "wx/os2/private.h"
29
30 #define BUTTON_HEIGHT_FROM_CHAR_HEIGHT(cy) (11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy)/10)
31
32 //
33 // Should be at the very least less than winDEFAULT_BUTTON_MARGIN
34 //
35 #define FOCUS_MARGIN 3
36
37 #ifndef BST_CHECKED
38 #define BST_CHECKED 0x0001
39 #endif
40
41 IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl)
42
43 // Button
44
45 bool wxButton::Create( wxWindow* pParent,
46 wxWindowID vId,
47 const wxString& rsLbl,
48 const wxPoint& rPos,
49 const wxSize& rSize,
50 long lStyle,
51 const wxValidator& rValidator,
52 const wxString& rsName)
53 {
54 wxString rsLabel(rsLbl);
55 if (rsLabel.empty() && wxIsStockID(vId))
56 rsLabel = wxGetStockLabel(vId);
57
58 wxString sLabel = ::wxPMTextToLabel(rsLabel);
59
60 SetName(rsName);
61 #if wxUSE_VALIDATORS
62 SetValidator(rValidator);
63 #endif
64 m_windowStyle = lStyle;
65 pParent->AddChild((wxButton *)this);
66 if (vId == -1)
67 m_windowId = NewControlId();
68 else
69 m_windowId = vId;
70 lStyle = WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON;
71
72 //
73 // OS/2 PM does not have Right/Left/Top/Bottom styles.
74 // We will have to define an additional style when we implement notebooks
75 // for a notebook page button
76 //
77 if (m_windowStyle & wxCLIP_SIBLINGS )
78 lStyle |= WS_CLIPSIBLINGS;
79
80 m_hWnd = (WXHWND)::WinCreateWindow( GetHwndOf(pParent) // Parent handle
81 ,WC_BUTTON // A Button class window
82 ,(PSZ)sLabel.c_str() // Button text
83 ,lStyle // Button style
84 ,0, 0, 0, 0 // Location and size
85 ,GetHwndOf(pParent) // Owner handle
86 ,HWND_TOP // Top of Z-Order
87 ,vId // Identifier
88 ,NULL // No control data
89 ,NULL // No Presentation parameters
90 );
91 if (m_hWnd == 0)
92 {
93 return false;
94 }
95
96 //
97 // Subclass again for purposes of dialog editing mode
98 //
99 SubclassWin(m_hWnd);
100 wxFont* pButtonFont = new wxFont( 8
101 ,wxSWISS
102 ,wxNORMAL
103 ,wxNORMAL
104 );
105 SetFont(*pButtonFont);
106 SetXComp(0);
107 SetYComp(0);
108 SetSize( rPos.x
109 ,rPos.y
110 ,rSize.x
111 ,rSize.y
112 );
113 delete pButtonFont;
114 return true;
115 } // end of wxButton::Create
116
117 wxButton::~wxButton()
118 {
119 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
120
121 if (tlw)
122 {
123 if (tlw->GetDefaultItem() == this)
124 {
125 //
126 // Don't leave the panel with invalid default item
127 //
128 tlw->SetDefaultItem(NULL);
129 }
130 }
131 } // end of wxButton::~wxButton
132
133 // ----------------------------------------------------------------------------
134 // size management including autosizing
135 // ----------------------------------------------------------------------------
136
137 wxSize wxButton::DoGetBestSize() const
138 {
139 wxString rsLabel = wxGetWindowText(GetHWND());
140 int nWidthButton;
141 int nWidthChar;
142 int nHeightChar;
143 wxFont vFont = (wxFont)GetFont();
144
145 GetTextExtent( rsLabel
146 ,&nWidthButton
147 ,NULL
148 );
149
150 wxGetCharSize( GetHWND()
151 ,&nWidthChar
152 ,&nHeightChar
153 ,&vFont
154 );
155
156 //
157 // Add a margin - the button is wider than just its label
158 //
159 nWidthButton += 3 * nWidthChar;
160
161 //
162 // The button height is proportional to the height of the font used
163 //
164 int nHeightButton = BUTTON_HEIGHT_FROM_CHAR_HEIGHT(nHeightChar);
165
166 //
167 // Need a little extra to make it look right
168 //
169 nHeightButton += (int)(nHeightChar/1.5);
170
171 if (!HasFlag(wxBU_EXACTFIT))
172 {
173 wxSize vSize = GetDefaultSize();
174
175 if (nWidthButton > vSize.x)
176 vSize.x = nWidthButton;
177 if (nHeightButton > vSize.y)
178 vSize.y = nHeightButton;
179 return vSize;
180 }
181 return wxSize( nWidthButton
182 ,nHeightButton
183 );
184 } // end of wxButton::DoGetBestSize
185
186 /* static */
187 wxSize wxButton::GetDefaultSize()
188 {
189 static wxSize vSizeBtn;
190
191 if (vSizeBtn.x == 0)
192 {
193 wxScreenDC vDc;
194
195 vDc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
196
197 //
198 // The size of a standard button in the dialog units is 50x14,
199 // translate this to pixels
200 // NB1: the multipliers come from the Windows convention
201 // NB2: the extra +1/+2 were needed to get the size be the same as the
202 // size of the buttons in the standard dialog - I don't know how
203 // this happens, but on my system this size is 75x23 in pixels and
204 // 23*8 isn't even divisible by 14... Would be nice to understand
205 // why these constants are needed though!
206 vSizeBtn.x = (50 * (vDc.GetCharWidth() + 1))/4;
207 vSizeBtn.y = ((14 * vDc.GetCharHeight()) + 2)/8;
208 }
209 return vSizeBtn;
210 } // end of wxButton::GetDefaultSize
211
212 void wxButton::Command (
213 wxCommandEvent& rEvent
214 )
215 {
216 ProcessCommand (rEvent);
217 } // end of wxButton::Command
218
219 // ----------------------------------------------------------------------------
220 // helpers
221 // ----------------------------------------------------------------------------
222
223 bool wxButton::SendClickEvent()
224 {
225 wxCommandEvent vEvent( wxEVT_COMMAND_BUTTON_CLICKED
226 ,GetId()
227 );
228
229 vEvent.SetEventObject(this);
230 return ProcessCommand(vEvent);
231 } // end of wxButton::SendClickEvent
232
233 void wxButton::SetDefault()
234 {
235 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
236
237 wxCHECK_RET( tlw, _T("button without top level window?") );
238
239 //
240 // Set this one as the default button both for wxWidgets and Windows
241 //
242 wxWindow* pWinOldDefault = tlw->SetDefaultItem(this);
243
244 SetDefaultStyle( wxDynamicCast(pWinOldDefault, wxButton), false);
245 SetDefaultStyle( this, true );
246 } // end of wxButton::SetDefault
247
248 void wxButton::SetTmpDefault()
249 {
250 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
251
252 wxCHECK_RET( tlw, _T("button without top level window?") );
253
254 wxWindow* pWinOldDefault = tlw->GetDefaultItem();
255
256 tlw->SetTmpDefaultItem(this);
257 SetDefaultStyle( wxDynamicCast(pWinOldDefault, wxButton), false);
258 SetDefaultStyle( this, true );
259 } // end of wxButton::SetTmpDefault
260
261 void wxButton::UnsetTmpDefault()
262 {
263 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
264
265 wxCHECK_RET( tlw, _T("button without top level window?") );
266
267 tlw->SetTmpDefaultItem(NULL);
268
269 wxWindow* pWinOldDefault = tlw->GetDefaultItem();
270
271 SetDefaultStyle( this, false );
272 SetDefaultStyle( wxDynamicCast(pWinOldDefault, wxButton), true );
273 } // end of wxButton::UnsetTmpDefault
274
275 void wxButton::SetDefaultStyle(
276 wxButton* pBtn
277 , bool bOn
278 )
279 {
280 long lStyle;
281 //
282 // We may be called with NULL pointer -- simpler to do the check here than
283 // in the caller which does wxDynamicCast()
284 //
285 if (!pBtn)
286 return;
287
288 //
289 // First, let DefDlgProc() know about the new default button
290 //
291 if (bOn)
292 {
293 if (!wxTheApp->IsActive())
294 return;
295
296 //
297 // In OS/2 the dialog/panel doesn't really know it has a default
298 // button, the default button simply has that style. We'll just
299 // simulate by setting focus to it
300 //
301 pBtn->SetFocus();
302 }
303 lStyle = ::WinQueryWindowULong(GetHwndOf(pBtn), QWL_STYLE);
304 if (!(lStyle & BS_DEFAULT) == bOn)
305 {
306 if ((lStyle & BS_USERBUTTON) != BS_USERBUTTON)
307 {
308 if (bOn)
309 lStyle |= BS_DEFAULT;
310 else
311 lStyle &= ~BS_DEFAULT;
312 ::WinSetWindowULong(GetHwndOf(pBtn), QWL_STYLE, lStyle);
313 }
314 else
315 {
316 //
317 // Redraw the button - it will notice itself that it's not the
318 // default one any longer
319 //
320 pBtn->Refresh();
321 }
322 }
323 } // end of wxButton::UpdateDefaultStyle
324
325 // ----------------------------------------------------------------------------
326 // event/message handlers
327 // ----------------------------------------------------------------------------
328
329 bool wxButton::OS2Command(WXUINT uParam, WXWORD WXUNUSED(wId))
330 {
331 bool bProcessed = false;
332
333 switch (uParam)
334 {
335 case BN_CLICKED: // normal buttons send this
336 case BN_DBLCLICKED: // owner-drawn ones also send this
337 bProcessed = SendClickEvent();
338 break;
339 }
340
341 return bProcessed;
342 } // end of wxButton::OS2Command
343
344 WXHBRUSH wxButton::OnCtlColor( WXHDC WXUNUSED(pDC),
345 WXHWND WXUNUSED(pWnd),
346 WXUINT WXUNUSED(nCtlColor),
347 WXUINT WXUNUSED(uMessage),
348 WXWPARAM WXUNUSED(wParam),
349 WXLPARAM WXUNUSED(lParam) )
350 {
351 wxBrush* pBackgroundBrush = wxTheBrushList->FindOrCreateBrush( GetBackgroundColour()
352 ,wxSOLID
353 );
354
355 return (WXHBRUSH)pBackgroundBrush->GetResourceHandle();
356 } // end of wxButton::OnCtlColor
357
358 void wxButton::MakeOwnerDrawn()
359 {
360 long lStyle = 0L;
361
362 lStyle = ::WinQueryWindowULong(GetHwnd(), QWL_STYLE);
363 if ((lStyle & BS_USERBUTTON) != BS_USERBUTTON)
364 {
365 //
366 // Make it so
367 //
368 lStyle |= BS_USERBUTTON;
369 ::WinSetWindowULong(GetHwnd(), QWL_STYLE, lStyle);
370 }
371 } // end of wxButton::MakeOwnerDrawn
372
373 WXDWORD wxButton::OS2GetStyle(
374 long lStyle
375 , WXDWORD* pdwExstyle
376 ) const
377 {
378 //
379 // Buttons never have an external border, they draw their own one
380 //
381 WXDWORD dwStyle = wxControl::OS2GetStyle( (lStyle & ~wxBORDER_MASK) | wxBORDER_NONE
382 ,pdwExstyle
383 );
384
385 //
386 // We must use WS_CLIPSIBLINGS with the buttons or they would draw over
387 // each other in any resizeable dialog which has more than one button in
388 // the bottom
389 //
390 dwStyle |= WS_CLIPSIBLINGS;
391 return dwStyle;
392 } // end of wxButton::OS2GetStyle
393
394 MRESULT wxButton::WindowProc( WXUINT uMsg,
395 WXWPARAM wParam,
396 WXLPARAM lParam )
397 {
398 //
399 // When we receive focus, we want to temporary 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 //
403 if (uMsg == WM_SETFOCUS)
404 {
405 if (SHORT1FROMMP(lParam) == TRUE)
406 SetTmpDefault();
407 else
408 UnsetTmpDefault();
409
410 //
411 // Let the default processign take place too
412 //
413 }
414
415 else if (uMsg == WM_BUTTON1DBLCLK)
416 {
417 //
418 // Emulate a click event to force an owner-drawn button to change its
419 // appearance - without this, it won't do it
420 //
421 (void)wxControl::OS2WindowProc( WM_BUTTON1DOWN
422 ,wParam
423 ,lParam
424 );
425
426 //
427 // And conitnue with processing the message normally as well
428 //
429 }
430
431 //
432 // Let the base class do all real processing
433 //
434 return (wxControl::OS2WindowProc( uMsg
435 ,wParam
436 ,lParam
437 ));
438 } // end of wxWindowProc