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