]> git.saurik.com Git - wxWidgets.git/blame - src/msw/radiobox.cpp
No real changes, just refactor wxControlContainer code a little.
[wxWidgets.git] / src / msw / radiobox.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
faa49bfd 2// Name: src/msw/radiobox.cpp
3ca6a5f0 3// Purpose: wxRadioBox implementation
2bda0e17
KB
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
e373f51b
VZ
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
da07e033 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
1e6feb95
VZ
27#if wxUSE_RADIOBOX
28
cc11cc69
WS
29#include "wx/radiobox.h"
30
2bda0e17 31#ifndef WX_PRECOMP
7ee21e3a 32 #include "wx/hashmap.h"
da07e033
VZ
33 #include "wx/bitmap.h"
34 #include "wx/brush.h"
f6bcfd97 35 #include "wx/settings.h"
381dd4bf 36 #include "wx/log.h"
2bda0e17
KB
37#endif
38
bd0a76e2 39#include "wx/msw/subwin.h"
2bda0e17 40
f048e32f 41#if wxUSE_TOOLTIPS
f048e32f
VZ
42 #include "wx/tooltip.h"
43#endif // wxUSE_TOOLTIPS
44
3ff066a4 45// TODO: wxCONSTRUCTOR
bc9fb572
JS
46#if 0 // wxUSE_EXTENDED_RTTI
47WX_DEFINE_FLAGS( wxRadioBoxStyle )
48
3ff066a4 49wxBEGIN_FLAGS( wxRadioBoxStyle )
bc9fb572
JS
50 // new style border flags, we put them first to
51 // use them for streaming out
3ff066a4
SC
52 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
53 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
54 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
55 wxFLAGS_MEMBER(wxBORDER_RAISED)
56 wxFLAGS_MEMBER(wxBORDER_STATIC)
57 wxFLAGS_MEMBER(wxBORDER_NONE)
4d08943e 58
bc9fb572 59 // old style border flags
3ff066a4
SC
60 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
61 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
62 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
63 wxFLAGS_MEMBER(wxRAISED_BORDER)
64 wxFLAGS_MEMBER(wxSTATIC_BORDER)
cb0afb26 65 wxFLAGS_MEMBER(wxBORDER)
bc9fb572
JS
66
67 // standard window styles
3ff066a4
SC
68 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
69 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
70 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
71 wxFLAGS_MEMBER(wxWANTS_CHARS)
cb0afb26 72 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
3ff066a4
SC
73 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
74 wxFLAGS_MEMBER(wxVSCROLL)
75 wxFLAGS_MEMBER(wxHSCROLL)
76
77 wxFLAGS_MEMBER(wxRA_SPECIFY_COLS)
3ff066a4 78 wxFLAGS_MEMBER(wxRA_SPECIFY_ROWS)
3ff066a4 79wxEND_FLAGS( wxRadioBoxStyle )
bc9fb572
JS
80
81IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioBox, wxControl,"wx/radiobox.h")
82
3ff066a4 83wxBEGIN_PROPERTIES_TABLE(wxRadioBox)
ce7fe42e 84 wxEVENT_PROPERTY( Select , wxEVT_RADIOBOX , wxCommandEvent )
3ff066a4
SC
85 wxPROPERTY_FLAGS( WindowStyle , wxRadioBoxStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
86wxEND_PROPERTIES_TABLE()
bc9fb572
JS
87
88#else
d3acd369 89IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl)
bc9fb572 90#endif
2bda0e17 91
066f1b7a 92/*
4d08943e
WS
93 selection
94 content
95 label
96 dimension
97 item
066f1b7a
SC
98*/
99
e373f51b
VZ
100// ---------------------------------------------------------------------------
101// private functions
102// ---------------------------------------------------------------------------
103
e373f51b
VZ
104// wnd proc for radio buttons
105LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hWnd,
106 UINT message,
107 WPARAM wParam,
108 LPARAM lParam);
109
110// ---------------------------------------------------------------------------
111// global vars
112// ---------------------------------------------------------------------------
113
7ee21e3a
VZ
114namespace
115{
116
e373f51b 117// the pointer to standard radio button wnd proc
7ee21e3a
VZ
118WXFARPROC s_wndprocRadioBtn = (WXFARPROC)NULL;
119
120// Hash allowing to find wxRadioBox containing the given radio button by its
121// HWND. This is used by (subclassed) radio button window proc to find the
122// radio box it belongs to.
123WX_DECLARE_HASH_MAP(HWND, wxRadioBox *,
124 wxPointerHash, wxPointerEqual,
125 RadioBoxFromButton);
126
127RadioBoxFromButton gs_boxFromButton;
128
129} // anonymous namespace
e373f51b
VZ
130
131// ===========================================================================
132// implementation
133// ===========================================================================
134
7ee21e3a
VZ
135/* static */
136wxRadioBox* wxRadioBox::GetFromRadioButtonHWND(WXHWND hwnd)
137{
138 const RadioBoxFromButton::const_iterator it = gs_boxFromButton.find(hwnd);
139 return it == gs_boxFromButton.end() ? NULL : it->second;
140}
141
e373f51b 142// ---------------------------------------------------------------------------
bd0a76e2 143// wxRadioBox creation
e373f51b
VZ
144// ---------------------------------------------------------------------------
145
2bda0e17 146// Radio box item
bd0a76e2 147void wxRadioBox::Init()
2bda0e17 148{
bd0a76e2 149 m_selectedButton = wxNOT_FOUND;
da07e033 150 m_radioButtons = NULL;
117f566f 151 m_dummyHwnd = NULL;
e373f51b
VZ
152 m_radioWidth = NULL;
153 m_radioHeight = NULL;
2bda0e17
KB
154}
155
f048e32f
VZ
156bool wxRadioBox::Create(wxWindow *parent,
157 wxWindowID id,
158 const wxString& title,
159 const wxPoint& pos,
160 const wxSize& size,
161 int n,
162 const wxString choices[],
163 int majorDim,
164 long style,
165 const wxValidator& val,
166 const wxString& name)
2bda0e17 167{
f048e32f 168 // common initialization
bd0a76e2 169 if ( !wxStaticBox::Create(parent, id, title, pos, size, style, name) )
4d08943e 170 return false;
2bda0e17 171
ad63992e
VZ
172 // the code elsewhere in this file supposes that either wxRA_SPECIFY_COLS
173 // or wxRA_SPECIFY_ROWS is set, ensure that this is indeed the case
174 if ( !(style & (wxRA_SPECIFY_ROWS | wxRA_SPECIFY_COLS)) )
175 style |= wxRA_SPECIFY_COLS;
176
bd0a76e2
VZ
177#if wxUSE_VALIDATORS
178 SetValidator(val);
179#else
180 wxUnusedVar(val);
181#endif // wxUSE_VALIDATORS/!wxUSE_VALIDATORS
da07e033 182
52ca4ec4
VZ
183 // We need an extra one to keep track of the 'dummy' item we
184 // create to end the radio group, so it will be destroyed and
185 // it's id will be released. But we want it separate from the
186 // other buttons since the wxSubwindows will operate on it as
187 // well and we just want to ignore it until destroying it.
188 // For instance, we don't want the bounding box of the radio
189 // buttons to include the dummy button
bd0a76e2 190 m_radioButtons = new wxSubwindows(n);
52ca4ec4 191
e373f51b
VZ
192 m_radioWidth = new int[n];
193 m_radioHeight = new int[n];
f048e32f 194
f048e32f
VZ
195 for ( int i = 0; i < n; i++ )
196 {
197 m_radioWidth[i] =
4d08943e 198 m_radioHeight[i] = wxDefaultCoord;
d3acd369 199 long styleBtn = BS_AUTORADIOBUTTON | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
1f621c2f 200 if ( i == 0 )
f048e32f
VZ
201 styleBtn |= WS_GROUP;
202
52ca4ec4 203 wxWindowIDRef subid = NewControlId();
da07e033 204
9a83f860 205 HWND hwndBtn = ::CreateWindow(wxT("BUTTON"),
017dc06b 206 choices[i].t_str(),
f048e32f
VZ
207 styleBtn,
208 0, 0, 0, 0, // will be set in SetSize()
99c613c5 209 GetHwndOf(parent),
dca0f651 210 (HMENU)wxUIntToPtr(subid.GetValue()),
f048e32f 211 wxGetInstance(),
e373f51b 212 NULL);
2bda0e17 213
f048e32f
VZ
214 if ( !hwndBtn )
215 {
f6bcfd97 216 wxLogLastError(wxT("CreateWindow(radio btn)"));
f048e32f 217
4d08943e 218 return false;
f048e32f
VZ
219 }
220
52ca4ec4
VZ
221 // Keep track of the subwindow
222 m_radioButtons->Set(i, hwndBtn, subid);
42e69d6b 223
e373f51b 224 SubclassRadioButton((WXHWND)hwndBtn);
2bda0e17 225
52ca4ec4
VZ
226 // Also, make it a subcontrol of this control
227 m_subControls.Add(subid);
da07e033 228 }
e373f51b 229
da07e033 230 // Create a dummy radio control to end the group.
117f566f 231 m_dummyId = NewControlId();
cf2810aa 232
9a83f860 233 m_dummyHwnd = (WXHWND)::CreateWindow(wxT("BUTTON"),
fda7962d 234 wxEmptyString,
f048e32f 235 WS_GROUP | BS_AUTORADIOBUTTON | WS_CHILD,
99c613c5 236 0, 0, 0, 0, GetHwndOf(parent),
dca0f651
VZ
237 (HMENU)wxUIntToPtr(m_dummyId.GetValue()),
238 wxGetInstance(), NULL);
52ca4ec4 239
2bda0e17 240
bd0a76e2
VZ
241 m_radioButtons->SetFont(GetFont());
242
64133c38
JS
243#ifdef __WXWINCE__
244 // Set the z-order correctly
d26e1ab2 245 SetWindowPos(GetHwnd(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
64133c38
JS
246#endif
247
f3984afa 248 SetMajorDim(majorDim == 0 ? n : majorDim, style);
da07e033 249 SetSelection(0);
f048e32f 250 SetSize(pos.x, pos.y, size.x, size.y);
2bda0e17 251
7a5a5718 252 // Now that we have items determine what is the best size and set it.
170acdc9 253 SetInitialSize(size);
4d08943e 254
d9c4ffa8
VZ
255 // And update all the buttons positions to match it.
256 const wxSize actualSize = GetSize();
257 PositionAllButtons(pos.x, pos.y, actualSize.x, actualSize.y);
258
4d08943e 259 return true;
2bda0e17 260}
2bda0e17 261
584ad2a3
MB
262bool wxRadioBox::Create(wxWindow *parent,
263 wxWindowID id,
264 const wxString& title,
265 const wxPoint& pos,
266 const wxSize& size,
267 const wxArrayString& choices,
268 int majorDim,
269 long style,
270 const wxValidator& val,
271 const wxString& name)
272{
273 wxCArrayString chs(choices);
274 return Create(parent, id, title, pos, size, chs.GetCount(),
275 chs.GetStrings(), majorDim, style, val, name);
276}
277
e373f51b 278wxRadioBox::~wxRadioBox()
2bda0e17 279{
c6212a0c 280 SendDestroyEvent();
1eb20d4a 281
7ee21e3a
VZ
282 // Unsubclass all the radio buttons and remove their soon-to-be-invalid
283 // HWNDs from the global map. Notice that we need to unsubclass because
284 // otherwise we'd need the entries in gs_boxFromButton for the buttons
285 // being deleted to handle the messages generated during their destruction.
286 for ( size_t item = 0; item < m_radioButtons->GetCount(); item++ )
287 {
288 HWND hwnd = m_radioButtons->Get(item);
289
290 wxSetWindowProc(hwnd, reinterpret_cast<WNDPROC>(s_wndprocRadioBtn));
291 gs_boxFromButton.erase(hwnd);
292 }
293
bd0a76e2 294 delete m_radioButtons;
7ee21e3a 295
117f566f
VZ
296 if ( m_dummyHwnd )
297 DestroyWindow((HWND)m_dummyHwnd);
7ee21e3a 298
bd0a76e2
VZ
299 delete[] m_radioWidth;
300 delete[] m_radioHeight;
301}
302
303// NB: if this code is changed, wxGetWindowForHWND() which relies on having the
304// radiobox pointer in GWL_USERDATA for radio buttons must be updated too!
305void wxRadioBox::SubclassRadioButton(WXHWND hWndBtn)
306{
307 HWND hwndBtn = (HWND)hWndBtn;
308
309 if ( !s_wndprocRadioBtn )
310 s_wndprocRadioBtn = (WXFARPROC)wxGetWindowProc(hwndBtn);
311
312 wxSetWindowProc(hwndBtn, wxRadioBtnWndProc);
7ee21e3a
VZ
313
314 gs_boxFromButton[hwndBtn] = this;
bd0a76e2
VZ
315}
316
317// ----------------------------------------------------------------------------
318// events generation
319// ----------------------------------------------------------------------------
320
0edeeb6d 321bool wxRadioBox::MSWCommand(WXUINT cmd, WXWORD id_)
bd0a76e2 322{
0edeeb6d
VZ
323 const int id = (signed short)id_;
324
bd0a76e2 325 if ( cmd == BN_CLICKED )
da07e033 326 {
bd0a76e2
VZ
327 if (id == GetId())
328 return true;
329
330 int selectedButton = wxNOT_FOUND;
331
aa61d352
VZ
332 const unsigned int count = GetCount();
333 for ( unsigned int i = 0; i < count; i++ )
bd0a76e2 334 {
bf511d57
VZ
335 const HWND hwndBtn = (*m_radioButtons)[i];
336 if ( id == wxGetWindowId(hwndBtn) )
bd0a76e2 337 {
bf511d57
VZ
338 // we can get BN_CLICKED for a button which just became focused
339 // but it may not be checked, in which case we shouldn't
340 // generate a radiobox selection changed event for it
341 if ( ::SendMessage(hwndBtn, BM_GETCHECK, 0, 0) == BST_CHECKED )
342 selectedButton = i;
bd0a76e2
VZ
343
344 break;
345 }
346 }
347
348 if ( selectedButton == wxNOT_FOUND )
349 {
350 // just ignore it - due to a hack with WM_NCHITTEST handling in our
351 // wnd proc, we can receive dummy click messages when we click near
352 // the radiobox edge (this is ugly but Julian wouldn't let me get
353 // rid of this...)
354 return false;
355 }
356
357 if ( selectedButton != m_selectedButton )
358 {
359 m_selectedButton = selectedButton;
360
361 SendNotificationEvent();
362 }
363 //else: don't generate events when the selection doesn't change
364
365 return true;
da07e033 366 }
bd0a76e2
VZ
367 else
368 return false;
369}
42e69d6b 370
bd0a76e2
VZ
371void wxRadioBox::Command(wxCommandEvent & event)
372{
687706f5 373 SetSelection (event.GetInt());
bd0a76e2
VZ
374 SetFocus();
375 ProcessCommand(event);
376}
2bda0e17 377
bd0a76e2
VZ
378void wxRadioBox::SendNotificationEvent()
379{
ce7fe42e 380 wxCommandEvent event(wxEVT_RADIOBOX, m_windowId);
bd0a76e2 381 event.SetInt( m_selectedButton );
aa61d352 382 event.SetString(GetString(m_selectedButton));
bd0a76e2
VZ
383 event.SetEventObject( this );
384 ProcessCommand(event);
2bda0e17
KB
385}
386
bd0a76e2
VZ
387// ----------------------------------------------------------------------------
388// simple accessors
389// ----------------------------------------------------------------------------
390
aa61d352 391unsigned int wxRadioBox::GetCount() const
2bda0e17 392{
04988ec4 393 return m_radioButtons ? m_radioButtons->GetCount() : 0u;
bd0a76e2 394}
d66a042c 395
aa61d352 396void wxRadioBox::SetString(unsigned int item, const wxString& label)
bd0a76e2 397{
789f6795 398 wxCHECK_RET( IsValid(item), wxT("invalid radiobox index") );
e373f51b 399
bd0a76e2
VZ
400 m_radioWidth[item] =
401 m_radioHeight[item] = wxDefaultCoord;
2bda0e17 402
bd0a76e2 403 ::SetWindowText((*m_radioButtons)[item], label.c_str());
31582e4e
RD
404
405 InvalidateBestSize();
2bda0e17
KB
406}
407
bd0a76e2 408void wxRadioBox::SetSelection(int N)
2bda0e17 409{
789f6795 410 wxCHECK_RET( IsValid(N), wxT("invalid radiobox index") );
bd0a76e2 411
27758ba7
VZ
412 // unselect the old button
413 if ( m_selectedButton != wxNOT_FOUND )
414 ::SendMessage((*m_radioButtons)[m_selectedButton], BM_SETCHECK, 0, 0L);
415
416 // and select the new one
bd0a76e2
VZ
417 ::SendMessage((*m_radioButtons)[N], BM_SETCHECK, 1, 0L);
418
419 m_selectedButton = N;
2bda0e17
KB
420}
421
422// Find string for position
aa61d352 423wxString wxRadioBox::GetString(unsigned int item) const
2bda0e17 424{
789f6795 425 wxCHECK_MSG( IsValid(item), wxEmptyString,
1e6feb95
VZ
426 wxT("invalid radiobox index") );
427
bd0a76e2
VZ
428 return wxGetWindowText((*m_radioButtons)[item]);
429}
430
431void wxRadioBox::SetFocus()
432{
433 if ( GetCount() > 0 )
434 {
435 ::SetFocus((*m_radioButtons)[m_selectedButton == wxNOT_FOUND
436 ? 0
437 : m_selectedButton]);
438 }
439}
440
441// Enable a specific button
aa61d352 442bool wxRadioBox::Enable(unsigned int item, bool enable)
bd0a76e2 443{
789f6795 444 wxCHECK_MSG( IsValid(item), false,
bd0a76e2
VZ
445 wxT("invalid item in wxRadioBox::Enable()") );
446
0826c4d3 447 BOOL ret = MSWEnableHWND((*m_radioButtons)[item], enable);
789f6795 448
3bfa7be9
VZ
449 return (ret == 0) != enable;
450}
451
aa61d352 452bool wxRadioBox::IsItemEnabled(unsigned int item) const
3bfa7be9
VZ
453{
454 wxCHECK_MSG( IsValid(item), false,
f2be5580 455 wxT("invalid item in wxRadioBox::IsItemEnabled()") );
3bfa7be9
VZ
456
457 return ::IsWindowEnabled((*m_radioButtons)[item]) != 0;
2bda0e17
KB
458}
459
bd0a76e2 460// Show a specific button
aa61d352 461bool wxRadioBox::Show(unsigned int item, bool show)
bd0a76e2 462{
789f6795 463 wxCHECK_MSG( IsValid(item), false,
bd0a76e2
VZ
464 wxT("invalid item in wxRadioBox::Show()") );
465
789f6795
WS
466 BOOL ret = ::ShowWindow((*m_radioButtons)[item], show ? SW_SHOW : SW_HIDE);
467
3bfa7be9
VZ
468 bool changed = (ret != 0) != show;
469 if ( changed )
470 {
31582e4e 471 InvalidateBestSize();
3bfa7be9
VZ
472 }
473
31582e4e 474 return changed;
bd0a76e2
VZ
475}
476
aa61d352 477bool wxRadioBox::IsItemShown(unsigned int item) const
3bfa7be9
VZ
478{
479 wxCHECK_MSG( IsValid(item), false,
f2be5580 480 wxT("invalid item in wxRadioBox::IsItemShown()") );
3bfa7be9
VZ
481
482 // don't use IsWindowVisible() here because it would return false if the
483 // radiobox itself is hidden while we want to only return false if this
484 // button specifically is hidden
485 return (::GetWindowLong((*m_radioButtons)[item],
486 GWL_STYLE) & WS_VISIBLE) != 0;
487}
488
c670c855
VZ
489#if wxUSE_TOOLTIPS
490
491bool wxRadioBox::HasToolTips() const
492{
493 return wxStaticBox::HasToolTips() || wxRadioBoxBase::HasItemToolTips();
494}
495
496void wxRadioBox::DoSetItemToolTip(unsigned int item, wxToolTip *tooltip)
497{
498 // we have already checked for the item to be valid in wxRadioBoxBase
499 const HWND hwndRbtn = (*m_radioButtons)[item];
500 if ( tooltip != NULL )
f7dd07f6 501 tooltip->AddOtherWindow(hwndRbtn);
c670c855 502 else // unset the tooltip
6418ad5e
FM
503 wxToolTip::Remove(hwndRbtn, 0, wxRect(0,0,0,0));
504 // the second parameter can be zero since it's ignored by Remove()
505 // as we pass a rect for which wxRect::IsEmpty()==true...
c670c855
VZ
506}
507
508#endif // wxUSE_TOOLTIPS
509
34526434
VZ
510bool wxRadioBox::Reparent(wxWindowBase *newParent)
511{
512 if ( !wxStaticBox::Reparent(newParent) )
513 {
514 return false;
515 }
516
517 HWND hwndParent = GetHwndOf(GetParent());
518 for ( size_t item = 0; item < m_radioButtons->GetCount(); item++ )
519 {
520 ::SetParent((*m_radioButtons)[item], hwndParent);
521 }
522#ifdef __WXWINCE__
523 // put static box under the buttons in the Z-order
524 SetWindowPos(GetHwnd(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
525#endif
526 return true;
527}
528
bd0a76e2
VZ
529WX_FORWARD_STD_METHODS_TO_SUBWINDOWS(wxRadioBox, wxStaticBox, m_radioButtons)
530
3ca6a5f0
BP
531// ----------------------------------------------------------------------------
532// size calculations
533// ----------------------------------------------------------------------------
534
535wxSize wxRadioBox::GetMaxButtonSize() const
536{
537 // calculate the max button size
538 int widthMax = 0,
539 heightMax = 0;
aa61d352
VZ
540 const unsigned int count = GetCount();
541 for ( unsigned int i = 0 ; i < count; i++ )
3ca6a5f0
BP
542 {
543 int width, height;
544 if ( m_radioWidth[i] < 0 )
545 {
bd0a76e2 546 GetTextExtent(wxGetWindowText((*m_radioButtons)[i]), &width, &height);
3ca6a5f0
BP
547
548 // adjust the size to take into account the radio box itself
549 // FIXME this is totally bogus!
550 width += RADIO_SIZE;
551 height *= 3;
552 height /= 2;
553 }
554 else
555 {
556 width = m_radioWidth[i];
557 height = m_radioHeight[i];
558 }
559
560 if ( widthMax < width )
561 widthMax = width;
562 if ( heightMax < height )
563 heightMax = height;
564 }
565
566 return wxSize(widthMax, heightMax);
567}
568
569wxSize wxRadioBox::GetTotalButtonSize(const wxSize& sizeBtn) const
570{
571 // the radiobox should be big enough for its buttons
572 int cx1, cy1;
7a5e53ab 573 wxGetCharSize(m_hWnd, &cx1, &cy1, GetFont());
3ca6a5f0
BP
574
575 int extraHeight = cy1;
576
21e0a4d5
VZ
577 int height = GetRowCount() * sizeBtn.y + cy1/2 + extraHeight;
578 int width = GetColumnCount() * (sizeBtn.x + cx1) + cx1;
3ca6a5f0 579
c7fd6717 580 // Add extra space under the label, if it exists.
1a87edf2 581 if (!wxControl::GetLabel().empty())
c7fd6717
JS
582 height += cy1/2;
583
3ca6a5f0
BP
584 // and also wide enough for its label
585 int widthLabel;
32cd189d 586 GetTextExtent(GetLabelText(), &widthLabel, NULL);
3ca6a5f0
BP
587 widthLabel += RADIO_SIZE; // FIXME this is bogus too
588 if ( widthLabel > width )
589 width = widthLabel;
590
591 return wxSize(width, height);
592}
593
594wxSize wxRadioBox::DoGetBestSize() const
595{
d96b9caf
VZ
596 if ( !m_radioButtons )
597 {
598 // if we're not fully initialized yet, we can't meaningfully compute
599 // our best size, we'll do it later
600 return wxSize(1, 1);
601 }
602
31582e4e
RD
603 wxSize best = GetTotalButtonSize(GetMaxButtonSize());
604 CacheBestSize(best);
605 return best;
3ca6a5f0
BP
606}
607
bfc6fde4 608void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
1f916a19 609{
fa3987ef
VZ
610 if ( (width == wxDefaultCoord && (sizeFlags & wxSIZE_AUTO_WIDTH)) ||
611 (height == wxDefaultCoord && (sizeFlags & wxSIZE_AUTO_HEIGHT)) )
612 {
613 // Attempt to have a look coherent with other platforms: We compute the
614 // biggest toggle dim, then we align all items according this value.
615 const wxSize totSize = GetTotalButtonSize(GetMaxButtonSize());
c219cecc 616
fa3987ef
VZ
617 // only change our width/height if asked for
618 if ( width == wxDefaultCoord && (sizeFlags & wxSIZE_AUTO_WIDTH) )
619 width = totSize.x;
4438caf4 620
fa3987ef
VZ
621 if ( height == wxDefaultCoord && (sizeFlags & wxSIZE_AUTO_HEIGHT) )
622 height = totSize.y;
623 }
4438caf4 624
fa3987ef
VZ
625 wxStaticBox::DoSetSize(x, y, width, height);
626}
f048e32f 627
fa3987ef
VZ
628void wxRadioBox::DoMoveWindow(int x, int y, int width, int height)
629{
630 wxStaticBox::DoMoveWindow(x, y, width, height);
4438caf4 631
d9c4ffa8
VZ
632 PositionAllButtons(x, y, width, height);
633}
634
635void
636wxRadioBox::PositionAllButtons(int x, int y, int width, int WXUNUSED(height))
637{
3ca6a5f0
BP
638 wxSize maxSize = GetMaxButtonSize();
639 int maxWidth = maxSize.x,
640 maxHeight = maxSize.y;
4438caf4 641
3ca6a5f0
BP
642 // Now position all the buttons: the current button will be put at
643 // wxPoint(x_offset, y_offset) and the new row/column will start at
644 // startX/startY. The size of all buttons will be the same wxSize(maxWidth,
645 // maxHeight) except for the buttons in the last column which should extend
646 // to the right border of radiobox and thus can be wider than this.
c219cecc 647
3ca6a5f0 648 // Also, remember that wxRA_SPECIFY_COLS means that we arrange buttons in
21e0a4d5 649 // left to right order and GetMajorDim() is the number of columns while
3ca6a5f0 650 // wxRA_SPECIFY_ROWS means that the buttons are arranged top to bottom and
21e0a4d5 651 // GetMajorDim() is the number of rows.
4438caf4 652
fa3987ef
VZ
653 int cx1, cy1;
654 wxGetCharSize(m_hWnd, &cx1, &cy1, GetFont());
655
656 int x_offset = x + cx1;
657 int y_offset = y + cy1;
1f916a19 658
c7fd6717 659 // Add extra space under the label, if it exists.
1a87edf2 660 if (!wxControl::GetLabel().empty())
c7fd6717 661 y_offset += cy1/2;
3ca6a5f0 662
4438caf4
VZ
663 int startX = x_offset;
664 int startY = y_offset;
1f916a19 665
aa61d352
VZ
666 const unsigned int count = GetCount();
667 for (unsigned int i = 0; i < count; i++)
1f916a19 668 {
3ca6a5f0
BP
669 // the last button in the row may be wider than the other ones as the
670 // radiobox may be wider than the sum of the button widths (as it
671 // happens, for example, when the radiobox label is very long)
672 bool isLastInTheRow;
673 if ( m_windowStyle & wxRA_SPECIFY_COLS )
674 {
675 // item is the last in its row if it is a multiple of the number of
676 // columns or if it is just the last item
aa61d352 677 unsigned int n = i + 1;
21e0a4d5 678 isLastInTheRow = ((n % GetMajorDim()) == 0) || (n == count);
3ca6a5f0
BP
679 }
680 else // wxRA_SPECIFY_ROWS
4438caf4 681 {
3ca6a5f0 682 // item is the last in the row if it is in the last columns
21e0a4d5 683 isLastInTheRow = i >= (count/GetMajorDim())*GetMajorDim();
3ca6a5f0
BP
684 }
685
686 // is this the start of new row/column?
21e0a4d5 687 if ( i && (i % GetMajorDim() == 0) )
3ca6a5f0
BP
688 {
689 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
4438caf4 690 {
3ca6a5f0 691 // start of new column
4438caf4
VZ
692 y_offset = startY;
693 x_offset += maxWidth + cx1;
694 }
3ca6a5f0 695 else // start of new row
4438caf4
VZ
696 {
697 x_offset = startX;
698 y_offset += maxHeight;
699 if (m_radioWidth[0]>0)
700 y_offset += cy1/2;
701 }
702 }
3ca6a5f0
BP
703
704 int widthBtn;
705 if ( isLastInTheRow )
4438caf4 706 {
3ca6a5f0
BP
707 // make the button go to the end of radio box
708 widthBtn = startX + width - x_offset - 2*cx1;
709 if ( widthBtn < maxWidth )
710 widthBtn = maxWidth;
4438caf4
VZ
711 }
712 else
713 {
3ca6a5f0
BP
714 // normal button, always of the same size
715 widthBtn = maxWidth;
4438caf4 716 }
1f916a19 717
bd0a76e2
VZ
718 // make all buttons of the same, maximal size - like this they cover
719 // the radiobox entirely and the radiobox tooltips are always shown
720 // (otherwise they are not when the mouse pointer is in the radiobox
721 // part not belonging to any radiobutton)
99c613c5 722 DoMoveSibling((*m_radioButtons)[i], x_offset, y_offset, widthBtn, maxHeight);
4438caf4 723
3ca6a5f0
BP
724 // where do we put the next button?
725 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
4438caf4 726 {
3ca6a5f0 727 // below this one
4438caf4
VZ
728 y_offset += maxHeight;
729 if (m_radioWidth[0]>0)
730 y_offset += cy1/2;
731 }
732 else
3ca6a5f0
BP
733 {
734 // to the right of this one
735 x_offset += widthBtn + cx1;
736 }
1f916a19 737 }
1f916a19
JS
738}
739
dc26eeb3
VZ
740int wxRadioBox::GetItemFromPoint(const wxPoint& pt) const
741{
742 const unsigned int count = GetCount();
743 for ( unsigned int i = 0; i < count; i++ )
744 {
745 RECT rect = wxGetWindowRect((*m_radioButtons)[i]);
746
747 if ( rect.left <= pt.x && pt.x < rect.right &&
748 rect.top <= pt.y && pt.y < rect.bottom )
749 {
750 return i;
751 }
752 }
753
754 return wxNOT_FOUND;
755}
756
c3732409
VZ
757// ----------------------------------------------------------------------------
758// radio box drawing
759// ----------------------------------------------------------------------------
760
761#ifndef __WXWINCE__
762
763WXHRGN wxRadioBox::MSWGetRegionWithoutChildren()
764{
765 RECT rc;
766 ::GetWindowRect(GetHwnd(), &rc);
767 HRGN hrgn = ::CreateRectRgn(rc.left, rc.top, rc.right + 1, rc.bottom + 1);
768
aa61d352
VZ
769 const unsigned int count = GetCount();
770 for ( unsigned int i = 0; i < count; ++i )
c3732409 771 {
455c8f1d
VZ
772 // don't clip out hidden children
773 if ( !IsItemShown(i) )
774 continue;
775
c3732409
VZ
776 ::GetWindowRect((*m_radioButtons)[i], &rc);
777 AutoHRGN hrgnchild(::CreateRectRgnIndirect(&rc));
778 ::CombineRgn(hrgn, hrgn, hrgnchild, RGN_DIFF);
779 }
780
781 return (WXHRGN)hrgn;
782}
783
784#endif // __WXWINCE__
785
e373f51b
VZ
786// ---------------------------------------------------------------------------
787// window proc for radio buttons
788// ---------------------------------------------------------------------------
789
790LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
8614c467 791 UINT message,
e373f51b
VZ
792 WPARAM wParam,
793 LPARAM lParam)
794{
7ee21e3a
VZ
795
796 wxRadioBox * const radiobox = wxRadioBox::GetFromRadioButtonHWND(hwnd);
797 wxCHECK_MSG( radiobox, 0, wxT("Should have the associated radio box") );
798
8614c467 799 switch ( message )
e373f51b 800 {
8614c467
VZ
801 case WM_GETDLGCODE:
802 // we must tell IsDialogMessage()/our kbd processing code that we
803 // want to process arrows ourselves because neither of them is
804 // smart enough to handle arrows properly for us
805 {
8ad6ad99 806 long lDlgCode = ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd,
8614c467 807 message, wParam, lParam);
8ad6ad99 808
8614c467
VZ
809 return lDlgCode | DLGC_WANTARROWS;
810 }
e373f51b 811
8614c467 812 case WM_KEYDOWN:
9a5ccab4 813 {
4d08943e 814 bool processed = true;
f048e32f 815
e421922f 816 wxDirection dir;
8614c467
VZ
817 switch ( wParam )
818 {
819 case VK_UP:
1e6feb95 820 dir = wxUP;
8614c467 821 break;
f048e32f 822
8614c467 823 case VK_LEFT:
1e6feb95 824 dir = wxLEFT;
8614c467 825 break;
f048e32f 826
8614c467 827 case VK_DOWN:
1e6feb95 828 dir = wxDOWN;
8614c467 829 break;
f048e32f 830
8614c467 831 case VK_RIGHT:
1e6feb95 832 dir = wxRIGHT;
8614c467
VZ
833 break;
834
835 default:
4d08943e 836 processed = false;
1e6feb95
VZ
837
838 // just to suppress the compiler warning
839 dir = wxALL;
8614c467
VZ
840 }
841
842 if ( processed )
f048e32f 843 {
1e6feb95
VZ
844 int selOld = radiobox->GetSelection();
845 int selNew = radiobox->GetNextItem
846 (
847 selOld,
848 dir,
849 radiobox->GetWindowStyle()
850 );
3ca6a5f0 851
8614c467
VZ
852 if ( selNew != selOld )
853 {
854 radiobox->SetSelection(selNew);
974931fe 855 radiobox->SetFocus();
8614c467
VZ
856
857 // emulate the button click
858 radiobox->SendNotificationEvent();
9a5ccab4 859
8614c467
VZ
860 return 0;
861 }
f048e32f 862 }
9a5ccab4 863 }
21687069
VZ
864 break;
865
1dab049c
VZ
866 case WM_SETFOCUS:
867 case WM_KILLFOCUS:
868 {
1dab049c
VZ
869 // if we don't do this, no focus events are generated for the
870 // radiobox and, besides, we need to notify the parent about
871 // the focus change, otherwise the focus handling logic in
872 // wxControlContainer doesn't work
873 if ( message == WM_SETFOCUS )
874 radiobox->HandleSetFocus((WXHWND)wParam);
875 else
876 radiobox->HandleKillFocus((WXHWND)wParam);
877 }
878 break;
879
bd0a76e2 880#ifndef __WXWINCE__
fac93396 881 case WM_HELP:
e421922f 882 {
bd0a76e2
VZ
883 bool processed = false;
884
885 wxEvtHandler * const handler = radiobox->GetEventHandler();
fac93396 886
e421922f 887 HELPINFO* info = (HELPINFO*) lParam;
bd0a76e2 888 if ( info->iContextType == HELPINFO_WINDOW )
fac93396 889 {
bd0a76e2
VZ
890 for ( wxWindow* subjectOfHelp = radiobox;
891 subjectOfHelp;
892 subjectOfHelp = subjectOfHelp->GetParent() )
e421922f 893 {
bd0a76e2
VZ
894 wxHelpEvent helpEvent(wxEVT_HELP,
895 subjectOfHelp->GetId(),
896 wxPoint(info->MousePos.x,
897 info->MousePos.y));
e421922f 898 helpEvent.SetEventObject(radiobox);
bd0a76e2
VZ
899 if ( handler->ProcessEvent(helpEvent) )
900 {
901 processed = true;
902 break;
903 }
e421922f 904 }
fac93396 905 }
e421922f
VZ
906 else if (info->iContextType == HELPINFO_MENUITEM)
907 {
bd0a76e2 908 wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId);
e421922f 909 helpEvent.SetEventObject(radiobox);
bd0a76e2 910 processed = handler->ProcessEvent(helpEvent);
e421922f 911 }
bd0a76e2
VZ
912
913 if ( processed )
e421922f 914 return 0;
e421922f 915 }
c3732409 916 break;
bd0a76e2 917#endif // !__WXWINCE__
e373f51b
VZ
918 }
919
8ad6ad99 920 return ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd, message, wParam, lParam);
e373f51b 921}
42e69d6b 922
1e6feb95 923#endif // wxUSE_RADIOBOX