]> git.saurik.com Git - wxWidgets.git/blob - src/msw/radiobox.cpp
51fbac285ac3699e6c5f2a42eda3d03802af6323
[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 bool wxRadioBox::CanBeFocused() const
448 {
449 // If the control itself is hidden or disabled, no need to check anything
450 // else.
451 if ( !wxStaticBox::CanBeFocused() )
452 return false;
453
454 // Otherwise, check if we have any buttons that can be focused.
455 for ( size_t item = 0; item < m_radioButtons->GetCount(); item++ )
456 {
457 if ( IsItemEnabled(item) && IsItemShown(item) )
458 return true;
459 }
460
461 // We didn't find any items that can accept focus, so neither can we as a
462 // whole accept it.
463 return false;
464 }
465
466 // Enable a specific button
467 bool wxRadioBox::Enable(unsigned int item, bool enable)
468 {
469 wxCHECK_MSG( IsValid(item), false,
470 wxT("invalid item in wxRadioBox::Enable()") );
471
472 BOOL ret = MSWEnableHWND((*m_radioButtons)[item], enable);
473
474 return (ret == 0) != enable;
475 }
476
477 bool wxRadioBox::IsItemEnabled(unsigned int item) const
478 {
479 wxCHECK_MSG( IsValid(item), false,
480 wxT("invalid item in wxRadioBox::IsItemEnabled()") );
481
482 return ::IsWindowEnabled((*m_radioButtons)[item]) != 0;
483 }
484
485 // Show a specific button
486 bool wxRadioBox::Show(unsigned int item, bool show)
487 {
488 wxCHECK_MSG( IsValid(item), false,
489 wxT("invalid item in wxRadioBox::Show()") );
490
491 BOOL ret = ::ShowWindow((*m_radioButtons)[item], show ? SW_SHOW : SW_HIDE);
492
493 bool changed = (ret != 0) != show;
494 if ( changed )
495 {
496 InvalidateBestSize();
497 }
498
499 return changed;
500 }
501
502 bool wxRadioBox::IsItemShown(unsigned int item) const
503 {
504 wxCHECK_MSG( IsValid(item), false,
505 wxT("invalid item in wxRadioBox::IsItemShown()") );
506
507 // don't use IsWindowVisible() here because it would return false if the
508 // radiobox itself is hidden while we want to only return false if this
509 // button specifically is hidden
510 return (::GetWindowLong((*m_radioButtons)[item],
511 GWL_STYLE) & WS_VISIBLE) != 0;
512 }
513
514 #if wxUSE_TOOLTIPS
515
516 bool wxRadioBox::HasToolTips() const
517 {
518 return wxStaticBox::HasToolTips() || wxRadioBoxBase::HasItemToolTips();
519 }
520
521 void wxRadioBox::DoSetItemToolTip(unsigned int item, wxToolTip *tooltip)
522 {
523 // we have already checked for the item to be valid in wxRadioBoxBase
524 const HWND hwndRbtn = (*m_radioButtons)[item];
525 if ( tooltip != NULL )
526 tooltip->AddOtherWindow(hwndRbtn);
527 else // unset the tooltip
528 wxToolTip::Remove(hwndRbtn, 0, wxRect(0,0,0,0));
529 // the second parameter can be zero since it's ignored by Remove()
530 // as we pass a rect for which wxRect::IsEmpty()==true...
531 }
532
533 #endif // wxUSE_TOOLTIPS
534
535 bool wxRadioBox::Reparent(wxWindowBase *newParent)
536 {
537 if ( !wxStaticBox::Reparent(newParent) )
538 {
539 return false;
540 }
541
542 HWND hwndParent = GetHwndOf(GetParent());
543 for ( size_t item = 0; item < m_radioButtons->GetCount(); item++ )
544 {
545 ::SetParent((*m_radioButtons)[item], hwndParent);
546 }
547 #ifdef __WXWINCE__
548 // put static box under the buttons in the Z-order
549 SetWindowPos(GetHwnd(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
550 #endif
551 return true;
552 }
553
554 WX_FORWARD_STD_METHODS_TO_SUBWINDOWS(wxRadioBox, wxStaticBox, m_radioButtons)
555
556 // ----------------------------------------------------------------------------
557 // size calculations
558 // ----------------------------------------------------------------------------
559
560 wxSize wxRadioBox::GetMaxButtonSize() const
561 {
562 // calculate the max button size
563 int widthMax = 0,
564 heightMax = 0;
565 const unsigned int count = GetCount();
566 for ( unsigned int i = 0 ; i < count; i++ )
567 {
568 int width, height;
569 if ( m_radioWidth[i] < 0 )
570 {
571 GetTextExtent(wxGetWindowText((*m_radioButtons)[i]), &width, &height);
572
573 // adjust the size to take into account the radio box itself
574 // FIXME this is totally bogus!
575 width += RADIO_SIZE;
576 height *= 3;
577 height /= 2;
578 }
579 else
580 {
581 width = m_radioWidth[i];
582 height = m_radioHeight[i];
583 }
584
585 if ( widthMax < width )
586 widthMax = width;
587 if ( heightMax < height )
588 heightMax = height;
589 }
590
591 return wxSize(widthMax, heightMax);
592 }
593
594 wxSize wxRadioBox::GetTotalButtonSize(const wxSize& sizeBtn) const
595 {
596 // the radiobox should be big enough for its buttons
597 int cx1, cy1;
598 wxGetCharSize(m_hWnd, &cx1, &cy1, GetFont());
599
600 int extraHeight = cy1;
601
602 int height = GetRowCount() * sizeBtn.y + cy1/2 + extraHeight;
603 int width = GetColumnCount() * (sizeBtn.x + cx1) + cx1;
604
605 // Add extra space under the label, if it exists.
606 if (!wxControl::GetLabel().empty())
607 height += cy1/2;
608
609 // and also wide enough for its label
610 int widthLabel;
611 GetTextExtent(GetLabelText(), &widthLabel, NULL);
612 widthLabel += RADIO_SIZE; // FIXME this is bogus too
613 if ( widthLabel > width )
614 width = widthLabel;
615
616 return wxSize(width, height);
617 }
618
619 wxSize wxRadioBox::DoGetBestSize() const
620 {
621 if ( !m_radioButtons )
622 {
623 // if we're not fully initialized yet, we can't meaningfully compute
624 // our best size, we'll do it later
625 return wxSize(1, 1);
626 }
627
628 wxSize best = GetTotalButtonSize(GetMaxButtonSize());
629 CacheBestSize(best);
630 return best;
631 }
632
633 void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
634 {
635 if ( (width == wxDefaultCoord && (sizeFlags & wxSIZE_AUTO_WIDTH)) ||
636 (height == wxDefaultCoord && (sizeFlags & wxSIZE_AUTO_HEIGHT)) )
637 {
638 // Attempt to have a look coherent with other platforms: We compute the
639 // biggest toggle dim, then we align all items according this value.
640 const wxSize totSize = GetTotalButtonSize(GetMaxButtonSize());
641
642 // only change our width/height if asked for
643 if ( width == wxDefaultCoord && (sizeFlags & wxSIZE_AUTO_WIDTH) )
644 width = totSize.x;
645
646 if ( height == wxDefaultCoord && (sizeFlags & wxSIZE_AUTO_HEIGHT) )
647 height = totSize.y;
648 }
649
650 wxStaticBox::DoSetSize(x, y, width, height);
651 }
652
653 void wxRadioBox::DoMoveWindow(int x, int y, int width, int height)
654 {
655 wxStaticBox::DoMoveWindow(x, y, width, height);
656
657 PositionAllButtons(x, y, width, height);
658 }
659
660 void
661 wxRadioBox::PositionAllButtons(int x, int y, int width, int WXUNUSED(height))
662 {
663 wxSize maxSize = GetMaxButtonSize();
664 int maxWidth = maxSize.x,
665 maxHeight = maxSize.y;
666
667 // Now position all the buttons: the current button will be put at
668 // wxPoint(x_offset, y_offset) and the new row/column will start at
669 // startX/startY. The size of all buttons will be the same wxSize(maxWidth,
670 // maxHeight) except for the buttons in the last column which should extend
671 // to the right border of radiobox and thus can be wider than this.
672
673 // Also, remember that wxRA_SPECIFY_COLS means that we arrange buttons in
674 // left to right order and GetMajorDim() is the number of columns while
675 // wxRA_SPECIFY_ROWS means that the buttons are arranged top to bottom and
676 // GetMajorDim() is the number of rows.
677
678 int cx1, cy1;
679 wxGetCharSize(m_hWnd, &cx1, &cy1, GetFont());
680
681 int x_offset = x + cx1;
682 int y_offset = y + cy1;
683
684 // Add extra space under the label, if it exists.
685 if (!wxControl::GetLabel().empty())
686 y_offset += cy1/2;
687
688 int startX = x_offset;
689 int startY = y_offset;
690
691 const unsigned int count = GetCount();
692 for (unsigned int i = 0; i < count; i++)
693 {
694 // the last button in the row may be wider than the other ones as the
695 // radiobox may be wider than the sum of the button widths (as it
696 // happens, for example, when the radiobox label is very long)
697 bool isLastInTheRow;
698 if ( m_windowStyle & wxRA_SPECIFY_COLS )
699 {
700 // item is the last in its row if it is a multiple of the number of
701 // columns or if it is just the last item
702 unsigned int n = i + 1;
703 isLastInTheRow = ((n % GetMajorDim()) == 0) || (n == count);
704 }
705 else // wxRA_SPECIFY_ROWS
706 {
707 // item is the last in the row if it is in the last columns
708 isLastInTheRow = i >= (count/GetMajorDim())*GetMajorDim();
709 }
710
711 // is this the start of new row/column?
712 if ( i && (i % GetMajorDim() == 0) )
713 {
714 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
715 {
716 // start of new column
717 y_offset = startY;
718 x_offset += maxWidth + cx1;
719 }
720 else // start of new row
721 {
722 x_offset = startX;
723 y_offset += maxHeight;
724 if (m_radioWidth[0]>0)
725 y_offset += cy1/2;
726 }
727 }
728
729 int widthBtn;
730 if ( isLastInTheRow )
731 {
732 // make the button go to the end of radio box
733 widthBtn = startX + width - x_offset - 2*cx1;
734 if ( widthBtn < maxWidth )
735 widthBtn = maxWidth;
736 }
737 else
738 {
739 // normal button, always of the same size
740 widthBtn = maxWidth;
741 }
742
743 // make all buttons of the same, maximal size - like this they cover
744 // the radiobox entirely and the radiobox tooltips are always shown
745 // (otherwise they are not when the mouse pointer is in the radiobox
746 // part not belonging to any radiobutton)
747 DoMoveSibling((*m_radioButtons)[i], x_offset, y_offset, widthBtn, maxHeight);
748
749 // where do we put the next button?
750 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
751 {
752 // below this one
753 y_offset += maxHeight;
754 if (m_radioWidth[0]>0)
755 y_offset += cy1/2;
756 }
757 else
758 {
759 // to the right of this one
760 x_offset += widthBtn + cx1;
761 }
762 }
763 }
764
765 int wxRadioBox::GetItemFromPoint(const wxPoint& pt) const
766 {
767 const unsigned int count = GetCount();
768 for ( unsigned int i = 0; i < count; i++ )
769 {
770 RECT rect = wxGetWindowRect((*m_radioButtons)[i]);
771
772 if ( rect.left <= pt.x && pt.x < rect.right &&
773 rect.top <= pt.y && pt.y < rect.bottom )
774 {
775 return i;
776 }
777 }
778
779 return wxNOT_FOUND;
780 }
781
782 // ----------------------------------------------------------------------------
783 // radio box drawing
784 // ----------------------------------------------------------------------------
785
786 #ifndef __WXWINCE__
787
788 WXHRGN wxRadioBox::MSWGetRegionWithoutChildren()
789 {
790 RECT rc;
791 ::GetWindowRect(GetHwnd(), &rc);
792 HRGN hrgn = ::CreateRectRgn(rc.left, rc.top, rc.right + 1, rc.bottom + 1);
793
794 const unsigned int count = GetCount();
795 for ( unsigned int i = 0; i < count; ++i )
796 {
797 // don't clip out hidden children
798 if ( !IsItemShown(i) )
799 continue;
800
801 ::GetWindowRect((*m_radioButtons)[i], &rc);
802 AutoHRGN hrgnchild(::CreateRectRgnIndirect(&rc));
803 ::CombineRgn(hrgn, hrgn, hrgnchild, RGN_DIFF);
804 }
805
806 return (WXHRGN)hrgn;
807 }
808
809 #endif // __WXWINCE__
810
811 // ---------------------------------------------------------------------------
812 // window proc for radio buttons
813 // ---------------------------------------------------------------------------
814
815 LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
816 UINT message,
817 WPARAM wParam,
818 LPARAM lParam)
819 {
820
821 wxRadioBox * const radiobox = wxRadioBox::GetFromRadioButtonHWND(hwnd);
822 wxCHECK_MSG( radiobox, 0, wxT("Should have the associated radio box") );
823
824 switch ( message )
825 {
826 case WM_GETDLGCODE:
827 // we must tell IsDialogMessage()/our kbd processing code that we
828 // want to process arrows ourselves because neither of them is
829 // smart enough to handle arrows properly for us
830 {
831 long lDlgCode = ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd,
832 message, wParam, lParam);
833
834 return lDlgCode | DLGC_WANTARROWS;
835 }
836
837 case WM_KEYDOWN:
838 {
839 bool processed = true;
840
841 wxDirection dir;
842 switch ( wParam )
843 {
844 case VK_UP:
845 dir = wxUP;
846 break;
847
848 case VK_LEFT:
849 dir = wxLEFT;
850 break;
851
852 case VK_DOWN:
853 dir = wxDOWN;
854 break;
855
856 case VK_RIGHT:
857 dir = wxRIGHT;
858 break;
859
860 default:
861 processed = false;
862
863 // just to suppress the compiler warning
864 dir = wxALL;
865 }
866
867 if ( processed )
868 {
869 int selOld = radiobox->GetSelection();
870 int selNew = radiobox->GetNextItem
871 (
872 selOld,
873 dir,
874 radiobox->GetWindowStyle()
875 );
876
877 if ( selNew != selOld )
878 {
879 radiobox->SetSelection(selNew);
880 radiobox->SetFocus();
881
882 // emulate the button click
883 radiobox->SendNotificationEvent();
884
885 return 0;
886 }
887 }
888 }
889 break;
890
891 case WM_SETFOCUS:
892 case WM_KILLFOCUS:
893 {
894 // if we don't do this, no focus events are generated for the
895 // radiobox and, besides, we need to notify the parent about
896 // the focus change, otherwise the focus handling logic in
897 // wxControlContainer doesn't work
898 if ( message == WM_SETFOCUS )
899 radiobox->HandleSetFocus((WXHWND)wParam);
900 else
901 radiobox->HandleKillFocus((WXHWND)wParam);
902 }
903 break;
904
905 #ifndef __WXWINCE__
906 case WM_HELP:
907 {
908 bool processed = false;
909
910 wxEvtHandler * const handler = radiobox->GetEventHandler();
911
912 HELPINFO* info = (HELPINFO*) lParam;
913 if ( info->iContextType == HELPINFO_WINDOW )
914 {
915 for ( wxWindow* subjectOfHelp = radiobox;
916 subjectOfHelp;
917 subjectOfHelp = subjectOfHelp->GetParent() )
918 {
919 wxHelpEvent helpEvent(wxEVT_HELP,
920 subjectOfHelp->GetId(),
921 wxPoint(info->MousePos.x,
922 info->MousePos.y));
923 helpEvent.SetEventObject(radiobox);
924 if ( handler->ProcessEvent(helpEvent) )
925 {
926 processed = true;
927 break;
928 }
929 }
930 }
931 else if (info->iContextType == HELPINFO_MENUITEM)
932 {
933 wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId);
934 helpEvent.SetEventObject(radiobox);
935 processed = handler->ProcessEvent(helpEvent);
936 }
937
938 if ( processed )
939 return 0;
940 }
941 break;
942 #endif // !__WXWINCE__
943 }
944
945 return ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd, message, wParam, lParam);
946 }
947
948 #endif // wxUSE_RADIOBOX