1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/cocoa/radiobox.mm
4 // Author: David Elliott
7 // Copyright: (c) 2003 David Elliott
8 // (c) 2007 Software 2000 Ltd.
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
16 #include "wx/radiobox.h"
20 #include "wx/arrstr.h"
23 #include "wx/cocoa/string.h"
24 #include "wx/cocoa/autorelease.h"
26 #import <Foundation/NSArray.h>
27 #include "wx/cocoa/objc/NSView.h"
28 #import <AppKit/NSButton.h>
29 #import <AppKit/NSBox.h>
30 #import <AppKit/NSMatrix.h>
32 IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl)
33 BEGIN_EVENT_TABLE(wxRadioBox, wxControl)
36 void wxRadioBox::AssociateNSBox(WX_NSBox cocoaObjcClass)
38 NSMatrix *radioBox = [(WX_NSBox)cocoaObjcClass contentView];
39 // Associate the NSMatrix (the NSBox's contentView) with the wxCocoaNSControl MI base class.
40 AssociateNSControl(radioBox);
41 // Set the target/action.. we don't really need to unset these
42 [radioBox setTarget:wxCocoaNSControl::sm_cocoaTarget];
43 [radioBox setAction:@selector(wxNSControlAction:)];
46 void wxRadioBox::DisassociateNSBox(WX_NSBox cocoaObjcClass)
48 DisassociateNSControl([(WX_NSBox)cocoaObjcClass contentView]);
51 WX_IMPLEMENT_COCOA_OWNER(wxRadioBox,NSBox,NSView,NSView)
53 bool wxRadioBox::Create(wxWindow *parent, wxWindowID winid,
54 const wxString& title,
57 const wxArrayString& choices,
59 long style, const wxValidator& validator,
62 wxCArrayString chs(choices);
64 return Create(parent, winid, title, pos, size, chs.GetCount(),
65 chs.GetStrings(), majorDim, style, validator, name);
68 bool wxRadioBox::Create(wxWindow *parent, wxWindowID winid,
69 const wxString& title,
72 int n, const wxString choices[],
74 long style, const wxValidator& validator,
77 // We autorelease heavily so we want our own pool
78 wxAutoNSAutoreleasePool pool;
80 if(!CreateControl(parent,winid,pos,size,style,validator,name))
83 majorDim = majorDim == 0 ? n : majorDim;
84 // TODO: Don't forget to call SetMajorDim
85 // We can't yet as we can't implement GetCount() until after
86 // we make the NSMatrix.
87 int minorDim = (n + majorDim - 1) / majorDim;
90 // Create a prototype cell for use with the NSMatrix build
91 NSCell *currCell = [[NSButtonCell alloc] initTextCell:@""];
92 [(NSButtonCell*)currCell setButtonType:NSRadioButton];
94 // Build up an array of all cells plus any extra empty cells
95 NSMutableArray *allCells = [NSMutableArray arrayWithCapacity:n];
96 for(int i=0; i<n; ++i)
98 CocoaSetLabelForObject(choices[i], currCell);
99 [allCells addObject: currCell];
101 // NOTE: We can still safely message currCell as the array has retained it.
102 currCell = [currCell copy];
106 // NOTE: Although an image cell with no image is documented to return NSZeroSize from
107 // the cellSize method, the documentation is WRONG. It will actually return a huge size
108 // (thousands) which makes every cell in the matrix that big. Not good.
109 // Be safe and initialize a text cell with an empty string. That always works.
110 currCell = [[NSCell alloc] initTextCell:@""];
111 [currCell setEnabled:NO]; // Don't allow user to select this cell
112 for(int i=n; i < majorDim * minorDim; ++i)
114 [allCells addObject: currCell];
115 // NOTE: Use the same instance.. this should work and save some heap allocations.
118 currCell = [currCell copy];
124 // Although the documentation on addColumnWithCells:/addRowWithCells: explicitly
125 // states that it will determine the initial dimension upon the first call if
126 // the initial size is 0x0 it LIES. It will fail an assertion in the code
127 // if you use the simpler initWithFrame: initializer.
128 // Therefore, we specify the major dimension and leave the minor dimension as 0
129 // so that we can add the rows/columns without failing the assertion.
130 NSMatrix* radioBox = [[NSMatrix alloc]
131 initWithFrame:NSZeroRect
132 mode:NSRadioModeMatrix
134 numberOfRows:style&wxRA_SPECIFY_COLS?0:majorDim
135 numberOfColumns:style&wxRA_SPECIFY_COLS?majorDim:0
138 SEL addMajorWithCellsSelector;
139 // If column count is the major dimension then we add by row
140 if( style & wxRA_SPECIFY_COLS )
141 addMajorWithCellsSelector = @selector(addRowWithCells:);
142 // If row count is the major dimension then we add by column
144 addMajorWithCellsSelector = @selector(addColumnWithCells:);
146 for(int i=0; i<minorDim; ++i)
149 performSelector:addMajorWithCellsSelector
150 withObject:[allCells subarrayWithRange:NSMakeRange(i*majorDim, majorDim)]];
153 NSBox *theBox = [[NSBox alloc] initWithFrame:MakeDefaultNSRect(size)];
155 // Replace the box's content view with the NSMatrix we just created
156 // IMPORTANT: This must be done before calling SetNSBox.
157 [theBox setContentView:radioBox];
158 [radioBox release]; // The NSBox retains it for us.
164 CocoaSetLabelForObject(title, GetNSBox());
165 // [GetNSBox() setBorderType:NSLineBorder]; // why??
167 SetMajorDim(majorDim, style);
169 // Set the selection to the first item if we have any items.
170 // This is for parity with other wx ports which do the same thing.
175 m_parent->CocoaAddChild(this);
177 // Do the sizer dance
178 [GetNSBox() sizeToFit];
179 SetInitialFrameRect(pos, size);
184 wxRadioBox::~wxRadioBox()
186 DisassociateNSBox(GetNSBox());
189 WX_NSMatrix wxRadioBox::GetNSMatrix() const
191 return (NSMatrix*)[(NSBox*)m_cocoaNSView contentView];
195 void wxRadioBox::SetSelection(int n)
197 int r = GetRowForIndex(n);
198 int c = GetColumnForIndex(n);
199 [GetNSMatrix() selectCellAtRow:r column:c];
202 int wxRadioBox::GetSelection() const
204 NSMatrix *radioBox = GetNSMatrix();
205 NSInteger r = [radioBox selectedRow];
206 NSInteger c = [radioBox selectedColumn];
207 if(m_windowStyle & wxRA_SPECIFY_COLS)
208 return r * GetMajorDim() + c;
210 return c * GetMajorDim() + r;
214 unsigned int wxRadioBox::GetCount() const
216 NSMatrix *radioBox = GetNSMatrix();
217 NSInteger rowCount, columnCount;
218 [radioBox getNumberOfRows:&rowCount columns:&columnCount];
220 // FIXME: This is wrong if padding cells were made
221 return rowCount * columnCount;
224 wxString wxRadioBox::GetString(unsigned int n) const
226 int r = GetRowForIndex(n);
227 int c = GetColumnForIndex(n);
228 // FIXME: Cocoa stores the mnemonic-stripped title.
229 return wxStringWithNSString([[GetNSMatrix() cellAtRow:r column:c] title]);
232 void wxRadioBox::SetString(unsigned int n, const wxString& label)
234 int r = GetRowForIndex(n);
235 int c = GetColumnForIndex(n);
236 CocoaSetLabelForObject(label, [GetNSMatrix() cellAtRow:r column:c]);
239 // change the individual radio button state
240 bool wxRadioBox::Enable(unsigned int n, bool enable)
242 int r = GetRowForIndex(n);
243 int c = GetColumnForIndex(n);
244 NSCell *cell = [GetNSMatrix() cellAtRow:r column:c];
247 bool wasEnabled = [cell isEnabled];
248 [cell setEnabled:enable];
249 return (wasEnabled && !enable) || (!wasEnabled && enable);
252 bool wxRadioBox::Show(unsigned int n, bool show)
255 // NOTE: Cocoa has no visible state for cells so we'd need to replace the
256 // cell with a dummy one to hide it or alternatively subclass NSButtonCell
257 // and add the behaviour.
261 wxSize wxRadioBox::DoGetBestSize() const
263 // The NSBox responds to sizeToFit by sending sizeToFit to its contentView
264 // which is the NSMatrix and does the right thing.
265 return wxControl::DoGetBestSize();
268 void wxRadioBox::CocoaTarget_action(void)
270 wxCommandEvent event(wxEVT_RADIOBOX, GetId());
271 InitCommandEvent(event);
272 event.SetInt(GetSelection()); // i.e. SetSelection.