]> git.saurik.com Git - wxWidgets.git/blob - src/msw/radiobox.cpp
more region tests
[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 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/settings.h"
36 #include "wx/log.h"
37 #endif
38
39 #include "wx/msw/private.h"
40
41 #if wxUSE_TOOLTIPS
42 #if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__)
43 #include <commctrl.h>
44 #endif
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 WXFARPROC s_wndprocRadioBtn = (WXFARPROC)NULL;
90
91 #endif // __WIN32__
92
93 // ===========================================================================
94 // implementation
95 // ===========================================================================
96
97 // ---------------------------------------------------------------------------
98 // wxRadioBox
99 // ---------------------------------------------------------------------------
100
101 // returns the number of rows
102 int wxRadioBox::GetNumVer() const
103 {
104 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
105 {
106 return m_majorDim;
107 }
108 else
109 {
110 return (m_noItems + m_majorDim - 1)/m_majorDim;
111 }
112 }
113
114 // returns the number of columns
115 int wxRadioBox::GetNumHor() const
116 {
117 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
118 {
119 return (m_noItems + m_majorDim - 1)/m_majorDim;
120 }
121 else
122 {
123 return m_majorDim;
124 }
125 }
126
127 bool wxRadioBox::MSWCommand(WXUINT cmd, WXWORD id)
128 {
129 if ( cmd == BN_CLICKED )
130 {
131 if (id == GetId())
132 return TRUE;
133
134 int selectedButton = -1;
135
136 for ( int i = 0; i < m_noItems; i++ )
137 {
138 if ( id == wxGetWindowId(m_radioButtons[i]) )
139 {
140 selectedButton = i;
141
142 break;
143 }
144 }
145
146 if ( selectedButton == -1 )
147 {
148 // just ignore it - due to a hack with WM_NCHITTEST handling in our
149 // wnd proc, we can receive dummy click messages when we click near
150 // the radiobox edge (this is ugly but Julian wouldn't let me get
151 // rid of this...)
152 return FALSE;
153 }
154
155 if ( selectedButton != m_selectedButton )
156 {
157 m_selectedButton = selectedButton;
158
159 SendNotificationEvent();
160 }
161 //else: don't generate events when the selection doesn't change
162
163 return TRUE;
164 }
165 else
166 return FALSE;
167 }
168
169 #if WXWIN_COMPATIBILITY
170 wxRadioBox::wxRadioBox(wxWindow *parent, wxFunction func, const char *title,
171 int x, int y, int width, int height,
172 int n, char **choices,
173 int majorDim, long style, const char *name)
174 {
175 wxString *choices2 = new wxString[n];
176 for ( int i = 0; i < n; i ++) choices2[i] = choices[i];
177 Create(parent, -1, title, wxPoint(x, y), wxSize(width, height), n, choices2, majorDim, style,
178 wxDefaultValidator, name);
179 Callback(func);
180 delete choices2;
181 }
182
183 #endif // WXWIN_COMPATIBILITY
184
185 // Radio box item
186 wxRadioBox::wxRadioBox()
187 {
188 m_selectedButton = -1;
189 m_noItems = 0;
190 m_noRowsOrCols = 0;
191 m_radioButtons = NULL;
192 m_majorDim = 0;
193 m_radioWidth = NULL;
194 m_radioHeight = NULL;
195 }
196
197 bool wxRadioBox::Create(wxWindow *parent,
198 wxWindowID id,
199 const wxString& title,
200 const wxPoint& pos,
201 const wxSize& size,
202 int n,
203 const wxString choices[],
204 int majorDim,
205 long style,
206 const wxValidator& val,
207 const wxString& name)
208 {
209 // initialize members
210 m_selectedButton = -1;
211 m_noItems = 0;
212
213 m_majorDim = majorDim == 0 ? n : majorDim;
214 m_noRowsOrCols = majorDim;
215
216 // common initialization
217 if ( !CreateControl(parent, id, pos, size, style, val, name) )
218 return FALSE;
219
220 // create the static box
221 if ( !MSWCreateControl(wxT("BUTTON"), BS_GROUPBOX | WS_GROUP,
222 pos, size, title, 0) )
223 return FALSE;
224
225 // and now create the buttons
226 m_noItems = n;
227 #if RADIOBTN_PARENT_IS_RADIOBOX
228 HWND hwndParent = GetHwnd();
229 #else
230 HWND hwndParent = GetHwndOf(parent);
231 #endif
232
233 // Some radio boxes test consecutive id.
234 (void)NewControlId();
235 m_radioButtons = new WXHWND[n];
236 m_radioWidth = new int[n];
237 m_radioHeight = new int[n];
238
239 WXHFONT hfont = 0;
240 wxFont& font = GetFont();
241 if ( font.Ok() )
242 {
243 hfont = font.GetResourceHandle();
244 }
245
246 for ( int i = 0; i < n; i++ )
247 {
248 m_radioWidth[i] =
249 m_radioHeight[i] = -1;
250 long styleBtn = BS_AUTORADIOBUTTON | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
251 if ( i == 0 && style == 0 )
252 styleBtn |= WS_GROUP;
253
254 long newId = NewControlId();
255
256 HWND hwndBtn = ::CreateWindow(_T("BUTTON"),
257 choices[i],
258 styleBtn,
259 0, 0, 0, 0, // will be set in SetSize()
260 hwndParent,
261 (HMENU)newId,
262 wxGetInstance(),
263 NULL);
264
265 if ( !hwndBtn )
266 {
267 wxLogLastError(wxT("CreateWindow(radio btn)"));
268
269 return FALSE;
270 }
271
272 m_radioButtons[i] = (WXHWND)hwndBtn;
273
274 SubclassRadioButton((WXHWND)hwndBtn);
275
276 if ( hfont )
277 {
278 ::SendMessage(hwndBtn, WM_SETFONT, (WPARAM)hfont, 0L);
279 }
280
281 m_subControls.Add(newId);
282 }
283
284 // Create a dummy radio control to end the group.
285 (void)::CreateWindow(_T("BUTTON"),
286 _T(""),
287 WS_GROUP | BS_AUTORADIOBUTTON | WS_CHILD,
288 0, 0, 0, 0, hwndParent,
289 (HMENU)NewControlId(), wxGetInstance(), NULL);
290
291 SetSelection(0);
292
293 SetSize(pos.x, pos.y, size.x, size.y);
294
295 return TRUE;
296 }
297
298 wxRadioBox::~wxRadioBox()
299 {
300 m_isBeingDeleted = TRUE;
301
302 if (m_radioButtons)
303 {
304 int i;
305 for (i = 0; i < m_noItems; i++)
306 ::DestroyWindow((HWND)m_radioButtons[i]);
307 delete[] m_radioButtons;
308 }
309
310 if (m_radioWidth)
311 delete[] m_radioWidth;
312 if (m_radioHeight)
313 delete[] m_radioHeight;
314
315 }
316
317 wxString wxRadioBox::GetLabel(int item) const
318 {
319 wxCHECK_MSG( item >= 0 && item < m_noItems, wxT(""), wxT("invalid radiobox index") );
320
321 return wxGetWindowText(m_radioButtons[item]);
322 }
323
324 void wxRadioBox::SetLabel(int item, const wxString& label)
325 {
326 wxCHECK_RET( item >= 0 && item < m_noItems, wxT("invalid radiobox index") );
327
328 m_radioWidth[item] = m_radioHeight[item] = -1;
329 SetWindowText((HWND)m_radioButtons[item], label.c_str());
330 }
331
332 void wxRadioBox::SetLabel(int WXUNUSED(item), wxBitmap *WXUNUSED(bitmap))
333 {
334 /*
335 m_radioWidth[item] = bitmap->GetWidth() + FB_MARGIN;
336 m_radioHeight[item] = bitmap->GetHeight() + FB_MARGIN;
337 */
338 wxFAIL_MSG(wxT("not implemented"));
339 }
340
341 int wxRadioBox::FindString(const wxString& s) const
342 {
343 for (int i = 0; i < m_noItems; i++)
344 {
345 if ( s == wxGetWindowText(m_radioButtons[i]) )
346 return i;
347 }
348
349 return wxNOT_FOUND;
350 }
351
352 void wxRadioBox::SetSelection(int N)
353 {
354 wxCHECK_RET( (N >= 0) && (N < m_noItems), wxT("invalid radiobox index") );
355
356 // Following necessary for Win32s, because Win32s translate BM_SETCHECK
357 if (m_selectedButton >= 0 && m_selectedButton < m_noItems)
358 ::SendMessage((HWND) m_radioButtons[m_selectedButton], BM_SETCHECK, 0, 0L);
359
360 ::SendMessage((HWND)m_radioButtons[N], BM_SETCHECK, 1, 0L);
361 ::SetFocus((HWND)m_radioButtons[N]);
362
363 m_selectedButton = N;
364 }
365
366 // Get single selection, for single choice list items
367 int wxRadioBox::GetSelection() const
368 {
369 return m_selectedButton;
370 }
371
372 // Find string for position
373 wxString wxRadioBox::GetString(int N) const
374 {
375 return wxGetWindowText(m_radioButtons[N]);
376 }
377
378 // ----------------------------------------------------------------------------
379 // size calculations
380 // ----------------------------------------------------------------------------
381
382 wxSize wxRadioBox::GetMaxButtonSize() const
383 {
384 // calculate the max button size
385 int widthMax = 0,
386 heightMax = 0;
387 for ( int i = 0 ; i < m_noItems; i++ )
388 {
389 int width, height;
390 if ( m_radioWidth[i] < 0 )
391 {
392 GetTextExtent(wxGetWindowText(m_radioButtons[i]), &width, &height);
393
394 // adjust the size to take into account the radio box itself
395 // FIXME this is totally bogus!
396 width += RADIO_SIZE;
397 height *= 3;
398 height /= 2;
399 }
400 else
401 {
402 width = m_radioWidth[i];
403 height = m_radioHeight[i];
404 }
405
406 if ( widthMax < width )
407 widthMax = width;
408 if ( heightMax < height )
409 heightMax = height;
410 }
411
412 return wxSize(widthMax, heightMax);
413 }
414
415 wxSize wxRadioBox::GetTotalButtonSize(const wxSize& sizeBtn) const
416 {
417 // the radiobox should be big enough for its buttons
418 int cx1, cy1;
419 wxGetCharSize(m_hWnd, &cx1, &cy1, &GetFont());
420
421 int extraHeight = cy1;
422
423 #if defined(CTL3D) && !CTL3D
424 // Requires a bigger group box in plain Windows
425 extraHeight *= 3;
426 extraHeight /= 2;
427 #endif
428
429 int height = GetNumVer() * sizeBtn.y + cy1/2 + extraHeight;
430 int width = GetNumHor() * (sizeBtn.x + cx1) + cx1;
431
432 // and also wide enough for its label
433 int widthLabel;
434 GetTextExtent(GetTitle(), &widthLabel, NULL);
435 widthLabel += RADIO_SIZE; // FIXME this is bogus too
436 if ( widthLabel > width )
437 width = widthLabel;
438
439 return wxSize(width, height);
440 }
441
442 wxSize wxRadioBox::DoGetBestSize() const
443 {
444 return GetTotalButtonSize(GetMaxButtonSize());
445 }
446
447 // Restored old code.
448 void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
449 {
450 int currentX, currentY;
451 GetPosition(&currentX, &currentY);
452 int widthOld, heightOld;
453 GetSize(&widthOld, &heightOld);
454
455 int xx = x;
456 int yy = y;
457
458 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
459 xx = currentX;
460 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
461 yy = currentY;
462
463 #if RADIOBTN_PARENT_IS_RADIOBOX
464 int y_offset = 0;
465 int x_offset = 0;
466 #else
467 int y_offset = yy;
468 int x_offset = xx;
469 #endif
470
471 int cx1, cy1;
472 wxGetCharSize(m_hWnd, &cx1, &cy1, & GetFont());
473
474 // Attempt to have a look coherent with other platforms: We compute the
475 // biggest toggle dim, then we align all items according this value.
476 wxSize maxSize = GetMaxButtonSize();
477 int maxWidth = maxSize.x,
478 maxHeight = maxSize.y;
479
480 wxSize totSize = GetTotalButtonSize(maxSize);
481 int totWidth = totSize.x,
482 totHeight = totSize.y;
483
484 // only change our width/height if asked for
485 if ( width == -1 )
486 {
487 if ( sizeFlags & wxSIZE_AUTO_WIDTH )
488 width = totWidth;
489 else
490 width = widthOld;
491 }
492
493 if ( height == -1 )
494 {
495 if ( sizeFlags & wxSIZE_AUTO_HEIGHT )
496 height = totHeight;
497 else
498 height = heightOld;
499 }
500
501 ::MoveWindow(GetHwnd(), xx, yy, width, height, TRUE);
502
503 // Now position all the buttons: the current button will be put at
504 // wxPoint(x_offset, y_offset) and the new row/column will start at
505 // startX/startY. The size of all buttons will be the same wxSize(maxWidth,
506 // maxHeight) except for the buttons in the last column which should extend
507 // to the right border of radiobox and thus can be wider than this.
508
509 // Also, remember that wxRA_SPECIFY_COLS means that we arrange buttons in
510 // left to right order and m_majorDim is the number of columns while
511 // wxRA_SPECIFY_ROWS means that the buttons are arranged top to bottom and
512 // m_majorDim is the number of rows.
513
514 x_offset += cx1;
515 y_offset += cy1;
516
517 #if defined(CTL3D) && (!CTL3D)
518 y_offset += (int)(cy1/2); // Fudge factor since buttons overlapped label
519 // JACS 2/12/93. CTL3D draws group label quite high.
520 #endif
521
522 int startX = x_offset;
523 int startY = y_offset;
524
525 for ( int i = 0; i < m_noItems; i++ )
526 {
527 // the last button in the row may be wider than the other ones as the
528 // radiobox may be wider than the sum of the button widths (as it
529 // happens, for example, when the radiobox label is very long)
530 bool isLastInTheRow;
531 if ( m_windowStyle & wxRA_SPECIFY_COLS )
532 {
533 // item is the last in its row if it is a multiple of the number of
534 // columns or if it is just the last item
535 int n = i + 1;
536 isLastInTheRow = ((n % m_majorDim) == 0) || (n == m_noItems);
537 }
538 else // wxRA_SPECIFY_ROWS
539 {
540 // item is the last in the row if it is in the last columns
541 isLastInTheRow = i >= (m_noItems/m_majorDim)*m_majorDim;
542 }
543
544 // is this the start of new row/column?
545 if ( i && (i % m_majorDim == 0) )
546 {
547 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
548 {
549 // start of new column
550 y_offset = startY;
551 x_offset += maxWidth + cx1;
552 }
553 else // start of new row
554 {
555 x_offset = startX;
556 y_offset += maxHeight;
557 if (m_radioWidth[0]>0)
558 y_offset += cy1/2;
559 }
560 }
561
562 int widthBtn;
563 if ( isLastInTheRow )
564 {
565 // make the button go to the end of radio box
566 widthBtn = startX + width - x_offset - 2*cx1;
567 if ( widthBtn < maxWidth )
568 widthBtn = maxWidth;
569 }
570 else
571 {
572 // normal button, always of the same size
573 widthBtn = maxWidth;
574 }
575
576 // VZ: make all buttons of the same, maximal size - like this they
577 // cover the radiobox entirely and the radiobox tooltips are always
578 // shown (otherwise they are not when the mouse pointer is in the
579 // radiobox part not belonging to any radiobutton)
580 ::MoveWindow((HWND)m_radioButtons[i],
581 x_offset, y_offset, widthBtn, maxHeight,
582 TRUE);
583
584 // where do we put the next button?
585 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
586 {
587 // below this one
588 y_offset += maxHeight;
589 if (m_radioWidth[0]>0)
590 y_offset += cy1/2;
591 }
592 else
593 {
594 // to the right of this one
595 x_offset += widthBtn + cx1;
596 }
597 }
598 }
599
600 void wxRadioBox::GetSize(int *width, int *height) const
601 {
602 RECT rect;
603 rect.left = -1; rect.right = -1; rect.top = -1; rect.bottom = -1;
604
605 if (m_hWnd)
606 wxFindMaxSize(m_hWnd, &rect);
607
608 int i;
609 for (i = 0; i < m_noItems; i++)
610 wxFindMaxSize(m_radioButtons[i], &rect);
611
612 *width = rect.right - rect.left;
613 *height = rect.bottom - rect.top;
614 }
615
616 void wxRadioBox::GetPosition(int *x, int *y) const
617 {
618 wxWindow *parent = GetParent();
619 RECT rect = { -1, -1, -1, -1 };
620
621 int i;
622 for (i = 0; i < m_noItems; i++)
623 wxFindMaxSize(m_radioButtons[i], &rect);
624
625 if (m_hWnd)
626 wxFindMaxSize(m_hWnd, &rect);
627
628 // Since we now have the absolute screen coords, if there's a parent we
629 // must subtract its top left corner
630 POINT point;
631 point.x = rect.left;
632 point.y = rect.top;
633 if (parent)
634 {
635 ::ScreenToClient((HWND) parent->GetHWND(), &point);
636 }
637
638 // We may be faking the client origin. So a window that's really at (0, 30)
639 // may appear (to wxWin apps) to be at (0, 0).
640 if (GetParent())
641 {
642 wxPoint pt(GetParent()->GetClientAreaOrigin());
643 point.x -= pt.x;
644 point.y -= pt.y;
645 }
646
647 *x = point.x;
648 *y = point.y;
649 }
650
651 void wxRadioBox::SetFocus()
652 {
653 if (m_noItems > 0)
654 {
655 if (m_selectedButton == -1)
656 ::SetFocus((HWND) m_radioButtons[0]);
657 else
658 ::SetFocus((HWND) m_radioButtons[m_selectedButton]);
659 }
660
661 }
662
663 bool wxRadioBox::Show(bool show)
664 {
665 if ( !wxControl::Show(show) )
666 return FALSE;
667
668 int nCmdShow = show ? SW_SHOW : SW_HIDE;
669 for ( int i = 0; i < m_noItems; i++ )
670 {
671 ::ShowWindow((HWND)m_radioButtons[i], nCmdShow);
672 }
673
674 return TRUE;
675 }
676
677 // Enable a specific button
678 void wxRadioBox::Enable(int item, bool enable)
679 {
680 wxCHECK_RET( item >= 0 && item < m_noItems,
681 wxT("invalid item in wxRadioBox::Enable()") );
682
683 ::EnableWindow((HWND) m_radioButtons[item], enable);
684 }
685
686 // Enable all controls
687 bool wxRadioBox::Enable(bool enable)
688 {
689 if ( !wxControl::Enable(enable) )
690 return FALSE;
691
692 for (int i = 0; i < m_noItems; i++)
693 ::EnableWindow((HWND) m_radioButtons[i], enable);
694
695 return TRUE;
696 }
697
698 // Show a specific button
699 void wxRadioBox::Show(int item, bool show)
700 {
701 wxCHECK_RET( item >= 0 && item < m_noItems,
702 wxT("invalid item in wxRadioBox::Show()") );
703
704 ::ShowWindow((HWND)m_radioButtons[item], show ? SW_SHOW : SW_HIDE);
705 }
706
707 // For single selection items only
708 wxString wxRadioBox::GetStringSelection() const
709 {
710 wxString result;
711 int sel = GetSelection();
712 if (sel > -1)
713 result = GetString(sel);
714
715 return result;
716 }
717
718 bool wxRadioBox::SetStringSelection(const wxString& s)
719 {
720 int sel = FindString (s);
721 if (sel > -1)
722 {
723 SetSelection (sel);
724 return TRUE;
725 }
726 else
727 return FALSE;
728 }
729
730 bool wxRadioBox::ContainsHWND(WXHWND hWnd) const
731 {
732 int i;
733 for (i = 0; i < Number(); i++)
734 {
735 if (GetRadioButtons()[i] == hWnd)
736 return TRUE;
737 }
738
739 return FALSE;
740 }
741
742 void wxRadioBox::Command(wxCommandEvent & event)
743 {
744 SetSelection (event.m_commandInt);
745 ProcessCommand (event);
746 }
747
748 // NB: if this code is changed, wxGetWindowForHWND() which relies on having the
749 // radiobox pointer in GWL_USERDATA for radio buttons must be updated too!
750 void wxRadioBox::SubclassRadioButton(WXHWND hWndBtn)
751 {
752 // No GWL_USERDATA in Win16, so omit this subclassing.
753 #ifdef __WIN32__
754 HWND hwndBtn = (HWND)hWndBtn;
755
756 if ( !s_wndprocRadioBtn )
757 s_wndprocRadioBtn = (WXFARPROC)::GetWindowLong(hwndBtn, GWL_WNDPROC);
758
759 ::SetWindowLong(hwndBtn, GWL_WNDPROC, (long)wxRadioBtnWndProc);
760 ::SetWindowLong(hwndBtn, GWL_USERDATA, (long)this);
761 #endif // __WIN32__
762 }
763
764 void wxRadioBox::SendNotificationEvent()
765 {
766 wxCommandEvent event(wxEVT_COMMAND_RADIOBOX_SELECTED, m_windowId);
767 event.SetInt( m_selectedButton );
768 event.SetString( GetString(m_selectedButton) );
769 event.SetEventObject( this );
770 ProcessCommand(event);
771 }
772
773 bool wxRadioBox::SetFont(const wxFont& font)
774 {
775 if ( !wxControl::SetFont(font) )
776 {
777 // nothing to do
778 return FALSE;
779 }
780
781 // also set the font of our radio buttons
782 WXHFONT hfont = wxFont(font).GetResourceHandle();
783 for ( int n = 0; n < m_noItems; n++ )
784 {
785 HWND hwndBtn = (HWND)m_radioButtons[n];
786 ::SendMessage(hwndBtn, WM_SETFONT, (WPARAM)hfont, 0L);
787
788 // otherwise the buttons are not redrawn correctly
789 ::InvalidateRect(hwndBtn, NULL, FALSE /* don't erase bg */);
790 }
791
792 return TRUE;
793 }
794
795 // ----------------------------------------------------------------------------
796 // our window proc
797 // ----------------------------------------------------------------------------
798
799 long wxRadioBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
800 {
801 switch ( nMsg )
802 {
803 #ifdef __WIN32__
804 case WM_CTLCOLORSTATIC:
805 // set the colour of the radio buttons to be the same as ours
806 {
807 HDC hdc = (HDC)wParam;
808
809 const wxColour& colBack = GetBackgroundColour();
810 ::SetBkColor(hdc, wxColourToRGB(colBack));
811 ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
812
813 wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
814
815 return (WXHBRUSH)brush->GetResourceHandle();
816 }
817 #endif // Win32
818
819 // This is required for the radiobox to be sensitive to mouse input,
820 // e.g. for Dialog Editor.
821 case WM_NCHITTEST:
822 {
823 int xPos = LOWORD(lParam); // horizontal position of cursor
824 int yPos = HIWORD(lParam); // vertical position of cursor
825
826 ScreenToClient(&xPos, &yPos);
827
828 // Make sure you can drag by the top of the groupbox, but let
829 // other (enclosed) controls get mouse events also
830 if (yPos < 10)
831 return (long)HTCLIENT;
832 }
833 break;
834 }
835
836 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
837 }
838
839 WXHBRUSH wxRadioBox::OnCtlColor(WXHDC pDC, WXHWND WXUNUSED(pWnd), WXUINT WXUNUSED(nCtlColor),
840 #if wxUSE_CTL3D
841 WXUINT message,
842 WXWPARAM wParam,
843 WXLPARAM lParam
844 #else
845 WXUINT WXUNUSED(message),
846 WXWPARAM WXUNUSED(wParam),
847 WXLPARAM WXUNUSED(lParam)
848 #endif
849 )
850 {
851 #if wxUSE_CTL3D
852 if ( m_useCtl3D )
853 {
854 HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
855 return (WXHBRUSH) hbrush;
856 }
857 #endif // wxUSE_CTL3D
858
859 HDC hdc = (HDC)pDC;
860 if (GetParent()->GetTransparentBackground())
861 SetBkMode(hdc, TRANSPARENT);
862 else
863 SetBkMode(hdc, OPAQUE);
864
865 wxColour colBack = GetBackgroundColour();
866
867 if (!IsEnabled())
868 colBack = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE);
869
870 ::SetBkColor(hdc, wxColourToRGB(colBack));
871 ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
872
873 wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
874
875 return (WXHBRUSH)brush->GetResourceHandle();
876 }
877
878
879 // ---------------------------------------------------------------------------
880 // window proc for radio buttons
881 // ---------------------------------------------------------------------------
882
883 #ifdef __WIN32__
884
885 LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
886 UINT message,
887 WPARAM wParam,
888 LPARAM lParam)
889 {
890 switch ( message )
891 {
892 case WM_GETDLGCODE:
893 // we must tell IsDialogMessage()/our kbd processing code that we
894 // want to process arrows ourselves because neither of them is
895 // smart enough to handle arrows properly for us
896 {
897 long lDlgCode = ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd,
898 message, wParam, lParam);
899
900 return lDlgCode | DLGC_WANTARROWS;
901 }
902
903 #if wxUSE_TOOLTIPS
904 case WM_NOTIFY:
905 {
906 NMHDR* hdr = (NMHDR *)lParam;
907 if ( (int)hdr->code == TTN_NEEDTEXT )
908 {
909 wxRadioBox *radiobox = (wxRadioBox *)
910 ::GetWindowLong(hwnd, GWL_USERDATA);
911
912 wxCHECK_MSG( radiobox, 0,
913 wxT("radio button without radio box?") );
914
915 wxToolTip *tooltip = radiobox->GetToolTip();
916 if ( tooltip )
917 {
918 TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam;
919 ttt->lpszText = (wxChar *)tooltip->GetTip().c_str();
920 }
921
922 // processed
923 return 0;
924 }
925 }
926 break;
927 #endif // wxUSE_TOOLTIPS
928
929 case WM_KEYDOWN:
930 {
931 wxRadioBox *radiobox = (wxRadioBox *)
932 ::GetWindowLong(hwnd, GWL_USERDATA);
933
934 wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") );
935
936 bool processed = TRUE;
937
938 bool horz = (radiobox->GetWindowStyle() & wxRA_SPECIFY_COLS) != 0;
939 int num = radiobox->Number(),
940 rows = radiobox->GetNumVer(),
941 cols = radiobox->GetNumHor();
942
943 int selOld = radiobox->GetSelection();
944 int selNew = selOld;
945
946 // wrapping will be handled below for the cases when we
947 // add/substract more than 1 but here otherwise as it's simpler
948 switch ( wParam )
949 {
950 case VK_UP:
951 if ( horz )
952 selNew -= cols;
953 else
954 {
955 if ( selNew )
956 selNew--;
957 else
958 selNew = num - 1;
959 }
960 break;
961
962 case VK_LEFT:
963 if ( horz )
964 {
965 if ( selNew )
966 selNew--;
967 else
968 selNew = num - 1;
969 }
970 else
971 selNew -= rows;
972 break;
973
974 case VK_DOWN:
975 if ( horz )
976 selNew += cols;
977 else
978 {
979 if ( ++selNew == num )
980 selNew = 0;
981 }
982 break;
983
984 case VK_RIGHT:
985 if ( horz )
986 {
987 if ( ++selNew == num )
988 selNew = 0;
989 }
990 else
991 selNew += rows;
992 break;
993
994 default:
995 processed = FALSE;
996 }
997
998 if ( processed )
999 {
1000 // ensure that selNew is in range [0..num)
1001 if ( selNew >= num )
1002 {
1003 selNew -= num;
1004
1005 int dim = horz ? cols : rows;
1006 selNew += dim - 1;
1007 selNew %= dim;
1008 }
1009 else if ( selNew < 0 )
1010 {
1011 selNew += num;
1012
1013 int dim = horz ? cols : rows;
1014 if ( selNew % dim == 0 )
1015 {
1016 selNew -= dim - 1;
1017 }
1018 else
1019 {
1020 selNew++;
1021 }
1022 }
1023
1024 if ( selNew != selOld )
1025 {
1026 radiobox->SetSelection(selNew);
1027
1028 // emulate the button click
1029 radiobox->SendNotificationEvent();
1030
1031 return 0;
1032 }
1033 }
1034 }
1035 break;
1036
1037 #ifdef __WIN32__
1038 case WM_HELP:
1039 {
1040 wxRadioBox *radiobox = (wxRadioBox *)
1041 ::GetWindowLong(hwnd, GWL_USERDATA);
1042
1043 wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") );
1044
1045 bool processed = TRUE;
1046
1047 HELPINFO* info = (HELPINFO*) lParam;
1048 // Don't yet process menu help events, just windows
1049 if (info->iContextType == HELPINFO_WINDOW)
1050 {
1051 wxWindow* subjectOfHelp = radiobox;
1052 bool eventProcessed = FALSE;
1053 while (subjectOfHelp && !eventProcessed)
1054 {
1055 wxHelpEvent helpEvent(wxEVT_HELP, subjectOfHelp->GetId(), wxPoint(info->MousePos.x, info->MousePos.y) ) ; // info->iCtrlId);
1056 helpEvent.SetEventObject(radiobox);
1057 eventProcessed = radiobox->GetEventHandler()->ProcessEvent(helpEvent);
1058
1059 // Go up the window hierarchy until the event is handled (or not)
1060 subjectOfHelp = subjectOfHelp->GetParent();
1061 }
1062 processed = eventProcessed;
1063 }
1064 else if (info->iContextType == HELPINFO_MENUITEM)
1065 {
1066 wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId) ;
1067 helpEvent.SetEventObject(radiobox);
1068 processed = radiobox->GetEventHandler()->ProcessEvent(helpEvent);
1069 }
1070 else processed = FALSE;
1071
1072 if (processed)
1073 return 0;
1074
1075 break;
1076 }
1077 #endif
1078 }
1079
1080 return ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd, message, wParam, lParam);
1081 }
1082
1083 #endif // __WIN32__
1084