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>
37 // ============================================================================
39 // ============================================================================
41 static CGFloat _TableColumnMaxWidthForItems(NSTableColumn *tableColumn, NSArray *items)
43 wxAutoNSAutoreleasePool pool;
45 NSCell *dataCell = [[[tableColumn dataCell] copy] autorelease];
47 NSEnumerator *itemEnum = [items objectEnumerator];
49 while( (item = [itemEnum nextObject]) != nil )
51 [dataCell setStringValue: item];
52 NSSize itemSize = [dataCell cellSize];
53 CGFloat itemWidth = itemSize.width;
60 static void _SetWidthOfTableColumnToFitItems(NSTableColumn *tableColumn, NSArray *items)
62 CGFloat width = _TableColumnMaxWidthForItems(tableColumn, items);
63 [tableColumn setWidth:width];
64 [tableColumn setMinWidth:width];
67 // ============================================================================
69 // ============================================================================
71 IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControlWithItems)
72 BEGIN_EVENT_TABLE(wxListBox, wxListBoxBase)
74 WX_IMPLEMENT_COCOA_OWNER(wxListBox,NSTableView,NSControl,NSView)
76 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
79 const wxArrayString& choices,
81 const wxValidator& validator,
84 wxCArrayString chs(choices);
86 return Create(parent, winid, pos, size, chs.GetCount(), chs.GetStrings(),
87 style, validator, name);
90 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
93 int n, const wxString choices[],
95 const wxValidator& validator,
100 Single-selection list.
103 Multiple-selection list: the user can toggle multiple items on and off.
106 Extended-selection list: the user can select multiple items using the SHIFT key and the mouse or special key combinations.
109 Create horizontal scrollbar if contents are too wide (Windows only).
112 Always show a vertical scrollbar.
115 Only create a vertical scrollbar if needed.
118 The listbox contents are sorted in alphabetical order.
120 wxAutoNSAutoreleasePool pool;
121 if(!CreateControl(parent,winid,pos,size,style,validator,name))
125 m_cocoaItems = wxGCSafeRetain([NSMutableArray arrayWithCapacity:n]);
126 for(int i=0; i < n; i++)
128 [m_cocoaItems addObject: wxNSStringWithWxString(choices[i])];
131 m_itemClientData.Clear();
132 // Initialize n elements to NULL
133 m_itemClientData.SetCount(n,NULL);
135 SetNSTableView([[NSTableView alloc] initWithFrame: MakeDefaultNSRect(size)]);
136 [m_cocoaNSView release];
137 [GetNSTableView() setHeaderView: nil];
139 // Set up the data source
140 m_cocoaDataSource = [[WX_GET_OBJC_CLASS(wxCocoaNSTableDataSource) alloc] init];
141 [GetNSTableView() setDataSource:m_cocoaDataSource];
143 // Add the single column
144 NSTableColumn *tableColumn = [[NSTableColumn alloc] initWithIdentifier:nil];
145 [GetNSTableView() addTableColumn: tableColumn];
146 [tableColumn release];
148 [GetNSTableView() sizeToFit];
151 m_parent->CocoaAddChild(this);
152 // NSTableView does WEIRD things with sizes. Wrapping it in an
153 // NSScrollView seems to be the only reasonable solution.
154 CocoaCreateNSScrollView();
155 SetInitialFrameRect(pos,size);
157 [m_wxCocoaScrollView->GetNSScrollView() setHasVerticalScroller:YES];
158 // Pre-10.3: Always show vertical scroller, never show horizontal scroller
159 // Post-10.3: Show scrollers dynamically (turn them both on, set auto-hide)
160 if([m_wxCocoaScrollView->GetNSScrollView() respondsToSelector:@selector(setAutohidesScrollers:)])
162 [m_wxCocoaScrollView->GetNSScrollView() setHasHorizontalScroller:YES];
163 [m_wxCocoaScrollView->GetNSScrollView() setAutohidesScrollers:YES];
166 // Set up extended/multiple selection flags
167 if ((style & wxLB_EXTENDED) || (style & wxLB_MULTIPLE))
168 //diff is that mult requires shift down for multi selection
169 [GetNSTableView() setAllowsMultipleSelection:true];
171 [GetNSTableView() setAllowsColumnSelection:false];
172 _SetWidthOfTableColumnToFitItems(tableColumn, m_cocoaItems);
176 wxSize wxListBox::DoGetBestSize() const
178 wxSize size = wxControlWithItems::DoGetBestSize();
179 // Limit best size to 100x100. It can be smaller if none of the items are very
180 // wide or if there aren't many items, but anything bigger than 100x100 ought
181 // to be asked for by the programmer. The 100x100 size is based on being barely
182 // enough for a scroller to be usable.
183 if(size.GetWidth() > 100)
185 if(size.GetHeight() > 100)
190 wxListBox::~wxListBox()
192 [GetNSTableView() setDataSource: nil];
193 [m_cocoaDataSource release];
194 wxGCSafeRelease(m_cocoaItems);
196 DisassociateNSTableView(GetNSTableView());
199 bool wxListBox::_WxCocoa_GetNeedsUpdate()
201 return m_needsUpdate;
204 void wxListBox::_WxCocoa_SetNeedsUpdate(bool needsUpdate)
206 m_needsUpdate = needsUpdate;
209 void wxListBox::OnInternalIdle()
211 wxControlWithItems::OnInternalIdle();
212 if(_WxCocoa_GetNeedsUpdate())
214 _SetWidthOfTableColumnToFitItems([[GetNSTableView() tableColumns] objectAtIndex:0], m_cocoaItems);
215 [GetNSTableView() tile];
216 [GetNSTableView() reloadData];
217 _WxCocoa_SetNeedsUpdate(false);
221 int wxListBox::CocoaDataSource_numberOfRows()
223 return [m_cocoaItems count];
226 struct objc_object* wxListBox::CocoaDataSource_objectForTableColumn(
227 WX_NSTableColumn tableColumn, int rowIndex)
229 return [m_cocoaItems objectAtIndex:rowIndex];
232 // pure virtuals from wxListBoxBase
233 bool wxListBox::IsSelected(int n) const
235 return [GetNSTableView() isRowSelected: n];
238 void wxListBox::DoSetSelection(int n, bool select)
241 [GetNSTableView() selectRow: n byExtendingSelection:NO];
243 [GetNSTableView() deselectRow: n];
246 int wxListBox::GetSelections(wxArrayInt& aSelections) const
249 NSEnumerator *enumerator = [GetNSTableView() selectedRowEnumerator];
250 while(NSNumber *num = [enumerator nextObject])
252 aSelections.Add([num intValue]);
254 return [GetNSTableView() numberOfSelectedRows];
257 int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items, unsigned int pos, void **clientData, wxClientDataType type)
259 wxAutoNSAutoreleasePool pool;
261 const unsigned int numItems = items.GetCount();
262 for ( unsigned int i = 0; i < numItems; ++i, ++pos )
264 [m_cocoaItems insertObject: wxNSStringWithWxString(items[i])
266 m_itemClientData.Insert(NULL, pos);
267 AssignNewItemClientData(pos, clientData, i, type);
270 _WxCocoa_SetNeedsUpdate(true);
274 void wxListBox::DoSetFirstItem(int n)
276 [m_cocoaItems exchangeObjectAtIndex:0 withObjectAtIndex:n];
277 void* pOld = m_itemClientData[n];
278 m_itemClientData[n] = m_itemClientData[0];
279 m_itemClientData[0] = pOld;
280 _WxCocoa_SetNeedsUpdate(true);
284 // pure virtuals from wxItemContainer
286 void wxListBox::DoClear()
288 [m_cocoaItems removeAllObjects];
289 m_itemClientData.Clear();
290 _WxCocoa_SetNeedsUpdate(true);
293 void wxListBox::DoDeleteOneItem(unsigned int n)
295 [m_cocoaItems removeObjectAtIndex:n];
296 m_itemClientData.RemoveAt(n);
297 _WxCocoa_SetNeedsUpdate(true);
301 unsigned int wxListBox::GetCount() const
303 return (unsigned int)[m_cocoaItems count];
306 wxString wxListBox::GetString(unsigned int n) const
308 return wxStringWithNSString([m_cocoaItems objectAtIndex:n]);
311 void wxListBox::SetString(unsigned int n, const wxString& s)
313 wxAutoNSAutoreleasePool pool;
314 [m_cocoaItems removeObjectAtIndex:n];
315 [m_cocoaItems insertObject: wxNSStringWithWxString(s) atIndex: n];
316 _WxCocoa_SetNeedsUpdate(true);
319 int wxListBox::FindString(const wxString& s, bool bCase) const
321 // FIXME: use wxItemContainerImmutable::FindString for bCase parameter
322 wxAutoNSAutoreleasePool pool;
323 return [m_cocoaItems indexOfObject:wxNSStringWithWxString(s)];
327 int wxListBox::GetSelection() const
329 return [GetNSTableView() selectedRow];
332 void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
334 m_itemClientData[n] = clientData;
337 void* wxListBox::DoGetItemClientData(unsigned int n) const
339 return m_itemClientData[n];
342 #endif // wxUSE_LISTBOX