1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/cocoa/radiobox.mm
4 // Author: David Elliott
8 // Copyright: (c) 2003 David Elliott
9 // (c) 2007 Software 2000 Ltd.
10 // Licence: wxWidgets licence
11 /////////////////////////////////////////////////////////////////////////////
13 #include "wx/wxprec.h"
17 #include "wx/radiobox.h"
21 #include "wx/arrstr.h"
24 #include "wx/cocoa/string.h"
25 #include "wx/cocoa/autorelease.h"
27 #import <Foundation/NSArray.h>
28 #include "wx/cocoa/objc/NSView.h"
29 #import <AppKit/NSButton.h>
30 #import <AppKit/NSBox.h>
31 #import <AppKit/NSMatrix.h>
33 IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl)
34 BEGIN_EVENT_TABLE(wxRadioBox, wxControl)
37 void wxRadioBox::AssociateNSBox(WX_NSBox cocoaObjcClass)
39 NSMatrix *radioBox = [(WX_NSBox)cocoaObjcClass contentView];
40 // Associate the NSMatrix (the NSBox's contentView) with the wxCocoaNSControl MI base class.
41 AssociateNSControl(radioBox);
42 // Set the target/action.. we don't really need to unset these
43 [radioBox setTarget:wxCocoaNSControl::sm_cocoaTarget];
44 [radioBox setAction:@selector(wxNSControlAction:)];
47 void wxRadioBox::DisassociateNSBox(WX_NSBox cocoaObjcClass)
49 DisassociateNSControl([(WX_NSBox)cocoaObjcClass contentView]);
52 WX_IMPLEMENT_COCOA_OWNER(wxRadioBox,NSBox,NSView,NSView)
54 bool wxRadioBox::Create(wxWindow *parent, wxWindowID winid,
55 const wxString& title,
58 const wxArrayString& choices,
60 long style, const wxValidator& validator,
63 wxCArrayString chs(choices);
65 return Create(parent, winid, title, pos, size, chs.GetCount(),
66 chs.GetStrings(), majorDim, style, validator, name);
69 bool wxRadioBox::Create(wxWindow *parent, wxWindowID winid,
70 const wxString& title,
73 int n, const wxString choices[],
75 long style, const wxValidator& validator,
78 // We autorelease heavily so we want our own pool
79 wxAutoNSAutoreleasePool pool;
81 if(!CreateControl(parent,winid,pos,size,style,validator,name))
84 majorDim = majorDim == 0 ? n : majorDim;
85 // TODO: Don't forget to call SetMajorDim
86 // We can't yet as we can't implement GetCount() until after
87 // we make the NSMatrix.
88 int minorDim = (n + majorDim - 1) / majorDim;
91 // Create a prototype cell for use with the NSMatrix build
92 NSCell *currCell = [[NSButtonCell alloc] initTextCell:@""];
93 [(NSButtonCell*)currCell setButtonType:NSRadioButton];
95 // Build up an array of all cells plus any extra empty cells
96 NSMutableArray *allCells = [NSMutableArray arrayWithCapacity:n];
97 for(int i=0; i<n; ++i)
99 [currCell setTitle: wxNSStringWithWxString(wxStripMenuCodes(choices[i], wxStrip_Mnemonics))];
100 [allCells addObject: currCell];
102 // NOTE: We can still safely message currCell as the array has retained it.
103 currCell = [currCell copy];
107 // NOTE: Although an image cell with no image is documented to return NSZeroSize from
108 // the cellSize method, the documentation is WRONG. It will actually return a huge size
109 // (thousands) which makes every cell in the matrix that big. Not good.
110 // Be safe and initialize a text cell with an empty string. That always works.
111 currCell = [[NSCell alloc] initTextCell:@""];
112 [currCell setEnabled:NO]; // Don't allow user to select this cell
113 for(int i=n; i < majorDim * minorDim; ++i)
115 [allCells addObject: currCell];
116 // NOTE: Use the same instance.. this should work and save some heap allocations.
119 currCell = [currCell copy];
125 // Although the documentation on addColumnWithCells:/addRowWithCells: explicitly
126 // states that it will determine the initial dimension upon the first call if
127 // the initial size is 0x0 it LIES. It will fail an assertion in the code
128 // if you use the simpler initWithFrame: initializer.
129 // Therefore, we specify the major dimension and leave the minor dimension as 0
130 // so that we can add the rows/columns without failing the assertion.
131 NSMatrix* radioBox = [[NSMatrix alloc]
132 initWithFrame:NSZeroRect
133 mode:NSRadioModeMatrix
135 numberOfRows:style&wxRA_SPECIFY_COLS?0:majorDim
136 numberOfColumns:style&wxRA_SPECIFY_COLS?majorDim:0
139 SEL addMajorWithCellsSelector;
140 // If column count is the major dimension then we add by row
141 if( style & wxRA_SPECIFY_COLS )
142 addMajorWithCellsSelector = @selector(addRowWithCells:);
143 // If row count is the major dimension then we add by column
145 addMajorWithCellsSelector = @selector(addColumnWithCells:);
147 for(int i=0; i<minorDim; ++i)
150 performSelector:addMajorWithCellsSelector
151 withObject:[allCells subarrayWithRange:NSMakeRange(i*majorDim, majorDim)]];
154 NSBox *theBox = [[NSBox alloc] initWithFrame:MakeDefaultNSRect(size)];
156 // Replace the box's content view with the NSMatrix we just created
157 // IMPORTANT: This must be done before calling SetNSBox.
158 [theBox setContentView:radioBox];
159 [radioBox release]; // The NSBox retains it for us.
165 [GetNSBox() setTitle:wxNSStringWithWxString(wxStripMenuCodes(title, wxStrip_Mnemonics))];
166 // [GetNSBox() setBorderType:NSLineBorder]; // why??
168 SetMajorDim(majorDim, style);
171 m_parent->CocoaAddChild(this);
173 // Do the sizer dance
174 [GetNSBox() sizeToFit];
175 SetInitialFrameRect(pos, size);
180 wxRadioBox::~wxRadioBox()
182 DisassociateNSBox(GetNSBox());
185 WX_NSMatrix wxRadioBox::GetNSMatrix() const
187 return (NSMatrix*)[(NSBox*)m_cocoaNSView contentView];
191 void wxRadioBox::SetSelection(int n)
193 int r = GetRowForIndex(n);
194 int c = GetColumnForIndex(n);
195 [GetNSMatrix() selectCellAtRow:r column:c];
198 int wxRadioBox::GetSelection() const
200 NSMatrix *radioBox = GetNSMatrix();
201 NSInteger r = [radioBox selectedRow];
202 NSInteger c = [radioBox selectedColumn];
203 if(m_windowStyle & wxRA_SPECIFY_COLS)
204 return r * GetMajorDim() + c;
206 return c * GetMajorDim() + r;
210 unsigned int wxRadioBox::GetCount() const
212 NSMatrix *radioBox = GetNSMatrix();
213 NSInteger rowCount, columnCount;
214 [radioBox getNumberOfRows:&rowCount columns:&columnCount];
216 // FIXME: This is wrong if padding cells were made
217 return rowCount * columnCount;
220 wxString wxRadioBox::GetString(unsigned int n) const
222 int r = GetRowForIndex(n);
223 int c = GetColumnForIndex(n);
224 // FIXME: Cocoa stores the mnemonic-stripped title.
225 return wxStringWithNSString([[GetNSMatrix() cellAtRow:r column:c] title]);
228 void wxRadioBox::SetString(unsigned int n, const wxString& label)
230 int r = GetRowForIndex(n);
231 int c = GetColumnForIndex(n);
232 [[GetNSMatrix() cellAtRow:r column:c] setTitle:wxNSStringWithWxString(wxStripMenuCodes(label, wxStrip_Mnemonics))];
235 // change the individual radio button state
236 bool wxRadioBox::Enable(unsigned int n, bool enable)
238 int r = GetRowForIndex(n);
239 int c = GetColumnForIndex(n);
240 NSCell *cell = [GetNSMatrix() cellAtRow:r column:c];
243 bool wasEnabled = [cell isEnabled];
244 [cell setEnabled:enable];
245 return (wasEnabled && !enable) || (!wasEnabled && enable);
248 bool wxRadioBox::Show(unsigned int n, bool show)
251 // NOTE: Cocoa has no visible state for cells so we'd need to replace the
252 // cell with a dummy one to hide it or alternatively subclass NSButtonCell
253 // and add the behavior.
257 wxSize wxRadioBox::DoGetBestSize() const
259 // The NSBox responds to sizeToFit by sending sizeToFit to its contentView
260 // which is the NSMatrix and does the right thing.
261 return wxControl::DoGetBestSize();
264 void wxRadioBox::CocoaTarget_action(void)
266 wxCommandEvent event(wxEVT_COMMAND_RADIOBOX_SELECTED, GetId());
267 InitCommandEvent(event);
268 event.SetInt(GetSelection()); // i.e. SetSelection.