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         CocoaSetLabelForObject(choices[i], currCell);
 
 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     CocoaSetLabelForObject(title, GetNSBox());
 
 166 //    [GetNSBox() setBorderType:NSLineBorder]; // why??
 
 168     SetMajorDim(majorDim, style);
 
 170     // Set the selection to the first item if we have any items.
 
 171     // This is for parity with other wx ports which do the same thing.
 
 176         m_parent->CocoaAddChild(this);
 
 178     // Do the sizer dance
 
 179     [GetNSBox() sizeToFit];
 
 180     SetInitialFrameRect(pos, size);
 
 185 wxRadioBox::~wxRadioBox()
 
 187     DisassociateNSBox(GetNSBox());
 
 190 WX_NSMatrix wxRadioBox::GetNSMatrix() const
 
 192     return (NSMatrix*)[(NSBox*)m_cocoaNSView contentView];
 
 196 void wxRadioBox::SetSelection(int n)
 
 198     int r = GetRowForIndex(n);
 
 199     int c = GetColumnForIndex(n);
 
 200     [GetNSMatrix() selectCellAtRow:r column:c];
 
 203 int wxRadioBox::GetSelection() const
 
 205     NSMatrix *radioBox = GetNSMatrix();
 
 206     NSInteger r = [radioBox selectedRow];
 
 207     NSInteger c = [radioBox selectedColumn];
 
 208     if(m_windowStyle & wxRA_SPECIFY_COLS)
 
 209         return r * GetMajorDim() + c;
 
 211         return c * GetMajorDim() + r;
 
 215 unsigned int wxRadioBox::GetCount() const
 
 217     NSMatrix *radioBox = GetNSMatrix();
 
 218     NSInteger rowCount, columnCount;
 
 219     [radioBox getNumberOfRows:&rowCount columns:&columnCount];
 
 221     // FIXME: This is wrong if padding cells were made
 
 222     return rowCount * columnCount;
 
 225 wxString wxRadioBox::GetString(unsigned int n) const
 
 227     int r = GetRowForIndex(n);
 
 228     int c = GetColumnForIndex(n);
 
 229     // FIXME: Cocoa stores the mnemonic-stripped title.
 
 230     return wxStringWithNSString([[GetNSMatrix() cellAtRow:r column:c] title]);
 
 233 void wxRadioBox::SetString(unsigned int n, const wxString& label)
 
 235     int r = GetRowForIndex(n);
 
 236     int c = GetColumnForIndex(n);
 
 237     CocoaSetLabelForObject(label, [GetNSMatrix() cellAtRow:r column:c]);
 
 240     // change the individual radio button state
 
 241 bool wxRadioBox::Enable(unsigned int n, bool enable)
 
 243     int r = GetRowForIndex(n);
 
 244     int c = GetColumnForIndex(n);
 
 245     NSCell *cell = [GetNSMatrix() cellAtRow:r column:c];
 
 248     bool wasEnabled = [cell isEnabled];
 
 249     [cell setEnabled:enable];
 
 250     return (wasEnabled && !enable) || (!wasEnabled && enable);
 
 253 bool wxRadioBox::Show(unsigned int n, bool show)
 
 256     // NOTE: Cocoa has no visible state for cells so we'd need to replace the
 
 257     // cell with a dummy one to hide it or alternatively subclass NSButtonCell
 
 258     // and add the behavior.
 
 262 wxSize wxRadioBox::DoGetBestSize() const
 
 264     // The NSBox responds to sizeToFit by sending sizeToFit to its contentView
 
 265     // which is the NSMatrix and does the right thing.
 
 266     return wxControl::DoGetBestSize();
 
 269 void wxRadioBox::CocoaTarget_action(void)
 
 271     wxCommandEvent event(wxEVT_COMMAND_RADIOBOX_SELECTED, GetId());
 
 272     InitCommandEvent(event);
 
 273     event.SetInt(GetSelection()); // i.e. SetSelection.