1. '\n's in tooltip messages are handled (replaced by spaces anyhow, tooltip
[wxWidgets.git] / src / msw / radiobox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: radiobox.cpp
3 // Purpose: wxRadioBox
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 #ifdef __GNUG__
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 #ifndef WX_PRECOMP
32 #include "wx/bitmap.h"
33 #include "wx/brush.h"
34 #include "wx/radiobox.h"
35 #include "wx/log.h"
36 #endif
37
38 #include "wx/msw/private.h"
39
40 #if wxUSE_TOOLTIPS
41 #ifndef __GNUWIN32__
42 #include <commctrl.h>
43 #endif
44
45 #include "wx/tooltip.h"
46 #endif // wxUSE_TOOLTIPS
47
48 IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl)
49
50 // there are two possible ways to create the radio buttons: either as children
51 // of the radiobox or as siblings of it - allow playing with both variants for
52 // now, eventually we will choose the best one for our purposes
53 //
54 // two main problems are the keyboard navigation inside the radiobox (arrows
55 // should switch between buttons, not pass focus to the next control) and the
56 // tooltips - a tooltip is associated with the radiobox itself, not the
57 // children...
58 //
59 // the problems with setting this to 1:
60 // a) Alt-<mnemonic of radiobox> isn't handled properly by IsDialogMessage()
61 // because it sets focus to the next control accepting it which is not a
62 // radio button but a radiobox sibling in this case - the only solution to
63 // this would be to handle Alt-<mnemonic> ourselves
64 // b) the problems with setting radiobox colours under Win98/2K were reported
65 // but I couldn't reproduce it so I have no idea about what causes it
66 //
67 // the problems with setting this to 0:
68 // a) the tooltips are not shown for the radiobox - possible solution: make
69 // TTM_WINDOWFROMPOS handling code in msw/tooltip.cpp work (easier said than
70 // done because I don't know why it doesn't work)
71 #define RADIOBTN_PARENT_IS_RADIOBOX 0
72
73 // ---------------------------------------------------------------------------
74 // private functions
75 // ---------------------------------------------------------------------------
76
77 // wnd proc for radio buttons
78 #ifdef __WIN32__
79 LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hWnd,
80 UINT message,
81 WPARAM wParam,
82 LPARAM lParam);
83
84 // ---------------------------------------------------------------------------
85 // global vars
86 // ---------------------------------------------------------------------------
87
88 // the pointer to standard radio button wnd proc
89 static WNDPROC s_wndprocRadioBtn = (WNDPROC)NULL;
90
91 #endif // __WIN32__
92
93 // ===========================================================================
94 // implementation
95 // ===========================================================================
96
97 // ---------------------------------------------------------------------------
98 // wxRadioBox
99 // ---------------------------------------------------------------------------
100
101 int wxRadioBox::GetNumVer() const
102 {
103 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
104 {
105 return m_majorDim;
106 }
107 else
108 {
109 return (m_noItems + m_majorDim - 1)/m_majorDim;
110 }
111 }
112
113 int wxRadioBox::GetNumHor() const
114 {
115 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
116 {
117 return (m_noItems + m_majorDim - 1)/m_majorDim;
118 }
119 else
120 {
121 return m_majorDim;
122 }
123 }
124
125 bool wxRadioBox::MSWCommand(WXUINT cmd, WXWORD id)
126 {
127 if ( cmd == BN_CLICKED )
128 {
129 int selectedButton = -1;
130
131 for ( int i = 0; i < m_noItems; i++ )
132 {
133 if ( id == wxGetWindowId(m_radioButtons[i]) )
134 {
135 selectedButton = i;
136
137 break;
138 }
139 }
140
141 wxASSERT_MSG( selectedButton != -1, wxT("click from alien button?") );
142
143 if ( selectedButton != m_selectedButton )
144 {
145 m_selectedButton = selectedButton;
146
147 SendNotificationEvent();
148 }
149 //else: don't generate events when the selection doesn't change
150
151 return TRUE;
152 }
153 else
154 return FALSE;
155 }
156
157 #if WXWIN_COMPATIBILITY
158 wxRadioBox::wxRadioBox(wxWindow *parent, wxFunction func, const char *title,
159 int x, int y, int width, int height,
160 int n, char **choices,
161 int majorDim, long style, const char *name)
162 {
163 wxString *choices2 = new wxString[n];
164 for ( int i = 0; i < n; i ++) choices2[i] = choices[i];
165 Create(parent, -1, title, wxPoint(x, y), wxSize(width, height), n, choices2, majorDim, style,
166 wxDefaultValidator, name);
167 Callback(func);
168 delete choices2;
169 }
170
171 #endif // WXWIN_COMPATIBILITY
172
173 // Radio box item
174 wxRadioBox::wxRadioBox()
175 {
176 m_selectedButton = -1;
177 m_noItems = 0;
178 m_noRowsOrCols = 0;
179 m_radioButtons = NULL;
180 m_majorDim = 0;
181 m_radioWidth = NULL;
182 m_radioHeight = NULL;
183 }
184
185 bool wxRadioBox::Create(wxWindow *parent,
186 wxWindowID id,
187 const wxString& title,
188 const wxPoint& pos,
189 const wxSize& size,
190 int n,
191 const wxString choices[],
192 int majorDim,
193 long style,
194 const wxValidator& val,
195 const wxString& name)
196 {
197 // initialize members
198 m_selectedButton = -1;
199 m_noItems = 0;
200
201 m_majorDim = majorDim == 0 ? n : majorDim;
202 m_noRowsOrCols = majorDim;
203
204 // common initialization
205 if ( !CreateControl(parent, id, pos, size, style, val, name) )
206 return FALSE;
207
208 // create the static box
209 if ( !MSWCreateControl(wxT("BUTTON"), BS_GROUPBOX | WS_GROUP,
210 pos, size, title, 0) )
211 return FALSE;
212
213 // and now create the buttons
214 m_noItems = n;
215 #if RADIOBTN_PARENT_IS_RADIOBOX
216 HWND hwndParent = GetHwnd();
217 #else
218 HWND hwndParent = GetHwndOf(parent);
219 #endif
220
221 // Some radio boxes test consecutive id.
222 (void)NewControlId();
223 m_radioButtons = new WXHWND[n];
224 m_radioWidth = new int[n];
225 m_radioHeight = new int[n];
226
227 WXHFONT hfont = 0;
228 wxFont& font = GetFont();
229 if ( font.Ok() )
230 {
231 hfont = font.GetResourceHandle();
232 }
233
234 for ( int i = 0; i < n; i++ )
235 {
236 m_radioWidth[i] =
237 m_radioHeight[i] = -1;
238 long styleBtn = BS_AUTORADIOBUTTON | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
239 if ( i == 0 && style == 0 )
240 styleBtn |= WS_GROUP;
241
242 long newId = NewControlId();
243
244 HWND hwndBtn = ::CreateWindow(_T("BUTTON"),
245 choices[i],
246 styleBtn,
247 0, 0, 0, 0, // will be set in SetSize()
248 hwndParent,
249 (HMENU)newId,
250 wxGetInstance(),
251 NULL);
252
253 if ( !hwndBtn )
254 {
255 wxLogLastError("CreateWindow(radio btn)");
256
257 return FALSE;
258 }
259
260 m_radioButtons[i] = (WXHWND)hwndBtn;
261
262 SubclassRadioButton((WXHWND)hwndBtn);
263
264 if ( hfont )
265 {
266 ::SendMessage(hwndBtn, WM_SETFONT, (WPARAM)hfont, 0L);
267 }
268
269 m_subControls.Add(newId);
270 }
271
272 // Create a dummy radio control to end the group.
273 (void)::CreateWindow(_T("BUTTON"),
274 _T(""),
275 WS_GROUP | BS_AUTORADIOBUTTON | WS_CHILD,
276 0, 0, 0, 0, hwndParent,
277 (HMENU)NewControlId(), wxGetInstance(), NULL);
278
279 SetSelection(0);
280
281 SetSize(pos.x, pos.y, size.x, size.y);
282
283 return TRUE;
284 }
285
286 wxRadioBox::~wxRadioBox()
287 {
288 m_isBeingDeleted = TRUE;
289
290 if (m_radioButtons)
291 {
292 int i;
293 for (i = 0; i < m_noItems; i++)
294 ::DestroyWindow((HWND)m_radioButtons[i]);
295 delete[] m_radioButtons;
296 }
297
298 if (m_radioWidth)
299 delete[] m_radioWidth;
300 if (m_radioHeight)
301 delete[] m_radioHeight;
302
303 }
304
305 wxString wxRadioBox::GetLabel(int item) const
306 {
307 wxCHECK_MSG( item >= 0 && item < m_noItems, wxT(""), wxT("invalid radiobox index") );
308
309 return wxGetWindowText(m_radioButtons[item]);
310 }
311
312 void wxRadioBox::SetLabel(int item, const wxString& label)
313 {
314 wxCHECK_RET( item >= 0 && item < m_noItems, wxT("invalid radiobox index") );
315
316 m_radioWidth[item] = m_radioHeight[item] = -1;
317 SetWindowText((HWND)m_radioButtons[item], label.c_str());
318 }
319
320 void wxRadioBox::SetLabel(int item, wxBitmap *bitmap)
321 {
322 /*
323 m_radioWidth[item] = bitmap->GetWidth() + FB_MARGIN;
324 m_radioHeight[item] = bitmap->GetHeight() + FB_MARGIN;
325 */
326 wxFAIL_MSG(wxT("not implemented"));
327 }
328
329 int wxRadioBox::FindString(const wxString& s) const
330 {
331 for (int i = 0; i < m_noItems; i++)
332 {
333 if ( s == wxGetWindowText(m_radioButtons[i]) )
334 return i;
335 }
336
337 return wxNOT_FOUND;
338 }
339
340 void wxRadioBox::SetSelection(int N)
341 {
342 wxCHECK_RET( (N >= 0) && (N < m_noItems), wxT("invalid radiobox index") );
343
344 // Following necessary for Win32s, because Win32s translate BM_SETCHECK
345 if (m_selectedButton >= 0 && m_selectedButton < m_noItems)
346 ::SendMessage((HWND) m_radioButtons[m_selectedButton], BM_SETCHECK, 0, 0L);
347
348 ::SendMessage((HWND)m_radioButtons[N], BM_SETCHECK, 1, 0L);
349 ::SetFocus((HWND)m_radioButtons[N]);
350
351 m_selectedButton = N;
352 }
353
354 // Get single selection, for single choice list items
355 int wxRadioBox::GetSelection() const
356 {
357 return m_selectedButton;
358 }
359
360 // Find string for position
361 wxString wxRadioBox::GetString(int N) const
362 {
363 return wxGetWindowText(m_radioButtons[N]);
364 }
365
366 // Restored old code.
367 void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
368 {
369 int currentX, currentY;
370 GetPosition(&currentX, &currentY);
371 int widthOld, heightOld;
372 GetSize(&widthOld, &heightOld);
373
374 int xx = x;
375 int yy = y;
376
377 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
378 xx = currentX;
379 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
380 yy = currentY;
381
382 #if RADIOBTN_PARENT_IS_RADIOBOX
383 int y_offset = 0;
384 int x_offset = 0;
385 #else
386 int y_offset = yy;
387 int x_offset = xx;
388 #endif
389
390 int current_width, cyf;
391
392 int cx1,cy1;
393 wxGetCharSize(m_hWnd, &cx1, &cy1, & GetFont());
394
395 // Attempt to have a look coherent with other platforms: We compute the
396 // biggest toggle dim, then we align all items according this value.
397 int maxWidth = -1;
398 int maxHeight = -1;
399
400 int i;
401 for (i = 0 ; i < m_noItems; i++)
402 {
403 int eachWidth;
404 int eachHeight;
405 if (m_radioWidth[i]<0)
406 {
407 // It's a labelled toggle
408 GetTextExtent(wxGetWindowText(m_radioButtons[i]),
409 &current_width, &cyf);
410 eachWidth = (int)(current_width + RADIO_SIZE);
411 eachHeight = (int)((3*cyf)/2);
412 }
413 else
414 {
415 eachWidth = m_radioWidth[i];
416 eachHeight = m_radioHeight[i];
417 }
418
419 if (maxWidth<eachWidth)
420 maxWidth = eachWidth;
421 if (maxHeight<eachHeight)
422 maxHeight = eachHeight;
423 }
424
425 if (m_hWnd)
426 {
427 int totWidth;
428 int totHeight;
429
430 int nbHor = GetNumHor(),
431 nbVer = GetNumVer();
432
433 // this formula works, but I don't know why.
434 // Please, be sure what you do if you modify it!!
435 if (m_radioWidth[0]<0)
436 totHeight = (nbVer * maxHeight) + cy1/2;
437 else
438 totHeight = nbVer * (maxHeight+cy1/2);
439 totWidth = nbHor * (maxWidth+cx1);
440
441 int extraHeight = cy1;
442
443 #if defined(CTL3D) && !CTL3D
444 // Requires a bigger group box in plain Windows
445 extraHeight *= 3;
446 extraHeight /= 2;
447 #endif
448
449 // only change our width/height if asked for
450 if ( width == -1 )
451 {
452 if ( sizeFlags & wxSIZE_AUTO_WIDTH )
453 width = totWidth + cx1;
454 else
455 width = widthOld;
456 }
457
458 if ( height == -1 )
459 {
460 if ( sizeFlags & wxSIZE_AUTO_HEIGHT )
461 height = totHeight + extraHeight;
462 else
463 height = heightOld;
464 }
465
466 ::MoveWindow(GetHwnd(), xx, yy, width, height, TRUE);
467
468 x_offset += cx1;
469 y_offset += cy1;
470 }
471
472 #if defined(CTL3D) && (!CTL3D)
473 y_offset += (int)(cy1/2); // Fudge factor since buttons overlapped label
474 // JACS 2/12/93. CTL3D draws group label quite high.
475 #endif
476 int startX = x_offset;
477 int startY = y_offset;
478
479 for ( i = 0 ; i < m_noItems; i++)
480 {
481 // Bidimensional radio adjustment
482 if (i&&((i%m_majorDim)==0)) // Why is this omitted for i = 0?
483 {
484 if (m_windowStyle & wxRA_VERTICAL)
485 {
486 y_offset = startY;
487 x_offset += maxWidth + cx1;
488 }
489 else
490 {
491 x_offset = startX;
492 y_offset += maxHeight;
493 if (m_radioWidth[0]>0)
494 y_offset += cy1/2;
495 }
496 }
497 int eachWidth;
498 int eachHeight;
499 if (m_radioWidth[i]<0)
500 {
501 // It's a labeled item
502 GetTextExtent(wxGetWindowText(m_radioButtons[i]),
503 &current_width, &cyf);
504
505 // How do we find out radio button bitmap size!!
506 // By adjusting them carefully, manually :-)
507 eachWidth = (int)(current_width + RADIO_SIZE);
508 eachHeight = (int)((3*cyf)/2);
509 }
510 else
511 {
512 eachWidth = m_radioWidth[i];
513 eachHeight = m_radioHeight[i];
514 }
515
516 // VZ: make all buttons of the same, maximal size - like this they
517 // cover the radiobox entirely and the radiobox tooltips are always
518 // shown (otherwise they are not when the mouse pointer is in the
519 // radiobox part not belonging to any radiobutton)
520 ::MoveWindow((HWND)m_radioButtons[i],
521 x_offset, y_offset, maxWidth, maxHeight,
522 TRUE);
523
524 if (m_windowStyle & wxRA_SPECIFY_ROWS)
525 {
526 y_offset += maxHeight;
527 if (m_radioWidth[0]>0)
528 y_offset += cy1/2;
529 }
530 else
531 x_offset += maxWidth + cx1;
532 }
533 }
534
535 void wxRadioBox::GetSize(int *width, int *height) const
536 {
537 RECT rect;
538 rect.left = -1; rect.right = -1; rect.top = -1; rect.bottom = -1;
539
540 if (m_hWnd)
541 wxFindMaxSize(m_hWnd, &rect);
542
543 int i;
544 for (i = 0; i < m_noItems; i++)
545 wxFindMaxSize(m_radioButtons[i], &rect);
546
547 *width = rect.right - rect.left;
548 *height = rect.bottom - rect.top;
549 }
550
551 void wxRadioBox::GetPosition(int *x, int *y) const
552 {
553 wxWindow *parent = GetParent();
554 RECT rect = { -1, -1, -1, -1 };
555
556 int i;
557 for (i = 0; i < m_noItems; i++)
558 wxFindMaxSize(m_radioButtons[i], &rect);
559
560 if (m_hWnd)
561 wxFindMaxSize(m_hWnd, &rect);
562
563 // Since we now have the absolute screen coords, if there's a parent we
564 // must subtract its top left corner
565 POINT point;
566 point.x = rect.left;
567 point.y = rect.top;
568 if (parent)
569 {
570 ::ScreenToClient((HWND) parent->GetHWND(), &point);
571 }
572
573 // We may be faking the client origin. So a window that's really at (0, 30)
574 // may appear (to wxWin apps) to be at (0, 0).
575 if (GetParent())
576 {
577 wxPoint pt(GetParent()->GetClientAreaOrigin());
578 point.x -= pt.x;
579 point.y -= pt.y;
580 }
581
582 *x = point.x;
583 *y = point.y;
584 }
585
586 void wxRadioBox::SetFocus()
587 {
588 if (m_noItems > 0)
589 {
590 if (m_selectedButton == -1)
591 ::SetFocus((HWND) m_radioButtons[0]);
592 else
593 ::SetFocus((HWND) m_radioButtons[m_selectedButton]);
594 }
595
596 }
597
598 bool wxRadioBox::Show(bool show)
599 {
600 if ( !wxControl::Show(show) )
601 return FALSE;
602
603 int nCmdShow = show ? SW_SHOW : SW_HIDE;
604 for ( int i = 0; i < m_noItems; i++ )
605 {
606 ::ShowWindow((HWND)m_radioButtons[i], nCmdShow);
607 }
608
609 return TRUE;
610 }
611
612 // Enable a specific button
613 void wxRadioBox::Enable(int item, bool enable)
614 {
615 wxCHECK_RET( item >= 0 && item < m_noItems,
616 wxT("invalid item in wxRadioBox::Enable()") );
617
618 ::EnableWindow((HWND) m_radioButtons[item], enable);
619 }
620
621 // Enable all controls
622 bool wxRadioBox::Enable(bool enable)
623 {
624 if ( !wxControl::Enable(enable) )
625 return FALSE;
626
627 for (int i = 0; i < m_noItems; i++)
628 ::EnableWindow((HWND) m_radioButtons[i], enable);
629
630 return TRUE;
631 }
632
633 // Show a specific button
634 void wxRadioBox::Show(int item, bool show)
635 {
636 wxCHECK_RET( item >= 0 && item < m_noItems,
637 wxT("invalid item in wxRadioBox::Show()") );
638
639 ::ShowWindow((HWND)m_radioButtons[item], show ? SW_SHOW : SW_HIDE);
640 }
641
642 // For single selection items only
643 wxString wxRadioBox::GetStringSelection() const
644 {
645 wxString result;
646 int sel = GetSelection();
647 if (sel > -1)
648 result = GetString(sel);
649
650 return result;
651 }
652
653 bool wxRadioBox::SetStringSelection(const wxString& s)
654 {
655 int sel = FindString (s);
656 if (sel > -1)
657 {
658 SetSelection (sel);
659 return TRUE;
660 }
661 else
662 return FALSE;
663 }
664
665 bool wxRadioBox::ContainsHWND(WXHWND hWnd) const
666 {
667 int i;
668 for (i = 0; i < Number(); i++)
669 {
670 if (GetRadioButtons()[i] == hWnd)
671 return TRUE;
672 }
673
674 return FALSE;
675 }
676
677 void wxRadioBox::Command(wxCommandEvent & event)
678 {
679 SetSelection (event.m_commandInt);
680 ProcessCommand (event);
681 }
682
683 // NB: if this code is changed, wxGetWindowForHWND() which relies on having the
684 // radiobox pointer in GWL_USERDATA for radio buttons must be updated too!
685 void wxRadioBox::SubclassRadioButton(WXHWND hWndBtn)
686 {
687 // No GWL_USERDATA in Win16, so omit this subclassing.
688 #ifdef __WIN32__
689 HWND hwndBtn = (HWND)hWndBtn;
690
691 if ( !s_wndprocRadioBtn )
692 s_wndprocRadioBtn = (WNDPROC)::GetWindowLong(hwndBtn, GWL_WNDPROC);
693
694 ::SetWindowLong(hwndBtn, GWL_WNDPROC, (long)wxRadioBtnWndProc);
695 ::SetWindowLong(hwndBtn, GWL_USERDATA, (long)this);
696 #endif // __WIN32__
697 }
698
699 void wxRadioBox::SendNotificationEvent()
700 {
701 wxCommandEvent event(wxEVT_COMMAND_RADIOBOX_SELECTED, m_windowId);
702 event.SetInt( m_selectedButton );
703 event.SetString( GetString(m_selectedButton) );
704 event.SetEventObject( this );
705 ProcessCommand(event);
706 }
707
708 bool wxRadioBox::SetFont(const wxFont& font)
709 {
710 if ( !wxControl::SetFont(font) )
711 {
712 // nothing to do
713 return FALSE;
714 }
715
716 // also set the font of our radio buttons
717 WXHFONT hfont = wxFont(font).GetResourceHandle();
718 for ( int n = 0; n < m_noItems; n++ )
719 {
720 ::SendMessage((HWND)m_radioButtons[n], WM_SETFONT, (WPARAM)hfont, 0L);
721 }
722
723 // this is needed because otherwise the buttons are not redrawn correctly
724 Refresh();
725
726 return TRUE;
727 }
728
729 // ----------------------------------------------------------------------------
730 // our window proc
731 // ----------------------------------------------------------------------------
732
733 long wxRadioBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
734 {
735 switch ( nMsg )
736 {
737 #ifdef __WIN32__
738 case WM_CTLCOLORSTATIC:
739 // set the colour of the radio buttons to be the same as ours
740 {
741 HDC hdc = (HDC)wParam;
742
743 const wxColour& colBack = GetBackgroundColour();
744 ::SetBkColor(hdc, wxColourToRGB(colBack));
745 ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
746
747 wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
748
749 return (WXHBRUSH)brush->GetResourceHandle();
750 }
751 #endif // Win32
752
753 // This is required for the radiobox to be sensitive to mouse input,
754 // e.g. for Dialog Editor.
755 case WM_NCHITTEST:
756 {
757 int xPos = LOWORD(lParam); // horizontal position of cursor
758 int yPos = HIWORD(lParam); // vertical position of cursor
759
760 ScreenToClient(&xPos, &yPos);
761
762 // Make sure you can drag by the top of the groupbox, but let
763 // other (enclosed) controls get mouse events also
764 if (yPos < 10)
765 return (long)HTCLIENT;
766 }
767 break;
768 }
769
770 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
771 }
772
773 // ---------------------------------------------------------------------------
774 // window proc for radio buttons
775 // ---------------------------------------------------------------------------
776
777 #ifdef __WIN32__
778
779 LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
780 UINT message,
781 WPARAM wParam,
782 LPARAM lParam)
783 {
784 switch ( message )
785 {
786 case WM_GETDLGCODE:
787 // we must tell IsDialogMessage()/our kbd processing code that we
788 // want to process arrows ourselves because neither of them is
789 // smart enough to handle arrows properly for us
790 {
791 long lDlgCode = ::CallWindowProc(s_wndprocRadioBtn, hwnd,
792 message, wParam, lParam);
793 return lDlgCode | DLGC_WANTARROWS;
794 }
795
796 #if wxUSE_TOOLTIPS
797 case WM_NOTIFY:
798 {
799 NMHDR* hdr = (NMHDR *)lParam;
800 if ( (int)hdr->code == TTN_NEEDTEXT )
801 {
802 wxRadioBox *radiobox = (wxRadioBox *)
803 ::GetWindowLong(hwnd, GWL_USERDATA);
804
805 wxCHECK_MSG( radiobox, 0,
806 wxT("radio button without radio box?") );
807
808 wxToolTip *tooltip = radiobox->GetToolTip();
809 if ( tooltip )
810 {
811 TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam;
812 ttt->lpszText = (wxChar *)tooltip->GetTip().c_str();
813 }
814
815 // processed
816 return 0;
817 }
818 }
819 break;
820 #endif // wxUSE_TOOLTIPS
821
822 case WM_KEYDOWN:
823 {
824 wxRadioBox *radiobox = (wxRadioBox *)
825 ::GetWindowLong(hwnd, GWL_USERDATA);
826
827 wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") );
828
829 bool processed = TRUE;
830
831 int selOld = radiobox->GetSelection();
832 int selNew = selOld;
833
834 switch ( wParam )
835 {
836 case VK_UP:
837 selNew--;
838 break;
839
840 case VK_LEFT:
841 selNew -= radiobox->GetNumVer();
842 break;
843
844 case VK_DOWN:
845 selNew++;
846 break;
847
848 case VK_RIGHT:
849 selNew += radiobox->GetNumVer();
850 break;
851
852 default:
853 processed = FALSE;
854 }
855
856 if ( processed )
857 {
858 // ensure that selNew is in range [0..num)
859 int num = radiobox->Number();
860 selNew += num;
861 selNew %= num;
862
863 if ( selNew != selOld )
864 {
865 radiobox->SetSelection(selNew);
866
867 // emulate the button click
868 radiobox->SendNotificationEvent();
869
870 return 0;
871 }
872 }
873 }
874 }
875
876 return ::CallWindowProc(s_wndprocRadioBtn, hwnd, message, wParam, lParam);
877 }
878
879 #endif // __WIN32__
880