Unicode/WinCE corrections
[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/private.h"
42
43 #if wxUSE_TOOLTIPS
44 #if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__)
45 #include <commctrl.h>
46 #endif
47 #include "wx/tooltip.h"
48 #endif // wxUSE_TOOLTIPS
49
50 #if defined(__WXWINCE__) && !defined(WS_EX_TRANSPARENT)
51 #define WS_EX_TRANSPARENT 0
52 #endif
53
54 // TODO: wxCONSTRUCTOR
55 #if 0 // wxUSE_EXTENDED_RTTI
56 WX_DEFINE_FLAGS( wxRadioBoxStyle )
57
58 wxBEGIN_FLAGS( wxRadioBoxStyle )
59 // new style border flags, we put them first to
60 // use them for streaming out
61 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
62 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
63 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
64 wxFLAGS_MEMBER(wxBORDER_RAISED)
65 wxFLAGS_MEMBER(wxBORDER_STATIC)
66 wxFLAGS_MEMBER(wxBORDER_NONE)
67
68 // old style border flags
69 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
70 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
71 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
72 wxFLAGS_MEMBER(wxRAISED_BORDER)
73 wxFLAGS_MEMBER(wxSTATIC_BORDER)
74 wxFLAGS_MEMBER(wxBORDER)
75
76 // standard window styles
77 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
78 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
79 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
80 wxFLAGS_MEMBER(wxWANTS_CHARS)
81 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
82 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
83 wxFLAGS_MEMBER(wxVSCROLL)
84 wxFLAGS_MEMBER(wxHSCROLL)
85
86 wxFLAGS_MEMBER(wxRA_SPECIFY_COLS)
87 wxFLAGS_MEMBER(wxRA_HORIZONTAL)
88 wxFLAGS_MEMBER(wxRA_SPECIFY_ROWS)
89 wxFLAGS_MEMBER(wxRA_VERTICAL)
90
91 wxEND_FLAGS( wxRadioBoxStyle )
92
93 IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioBox, wxControl,"wx/radiobox.h")
94
95 wxBEGIN_PROPERTIES_TABLE(wxRadioBox)
96 wxEVENT_PROPERTY( Select , wxEVT_COMMAND_RADIOBOX_SELECTED , wxCommandEvent )
97 wxPROPERTY_FLAGS( WindowStyle , wxRadioBoxStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
98 wxEND_PROPERTIES_TABLE()
99
100 #else
101 IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl)
102 #endif
103
104 /*
105 selection
106 content
107 label
108 dimension
109 item
110 */
111
112 // there are two possible ways to create the radio buttons: either as children
113 // of the radiobox or as siblings of it - allow playing with both variants for
114 // now, eventually we will choose the best one for our purposes
115 //
116 // two main problems are the keyboard navigation inside the radiobox (arrows
117 // should switch between buttons, not pass focus to the next control) and the
118 // tooltips - a tooltip is associated with the radiobox itself, not the
119 // children...
120 //
121 // the problems with setting this to 1:
122 // a) Alt-<mnemonic of radiobox> isn't handled properly by IsDialogMessage()
123 // because it sets focus to the next control accepting it which is not a
124 // radio button but a radiobox sibling in this case - the only solution to
125 // this would be to handle Alt-<mnemonic> ourselves
126 // b) the problems with setting radiobox colours under Win98/2K were reported
127 // but I couldn't reproduce it so I have no idea about what causes it
128 //
129 // the problems with setting this to 0:
130 // a) the tooltips are not shown for the radiobox - possible solution: make
131 // TTM_WINDOWFROMPOS handling code in msw/tooltip.cpp work (easier said than
132 // done because I don't know why it doesn't work)
133 #define RADIOBTN_PARENT_IS_RADIOBOX 0
134
135 // ---------------------------------------------------------------------------
136 // private functions
137 // ---------------------------------------------------------------------------
138
139 // wnd proc for radio buttons
140 #ifdef __WIN32__
141 LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hWnd,
142 UINT message,
143 WPARAM wParam,
144 LPARAM lParam);
145
146 // ---------------------------------------------------------------------------
147 // global vars
148 // ---------------------------------------------------------------------------
149
150 // the pointer to standard radio button wnd proc
151 static WXFARPROC s_wndprocRadioBtn = (WXFARPROC)NULL;
152
153 #endif // __WIN32__
154
155 // ===========================================================================
156 // implementation
157 // ===========================================================================
158
159 // ---------------------------------------------------------------------------
160 // wxRadioBox
161 // ---------------------------------------------------------------------------
162
163 int wxRadioBox::GetCount() const
164 {
165 return m_noItems;
166 }
167
168 int wxRadioBox::GetColumnCount() const
169 {
170 return GetNumHor();
171 }
172
173 int wxRadioBox::GetRowCount() const
174 {
175 return GetNumVer();
176 }
177
178 // returns the number of rows
179 int wxRadioBox::GetNumVer() const
180 {
181 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
182 {
183 return m_majorDim;
184 }
185 else
186 {
187 return (m_noItems + m_majorDim - 1)/m_majorDim;
188 }
189 }
190
191 // returns the number of columns
192 int wxRadioBox::GetNumHor() const
193 {
194 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
195 {
196 return (m_noItems + m_majorDim - 1)/m_majorDim;
197 }
198 else
199 {
200 return m_majorDim;
201 }
202 }
203
204 bool wxRadioBox::MSWCommand(WXUINT cmd, WXWORD id)
205 {
206 if ( cmd == BN_CLICKED )
207 {
208 if (id == GetId())
209 return true;
210
211 int selectedButton = wxID_ANY;
212
213 for ( int i = 0; i < m_noItems; i++ )
214 {
215 if ( id == wxGetWindowId(m_radioButtons[i]) )
216 {
217 selectedButton = i;
218
219 break;
220 }
221 }
222
223 if ( selectedButton == wxID_ANY )
224 {
225 // just ignore it - due to a hack with WM_NCHITTEST handling in our
226 // wnd proc, we can receive dummy click messages when we click near
227 // the radiobox edge (this is ugly but Julian wouldn't let me get
228 // rid of this...)
229 return false;
230 }
231
232 if ( selectedButton != m_selectedButton )
233 {
234 m_selectedButton = selectedButton;
235
236 SendNotificationEvent();
237 }
238 //else: don't generate events when the selection doesn't change
239
240 return true;
241 }
242 else
243 return false;
244 }
245
246 // Radio box item
247 wxRadioBox::wxRadioBox()
248 {
249 m_selectedButton = wxID_ANY;
250 m_noItems = 0;
251 m_noRowsOrCols = 0;
252 m_radioButtons = NULL;
253 m_majorDim = 0;
254 m_radioWidth = NULL;
255 m_radioHeight = NULL;
256 }
257
258 bool wxRadioBox::Create(wxWindow *parent,
259 wxWindowID id,
260 const wxString& title,
261 const wxPoint& pos,
262 const wxSize& size,
263 int n,
264 const wxString choices[],
265 int majorDim,
266 long style,
267 const wxValidator& val,
268 const wxString& name)
269 {
270 // initialize members
271 m_selectedButton = wxID_ANY;
272 m_noItems = 0;
273
274 m_majorDim = majorDim == 0 ? n : majorDim;
275 m_noRowsOrCols = majorDim;
276
277 // common initialization
278 if ( !CreateControl(parent, id, pos, size, style, val, name) )
279 return false;
280
281 // create the static box
282 if ( !MSWCreateControl(wxT("BUTTON"), BS_GROUPBOX | WS_GROUP,
283 pos, size, title, WS_EX_TRANSPARENT) )
284 return false;
285
286 // and now create the buttons
287 m_noItems = n;
288 #if RADIOBTN_PARENT_IS_RADIOBOX
289 HWND hwndParent = GetHwnd();
290 #else
291 HWND hwndParent = GetHwndOf(parent);
292 #endif
293
294 // Some radio boxes test consecutive id.
295 (void)NewControlId();
296 m_radioButtons = new WXHWND[n];
297 m_radioWidth = new int[n];
298 m_radioHeight = new int[n];
299
300 WXHFONT hfont = 0;
301 wxFont font = GetFont();
302 if ( font.Ok() )
303 {
304 hfont = font.GetResourceHandle();
305 }
306
307 for ( int i = 0; i < n; i++ )
308 {
309 m_radioWidth[i] =
310 m_radioHeight[i] = wxDefaultCoord;
311 long styleBtn = BS_AUTORADIOBUTTON | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
312 if ( i == 0 && style == 0 )
313 styleBtn |= WS_GROUP;
314
315 long newId = NewControlId();
316
317 HWND hwndBtn = ::CreateWindow(_T("BUTTON"),
318 choices[i],
319 styleBtn,
320 0, 0, 0, 0, // will be set in SetSize()
321 hwndParent,
322 (HMENU)newId,
323 wxGetInstance(),
324 NULL);
325
326 if ( !hwndBtn )
327 {
328 wxLogLastError(wxT("CreateWindow(radio btn)"));
329
330 return false;
331 }
332
333 m_radioButtons[i] = (WXHWND)hwndBtn;
334
335 SubclassRadioButton((WXHWND)hwndBtn);
336
337 if ( hfont )
338 {
339 ::SendMessage(hwndBtn, WM_SETFONT, (WPARAM)hfont, 0L);
340 }
341
342 m_subControls.Add(newId);
343 }
344
345 // Create a dummy radio control to end the group.
346 (void)::CreateWindow(_T("BUTTON"),
347 wxEmptyString,
348 WS_GROUP | BS_AUTORADIOBUTTON | WS_CHILD,
349 0, 0, 0, 0, hwndParent,
350 (HMENU)NewControlId(), wxGetInstance(), NULL);
351
352 #ifdef __WXWINCE__
353 // Set the z-order correctly
354 SetWindowPos(GetHwnd(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
355 #endif
356
357 SetSelection(0);
358 SetSize(pos.x, pos.y, size.x, size.y);
359
360 // Now that we have items determine what is the best size and set it.
361 SetBestSize(size);
362
363 return true;
364 }
365
366 bool wxRadioBox::Create(wxWindow *parent,
367 wxWindowID id,
368 const wxString& title,
369 const wxPoint& pos,
370 const wxSize& size,
371 const wxArrayString& choices,
372 int majorDim,
373 long style,
374 const wxValidator& val,
375 const wxString& name)
376 {
377 wxCArrayString chs(choices);
378 return Create(parent, id, title, pos, size, chs.GetCount(),
379 chs.GetStrings(), majorDim, style, val, name);
380 }
381
382 wxRadioBox::~wxRadioBox()
383 {
384 m_isBeingDeleted = true;
385
386 if (m_radioButtons)
387 {
388 int i;
389 for (i = 0; i < m_noItems; i++)
390 ::DestroyWindow((HWND)m_radioButtons[i]);
391 delete[] m_radioButtons;
392 }
393
394 if (m_radioWidth)
395 delete[] m_radioWidth;
396 if (m_radioHeight)
397 delete[] m_radioHeight;
398
399 }
400
401 void wxRadioBox::SetString(int item, const wxString& label)
402 {
403 wxCHECK_RET( item >= 0 && item < m_noItems, wxT("invalid radiobox index") );
404
405 m_radioWidth[item] = m_radioHeight[item] = wxDefaultCoord;
406 SetWindowText((HWND)m_radioButtons[item], label.c_str());
407 }
408
409 void wxRadioBox::SetSelection(int N)
410 {
411 wxCHECK_RET( (N >= 0) && (N < m_noItems), wxT("invalid radiobox index") );
412
413 // Following necessary for Win32s, because Win32s translate BM_SETCHECK
414 if (m_selectedButton >= 0 && m_selectedButton < m_noItems)
415 ::SendMessage((HWND) m_radioButtons[m_selectedButton], BM_SETCHECK, 0, 0L);
416
417 ::SendMessage((HWND)m_radioButtons[N], BM_SETCHECK, 1, 0L);
418
419 m_selectedButton = N;
420 }
421
422 // Get single selection, for single choice list items
423 int wxRadioBox::GetSelection() const
424 {
425 return m_selectedButton;
426 }
427
428 // Find string for position
429 wxString wxRadioBox::GetString(int item) const
430 {
431 wxCHECK_MSG( item >= 0 && item < m_noItems, wxEmptyString,
432 wxT("invalid radiobox index") );
433
434 return wxGetWindowText(m_radioButtons[item]);
435 }
436
437 // ----------------------------------------------------------------------------
438 // size calculations
439 // ----------------------------------------------------------------------------
440
441 wxSize wxRadioBox::GetMaxButtonSize() const
442 {
443 // calculate the max button size
444 int widthMax = 0,
445 heightMax = 0;
446 for ( int i = 0 ; i < m_noItems; i++ )
447 {
448 int width, height;
449 if ( m_radioWidth[i] < 0 )
450 {
451 GetTextExtent(wxGetWindowText(m_radioButtons[i]), &width, &height);
452
453 // adjust the size to take into account the radio box itself
454 // FIXME this is totally bogus!
455 width += RADIO_SIZE;
456 height *= 3;
457 height /= 2;
458 }
459 else
460 {
461 width = m_radioWidth[i];
462 height = m_radioHeight[i];
463 }
464
465 if ( widthMax < width )
466 widthMax = width;
467 if ( heightMax < height )
468 heightMax = height;
469 }
470
471 return wxSize(widthMax, heightMax);
472 }
473
474 wxSize wxRadioBox::GetTotalButtonSize(const wxSize& sizeBtn) const
475 {
476 // the radiobox should be big enough for its buttons
477 int cx1, cy1;
478 wxGetCharSize(m_hWnd, &cx1, &cy1, GetFont());
479
480 int extraHeight = cy1;
481
482 /* We'll assume the adjustments below are OK for Win 3.1 too
483 #if defined(CTL3D) && !CTL3D
484 // Requires a bigger group box in plain Windows
485 extraHeight *= 3;
486 extraHeight /= 2;
487 #endif
488 */
489
490 int height = GetNumVer() * sizeBtn.y + cy1/2 + extraHeight;
491 int width = GetNumHor() * (sizeBtn.x + cx1) + cx1;
492
493 // Add extra space under the label, if it exists.
494 if (!wxControl::GetLabel().IsEmpty())
495 height += cy1/2;
496
497 // and also wide enough for its label
498 int widthLabel;
499 GetTextExtent(GetTitle(), &widthLabel, NULL);
500 widthLabel += RADIO_SIZE; // FIXME this is bogus too
501 if ( widthLabel > width )
502 width = widthLabel;
503
504 return wxSize(width, height);
505 }
506
507 wxSize wxRadioBox::DoGetBestSize() const
508 {
509 return GetTotalButtonSize(GetMaxButtonSize());
510 }
511
512 // Restored old code.
513 void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
514 {
515 int currentX, currentY;
516 GetPosition(&currentX, &currentY);
517 int widthOld, heightOld;
518 GetSize(&widthOld, &heightOld);
519
520 int xx = x;
521 int yy = y;
522
523 if (x == wxDefaultCoord && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
524 xx = currentX;
525 if (y == wxDefaultCoord && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
526 yy = currentY;
527
528 #if RADIOBTN_PARENT_IS_RADIOBOX
529 int y_offset = 0;
530 int x_offset = 0;
531 #else
532 int y_offset = yy;
533 int x_offset = xx;
534 #endif
535
536 int cx1, cy1;
537 wxGetCharSize(m_hWnd, &cx1, &cy1, GetFont());
538
539 // Attempt to have a look coherent with other platforms: We compute the
540 // biggest toggle dim, then we align all items according this value.
541 wxSize maxSize = GetMaxButtonSize();
542 int maxWidth = maxSize.x,
543 maxHeight = maxSize.y;
544
545 wxSize totSize = GetTotalButtonSize(maxSize);
546 int totWidth = totSize.x,
547 totHeight = totSize.y;
548
549 // only change our width/height if asked for
550 if ( width == wxDefaultCoord )
551 {
552 if ( sizeFlags & wxSIZE_AUTO_WIDTH )
553 width = totWidth;
554 else
555 width = widthOld;
556 }
557
558 if ( height == wxDefaultCoord )
559 {
560 if ( sizeFlags & wxSIZE_AUTO_HEIGHT )
561 height = totHeight;
562 else
563 height = heightOld;
564 }
565
566 ::MoveWindow(GetHwnd(), xx, yy, width, height, TRUE);
567
568 // Now position all the buttons: the current button will be put at
569 // wxPoint(x_offset, y_offset) and the new row/column will start at
570 // startX/startY. The size of all buttons will be the same wxSize(maxWidth,
571 // maxHeight) except for the buttons in the last column which should extend
572 // to the right border of radiobox and thus can be wider than this.
573
574 // Also, remember that wxRA_SPECIFY_COLS means that we arrange buttons in
575 // left to right order and m_majorDim is the number of columns while
576 // wxRA_SPECIFY_ROWS means that the buttons are arranged top to bottom and
577 // m_majorDim is the number of rows.
578
579 x_offset += cx1;
580 y_offset += cy1;
581
582 // Add extra space under the label, if it exists.
583 if (!wxControl::GetLabel().IsEmpty())
584 y_offset += cy1/2;
585
586 int startX = x_offset;
587 int startY = y_offset;
588
589 for ( int i = 0; i < m_noItems; i++ )
590 {
591 // the last button in the row may be wider than the other ones as the
592 // radiobox may be wider than the sum of the button widths (as it
593 // happens, for example, when the radiobox label is very long)
594 bool isLastInTheRow;
595 if ( m_windowStyle & wxRA_SPECIFY_COLS )
596 {
597 // item is the last in its row if it is a multiple of the number of
598 // columns or if it is just the last item
599 int n = i + 1;
600 isLastInTheRow = ((n % m_majorDim) == 0) || (n == m_noItems);
601 }
602 else // wxRA_SPECIFY_ROWS
603 {
604 // item is the last in the row if it is in the last columns
605 isLastInTheRow = i >= (m_noItems/m_majorDim)*m_majorDim;
606 }
607
608 // is this the start of new row/column?
609 if ( i && (i % m_majorDim == 0) )
610 {
611 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
612 {
613 // start of new column
614 y_offset = startY;
615 x_offset += maxWidth + cx1;
616 }
617 else // start of new row
618 {
619 x_offset = startX;
620 y_offset += maxHeight;
621 if (m_radioWidth[0]>0)
622 y_offset += cy1/2;
623 }
624 }
625
626 int widthBtn;
627 if ( isLastInTheRow )
628 {
629 // make the button go to the end of radio box
630 widthBtn = startX + width - x_offset - 2*cx1;
631 if ( widthBtn < maxWidth )
632 widthBtn = maxWidth;
633 }
634 else
635 {
636 // normal button, always of the same size
637 widthBtn = maxWidth;
638 }
639
640 // VZ: make all buttons of the same, maximal size - like this they
641 // cover the radiobox entirely and the radiobox tooltips are always
642 // shown (otherwise they are not when the mouse pointer is in the
643 // radiobox part not belonging to any radiobutton)
644 ::MoveWindow((HWND)m_radioButtons[i],
645 x_offset, y_offset, widthBtn, maxHeight,
646 TRUE);
647
648 // where do we put the next button?
649 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
650 {
651 // below this one
652 y_offset += maxHeight;
653 if (m_radioWidth[0]>0)
654 y_offset += cy1/2;
655 }
656 else
657 {
658 // to the right of this one
659 x_offset += widthBtn + cx1;
660 }
661 }
662 }
663
664 void wxRadioBox::GetSize(int *width, int *height) const
665 {
666 RECT rect = { -1, -1, -1, -1 };
667
668 if (m_hWnd)
669 wxFindMaxSize(m_hWnd, &rect);
670
671 int i;
672 for (i = 0; i < m_noItems; i++)
673 wxFindMaxSize(m_radioButtons[i], &rect);
674
675 *width = rect.right - rect.left;
676 *height = rect.bottom - rect.top;
677 }
678
679 void wxRadioBox::GetPosition(int *x, int *y) const
680 {
681 wxWindow *parent = GetParent();
682 RECT rect = { -1, -1, -1, -1 };
683
684 int i;
685 for (i = 0; i < m_noItems; i++)
686 wxFindMaxSize(m_radioButtons[i], &rect);
687
688 if (m_hWnd)
689 wxFindMaxSize(m_hWnd, &rect);
690
691 // Since we now have the absolute screen coords, if there's a parent we
692 // must subtract its top left corner
693 POINT point;
694 point.x = rect.left;
695 point.y = rect.top;
696 if (parent)
697 {
698 ::ScreenToClient((HWND) parent->GetHWND(), &point);
699 }
700
701 // We may be faking the client origin. So a window that's really at (0, 30)
702 // may appear (to wxWin apps) to be at (0, 0).
703 if (GetParent())
704 {
705 wxPoint pt(GetParent()->GetClientAreaOrigin());
706 point.x -= pt.x;
707 point.y -= pt.y;
708 }
709
710 *x = point.x;
711 *y = point.y;
712 }
713
714 void wxRadioBox::SetFocus()
715 {
716 if (m_noItems > 0)
717 {
718 ::SetFocus((HWND)m_radioButtons[m_selectedButton == wxID_ANY
719 ? 0
720 : m_selectedButton]);
721 }
722
723 }
724
725 bool wxRadioBox::Show(bool show)
726 {
727 if ( !wxControl::Show(show) )
728 return false;
729
730 int nCmdShow = show ? SW_SHOW : SW_HIDE;
731 for ( int i = 0; i < m_noItems; i++ )
732 {
733 ::ShowWindow((HWND)m_radioButtons[i], nCmdShow);
734 }
735
736 return true;
737 }
738
739 // Enable a specific button
740 void wxRadioBox::Enable(int item, bool enable)
741 {
742 wxCHECK_RET( item >= 0 && item < m_noItems,
743 wxT("invalid item in wxRadioBox::Enable()") );
744
745 ::EnableWindow((HWND) m_radioButtons[item], enable);
746 }
747
748 // Enable all controls
749 bool wxRadioBox::Enable(bool enable)
750 {
751 if ( !wxControl::Enable(enable) )
752 return false;
753
754 for (int i = 0; i < m_noItems; i++)
755 ::EnableWindow((HWND) m_radioButtons[i], enable);
756
757 return true;
758 }
759
760 // Show a specific button
761 void wxRadioBox::Show(int item, bool show)
762 {
763 wxCHECK_RET( item >= 0 && item < m_noItems,
764 wxT("invalid item in wxRadioBox::Show()") );
765
766 ::ShowWindow((HWND)m_radioButtons[item], show ? SW_SHOW : SW_HIDE);
767 }
768
769 bool wxRadioBox::ContainsHWND(WXHWND hWnd) const
770 {
771 size_t count = GetCount();
772 for ( size_t i = 0; i < count; i++ )
773 {
774 if ( GetRadioButtons()[i] == hWnd )
775 return true;
776 }
777
778 return false;
779 }
780
781 void wxRadioBox::Command(wxCommandEvent & event)
782 {
783 SetSelection (event.m_commandInt);
784 SetFocus();
785 ProcessCommand (event);
786 }
787
788 // NB: if this code is changed, wxGetWindowForHWND() which relies on having the
789 // radiobox pointer in GWL_USERDATA for radio buttons must be updated too!
790 void wxRadioBox::SubclassRadioButton(WXHWND hWndBtn)
791 {
792 HWND hwndBtn = (HWND)hWndBtn;
793
794 if ( !s_wndprocRadioBtn )
795 s_wndprocRadioBtn = (WXFARPROC)wxGetWindowProc(hwndBtn);
796
797 wxSetWindowProc(hwndBtn, wxRadioBtnWndProc);
798 wxSetWindowUserData(hwndBtn, this);
799 }
800
801 void wxRadioBox::SendNotificationEvent()
802 {
803 wxCommandEvent event(wxEVT_COMMAND_RADIOBOX_SELECTED, m_windowId);
804 event.SetInt( m_selectedButton );
805 event.SetString( GetString(m_selectedButton) );
806 event.SetEventObject( this );
807 ProcessCommand(event);
808 }
809
810 bool wxRadioBox::SetFont(const wxFont& font)
811 {
812 if ( !wxControl::SetFont(font) )
813 {
814 // nothing to do
815 return false;
816 }
817
818 // also set the font of our radio buttons
819 WXHFONT hfont = wxFont(font).GetResourceHandle();
820 for ( int n = 0; n < m_noItems; n++ )
821 {
822 HWND hwndBtn = (HWND)m_radioButtons[n];
823 ::SendMessage(hwndBtn, WM_SETFONT, (WPARAM)hfont, 0L);
824
825 // otherwise the buttons are not redrawn correctly
826 ::InvalidateRect(hwndBtn, NULL, FALSE /* don't erase bg */);
827 }
828
829 return true;
830 }
831
832 // ----------------------------------------------------------------------------
833 // our window proc
834 // ----------------------------------------------------------------------------
835
836 WXLRESULT wxRadioBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
837 {
838 #if RADIOBTN_PARENT_IS_RADIOBOX
839 switch ( nMsg )
840 {
841 // handle this message to set correct colours for our buttons here
842 case WM_CTLCOLORSTATIC:
843 {
844 WXHDC hdc;
845 WXHWND hwnd;
846 UnpackCtlColor(wParam, lParam, &hdc, &hwnd);
847
848 WXHBRUSH hbr = MSWControlColor((WXHDC)hdc);
849 if ( hbr )
850 return (WXLRESULT)hbr;
851 //else: fall through to default window proc
852 }
853 }
854 #endif // RADIOBTN_PARENT_IS_RADIOBOX
855
856 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
857 }
858
859 // ---------------------------------------------------------------------------
860 // window proc for radio buttons
861 // ---------------------------------------------------------------------------
862
863 #ifdef __WIN32__
864
865 LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
866 UINT message,
867 WPARAM wParam,
868 LPARAM lParam)
869 {
870 switch ( message )
871 {
872 case WM_GETDLGCODE:
873 // we must tell IsDialogMessage()/our kbd processing code that we
874 // want to process arrows ourselves because neither of them is
875 // smart enough to handle arrows properly for us
876 {
877 long lDlgCode = ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd,
878 message, wParam, lParam);
879
880 return lDlgCode | DLGC_WANTARROWS;
881 }
882
883 #if wxUSE_TOOLTIPS
884 case WM_NOTIFY:
885 {
886 NMHDR* hdr = (NMHDR *)lParam;
887 if ( hdr->code == TTN_NEEDTEXT )
888 {
889 wxRadioBox *
890 radiobox = (wxRadioBox *)wxGetWindowUserData(hwnd);
891
892 wxCHECK_MSG( radiobox, 0,
893 wxT("radio button without radio box?") );
894
895 wxToolTip *tooltip = radiobox->GetToolTip();
896 if ( tooltip )
897 {
898 TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam;
899 ttt->lpszText = (wxChar *)tooltip->GetTip().c_str();
900 }
901
902 // processed
903 return 0;
904 }
905 }
906 break;
907 #endif // wxUSE_TOOLTIPS
908
909 case WM_KEYDOWN:
910 {
911 wxRadioBox *radiobox = (wxRadioBox *)wxGetWindowUserData(hwnd);
912
913 wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") );
914
915 bool processed = true;
916
917 wxDirection dir;
918 switch ( wParam )
919 {
920 case VK_UP:
921 dir = wxUP;
922 break;
923
924 case VK_LEFT:
925 dir = wxLEFT;
926 break;
927
928 case VK_DOWN:
929 dir = wxDOWN;
930 break;
931
932 case VK_RIGHT:
933 dir = wxRIGHT;
934 break;
935
936 default:
937 processed = false;
938
939 // just to suppress the compiler warning
940 dir = wxALL;
941 }
942
943 if ( processed )
944 {
945 int selOld = radiobox->GetSelection();
946 int selNew = radiobox->GetNextItem
947 (
948 selOld,
949 dir,
950 radiobox->GetWindowStyle()
951 );
952
953 if ( selNew != selOld )
954 {
955 radiobox->SetSelection(selNew);
956 radiobox->SetFocus();
957
958 // emulate the button click
959 radiobox->SendNotificationEvent();
960
961 return 0;
962 }
963 }
964 }
965 break;
966
967 case WM_SETFOCUS:
968 case WM_KILLFOCUS:
969 {
970 wxRadioBox *radiobox = (wxRadioBox *)wxGetWindowUserData(hwnd);
971
972 wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") );
973
974 // if we don't do this, no focus events are generated for the
975 // radiobox and, besides, we need to notify the parent about
976 // the focus change, otherwise the focus handling logic in
977 // wxControlContainer doesn't work
978 if ( message == WM_SETFOCUS )
979 radiobox->HandleSetFocus((WXHWND)wParam);
980 else
981 radiobox->HandleKillFocus((WXHWND)wParam);
982 }
983 break;
984
985 #ifdef __WIN32__
986 case WM_HELP:
987 {
988 wxRadioBox *radiobox = (wxRadioBox *)wxGetWindowUserData(hwnd);
989
990 wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") );
991
992 bool processed wxDUMMY_INITIALIZE(true);
993
994 // HELPINFO doesn't seem to be supported on WinCE.
995 #ifndef __WXWINCE__
996 HELPINFO* info = (HELPINFO*) lParam;
997 // Don't yet process menu help events, just windows
998 if (info->iContextType == HELPINFO_WINDOW)
999 #endif
1000 {
1001 wxWindow* subjectOfHelp = radiobox;
1002 bool eventProcessed = false;
1003 while (subjectOfHelp && !eventProcessed)
1004 {
1005 wxHelpEvent helpEvent(wxEVT_HELP, subjectOfHelp->GetId(),
1006 #ifdef __WXWINCE__
1007 wxPoint(0, 0)
1008 #else
1009 wxPoint(info->MousePos.x, info->MousePos.y)
1010 #endif
1011 ) ; // info->iCtrlId);
1012 helpEvent.SetEventObject(radiobox);
1013 eventProcessed = radiobox->GetEventHandler()->ProcessEvent(helpEvent);
1014
1015 // Go up the window hierarchy until the event is handled (or not)
1016 subjectOfHelp = subjectOfHelp->GetParent();
1017 }
1018 processed = eventProcessed;
1019 }
1020 #ifndef __WXWINCE__
1021 else if (info->iContextType == HELPINFO_MENUITEM)
1022 {
1023 wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId) ;
1024 helpEvent.SetEventObject(radiobox);
1025 processed = radiobox->GetEventHandler()->ProcessEvent(helpEvent);
1026 }
1027 else
1028 processed = false;
1029 #endif
1030 if (processed)
1031 return 0;
1032
1033 break;
1034 }
1035 #endif // __WIN32__
1036 }
1037
1038 return ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd, message, wParam, lParam);
1039 }
1040
1041 #endif // __WIN32__
1042
1043 #endif // wxUSE_RADIOBOX