1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/cocoa/listbox.mm
4 // Author: David Elliott
8 // Copyright: (c) 2003 David Elliott
9 // Licence: wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
16 #include "wx/listbox.h"
23 #include "wx/cocoa/string.h"
24 #include "wx/cocoa/autorelease.h"
25 #include "wx/cocoa/ObjcRef.h"
26 #include "wx/cocoa/private/scrollview.h"
27 #include "wx/cocoa/NSTableDataSource.h"
29 #import <Foundation/NSArray.h>
30 #import <Foundation/NSEnumerator.h>
31 #import <AppKit/NSTableView.h>
32 #import <AppKit/NSTableColumn.h>
33 #import <AppKit/NSScrollView.h>
34 #import <AppKit/NSCell.h>
36 // ============================================================================
37 // @class wxCocoaListBoxNSTableDataSource
38 // ============================================================================
39 // 2.8 hack: We can't add an i-var to wxListBox so we add one here
40 @interface wxCocoaListBoxNSTableDataSource : wxCocoaNSTableDataSource
46 WX_DECLARE_GET_OBJC_CLASS(wxCocoaListBoxNSTableDataSource,wxCocoaNSTableDataSource)
48 @implementation wxCocoaListBoxNSTableDataSource
51 WX_IMPLEMENT_GET_OBJC_CLASS_WITH_UNIQUIFIED_SUPERCLASS(wxCocoaListBoxNSTableDataSource,wxCocoaNSTableDataSource)
54 // ============================================================================
56 // ============================================================================
58 static CGFloat _TableColumnMaxWidthForItems(NSTableColumn *tableColumn, NSArray *items)
60 wxAutoNSAutoreleasePool pool;
62 NSCell *dataCell = [[[tableColumn dataCell] copy] autorelease];
64 NSEnumerator *itemEnum = [items objectEnumerator];
66 while( (item = [itemEnum nextObject]) != nil )
68 [dataCell setStringValue: item];
69 NSSize itemSize = [dataCell cellSize];
70 CGFloat itemWidth = itemSize.width;
77 static void _SetWidthOfTableColumnToFitItems(NSTableColumn *tableColumn, NSArray *items)
79 CGFloat width = _TableColumnMaxWidthForItems(tableColumn, items);
80 [tableColumn setWidth:width];
81 [tableColumn setMinWidth:width];
84 // ============================================================================
86 // ============================================================================
88 IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControlWithItems)
89 BEGIN_EVENT_TABLE(wxListBox, wxListBoxBase)
90 EVT_IDLE(wxListBox::_WxCocoa_OnIdle)
92 WX_IMPLEMENT_COCOA_OWNER(wxListBox,NSTableView,NSControl,NSView)
94 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
97 const wxArrayString& choices,
99 const wxValidator& validator,
100 const wxString& name)
102 wxCArrayString chs(choices);
104 return Create(parent, winid, pos, size, chs.GetCount(), chs.GetStrings(),
105 style, validator, name);
108 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
111 int n, const wxString choices[],
113 const wxValidator& validator,
114 const wxString& name)
118 Single-selection list.
121 Multiple-selection list: the user can toggle multiple items on and off.
124 Extended-selection list: the user can select multiple items using the SHIFT key and the mouse or special key combinations.
127 Create horizontal scrollbar if contents are too wide (Windows only).
130 Always show a vertical scrollbar.
133 Only create a vertical scrollbar if needed.
136 The listbox contents are sorted in alphabetical order.
138 wxAutoNSAutoreleasePool pool;
139 if(!CreateControl(parent,winid,pos,size,style,validator,name))
143 m_cocoaItems = wxGCSafeRetain([NSMutableArray arrayWithCapacity:n]);
144 for(int i=0; i < n; i++)
146 [m_cocoaItems addObject: wxNSStringWithWxString(choices[i])];
149 m_itemClientData.Clear();
150 // Initialize n elements to NULL
151 m_itemClientData.SetCount(n,NULL);
153 SetNSTableView([[NSTableView alloc] initWithFrame: MakeDefaultNSRect(size)]);
154 [m_cocoaNSView release];
155 [GetNSTableView() setHeaderView: nil];
157 // Set up the data source
158 m_cocoaDataSource = [[WX_GET_OBJC_CLASS(wxCocoaListBoxNSTableDataSource) alloc] init];
159 [GetNSTableView() setDataSource:m_cocoaDataSource];
161 // Add the single column
162 NSTableColumn *tableColumn = [[NSTableColumn alloc] initWithIdentifier:nil];
163 [GetNSTableView() addTableColumn: tableColumn];
164 [tableColumn release];
166 [GetNSTableView() sizeToFit];
169 m_parent->CocoaAddChild(this);
170 // NSTableView does WEIRD things with sizes. Wrapping it in an
171 // NSScrollView seems to be the only reasonable solution.
172 CocoaCreateNSScrollView();
173 SetInitialFrameRect(pos,size);
175 [m_wxCocoaScrollView->GetNSScrollView() setHasVerticalScroller:YES];
176 // Pre-10.3: Always show vertical scroller, never show horizontal scroller
177 // Post-10.3: Show scrollers dynamically (turn them both on, set auto-hide)
178 if([m_wxCocoaScrollView->GetNSScrollView() respondsToSelector:@selector(setAutohidesScrollers:)])
180 [m_wxCocoaScrollView->GetNSScrollView() setHasHorizontalScroller:YES];
181 [m_wxCocoaScrollView->GetNSScrollView() setAutohidesScrollers:YES];
184 // Set up extended/multiple selection flags
185 if ((style & wxLB_EXTENDED) || (style & wxLB_MULTIPLE))
186 //diff is that mult requires shift down for multi selection
187 [GetNSTableView() setAllowsMultipleSelection:true];
189 [GetNSTableView() setAllowsColumnSelection:false];
190 _SetWidthOfTableColumnToFitItems(tableColumn, m_cocoaItems);
194 wxListBox::~wxListBox()
196 [GetNSTableView() setDataSource: nil];
197 [m_cocoaDataSource release];
198 wxGCSafeRelease(m_cocoaItems);
200 DisassociateNSTableView(GetNSTableView());
203 bool wxListBox::_WxCocoa_GetNeedsUpdate()
205 return static_cast<wxCocoaListBoxNSTableDataSource*>(m_cocoaDataSource)->m_needsUpdate;
208 void wxListBox::_WxCocoa_SetNeedsUpdate(bool needsUpdate)
210 static_cast<wxCocoaListBoxNSTableDataSource*>(m_cocoaDataSource)->m_needsUpdate = needsUpdate;
213 void wxListBox::_WxCocoa_OnIdle(wxIdleEvent &event)
216 if(_WxCocoa_GetNeedsUpdate())
218 _SetWidthOfTableColumnToFitItems([[GetNSTableView() tableColumns] objectAtIndex:0], m_cocoaItems);
219 [GetNSTableView() tile];
220 [GetNSTableView() reloadData];
221 _WxCocoa_SetNeedsUpdate(false);
225 int wxListBox::CocoaDataSource_numberOfRows()
227 return [m_cocoaItems count];
230 struct objc_object* wxListBox::CocoaDataSource_objectForTableColumn(
231 WX_NSTableColumn tableColumn, int rowIndex)
233 return [m_cocoaItems objectAtIndex:rowIndex];
236 // pure virtuals from wxListBoxBase
237 bool wxListBox::IsSelected(int n) const
239 return [GetNSTableView() isRowSelected: n];
242 void wxListBox::DoSetSelection(int n, bool select)
245 [GetNSTableView() selectRow: n byExtendingSelection:NO];
247 [GetNSTableView() deselectRow: n];
250 int wxListBox::GetSelections(wxArrayInt& aSelections) const
253 NSEnumerator *enumerator = [GetNSTableView() selectedRowEnumerator];
254 while(NSNumber *num = [enumerator nextObject])
256 aSelections.Add([num intValue]);
258 return [GetNSTableView() numberOfSelectedRows];
261 int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items, unsigned int pos, void **clientData, wxClientDataType type)
263 wxAutoNSAutoreleasePool pool;
265 const unsigned int numItems = items.GetCount();
266 for ( unsigned int i = 0; i < numItems; ++i, ++pos )
268 [m_cocoaItems insertObject: wxNSStringWithWxString(items[i])
270 m_itemClientData.Insert(NULL, pos);
271 AssignNewItemClientData(pos, clientData, i, type);
274 _WxCocoa_SetNeedsUpdate(true);
278 void wxListBox::DoSetFirstItem(int n)
280 [m_cocoaItems exchangeObjectAtIndex:0 withObjectAtIndex:n];
281 void* pOld = m_itemClientData[n];
282 m_itemClientData[n] = m_itemClientData[0];
283 m_itemClientData[0] = pOld;
284 _WxCocoa_SetNeedsUpdate(true);
288 // pure virtuals from wxItemContainer
290 void wxListBox::DoClear()
292 [m_cocoaItems removeAllObjects];
293 m_itemClientData.Clear();
294 _WxCocoa_SetNeedsUpdate(true);
297 void wxListBox::DoDeleteOneItem(unsigned int n)
299 [m_cocoaItems removeObjectAtIndex:n];
300 m_itemClientData.RemoveAt(n);
301 _WxCocoa_SetNeedsUpdate(true);
305 unsigned int wxListBox::GetCount() const
307 return (unsigned int)[m_cocoaItems count];
310 wxString wxListBox::GetString(unsigned int n) const
312 return wxStringWithNSString([m_cocoaItems objectAtIndex:n]);
315 void wxListBox::SetString(unsigned int n, const wxString& s)
317 wxAutoNSAutoreleasePool pool;
318 [m_cocoaItems removeObjectAtIndex:n];
319 [m_cocoaItems insertObject: wxNSStringWithWxString(s) atIndex: n];
320 _WxCocoa_SetNeedsUpdate(true);
323 int wxListBox::FindString(const wxString& s, bool bCase) const
325 // FIXME: use wxItemContainerImmutable::FindString for bCase parameter
326 wxAutoNSAutoreleasePool pool;
327 return [m_cocoaItems indexOfObject:wxNSStringWithWxString(s)];
331 int wxListBox::GetSelection() const
333 return [GetNSTableView() selectedRow];
336 void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
338 m_itemClientData[n] = clientData;
341 void* wxListBox::DoGetItemClientData(unsigned int n) const
343 return m_itemClientData[n];
346 #endif // wxUSE_LISTBOX