]> git.saurik.com Git - wxWidgets.git/blob - src/msw/radiobox.cpp
native scaling support for bitmap
[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 return true;
260 }
261
262 bool 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
278 wxRadioBox::~wxRadioBox()
279 {
280 SendDestroyEvent();
281
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
294 delete m_radioButtons;
295
296 if ( m_dummyHwnd )
297 DestroyWindow((HWND)m_dummyHwnd);
298
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!
305 void 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);
313
314 gs_boxFromButton[hwndBtn] = this;
315 }
316
317 // ----------------------------------------------------------------------------
318 // events generation
319 // ----------------------------------------------------------------------------
320
321 bool wxRadioBox::MSWCommand(WXUINT cmd, WXWORD id_)
322 {
323 const int id = (signed short)id_;
324
325 if ( cmd == BN_CLICKED )
326 {
327 if (id == GetId())
328 return true;
329
330 int selectedButton = wxNOT_FOUND;
331
332 const unsigned int count = GetCount();
333 for ( unsigned int i = 0; i < count; i++ )
334 {
335 const HWND hwndBtn = (*m_radioButtons)[i];
336 if ( id == wxGetWindowId(hwndBtn) )
337 {
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;
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;
366 }
367 else
368 return false;
369 }
370
371 void wxRadioBox::Command(wxCommandEvent & event)
372 {
373 SetSelection (event.GetInt());
374 SetFocus();
375 ProcessCommand(event);
376 }
377
378 void wxRadioBox::SendNotificationEvent()
379 {
380 wxCommandEvent event(wxEVT_RADIOBOX, m_windowId);
381 event.SetInt( m_selectedButton );
382 event.SetString(GetString(m_selectedButton));
383 event.SetEventObject( this );
384 ProcessCommand(event);
385 }
386
387 // ----------------------------------------------------------------------------
388 // simple accessors
389 // ----------------------------------------------------------------------------
390
391 unsigned int wxRadioBox::GetCount() const
392 {
393 return m_radioButtons ? m_radioButtons->GetCount() : 0u;
394 }
395
396 void wxRadioBox::SetString(unsigned int item, const wxString& label)
397 {
398 wxCHECK_RET( IsValid(item), wxT("invalid radiobox index") );
399
400 m_radioWidth[item] =
401 m_radioHeight[item] = wxDefaultCoord;
402
403 ::SetWindowText((*m_radioButtons)[item], label.c_str());
404
405 InvalidateBestSize();
406 }
407
408 void wxRadioBox::SetSelection(int N)
409 {
410 wxCHECK_RET( IsValid(N), wxT("invalid radiobox index") );
411
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
417 ::SendMessage((*m_radioButtons)[N], BM_SETCHECK, 1, 0L);
418
419 m_selectedButton = N;
420 }
421
422 // Find string for position
423 wxString wxRadioBox::GetString(unsigned int item) const
424 {
425 wxCHECK_MSG( IsValid(item), wxEmptyString,
426 wxT("invalid radiobox index") );
427
428 return wxGetWindowText((*m_radioButtons)[item]);
429 }
430
431 void 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
442 bool wxRadioBox::Enable(unsigned int item, bool enable)
443 {
444 wxCHECK_MSG( IsValid(item), false,
445 wxT("invalid item in wxRadioBox::Enable()") );
446
447 BOOL ret = MSWEnableHWND((*m_radioButtons)[item], enable);
448
449 return (ret == 0) != enable;
450 }
451
452 bool wxRadioBox::IsItemEnabled(unsigned int item) const
453 {
454 wxCHECK_MSG( IsValid(item), false,
455 wxT("invalid item in wxRadioBox::IsItemEnabled()") );
456
457 return ::IsWindowEnabled((*m_radioButtons)[item]) != 0;
458 }
459
460 // Show a specific button
461 bool wxRadioBox::Show(unsigned int item, bool show)
462 {
463 wxCHECK_MSG( IsValid(item), false,
464 wxT("invalid item in wxRadioBox::Show()") );
465
466 BOOL ret = ::ShowWindow((*m_radioButtons)[item], show ? SW_SHOW : SW_HIDE);
467
468 bool changed = (ret != 0) != show;
469 if ( changed )
470 {
471 InvalidateBestSize();
472 }
473
474 return changed;
475 }
476
477 bool wxRadioBox::IsItemShown(unsigned int item) const
478 {
479 wxCHECK_MSG( IsValid(item), false,
480 wxT("invalid item in wxRadioBox::IsItemShown()") );
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
489 #if wxUSE_TOOLTIPS
490
491 bool wxRadioBox::HasToolTips() const
492 {
493 return wxStaticBox::HasToolTips() || wxRadioBoxBase::HasItemToolTips();
494 }
495
496 void 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 )
501 tooltip->AddOtherWindow(hwndRbtn);
502 else // unset the tooltip
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...
506 }
507
508 #endif // wxUSE_TOOLTIPS
509
510 bool 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
529 WX_FORWARD_STD_METHODS_TO_SUBWINDOWS(wxRadioBox, wxStaticBox, m_radioButtons)
530
531 // ----------------------------------------------------------------------------
532 // size calculations
533 // ----------------------------------------------------------------------------
534
535 wxSize wxRadioBox::GetMaxButtonSize() const
536 {
537 // calculate the max button size
538 int widthMax = 0,
539 heightMax = 0;
540 const unsigned int count = GetCount();
541 for ( unsigned int i = 0 ; i < count; i++ )
542 {
543 int width, height;
544 if ( m_radioWidth[i] < 0 )
545 {
546 GetTextExtent(wxGetWindowText((*m_radioButtons)[i]), &width, &height);
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
569 wxSize wxRadioBox::GetTotalButtonSize(const wxSize& sizeBtn) const
570 {
571 // the radiobox should be big enough for its buttons
572 int cx1, cy1;
573 wxGetCharSize(m_hWnd, &cx1, &cy1, GetFont());
574
575 int extraHeight = cy1;
576
577 int height = GetRowCount() * sizeBtn.y + cy1/2 + extraHeight;
578 int width = GetColumnCount() * (sizeBtn.x + cx1) + cx1;
579
580 // Add extra space under the label, if it exists.
581 if (!wxControl::GetLabel().empty())
582 height += cy1/2;
583
584 // and also wide enough for its label
585 int widthLabel;
586 GetTextExtent(GetLabelText(), &widthLabel, NULL);
587 widthLabel += RADIO_SIZE; // FIXME this is bogus too
588 if ( widthLabel > width )
589 width = widthLabel;
590
591 return wxSize(width, height);
592 }
593
594 wxSize wxRadioBox::DoGetBestSize() const
595 {
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
603 wxSize best = GetTotalButtonSize(GetMaxButtonSize());
604 CacheBestSize(best);
605 return best;
606 }
607
608 void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
609 {
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());
616
617 // only change our width/height if asked for
618 if ( width == wxDefaultCoord && (sizeFlags & wxSIZE_AUTO_WIDTH) )
619 width = totSize.x;
620
621 if ( height == wxDefaultCoord && (sizeFlags & wxSIZE_AUTO_HEIGHT) )
622 height = totSize.y;
623 }
624
625 wxStaticBox::DoSetSize(x, y, width, height);
626 }
627
628 void wxRadioBox::DoMoveWindow(int x, int y, int width, int height)
629 {
630 wxStaticBox::DoMoveWindow(x, y, width, height);
631
632 PositionAllButtons(x, y, width, height);
633 }
634
635 void
636 wxRadioBox::PositionAllButtons(int x, int y, int width, int WXUNUSED(height))
637 {
638 wxSize maxSize = GetMaxButtonSize();
639 int maxWidth = maxSize.x,
640 maxHeight = maxSize.y;
641
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.
647
648 // Also, remember that wxRA_SPECIFY_COLS means that we arrange buttons in
649 // left to right order and GetMajorDim() is the number of columns while
650 // wxRA_SPECIFY_ROWS means that the buttons are arranged top to bottom and
651 // GetMajorDim() is the number of rows.
652
653 int cx1, cy1;
654 wxGetCharSize(m_hWnd, &cx1, &cy1, GetFont());
655
656 int x_offset = x + cx1;
657 int y_offset = y + cy1;
658
659 // Add extra space under the label, if it exists.
660 if (!wxControl::GetLabel().empty())
661 y_offset += cy1/2;
662
663 int startX = x_offset;
664 int startY = y_offset;
665
666 const unsigned int count = GetCount();
667 for (unsigned int i = 0; i < count; i++)
668 {
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
677 unsigned int n = i + 1;
678 isLastInTheRow = ((n % GetMajorDim()) == 0) || (n == count);
679 }
680 else // wxRA_SPECIFY_ROWS
681 {
682 // item is the last in the row if it is in the last columns
683 isLastInTheRow = i >= (count/GetMajorDim())*GetMajorDim();
684 }
685
686 // is this the start of new row/column?
687 if ( i && (i % GetMajorDim() == 0) )
688 {
689 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
690 {
691 // start of new column
692 y_offset = startY;
693 x_offset += maxWidth + cx1;
694 }
695 else // start of new row
696 {
697 x_offset = startX;
698 y_offset += maxHeight;
699 if (m_radioWidth[0]>0)
700 y_offset += cy1/2;
701 }
702 }
703
704 int widthBtn;
705 if ( isLastInTheRow )
706 {
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;
711 }
712 else
713 {
714 // normal button, always of the same size
715 widthBtn = maxWidth;
716 }
717
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)
722 DoMoveSibling((*m_radioButtons)[i], x_offset, y_offset, widthBtn, maxHeight);
723
724 // where do we put the next button?
725 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
726 {
727 // below this one
728 y_offset += maxHeight;
729 if (m_radioWidth[0]>0)
730 y_offset += cy1/2;
731 }
732 else
733 {
734 // to the right of this one
735 x_offset += widthBtn + cx1;
736 }
737 }
738 }
739
740 int 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
757 // ----------------------------------------------------------------------------
758 // radio box drawing
759 // ----------------------------------------------------------------------------
760
761 #ifndef __WXWINCE__
762
763 WXHRGN 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
769 const unsigned int count = GetCount();
770 for ( unsigned int i = 0; i < count; ++i )
771 {
772 // don't clip out hidden children
773 if ( !IsItemShown(i) )
774 continue;
775
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
786 // ---------------------------------------------------------------------------
787 // window proc for radio buttons
788 // ---------------------------------------------------------------------------
789
790 LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
791 UINT message,
792 WPARAM wParam,
793 LPARAM lParam)
794 {
795
796 wxRadioBox * const radiobox = wxRadioBox::GetFromRadioButtonHWND(hwnd);
797 wxCHECK_MSG( radiobox, 0, wxT("Should have the associated radio box") );
798
799 switch ( message )
800 {
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 {
806 long lDlgCode = ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd,
807 message, wParam, lParam);
808
809 return lDlgCode | DLGC_WANTARROWS;
810 }
811
812 case WM_KEYDOWN:
813 {
814 bool processed = true;
815
816 wxDirection dir;
817 switch ( wParam )
818 {
819 case VK_UP:
820 dir = wxUP;
821 break;
822
823 case VK_LEFT:
824 dir = wxLEFT;
825 break;
826
827 case VK_DOWN:
828 dir = wxDOWN;
829 break;
830
831 case VK_RIGHT:
832 dir = wxRIGHT;
833 break;
834
835 default:
836 processed = false;
837
838 // just to suppress the compiler warning
839 dir = wxALL;
840 }
841
842 if ( processed )
843 {
844 int selOld = radiobox->GetSelection();
845 int selNew = radiobox->GetNextItem
846 (
847 selOld,
848 dir,
849 radiobox->GetWindowStyle()
850 );
851
852 if ( selNew != selOld )
853 {
854 radiobox->SetSelection(selNew);
855 radiobox->SetFocus();
856
857 // emulate the button click
858 radiobox->SendNotificationEvent();
859
860 return 0;
861 }
862 }
863 }
864 break;
865
866 case WM_SETFOCUS:
867 case WM_KILLFOCUS:
868 {
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
880 #ifndef __WXWINCE__
881 case WM_HELP:
882 {
883 bool processed = false;
884
885 wxEvtHandler * const handler = radiobox->GetEventHandler();
886
887 HELPINFO* info = (HELPINFO*) lParam;
888 if ( info->iContextType == HELPINFO_WINDOW )
889 {
890 for ( wxWindow* subjectOfHelp = radiobox;
891 subjectOfHelp;
892 subjectOfHelp = subjectOfHelp->GetParent() )
893 {
894 wxHelpEvent helpEvent(wxEVT_HELP,
895 subjectOfHelp->GetId(),
896 wxPoint(info->MousePos.x,
897 info->MousePos.y));
898 helpEvent.SetEventObject(radiobox);
899 if ( handler->ProcessEvent(helpEvent) )
900 {
901 processed = true;
902 break;
903 }
904 }
905 }
906 else if (info->iContextType == HELPINFO_MENUITEM)
907 {
908 wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId);
909 helpEvent.SetEventObject(radiobox);
910 processed = handler->ProcessEvent(helpEvent);
911 }
912
913 if ( processed )
914 return 0;
915 }
916 break;
917 #endif // !__WXWINCE__
918 }
919
920 return ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd, message, wParam, lParam);
921 }
922
923 #endif // wxUSE_RADIOBOX