1. wxListCtrl fixes
[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 <stdio.h>
33 #include "wx/setup.h"
34 #include "wx/bitmap.h"
35 #include "wx/brush.h"
36 #include "wx/radiobox.h"
37 #endif
38
39 #include "wx/msw/private.h"
40
41 #if !USE_SHARED_LIBRARY
42 IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl)
43 #endif
44
45 // ---------------------------------------------------------------------------
46 // private functions
47 // ---------------------------------------------------------------------------
48
49 // get the id of the window
50 #ifdef __WIN32__
51 #define GET_WIN_ID(hwnd) ::GetWindowLong((HWND)hwnd, GWL_ID)
52 #else // Win16
53 #define GET_WIN_ID(hwnd) ::GetWindowWord((HWND)hwnd, GWW_ID)
54 #endif // Win32/16
55
56 // wnd proc for radio buttons
57 LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hWnd,
58 UINT message,
59 WPARAM wParam,
60 LPARAM lParam);
61
62 // ---------------------------------------------------------------------------
63 // global vars
64 // ---------------------------------------------------------------------------
65
66 // the pointer to standard radio button wnd proc
67 static WNDPROC s_wndprocRadioBtn = (WNDPROC)NULL;
68
69 // ===========================================================================
70 // implementation
71 // ===========================================================================
72
73 // ---------------------------------------------------------------------------
74 // wxRadioBox
75 // ---------------------------------------------------------------------------
76
77 int wxRadioBox::GetNumVer() const
78 {
79 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
80 {
81 return m_majorDim;
82 }
83 else
84 {
85 return (m_noItems + m_majorDim - 1)/m_majorDim;
86 }
87 }
88
89 int wxRadioBox::GetNumHor() const
90 {
91 if ( m_windowStyle & wxRA_SPECIFY_ROWS )
92 {
93 return (m_noItems + m_majorDim - 1)/m_majorDim;
94 }
95 else
96 {
97 return m_majorDim;
98 }
99 }
100
101 bool wxRadioBox::MSWCommand(WXUINT param, WXWORD id)
102 {
103 if ( param == BN_CLICKED )
104 {
105 m_selectedButton = -1;
106
107 for ( int i = 0; i < m_noItems; i++ )
108 {
109 if ( id == GET_WIN_ID(m_radioButtons[i]) )
110 {
111 m_selectedButton = i;
112
113 break;
114 }
115 }
116
117 wxASSERT_MSG( m_selectedButton != -1, "click from alien button?" );
118
119 wxCommandEvent event(wxEVT_COMMAND_RADIOBOX_SELECTED, m_windowId);
120 event.SetInt( m_selectedButton );
121 event.SetEventObject( this );
122 ProcessCommand(event);
123
124 return TRUE;
125 }
126 else
127 return FALSE;
128 }
129
130 #if WXWIN_COMPATIBILITY
131 wxRadioBox::wxRadioBox(wxWindow *parent, wxFunction func, const char *title,
132 int x, int y, int width, int height,
133 int n, char **choices,
134 int majorDim, long style, const char *name)
135 {
136 wxString *choices2 = new wxString[n];
137 for ( int i = 0; i < n; i ++) choices2[i] = choices[i];
138 Create(parent, -1, title, wxPoint(x, y), wxSize(width, height), n, choices2, majorDim, style,
139 wxDefaultValidator, name);
140 Callback(func);
141 delete choices2;
142 }
143
144 #endif
145
146 // Radio box item
147 wxRadioBox::wxRadioBox()
148 {
149 m_selectedButton = -1;
150 m_noItems = 0;
151 m_noRowsOrCols = 0;
152 m_radioButtons = NULL;
153 m_majorDim = 0;
154 m_radioWidth = NULL;
155 m_radioHeight = NULL;
156 }
157
158 bool wxRadioBox::Create(wxWindow *parent, wxWindowID id, const wxString& title,
159 const wxPoint& pos, const wxSize& size,
160 int n, const wxString choices[],
161 int majorDim, long style,
162 const wxValidator& val, const wxString& name)
163 {
164 m_selectedButton = -1;
165 m_noItems = n;
166
167 SetName(name);
168 SetValidator(val);
169
170 parent->AddChild(this);
171 m_backgroundColour = parent->GetBackgroundColour();
172 m_foregroundColour = parent->GetForegroundColour();
173
174 m_windowStyle = (long&)style;
175
176 int x = pos.x;
177 int y = pos.y;
178 int width = size.x;
179 int height = size.y;
180
181 if (id == -1)
182 m_windowId = NewControlId();
183 else
184 m_windowId = id;
185
186 if ( majorDim == 0 )
187 m_majorDim = n;
188 else
189 m_majorDim = majorDim;
190 m_noRowsOrCols = majorDim;
191
192 long msStyle = GROUP_FLAGS;
193
194 bool want3D;
195 WXDWORD exStyle = Determine3DEffects(0, &want3D);
196 // Even with extended styles, need to combine with WS_BORDER
197 // for them to look right.
198 /*
199 if ( want3D || wxStyleHasBorder(m_windowStyle) )
200 msStyle |= WS_BORDER;
201 */
202
203 HWND hwndParent = (HWND)parent->GetHWND();
204
205 m_hWnd = (WXHWND)::CreateWindowEx
206 (
207 (DWORD)exStyle,
208 GROUP_CLASS,
209 title,
210 msStyle,
211 0, 0, 0, 0,
212 hwndParent,
213 (HMENU)m_windowId,
214 wxGetInstance(),
215 NULL
216 );
217
218 #if wxUSE_CTL3D
219 if (want3D)
220 {
221 Ctl3dSubclassCtl((HWND)m_hWnd);
222 m_useCtl3D = TRUE;
223 }
224 #endif // wxUSE_CTL3D
225
226 SetFont(parent->GetFont());
227
228 SubclassWin(m_hWnd);
229
230 // Some radio boxes test consecutive id.
231 (void)NewControlId();
232 m_radioButtons = new WXHWND[n];
233 m_radioWidth = new int[n];
234 m_radioHeight = new int[n];
235 int i;
236 for (i = 0; i < n; i++)
237 {
238 m_radioWidth[i] = m_radioHeight[i] = -1;
239 long groupStyle = 0;
240 if ( i == 0 && style == 0 )
241 groupStyle = WS_GROUP;
242 long newId = NewControlId();
243 long msStyle = groupStyle | RADIO_FLAGS;
244
245 HWND hwndBtn = CreateWindowEx(exStyle, RADIO_CLASS,
246 choices[i], msStyle,
247 0,0,0,0,
248 hwndParent,
249 (HMENU)newId, wxGetInstance(),
250 NULL);
251
252 m_radioButtons[i] = (WXHWND)hwndBtn;
253 SubclassRadioButton((WXHWND)hwndBtn);
254
255 wxFont& font = GetFont();
256 if ( font.Ok() )
257 {
258 SendMessage(hwndBtn, WM_SETFONT,
259 (WPARAM)font.GetResourceHandle(), 0L);
260 }
261
262 m_subControls.Append((wxObject *)newId);
263 }
264
265 // Create a dummy radio control to end the group.
266 (void)CreateWindowEx(0, RADIO_CLASS, "", WS_GROUP | RADIO_FLAGS,
267 0, 0, 0, 0, hwndParent,
268 (HMENU)NewControlId(), wxGetInstance(), NULL);
269
270 SetSelection(0);
271
272 SetSize(x, y, width, height);
273
274 return TRUE;
275 }
276
277 wxRadioBox::~wxRadioBox()
278 {
279 m_isBeingDeleted = TRUE;
280
281 if (m_radioButtons)
282 {
283 int i;
284 for (i = 0; i < m_noItems; i++)
285 DestroyWindow((HWND) m_radioButtons[i]);
286 delete[] m_radioButtons;
287 }
288 if (m_radioWidth)
289 delete[] m_radioWidth;
290 if (m_radioHeight)
291 delete[] m_radioHeight;
292 if (m_hWnd)
293 ::DestroyWindow((HWND) m_hWnd);
294 m_hWnd = 0;
295
296 }
297
298 wxString wxRadioBox::GetLabel(int item) const
299 {
300 GetWindowText((HWND)m_radioButtons[item], wxBuffer, 300);
301 return wxString(wxBuffer);
302 }
303
304 void wxRadioBox::SetLabel(int item, const wxString& label)
305 {
306 m_radioWidth[item] = m_radioHeight[item] = -1;
307 SetWindowText((HWND)m_radioButtons[item], (const char *)label);
308 }
309
310 void wxRadioBox::SetLabel(int item, wxBitmap *bitmap)
311 {
312 /*
313 m_radioWidth[item] = bitmap->GetWidth() + FB_MARGIN;
314 m_radioHeight[item] = bitmap->GetHeight() + FB_MARGIN;
315 */
316 }
317
318 int wxRadioBox::FindString(const wxString& s) const
319 {
320 int i;
321 for (i = 0; i < m_noItems; i++)
322 {
323 GetWindowText((HWND) m_radioButtons[i], wxBuffer, 1000);
324 if (s == wxBuffer)
325 return i;
326 }
327 return -1;
328 }
329
330 void wxRadioBox::SetSelection(int N)
331 {
332 wxCHECK_RET( (N >= 0) && (N < m_noItems), "invalid radiobox index" );
333
334 // Following necessary for Win32s, because Win32s translate BM_SETCHECK
335 if (m_selectedButton >= 0 && m_selectedButton < m_noItems)
336 ::SendMessage((HWND) m_radioButtons[m_selectedButton], BM_SETCHECK, 0, 0L);
337
338 ::SendMessage((HWND)m_radioButtons[N], BM_SETCHECK, 1, 0L);
339 ::SetFocus((HWND)m_radioButtons[N]);
340
341 m_selectedButton = N;
342 }
343
344 // Get single selection, for single choice list items
345 int wxRadioBox::GetSelection() const
346 {
347 return m_selectedButton;
348 }
349
350 // Find string for position
351 wxString wxRadioBox::GetString(int N) const
352 {
353 return wxGetWindowText(m_radioButtons[N]);
354 }
355
356 // Restored old code.
357 void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
358 {
359 int currentX, currentY;
360 GetPosition(&currentX, &currentY);
361 int xx = x;
362 int yy = y;
363
364 if (x == -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
365 xx = currentX;
366 if (y == -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
367 yy = currentY;
368
369 char buf[400];
370
371 int y_offset = yy;
372 int x_offset = xx;
373 int current_width, cyf;
374
375 int cx1,cy1;
376 wxGetCharSize(m_hWnd, &cx1, &cy1, & GetFont());
377 // Attempt to have a look coherent with other platforms:
378 // We compute the biggest toggle dim, then we align all
379 // items according this value.
380 int maxWidth = -1;
381 int maxHeight = -1;
382
383 int i;
384 for (i = 0 ; i < m_noItems; i++)
385 {
386 int eachWidth;
387 int eachHeight;
388 if (m_radioWidth[i]<0)
389 {
390 // It's a labelled toggle
391 GetWindowText((HWND) m_radioButtons[i], buf, 300);
392 GetTextExtent(buf, &current_width, &cyf,NULL,NULL, & GetFont());
393 eachWidth = (int)(current_width + RADIO_SIZE);
394 eachHeight = (int)((3*cyf)/2);
395 }
396 else
397 {
398 eachWidth = m_radioWidth[i];
399 eachHeight = m_radioHeight[i];
400 }
401 if (maxWidth<eachWidth) maxWidth = eachWidth;
402 if (maxHeight<eachHeight) maxHeight = eachHeight;
403 }
404
405 if (m_hWnd)
406 {
407 int totWidth;
408 int totHeight;
409
410 int nbHor = GetNumHor(),
411 nbVer = GetNumVer();
412
413 // this formula works, but I don't know why.
414 // Please, be sure what you do if you modify it!!
415 if (m_radioWidth[0]<0)
416 totHeight = (nbVer * maxHeight) + cy1/2;
417 else
418 totHeight = nbVer * (maxHeight+cy1/2);
419 totWidth = nbHor * (maxWidth+cx1);
420
421 #if (!CTL3D)
422 // Requires a bigger group box in plain Windows
423 MoveWindow((HWND) m_hWnd,x_offset,y_offset,totWidth+cx1,totHeight+(3*cy1)/2,TRUE);
424 #else
425 MoveWindow((HWND) m_hWnd,x_offset,y_offset,totWidth+cx1,totHeight+cy1,TRUE);
426 #endif
427 x_offset += cx1;
428 y_offset += cy1;
429 }
430
431 #if (!CTL3D)
432 y_offset += (int)(cy1/2); // Fudge factor since buttons overlapped label
433 // JACS 2/12/93. CTL3D draws group label quite high.
434 #endif
435 int startX = x_offset;
436 int startY = y_offset;
437
438 for ( i = 0 ; i < m_noItems; i++)
439 {
440 // Bidimensional radio adjustment
441 if (i&&((i%m_majorDim)==0)) // Why is this omitted for i = 0?
442 {
443 if (m_windowStyle & wxRA_VERTICAL)
444 {
445 y_offset = startY;
446 x_offset += maxWidth + cx1;
447 }
448 else
449 {
450 x_offset = startX;
451 y_offset += maxHeight;
452 if (m_radioWidth[0]>0)
453 y_offset += cy1/2;
454 }
455 }
456 int eachWidth;
457 int eachHeight;
458 if (m_radioWidth[i]<0)
459 {
460 // It's a labeled item
461 GetWindowText((HWND) m_radioButtons[i], buf, 300);
462 GetTextExtent(buf, &current_width, &cyf,NULL,NULL, & GetFont());
463
464 // How do we find out radio button bitmap size!!
465 // By adjusting them carefully, manually :-)
466 eachWidth = (int)(current_width + RADIO_SIZE);
467 eachHeight = (int)((3*cyf)/2);
468 }
469 else
470 {
471 eachWidth = m_radioWidth[i];
472 eachHeight = m_radioHeight[i];
473 }
474
475 MoveWindow((HWND) m_radioButtons[i],x_offset,y_offset,eachWidth,eachHeight,TRUE);
476 if (m_windowStyle & wxRA_SPECIFY_ROWS)
477 {
478 y_offset += maxHeight;
479 if (m_radioWidth[0]>0)
480 y_offset += cy1/2;
481 }
482 else
483 x_offset += maxWidth + cx1;
484 }
485 }
486
487
488 void wxRadioBox::GetSize(int *width, int *height) const
489 {
490 RECT rect;
491 rect.left = -1; rect.right = -1; rect.top = -1; rect.bottom = -1;
492
493 if (m_hWnd)
494 wxFindMaxSize(m_hWnd, &rect);
495
496 int i;
497 for (i = 0; i < m_noItems; i++)
498 wxFindMaxSize(m_radioButtons[i], &rect);
499
500 *width = rect.right - rect.left;
501 *height = rect.bottom - rect.top;
502 }
503
504 void wxRadioBox::GetPosition(int *x, int *y) const
505 {
506 wxWindow *parent = GetParent();
507 RECT rect;
508 rect.left = -1; rect.right = -1; rect.top = -1; rect.bottom = -1;
509
510 int i;
511 for (i = 0; i < m_noItems; i++)
512 wxFindMaxSize(m_radioButtons[i], &rect);
513
514 if (m_hWnd)
515 wxFindMaxSize(m_hWnd, &rect);
516
517 // Since we now have the absolute screen coords,
518 // if there's a parent we must subtract its top left corner
519 POINT point;
520 point.x = rect.left;
521 point.y = rect.top;
522 if (parent)
523 {
524 ::ScreenToClient((HWND) parent->GetHWND(), &point);
525 }
526 // We may be faking the client origin.
527 // So a window that's really at (0, 30) may appear
528 // (to wxWin apps) to be at (0, 0).
529 if (GetParent())
530 {
531 wxPoint pt(GetParent()->GetClientAreaOrigin());
532 point.x -= pt.x;
533 point.y -= pt.y;
534 }
535
536 *x = point.x;
537 *y = point.y;
538 }
539
540 wxString wxRadioBox::GetLabel() const
541 {
542 if (m_hWnd)
543 {
544 GetWindowText((HWND) m_hWnd, wxBuffer, 300);
545 return wxString(wxBuffer);
546 }
547 else return wxString("");
548 }
549
550 void wxRadioBox::SetLabel(const wxString& label)
551 {
552 if (m_hWnd)
553 SetWindowText((HWND) m_hWnd, label);
554 }
555
556 void wxRadioBox::SetFocus()
557 {
558 if (m_noItems > 0)
559 {
560 if (m_selectedButton == -1)
561 ::SetFocus((HWND) m_radioButtons[0]);
562 else
563 ::SetFocus((HWND) m_radioButtons[m_selectedButton]);
564 }
565
566 }
567
568 bool wxRadioBox::Show(bool show)
569 {
570 m_isShown = show;
571 int cshow;
572 if (show)
573 cshow = SW_SHOW;
574 else
575 cshow = SW_HIDE;
576 if (m_hWnd)
577 ShowWindow((HWND) m_hWnd, cshow);
578 int i;
579 for (i = 0; i < m_noItems; i++)
580 ShowWindow((HWND) m_radioButtons[i], cshow);
581 return TRUE;
582 }
583
584 // Enable a specific button
585 void wxRadioBox::Enable(int item, bool enable)
586 {
587 if (item<0)
588 wxWindow::Enable(enable);
589 else if (item < m_noItems)
590 ::EnableWindow((HWND) m_radioButtons[item], enable);
591 }
592
593 // Enable all controls
594 void wxRadioBox::Enable(bool enable)
595 {
596 wxControl::Enable(enable);
597
598 int i;
599 for (i = 0; i < m_noItems; i++)
600 ::EnableWindow((HWND) m_radioButtons[i], enable);
601 }
602
603 // Show a specific button
604 void wxRadioBox::Show(int item, bool show)
605 {
606 if (item<0)
607 wxRadioBox::Show(show);
608 else if (item < m_noItems)
609 {
610 int cshow;
611 if (show)
612 cshow = SW_SHOW;
613 else
614 cshow = SW_HIDE;
615 ShowWindow((HWND) m_radioButtons[item], cshow);
616 }
617 }
618
619 WXHBRUSH wxRadioBox::OnCtlColor(WXHDC pDC, WXHWND pWnd, WXUINT nCtlColor,
620 WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
621 {
622 #if wxUSE_CTL3D
623 if ( m_useCtl3D )
624 {
625 HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
626 return (WXHBRUSH) hbrush;
627 }
628 #endif
629
630 if (GetParent()->GetTransparentBackground())
631 SetBkMode((HDC) pDC, TRANSPARENT);
632 else
633 SetBkMode((HDC) pDC, OPAQUE);
634
635 ::SetBkColor((HDC) pDC, RGB(GetBackgroundColour().Red(), GetBackgroundColour().Green(), GetBackgroundColour().Blue()));
636 ::SetTextColor((HDC) pDC, RGB(GetForegroundColour().Red(), GetForegroundColour().Green(), GetForegroundColour().Blue()));
637
638 wxBrush *backgroundBrush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID);
639
640 // Note that this will be cleaned up in wxApp::OnIdle, if backgroundBrush
641 // has a zero usage count.
642 // backgroundBrush->RealizeResource();
643 return (WXHBRUSH) backgroundBrush->GetResourceHandle();
644 }
645
646 // For single selection items only
647 wxString wxRadioBox::GetStringSelection() const
648 {
649 wxString result;
650 int sel = GetSelection();
651 if (sel > -1)
652 result = GetString(sel);
653
654 return result;
655 }
656
657 bool wxRadioBox::SetStringSelection(const wxString& s)
658 {
659 int sel = FindString (s);
660 if (sel > -1)
661 {
662 SetSelection (sel);
663 return TRUE;
664 }
665 else
666 return FALSE;
667 }
668
669 bool wxRadioBox::ContainsHWND(WXHWND hWnd) const
670 {
671 int i;
672 for (i = 0; i < Number(); i++)
673 if (GetRadioButtons()[i] == hWnd)
674 return TRUE;
675 return FALSE;
676 }
677
678 void wxRadioBox::Command (wxCommandEvent & event)
679 {
680 SetSelection (event.m_commandInt);
681 ProcessCommand (event);
682 }
683
684 long wxRadioBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
685 {
686 if (nMsg == WM_NCHITTEST)
687 {
688 int xPos = LOWORD(lParam); // horizontal position of cursor
689 int yPos = HIWORD(lParam); // vertical position of cursor
690
691 ScreenToClient(&xPos, &yPos);
692
693 // Make sure you can drag by the top of the groupbox, but let
694 // other (enclosed) controls get mouse events also
695 if (yPos < 10)
696 return (long)HTCLIENT;
697 }
698
699 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
700 }
701
702 void wxRadioBox::SubclassRadioButton(WXHWND hWndBtn)
703 {
704 HWND hwndBtn = (HWND)hWndBtn;
705
706 if ( !s_wndprocRadioBtn )
707 s_wndprocRadioBtn = (WNDPROC)::GetWindowLong(hwndBtn, GWL_WNDPROC);
708
709 ::SetWindowLong(hwndBtn, GWL_WNDPROC, (long)wxRadioBtnWndProc);
710 ::SetWindowLong(hwndBtn, GWL_USERDATA, (long)this);
711 }
712
713 // ---------------------------------------------------------------------------
714 // window proc for radio buttons
715 // ---------------------------------------------------------------------------
716
717 LRESULT APIENTRY _EXPORT wxRadioBtnWndProc(HWND hwnd,
718 UINT msg,
719 WPARAM wParam,
720 LPARAM lParam)
721 {
722 bool processed = TRUE;
723 if ( msg != WM_KEYDOWN )
724 processed = FALSE;
725
726 if ( processed )
727 {
728 wxRadioBox *radiobox = (wxRadioBox *)::GetWindowLong(hwnd, GWL_USERDATA);
729
730 wxCHECK_MSG( radiobox, 0, "radio button without radio box?" );
731
732 int sel = radiobox->GetSelection();
733
734 switch ( wParam )
735 {
736 case VK_UP:
737 sel--;
738 break;
739
740 case VK_LEFT:
741 sel -= radiobox->GetNumVer();
742 break;
743
744 case VK_DOWN:
745 sel++;
746 break;
747
748 case VK_RIGHT:
749 sel += radiobox->GetNumVer();
750 break;
751
752 case VK_TAB:
753 {
754 wxNavigationKeyEvent event;
755 event.SetDirection(!(::GetKeyState(VK_SHIFT) & 0x100));
756 event.SetWindowChange(FALSE);
757 event.SetEventObject(radiobox);
758
759 if ( radiobox->GetEventHandler()->ProcessEvent(event) )
760 return 0;
761 }
762 // fall through
763
764 default:
765 processed = FALSE;
766 }
767
768 if ( processed )
769 {
770 if ( sel >= 0 && sel < radiobox->Number() )
771 radiobox->SetSelection(sel);
772 }
773 }
774
775 if ( !processed )
776 return ::CallWindowProc(s_wndprocRadioBtn, hwnd, msg, wParam, lParam);
777 else
778 return 0;
779 }
780