]> git.saurik.com Git - wxWidgets.git/blame - src/msw/radiobox.cpp
fixed wxListCtrl::EditLabel for MSW (ListView_EditLabel fails if
[wxWidgets.git] / src / msw / radiobox.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
3ca6a5f0
BP
2// Name: msw/radiobox.cpp
3// Purpose: wxRadioBox implementation
2bda0e17
KB
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart and Markus Holzem
c085e333 9// Licence: wxWindows license
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
e373f51b
VZ
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
2bda0e17 20#ifdef __GNUG__
da07e033 21 #pragma implementation "radiobox.h"
2bda0e17
KB
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
da07e033 28 #pragma hdrstop
2bda0e17
KB
29#endif
30
31#ifndef WX_PRECOMP
da07e033
VZ
32 #include "wx/bitmap.h"
33 #include "wx/brush.h"
34 #include "wx/radiobox.h"
f6bcfd97 35 #include "wx/settings.h"
381dd4bf 36 #include "wx/log.h"
2bda0e17
KB
37#endif
38
39#include "wx/msw/private.h"
40
f048e32f 41#if wxUSE_TOOLTIPS
5ace0213 42 #ifndef __GNUWIN32_OLD__
8614c467 43 #include <commctrl.h>
5ace0213 44 #endif
f048e32f
VZ
45 #include "wx/tooltip.h"
46#endif // wxUSE_TOOLTIPS
47
d3acd369 48IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl)
2bda0e17 49
8614c467
VZ
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
f048e32f 53//
8614c467
VZ
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)
d1e418ea 71#define RADIOBTN_PARENT_IS_RADIOBOX 0
f048e32f 72
e373f51b
VZ
73// ---------------------------------------------------------------------------
74// private functions
75// ---------------------------------------------------------------------------
76
e373f51b 77// wnd proc for radio buttons
2a47d3c1 78#ifdef __WIN32__
e373f51b
VZ
79LRESULT 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
457e6c54 89static WXFARPROC s_wndprocRadioBtn = (WXFARPROC)NULL;
e373f51b 90
42e69d6b
VZ
91#endif // __WIN32__
92
e373f51b
VZ
93// ===========================================================================
94// implementation
95// ===========================================================================
96
97// ---------------------------------------------------------------------------
98// wxRadioBox
99// ---------------------------------------------------------------------------
100
3ca6a5f0 101// returns the number of rows
e373f51b
VZ
102int 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
3ca6a5f0 114// returns the number of columns
e373f51b
VZ
115int 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
42e69d6b 127bool wxRadioBox::MSWCommand(WXUINT cmd, WXWORD id)
2bda0e17 128{
42e69d6b 129 if ( cmd == BN_CLICKED )
da07e033 130 {
f6bcfd97
BP
131 if (id == GetId())
132 return TRUE;
133
9a5ccab4 134 int selectedButton = -1;
e373f51b
VZ
135
136 for ( int i = 0; i < m_noItems; i++ )
137 {
cc2b7472 138 if ( id == wxGetWindowId(m_radioButtons[i]) )
e373f51b 139 {
9a5ccab4 140 selectedButton = i;
e373f51b
VZ
141
142 break;
143 }
144 }
145
3ca6a5f0
BP
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 }
2bda0e17 154
9a5ccab4
VZ
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
e373f51b 162
da07e033
VZ
163 return TRUE;
164 }
e373f51b
VZ
165 else
166 return FALSE;
2bda0e17
KB
167}
168
169#if WXWIN_COMPATIBILITY
170wxRadioBox::wxRadioBox(wxWindow *parent, wxFunction func, const char *title,
da07e033
VZ
171 int x, int y, int width, int height,
172 int n, char **choices,
173 int majorDim, long style, const char *name)
2bda0e17
KB
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,
da07e033 178 wxDefaultValidator, name);
2bda0e17
KB
179 Callback(func);
180 delete choices2;
181}
182
d9317fd4 183#endif // WXWIN_COMPATIBILITY
2bda0e17
KB
184
185// Radio box item
e373f51b 186wxRadioBox::wxRadioBox()
2bda0e17 187{
da07e033
VZ
188 m_selectedButton = -1;
189 m_noItems = 0;
190 m_noRowsOrCols = 0;
191 m_radioButtons = NULL;
e373f51b
VZ
192 m_majorDim = 0;
193 m_radioWidth = NULL;
194 m_radioHeight = NULL;
2bda0e17
KB
195}
196
f048e32f
VZ
197bool 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)
2bda0e17 208{
f048e32f 209 // initialize members
da07e033 210 m_selectedButton = -1;
4afd7529 211 m_noItems = 0;
2bda0e17 212
3ca6a5f0 213 m_majorDim = majorDim == 0 ? n : majorDim;
da07e033 214 m_noRowsOrCols = majorDim;
da07e033 215
f048e32f
VZ
216 // common initialization
217 if ( !CreateControl(parent, id, pos, size, style, val, name) )
218 return FALSE;
2bda0e17 219
f048e32f 220 // create the static box
d9317fd4
VZ
221 if ( !MSWCreateControl(wxT("BUTTON"), BS_GROUPBOX | WS_GROUP,
222 pos, size, title, 0) )
f048e32f 223 return FALSE;
da07e033 224
f048e32f 225 // and now create the buttons
4afd7529 226 m_noItems = n;
f048e32f
VZ
227#if RADIOBTN_PARENT_IS_RADIOBOX
228 HWND hwndParent = GetHwnd();
229#else
230 HWND hwndParent = GetHwndOf(parent);
231#endif
da07e033
VZ
232
233 // Some radio boxes test consecutive id.
e373f51b 234 (void)NewControlId();
da07e033 235 m_radioButtons = new WXHWND[n];
e373f51b
VZ
236 m_radioWidth = new int[n];
237 m_radioHeight = new int[n];
f048e32f
VZ
238
239 WXHFONT hfont = 0;
240 wxFont& font = GetFont();
241 if ( font.Ok() )
da07e033 242 {
f048e32f
VZ
243 hfont = font.GetResourceHandle();
244 }
245
246 for ( int i = 0; i < n; i++ )
247 {
248 m_radioWidth[i] =
249 m_radioHeight[i] = -1;
d3acd369 250 long styleBtn = BS_AUTORADIOBUTTON | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
e373f51b 251 if ( i == 0 && style == 0 )
f048e32f
VZ
252 styleBtn |= WS_GROUP;
253
da07e033 254 long newId = NewControlId();
da07e033 255
f048e32f
VZ
256 HWND hwndBtn = ::CreateWindow(_T("BUTTON"),
257 choices[i],
258 styleBtn,
259 0, 0, 0, 0, // will be set in SetSize()
e373f51b 260 hwndParent,
f048e32f
VZ
261 (HMENU)newId,
262 wxGetInstance(),
e373f51b 263 NULL);
2bda0e17 264
f048e32f
VZ
265 if ( !hwndBtn )
266 {
f6bcfd97 267 wxLogLastError(wxT("CreateWindow(radio btn)"));
f048e32f
VZ
268
269 return FALSE;
270 }
271
e373f51b 272 m_radioButtons[i] = (WXHWND)hwndBtn;
42e69d6b 273
e373f51b 274 SubclassRadioButton((WXHWND)hwndBtn);
2bda0e17 275
f048e32f 276 if ( hfont )
da07e033 277 {
f048e32f 278 ::SendMessage(hwndBtn, WM_SETFONT, (WPARAM)hfont, 0L);
da07e033 279 }
e373f51b 280
f048e32f 281 m_subControls.Add(newId);
da07e033 282 }
e373f51b 283
da07e033 284 // Create a dummy radio control to end the group.
f048e32f
VZ
285 (void)::CreateWindow(_T("BUTTON"),
286 _T(""),
287 WS_GROUP | BS_AUTORADIOBUTTON | WS_CHILD,
e373f51b
VZ
288 0, 0, 0, 0, hwndParent,
289 (HMENU)NewControlId(), wxGetInstance(), NULL);
2bda0e17 290
da07e033 291 SetSelection(0);
2bda0e17 292
f048e32f 293 SetSize(pos.x, pos.y, size.x, size.y);
2bda0e17 294
da07e033 295 return TRUE;
2bda0e17 296}
2bda0e17 297
e373f51b 298wxRadioBox::~wxRadioBox()
2bda0e17 299{
da07e033 300 m_isBeingDeleted = TRUE;
1eb20d4a 301
da07e033
VZ
302 if (m_radioButtons)
303 {
304 int i;
305 for (i = 0; i < m_noItems; i++)
42e69d6b 306 ::DestroyWindow((HWND)m_radioButtons[i]);
da07e033
VZ
307 delete[] m_radioButtons;
308 }
42e69d6b 309
da07e033 310 if (m_radioWidth)
e373f51b 311 delete[] m_radioWidth;
da07e033 312 if (m_radioHeight)
e373f51b 313 delete[] m_radioHeight;
2bda0e17
KB
314
315}
316
d66a042c
VZ
317wxString wxRadioBox::GetLabel(int item) const
318{
223d09f6 319 wxCHECK_MSG( item >= 0 && item < m_noItems, wxT(""), wxT("invalid radiobox index") );
d66a042c
VZ
320
321 return wxGetWindowText(m_radioButtons[item]);
322}
323
debe6624 324void wxRadioBox::SetLabel(int item, const wxString& label)
2bda0e17 325{
223d09f6 326 wxCHECK_RET( item >= 0 && item < m_noItems, wxT("invalid radiobox index") );
d66a042c 327
e373f51b 328 m_radioWidth[item] = m_radioHeight[item] = -1;
42e69d6b 329 SetWindowText((HWND)m_radioButtons[item], label.c_str());
2bda0e17
KB
330}
331
33ac7e6f 332void wxRadioBox::SetLabel(int WXUNUSED(item), wxBitmap *WXUNUSED(bitmap))
2bda0e17 333{
da07e033 334 /*
e373f51b
VZ
335 m_radioWidth[item] = bitmap->GetWidth() + FB_MARGIN;
336 m_radioHeight[item] = bitmap->GetHeight() + FB_MARGIN;
da07e033 337 */
223d09f6 338 wxFAIL_MSG(wxT("not implemented"));
2bda0e17
KB
339}
340
341int wxRadioBox::FindString(const wxString& s) const
342{
42e69d6b 343 for (int i = 0; i < m_noItems; i++)
da07e033 344 {
42e69d6b 345 if ( s == wxGetWindowText(m_radioButtons[i]) )
da07e033
VZ
346 return i;
347 }
42e69d6b
VZ
348
349 return wxNOT_FOUND;
2bda0e17
KB
350}
351
debe6624 352void wxRadioBox::SetSelection(int N)
2bda0e17 353{
223d09f6 354 wxCHECK_RET( (N >= 0) && (N < m_noItems), wxT("invalid radiobox index") );
2bda0e17 355
da07e033
VZ
356 // Following necessary for Win32s, because Win32s translate BM_SETCHECK
357 if (m_selectedButton >= 0 && m_selectedButton < m_noItems)
e373f51b
VZ
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]);
2bda0e17 362
da07e033 363 m_selectedButton = N;
2bda0e17
KB
364}
365
366// Get single selection, for single choice list items
e373f51b 367int wxRadioBox::GetSelection() const
2bda0e17 368{
da07e033 369 return m_selectedButton;
2bda0e17
KB
370}
371
372// Find string for position
debe6624 373wxString wxRadioBox::GetString(int N) const
2bda0e17 374{
e373f51b 375 return wxGetWindowText(m_radioButtons[N]);
2bda0e17
KB
376}
377
3ca6a5f0
BP
378// ----------------------------------------------------------------------------
379// size calculations
380// ----------------------------------------------------------------------------
381
382wxSize 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
415wxSize 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
442wxSize wxRadioBox::DoGetBestSize() const
443{
444 return GetTotalButtonSize(GetMaxButtonSize());
445}
446
1f916a19 447// Restored old code.
bfc6fde4 448void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
1f916a19 449{
4438caf4
VZ
450 int currentX, currentY;
451 GetPosition(&currentX, &currentY);
c219cecc
VZ
452 int widthOld, heightOld;
453 GetSize(&widthOld, &heightOld);
454
4438caf4
VZ
455 int xx = x;
456 int yy = y;
457
ce96b7a0 458 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
4438caf4 459 xx = currentX;
ce96b7a0 460 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
4438caf4
VZ
461 yy = currentY;
462
f048e32f
VZ
463#if RADIOBTN_PARENT_IS_RADIOBOX
464 int y_offset = 0;
465 int x_offset = 0;
466#else
4438caf4
VZ
467 int y_offset = yy;
468 int x_offset = xx;
f048e32f
VZ
469#endif
470
3ca6a5f0 471 int cx1, cy1;
4438caf4
VZ
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.
3ca6a5f0
BP
476 wxSize maxSize = GetMaxButtonSize();
477 int maxWidth = maxSize.x,
478 maxHeight = maxSize.y;
4438caf4 479
3ca6a5f0
BP
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 )
1f916a19 486 {
3ca6a5f0
BP
487 if ( sizeFlags & wxSIZE_AUTO_WIDTH )
488 width = totWidth;
4438caf4 489 else
3ca6a5f0 490 width = widthOld;
1f916a19 491 }
1f916a19 492
3ca6a5f0 493 if ( height == -1 )
4438caf4 494 {
3ca6a5f0
BP
495 if ( sizeFlags & wxSIZE_AUTO_HEIGHT )
496 height = totHeight;
4438caf4 497 else
3ca6a5f0
BP
498 height = heightOld;
499 }
4438caf4 500
3ca6a5f0 501 ::MoveWindow(GetHwnd(), xx, yy, width, height, TRUE);
c219cecc 502
3ca6a5f0
BP
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.
c219cecc 508
3ca6a5f0
BP
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.
4438caf4 513
3ca6a5f0
BP
514 x_offset += cx1;
515 y_offset += cy1;
1f916a19 516
c219cecc 517#if defined(CTL3D) && (!CTL3D)
4438caf4
VZ
518 y_offset += (int)(cy1/2); // Fudge factor since buttons overlapped label
519 // JACS 2/12/93. CTL3D draws group label quite high.
1f916a19 520#endif
3ca6a5f0 521
4438caf4
VZ
522 int startX = x_offset;
523 int startY = y_offset;
1f916a19 524
3ca6a5f0 525 for ( int i = 0; i < m_noItems; i++ )
1f916a19 526 {
3ca6a5f0
BP
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
4438caf4 539 {
3ca6a5f0
BP
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 )
4438caf4 548 {
3ca6a5f0 549 // start of new column
4438caf4
VZ
550 y_offset = startY;
551 x_offset += maxWidth + cx1;
552 }
3ca6a5f0 553 else // start of new row
4438caf4
VZ
554 {
555 x_offset = startX;
556 y_offset += maxHeight;
557 if (m_radioWidth[0]>0)
558 y_offset += cy1/2;
559 }
560 }
3ca6a5f0
BP
561
562 int widthBtn;
563 if ( isLastInTheRow )
4438caf4 564 {
3ca6a5f0
BP
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;
4438caf4
VZ
569 }
570 else
571 {
3ca6a5f0
BP
572 // normal button, always of the same size
573 widthBtn = maxWidth;
4438caf4 574 }
1f916a19 575
f048e32f
VZ
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],
3ca6a5f0 581 x_offset, y_offset, widthBtn, maxHeight,
f048e32f 582 TRUE);
4438caf4 583
3ca6a5f0
BP
584 // where do we put the next button?
585 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
4438caf4 586 {
3ca6a5f0 587 // below this one
4438caf4
VZ
588 y_offset += maxHeight;
589 if (m_radioWidth[0]>0)
590 y_offset += cy1/2;
591 }
592 else
3ca6a5f0
BP
593 {
594 // to the right of this one
595 x_offset += widthBtn + cx1;
596 }
1f916a19 597 }
1f916a19
JS
598}
599
2bda0e17
KB
600void wxRadioBox::GetSize(int *width, int *height) const
601{
da07e033
VZ
602 RECT rect;
603 rect.left = -1; rect.right = -1; rect.top = -1; rect.bottom = -1;
2bda0e17 604
da07e033
VZ
605 if (m_hWnd)
606 wxFindMaxSize(m_hWnd, &rect);
2bda0e17 607
da07e033
VZ
608 int i;
609 for (i = 0; i < m_noItems; i++)
610 wxFindMaxSize(m_radioButtons[i], &rect);
2bda0e17 611
da07e033
VZ
612 *width = rect.right - rect.left;
613 *height = rect.bottom - rect.top;
2bda0e17
KB
614}
615
616void wxRadioBox::GetPosition(int *x, int *y) const
617{
da07e033 618 wxWindow *parent = GetParent();
f048e32f 619 RECT rect = { -1, -1, -1, -1 };
da07e033
VZ
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
f048e32f
VZ
628 // Since we now have the absolute screen coords, if there's a parent we
629 // must subtract its top left corner
da07e033
VZ
630 POINT point;
631 point.x = rect.left;
632 point.y = rect.top;
633 if (parent)
634 {
635 ::ScreenToClient((HWND) parent->GetHWND(), &point);
636 }
f048e32f
VZ
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).
da07e033
VZ
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;
2bda0e17
KB
649}
650
e373f51b 651void wxRadioBox::SetFocus()
2bda0e17 652{
da07e033
VZ
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 }
2bda0e17
KB
660
661}
662
debe6624 663bool wxRadioBox::Show(bool show)
2bda0e17 664{
42e69d6b
VZ
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
da07e033 674 return TRUE;
2bda0e17
KB
675}
676
677// Enable a specific button
debe6624 678void wxRadioBox::Enable(int item, bool enable)
2bda0e17 679{
42e69d6b 680 wxCHECK_RET( item >= 0 && item < m_noItems,
223d09f6 681 wxT("invalid item in wxRadioBox::Enable()") );
42e69d6b
VZ
682
683 ::EnableWindow((HWND) m_radioButtons[item], enable);
2bda0e17
KB
684}
685
686// Enable all controls
cc2b7472 687bool wxRadioBox::Enable(bool enable)
2bda0e17 688{
cc2b7472
VZ
689 if ( !wxControl::Enable(enable) )
690 return FALSE;
1eb20d4a 691
42e69d6b 692 for (int i = 0; i < m_noItems; i++)
da07e033 693 ::EnableWindow((HWND) m_radioButtons[i], enable);
cc2b7472
VZ
694
695 return TRUE;
2bda0e17
KB
696}
697
698// Show a specific button
debe6624 699void wxRadioBox::Show(int item, bool show)
2bda0e17 700{
42e69d6b 701 wxCHECK_RET( item >= 0 && item < m_noItems,
223d09f6 702 wxT("invalid item in wxRadioBox::Show()") );
42e69d6b
VZ
703
704 ::ShowWindow((HWND)m_radioButtons[item], show ? SW_SHOW : SW_HIDE);
2bda0e17
KB
705}
706
2bda0e17 707// For single selection items only
e373f51b 708wxString wxRadioBox::GetStringSelection() const
2bda0e17 709{
e373f51b
VZ
710 wxString result;
711 int sel = GetSelection();
da07e033 712 if (sel > -1)
e373f51b
VZ
713 result = GetString(sel);
714
715 return result;
2bda0e17
KB
716}
717
e373f51b 718bool wxRadioBox::SetStringSelection(const wxString& s)
2bda0e17 719{
da07e033
VZ
720 int sel = FindString (s);
721 if (sel > -1)
2bda0e17 722 {
da07e033
VZ
723 SetSelection (sel);
724 return TRUE;
2bda0e17 725 }
da07e033
VZ
726 else
727 return FALSE;
2bda0e17
KB
728}
729
2bda0e17
KB
730bool wxRadioBox::ContainsHWND(WXHWND hWnd) const
731{
da07e033 732 int i;
2bda0e17 733 for (i = 0; i < Number(); i++)
42e69d6b 734 {
da07e033
VZ
735 if (GetRadioButtons()[i] == hWnd)
736 return TRUE;
42e69d6b
VZ
737 }
738
da07e033 739 return FALSE;
2bda0e17
KB
740}
741
4afd7529 742void wxRadioBox::Command(wxCommandEvent & event)
2bda0e17 743{
da07e033
VZ
744 SetSelection (event.m_commandInt);
745 ProcessCommand (event);
2bda0e17
KB
746}
747
8614c467
VZ
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!
e373f51b
VZ
750void wxRadioBox::SubclassRadioButton(WXHWND hWndBtn)
751{
8614c467 752 // No GWL_USERDATA in Win16, so omit this subclassing.
42e69d6b 753#ifdef __WIN32__
e373f51b
VZ
754 HWND hwndBtn = (HWND)hWndBtn;
755
756 if ( !s_wndprocRadioBtn )
457e6c54 757 s_wndprocRadioBtn = (WXFARPROC)::GetWindowLong(hwndBtn, GWL_WNDPROC);
e373f51b
VZ
758
759 ::SetWindowLong(hwndBtn, GWL_WNDPROC, (long)wxRadioBtnWndProc);
760 ::SetWindowLong(hwndBtn, GWL_USERDATA, (long)this);
42e69d6b 761#endif // __WIN32__
e373f51b
VZ
762}
763
9a5ccab4
VZ
764void wxRadioBox::SendNotificationEvent()
765{
766 wxCommandEvent event(wxEVT_COMMAND_RADIOBOX_SELECTED, m_windowId);
767 event.SetInt( m_selectedButton );
36dda0de 768 event.SetString( GetString(m_selectedButton) );
9a5ccab4
VZ
769 event.SetEventObject( this );
770 ProcessCommand(event);
771}
772
4afd7529
VZ
773bool 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 {
f6bcfd97
BP
785 HWND hwndBtn = (HWND)m_radioButtons[n];
786 ::SendMessage(hwndBtn, WM_SETFONT, (WPARAM)hfont, 0L);
4afd7529 787
f6bcfd97
BP
788 // otherwise the buttons are not redrawn correctly
789 ::InvalidateRect(hwndBtn, NULL, FALSE /* don't erase bg */);
790 }
8614c467 791
4afd7529
VZ
792 return TRUE;
793}
794
8614c467
VZ
795// ----------------------------------------------------------------------------
796// our window proc
797// ----------------------------------------------------------------------------
798
c92d798f
JS
799long wxRadioBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
800{
d3acd369 801 switch ( nMsg )
c92d798f 802 {
8614c467 803#ifdef __WIN32__
d3acd369
VZ
804 case WM_CTLCOLORSTATIC:
805 // set the colour of the radio buttons to be the same as ours
806 {
807 HDC hdc = (HDC)wParam;
c92d798f 808
d3acd369
VZ
809 const wxColour& colBack = GetBackgroundColour();
810 ::SetBkColor(hdc, wxColourToRGB(colBack));
811 ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
c92d798f 812
d3acd369
VZ
813 wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
814
815 return (WXHBRUSH)brush->GetResourceHandle();
816 }
8614c467 817#endif // Win32
c92d798f 818
d3acd369
VZ
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 }
8614c467 833 break;
d3acd369 834 }
8614c467
VZ
835
836 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
c92d798f
JS
837}
838
33ac7e6f
KB
839WXHBRUSH wxRadioBox::OnCtlColor(WXHDC pDC, WXHWND WXUNUSED(pWnd), WXUINT WXUNUSED(nCtlColor),
840 WXUINT WXUNUSED(message),
841 WXWPARAM WXUNUSED(wParam),
842 WXLPARAM WXUNUSED(lParam))
f6bcfd97
BP
843{
844#if wxUSE_CTL3D
845 if ( m_useCtl3D )
846 {
847 HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
848 return (WXHBRUSH) hbrush;
849 }
850#endif // wxUSE_CTL3D
851
852 HDC hdc = (HDC)pDC;
853 if (GetParent()->GetTransparentBackground())
854 SetBkMode(hdc, TRANSPARENT);
855 else
856 SetBkMode(hdc, OPAQUE);
857
858 wxColour colBack = GetBackgroundColour();
859
860 if (!IsEnabled())
861 colBack = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE);
862
863 ::SetBkColor(hdc, wxColourToRGB(colBack));
864 ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
865
866 wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
867
868 return (WXHBRUSH)brush->GetResourceHandle();
869}
870
871
e373f51b
VZ
872// ---------------------------------------------------------------------------
873// window proc for radio buttons
874// ---------------------------------------------------------------------------
875
2a47d3c1
JS
876#ifdef __WIN32__
877
e373f51b 878LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
8614c467 879 UINT message,
e373f51b
VZ
880 WPARAM wParam,
881 LPARAM lParam)
882{
8614c467 883 switch ( message )
e373f51b 884 {
8614c467
VZ
885 case WM_GETDLGCODE:
886 // we must tell IsDialogMessage()/our kbd processing code that we
887 // want to process arrows ourselves because neither of them is
888 // smart enough to handle arrows properly for us
889 {
8ad6ad99 890 long lDlgCode = ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd,
8614c467 891 message, wParam, lParam);
8ad6ad99 892
8614c467
VZ
893 return lDlgCode | DLGC_WANTARROWS;
894 }
e373f51b 895
8614c467
VZ
896#if wxUSE_TOOLTIPS
897 case WM_NOTIFY:
f048e32f 898 {
8614c467
VZ
899 NMHDR* hdr = (NMHDR *)lParam;
900 if ( (int)hdr->code == TTN_NEEDTEXT )
e373f51b 901 {
8614c467
VZ
902 wxRadioBox *radiobox = (wxRadioBox *)
903 ::GetWindowLong(hwnd, GWL_USERDATA);
e373f51b 904
8614c467
VZ
905 wxCHECK_MSG( radiobox, 0,
906 wxT("radio button without radio box?") );
907
908 wxToolTip *tooltip = radiobox->GetToolTip();
909 if ( tooltip )
910 {
911 TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam;
912 ttt->lpszText = (wxChar *)tooltip->GetTip().c_str();
913 }
914
915 // processed
916 return 0;
e373f51b 917 }
f048e32f 918 }
8614c467 919 break;
f048e32f 920#endif // wxUSE_TOOLTIPS
f048e32f 921
8614c467 922 case WM_KEYDOWN:
9a5ccab4 923 {
8614c467
VZ
924 wxRadioBox *radiobox = (wxRadioBox *)
925 ::GetWindowLong(hwnd, GWL_USERDATA);
f048e32f 926
8614c467 927 wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") );
f048e32f 928
8614c467 929 bool processed = TRUE;
f048e32f 930
3ca6a5f0
BP
931 bool horz = (radiobox->GetWindowStyle() & wxRA_SPECIFY_COLS) != 0;
932 int num = radiobox->Number(),
933 rows = radiobox->GetNumVer(),
934 cols = radiobox->GetNumHor();
935
8614c467
VZ
936 int selOld = radiobox->GetSelection();
937 int selNew = selOld;
f048e32f 938
3ca6a5f0
BP
939 // wrapping will be handled below for the cases when we
940 // add/substract more than 1 but here otherwise as it's simpler
8614c467
VZ
941 switch ( wParam )
942 {
943 case VK_UP:
3ca6a5f0
BP
944 if ( horz )
945 selNew -= cols;
946 else
947 {
948 if ( selNew )
949 selNew--;
950 else
951 selNew = num - 1;
952 }
8614c467 953 break;
f048e32f 954
8614c467 955 case VK_LEFT:
3ca6a5f0
BP
956 if ( horz )
957 {
958 if ( selNew )
959 selNew--;
960 else
961 selNew = num - 1;
962 }
963 else
964 selNew -= rows;
8614c467 965 break;
f048e32f 966
8614c467 967 case VK_DOWN:
3ca6a5f0
BP
968 if ( horz )
969 selNew += cols;
970 else
971 {
972 if ( ++selNew == num )
973 selNew = 0;
974 }
8614c467 975 break;
f048e32f 976
8614c467 977 case VK_RIGHT:
3ca6a5f0
BP
978 if ( horz )
979 {
980 if ( ++selNew == num )
981 selNew = 0;
982 }
983 else
984 selNew += rows;
8614c467
VZ
985 break;
986
987 default:
988 processed = FALSE;
989 }
990
991 if ( processed )
f048e32f 992 {
8614c467 993 // ensure that selNew is in range [0..num)
3ca6a5f0
BP
994 if ( selNew >= num )
995 {
996 selNew -= num;
997
998 int dim = horz ? cols : rows;
999 selNew += dim - 1;
1000 selNew %= dim;
1001 }
1002 else if ( selNew < 0 )
1003 {
1004 selNew += num;
1005
1006 int dim = horz ? cols : rows;
1007 if ( selNew % dim == 0 )
1008 {
1009 selNew -= dim - 1;
1010 }
1011 else
1012 {
1013 selNew++;
1014 }
1015 }
8614c467
VZ
1016
1017 if ( selNew != selOld )
1018 {
1019 radiobox->SetSelection(selNew);
1020
1021 // emulate the button click
1022 radiobox->SendNotificationEvent();
9a5ccab4 1023
8614c467
VZ
1024 return 0;
1025 }
f048e32f 1026 }
9a5ccab4 1027 }
21687069
VZ
1028 break;
1029
fac93396
JS
1030#ifdef __WIN32__
1031 case WM_HELP:
1032 {
1033 wxRadioBox *radiobox = (wxRadioBox *)
1034 ::GetWindowLong(hwnd, GWL_USERDATA);
1035
1036 wxCHECK_MSG( radiobox, 0, wxT("radio button without radio box?") );
1037
1038 bool processed = TRUE;
1039
1040 HELPINFO* info = (HELPINFO*) lParam;
1041 // Don't yet process menu help events, just windows
1042 if (info->iContextType == HELPINFO_WINDOW)
1043 {
1044 wxWindow* subjectOfHelp = radiobox;
1045 bool eventProcessed = FALSE;
1046 while (subjectOfHelp && !eventProcessed)
1047 {
1048 wxHelpEvent helpEvent(wxEVT_HELP, subjectOfHelp->GetId(), wxPoint(info->MousePos.x, info->MousePos.y) ) ; // info->iCtrlId);
1049 helpEvent.SetEventObject(radiobox);
1050 eventProcessed = radiobox->GetEventHandler()->ProcessEvent(helpEvent);
1051
1052 // Go up the window hierarchy until the event is handled (or not)
1053 subjectOfHelp = subjectOfHelp->GetParent();
1054 }
1055 processed = eventProcessed;
1056 }
1057 else if (info->iContextType == HELPINFO_MENUITEM)
1058 {
1059 wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId) ;
1060 helpEvent.SetEventObject(radiobox);
1061 processed = radiobox->GetEventHandler()->ProcessEvent(helpEvent);
1062 }
1063 else processed = FALSE;
1064
1065 if (processed)
1066 return 0;
1067
1068 break;
1069 }
1070#endif
e373f51b
VZ
1071 }
1072
8ad6ad99 1073 return ::CallWindowProc(CASTWNDPROC s_wndprocRadioBtn, hwnd, message, wParam, lParam);
e373f51b 1074}
42e69d6b
VZ
1075
1076#endif // __WIN32__
e373f51b 1077