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