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