Added missing #import <Foundation/NSArray.h>
[wxWidgets.git] / src / cocoa / radiobox.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/cocoa/radiobox.mm
3 // Purpose:     wxRadioBox
4 // Author:      David Elliott
5 // Modified by:
6 // Created:     2003/02/15
7 // RCS-ID:      $Id$
8 // Copyright:   (c) 2003 David Elliott
9 //              (c) 2007 Software 2000 Ltd.
10 // Licence:     wxWidgets licence
11 /////////////////////////////////////////////////////////////////////////////
12
13 #include "wx/wxprec.h"
14
15 #if wxUSE_RADIOBOX
16
17 #include "wx/radiobox.h"
18
19 #ifndef WX_PRECOMP
20     #include "wx/app.h"
21     #include "wx/arrstr.h"
22 #endif //WX_PRECOMP
23
24 #include "wx/cocoa/string.h"
25 #include "wx/cocoa/autorelease.h"
26
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>
32
33 IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl)
34 BEGIN_EVENT_TABLE(wxRadioBox, wxControl)
35 END_EVENT_TABLE()
36
37 void wxRadioBox::AssociateNSBox(WX_NSBox cocoaObjcClass)
38 {
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:)];
45 }
46
47 void wxRadioBox::DisassociateNSBox(WX_NSBox cocoaObjcClass)
48 {
49     DisassociateNSControl([(WX_NSBox)cocoaObjcClass contentView]);
50 }
51
52 WX_IMPLEMENT_COCOA_OWNER(wxRadioBox,NSBox,NSView,NSView)
53
54 bool wxRadioBox::Create(wxWindow *parent, wxWindowID winid,
55             const wxString& title,
56             const wxPoint& pos,
57             const wxSize& size,
58             const wxArrayString& choices,
59             int majorDim,
60             long style, const wxValidator& validator,
61             const wxString& name)
62 {
63     wxCArrayString chs(choices);
64
65     return Create(parent, winid, title, pos, size, chs.GetCount(),
66                   chs.GetStrings(), majorDim, style, validator, name);
67 }
68
69 bool wxRadioBox::Create(wxWindow *parent, wxWindowID winid,
70             const wxString& title,
71             const wxPoint& pos,
72             const wxSize& size,
73             int n, const wxString choices[],
74             int majorDim,
75             long style, const wxValidator& validator,
76             const wxString& name)
77 {
78     // We autorelease heavily so we want our own pool
79     wxAutoNSAutoreleasePool pool;
80
81     if(!CreateControl(parent,winid,pos,size,style,validator,name))
82         return false;
83
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;
89
90
91     // Create a prototype cell for use with the NSMatrix build
92     NSCell *currCell = [[NSButtonCell alloc] initTextCell:@""];
93     [(NSButtonCell*)currCell setButtonType:NSRadioButton];
94
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)
98     {
99         [currCell setTitle: wxNSStringWithWxString(wxStripMenuCodes(choices[i], wxStrip_Mnemonics))];
100         [allCells addObject: currCell];
101         [currCell release];
102         // NOTE: We can still safely message currCell as the array has retained it.
103         currCell = [currCell copy];
104     }
105     [currCell release];
106
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)
114     {
115         [allCells addObject: currCell];
116         // NOTE: Use the same instance.. this should work and save some heap allocations.
117 #if 0
118         [currCell release];
119         currCell = [currCell copy];
120 #endif
121     }
122     [currCell release];
123     currCell = NULL;
124
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
134                 cellClass:nil
135                 numberOfRows:style&wxRA_SPECIFY_COLS?0:majorDim
136                 numberOfColumns:style&wxRA_SPECIFY_COLS?majorDim:0
137         ];
138
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
144     else
145         addMajorWithCellsSelector = @selector(addColumnWithCells:);
146
147     for(int i=0; i<minorDim; ++i)
148     {
149         [radioBox
150             performSelector:addMajorWithCellsSelector
151             withObject:[allCells subarrayWithRange:NSMakeRange(i*majorDim, majorDim)]];
152     }
153
154     NSBox *theBox = [[NSBox alloc] initWithFrame:MakeDefaultNSRect(size)];
155
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.
160
161     SetNSBox(theBox);
162     [theBox release];
163
164
165     [GetNSBox() setTitle:wxNSStringWithWxString(wxStripMenuCodes(title, wxStrip_Mnemonics))];
166 //    [GetNSBox() setBorderType:NSLineBorder]; // why??
167
168     SetMajorDim(majorDim, style);
169
170     if(m_parent)
171         m_parent->CocoaAddChild(this);
172
173     // Do the sizer dance
174     [GetNSBox() sizeToFit];
175     SetInitialFrameRect(pos, size);
176
177     return true;
178 }
179
180 wxRadioBox::~wxRadioBox()
181 {
182     DisassociateNSBox(GetNSBox());
183 }
184
185 WX_NSMatrix wxRadioBox::GetNSMatrix() const
186 {
187     return (NSMatrix*)[(NSBox*)m_cocoaNSView contentView];
188 }
189
190     // selection
191 void wxRadioBox::SetSelection(int n)
192 {
193     int r = GetRowForIndex(n);
194     int c = GetColumnForIndex(n);
195     [GetNSMatrix() selectCellAtRow:r column:c];
196 }
197
198 int wxRadioBox::GetSelection() const
199 {
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;
205     else
206         return c * GetMajorDim() + r;
207 }
208
209     // string access
210 unsigned int wxRadioBox::GetCount() const
211 {
212     NSMatrix *radioBox = GetNSMatrix();
213     NSInteger rowCount, columnCount;
214     [radioBox getNumberOfRows:&rowCount columns:&columnCount];
215
216     // FIXME: This is wrong if padding cells were made
217     return rowCount * columnCount;
218 }
219
220 wxString wxRadioBox::GetString(unsigned int n) const
221 {
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]);
226 }
227
228 void wxRadioBox::SetString(unsigned int n, const wxString& label)
229 {
230     int r = GetRowForIndex(n);
231     int c = GetColumnForIndex(n);
232     [[GetNSMatrix() cellAtRow:r column:c] setTitle:wxNSStringWithWxString(wxStripMenuCodes(label, wxStrip_Mnemonics))];
233 }
234
235     // change the individual radio button state
236 bool wxRadioBox::Enable(unsigned int n, bool enable)
237 {
238     int r = GetRowForIndex(n);
239     int c = GetColumnForIndex(n);
240     NSCell *cell = [GetNSMatrix() cellAtRow:r column:c];
241     if(cell == nil)
242         return false;
243     bool wasEnabled = [cell isEnabled];
244     [cell setEnabled:enable];
245     return (wasEnabled && !enable) || (!wasEnabled && enable);
246 }
247
248 bool wxRadioBox::Show(unsigned int n, bool show)
249 {
250     // TODO
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.
254     return false;
255 }
256
257 wxSize wxRadioBox::DoGetBestSize() const
258 {
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();
262 }
263
264 void wxRadioBox::CocoaTarget_action(void)
265 {
266     wxCommandEvent event(wxEVT_COMMAND_RADIOBOX_SELECTED, GetId());
267     InitCommandEvent(event);
268     event.SetInt(GetSelection()); // i.e. SetSelection.
269     Command(event);
270 }
271
272 #endif