1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/cocoa/listbox.mm
4 // Author: David Elliott
8 // Copyright: (c) 2003 David Elliott
9 // Licence: wxWindows 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 BEGIN_EVENT_TABLE(wxListBox, wxListBoxBase)
73 WX_IMPLEMENT_COCOA_OWNER(wxListBox,NSTableView,NSControl,NSView)
75 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
78 const wxArrayString& choices,
80 const wxValidator& validator,
83 wxCArrayString chs(choices);
85 return Create(parent, winid, pos, size, chs.GetCount(), chs.GetStrings(),
86 style, validator, name);
89 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
92 int n, const wxString choices[],
94 const wxValidator& validator,
99 Single-selection list.
102 Multiple-selection list: the user can toggle multiple items on and off.
105 Extended-selection list: the user can select multiple items using the SHIFT key and the mouse or special key combinations.
108 Create horizontal scrollbar if contents are too wide (Windows only).
111 Always show a vertical scrollbar.
114 Only create a vertical scrollbar if needed.
117 The listbox contents are sorted in alphabetical order.
119 wxAutoNSAutoreleasePool pool;
120 if(!CreateControl(parent,winid,pos,size,style,validator,name))
124 m_cocoaItems = wxGCSafeRetain([NSMutableArray arrayWithCapacity:n]);
125 for(int i=0; i < n; i++)
127 [m_cocoaItems addObject: wxNSStringWithWxString(choices[i])];
130 m_itemClientData.Clear();
131 // Initialize n elements to NULL
132 m_itemClientData.SetCount(n,NULL);
134 SetNSTableView([[NSTableView alloc] initWithFrame: MakeDefaultNSRect(size)]);
135 [m_cocoaNSView release];
136 [GetNSTableView() setHeaderView: nil];
138 // Set up the data source
139 m_cocoaDataSource = [[WX_GET_OBJC_CLASS(wxCocoaNSTableDataSource) alloc] init];
140 [GetNSTableView() setDataSource:m_cocoaDataSource];
142 // Add the single column
143 NSTableColumn *tableColumn = [[NSTableColumn alloc] initWithIdentifier:nil];
144 [GetNSTableView() addTableColumn: tableColumn];
145 // By default, entries should not be editable
146 [tableColumn setEditable:NO];
147 [tableColumn release];
149 [GetNSTableView() sizeToFit];
152 m_parent->CocoaAddChild(this);
153 // NSTableView does WEIRD things with sizes. Wrapping it in an
154 // NSScrollView seems to be the only reasonable solution.
155 CocoaCreateNSScrollView();
156 SetInitialFrameRect(pos,size);
158 if ((style & wxLB_NEEDED_SB) || (style & wxLB_ALWAYS_SB))
160 [m_wxCocoaScrollView->GetNSScrollView() setHasVerticalScroller: YES];
163 if (style & wxLB_HSCROLL)
165 [m_wxCocoaScrollView->GetNSScrollView() setHasHorizontalScroller: YES];
168 // We can't set auto-hiding individually for horizontal/vertical scrollers,
169 // so we have settled on always allowing hiding for both unless the vertical
170 // setting is "always show".
171 if (((style & wxLB_NEEDED_SB) || (style & wxLB_HSCROLL)) && !(style & wxLB_ALWAYS_SB))
173 if ([m_wxCocoaScrollView->GetNSScrollView() respondsToSelector:@selector(setAutohidesScrollers:)])
175 [m_wxCocoaScrollView->GetNSScrollView() setAutohidesScrollers: YES];
179 // Set up extended/multiple selection flags
180 if ((style & wxLB_EXTENDED) || (style & wxLB_MULTIPLE))
181 //diff is that mult requires shift down for multi selection
182 [GetNSTableView() setAllowsMultipleSelection:true];
184 [GetNSTableView() setAllowsColumnSelection:false];
185 _SetWidthOfTableColumnToFitItems(tableColumn, m_cocoaItems);
189 wxSize wxListBox::DoGetBestSize() const
191 wxSize size = wxControlWithItems::DoGetBestSize();
192 // Limit best size to 100x100. It can be smaller if none of the items are very
193 // wide or if there aren't many items, but anything bigger than 100x100 ought
194 // to be asked for by the programmer. The 100x100 size is based on being barely
195 // enough for a scroller to be usable.
196 if(size.GetWidth() > 100)
198 if(size.GetHeight() > 100)
203 wxListBox::~wxListBox()
205 [GetNSTableView() setDataSource: nil];
206 [m_cocoaDataSource release];
207 wxGCSafeRelease(m_cocoaItems);
209 DisassociateNSTableView(GetNSTableView());
212 bool wxListBox::_WxCocoa_GetNeedsUpdate()
214 return m_needsUpdate;
217 void wxListBox::_WxCocoa_SetNeedsUpdate(bool needsUpdate)
219 m_needsUpdate = needsUpdate;
222 void wxListBox::OnInternalIdle()
224 wxControlWithItems::OnInternalIdle();
225 if(_WxCocoa_GetNeedsUpdate())
227 _SetWidthOfTableColumnToFitItems([[GetNSTableView() tableColumns] objectAtIndex:0], m_cocoaItems);
228 [GetNSTableView() tile];
229 [GetNSTableView() reloadData];
230 _WxCocoa_SetNeedsUpdate(false);
234 int wxListBox::CocoaDataSource_numberOfRows()
236 return [m_cocoaItems count];
239 struct objc_object* wxListBox::CocoaDataSource_objectForTableColumn(
240 WX_NSTableColumn tableColumn, int rowIndex)
242 return [m_cocoaItems objectAtIndex:rowIndex];
245 // pure virtuals from wxListBoxBase
246 bool wxListBox::IsSelected(int n) const
248 return [GetNSTableView() isRowSelected: n];
251 void wxListBox::DoSetSelection(int n, bool select)
254 [GetNSTableView() selectRow: n byExtendingSelection:NO];
256 [GetNSTableView() deselectRow: n];
259 int wxListBox::GetSelections(wxArrayInt& aSelections) const
262 NSEnumerator *enumerator = [GetNSTableView() selectedRowEnumerator];
263 while(NSNumber *num = [enumerator nextObject])
265 aSelections.Add([num intValue]);
267 return [GetNSTableView() numberOfSelectedRows];
270 int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items, unsigned int pos, void **clientData, wxClientDataType type)
272 wxAutoNSAutoreleasePool pool;
274 const unsigned int numItems = items.GetCount();
275 for ( unsigned int i = 0; i < numItems; ++i, ++pos )
277 [m_cocoaItems insertObject: wxNSStringWithWxString(items[i])
279 m_itemClientData.Insert(NULL, pos);
280 AssignNewItemClientData(pos, clientData, i, type);
283 _WxCocoa_SetNeedsUpdate(true);
287 void wxListBox::DoSetFirstItem(int n)
289 [m_cocoaItems exchangeObjectAtIndex:0 withObjectAtIndex:n];
290 void* pOld = m_itemClientData[n];
291 m_itemClientData[n] = m_itemClientData[0];
292 m_itemClientData[0] = pOld;
293 _WxCocoa_SetNeedsUpdate(true);
297 // pure virtuals from wxItemContainer
299 void wxListBox::DoClear()
301 [m_cocoaItems removeAllObjects];
302 m_itemClientData.Clear();
303 _WxCocoa_SetNeedsUpdate(true);
306 void wxListBox::DoDeleteOneItem(unsigned int n)
308 [m_cocoaItems removeObjectAtIndex:n];
309 m_itemClientData.RemoveAt(n);
310 _WxCocoa_SetNeedsUpdate(true);
314 unsigned int wxListBox::GetCount() const
316 return (unsigned int)[m_cocoaItems count];
319 wxString wxListBox::GetString(unsigned int n) const
321 return wxStringWithNSString([m_cocoaItems objectAtIndex:n]);
324 void wxListBox::SetString(unsigned int n, const wxString& s)
326 wxAutoNSAutoreleasePool pool;
327 [m_cocoaItems removeObjectAtIndex:n];
328 [m_cocoaItems insertObject: wxNSStringWithWxString(s) atIndex: n];
329 _WxCocoa_SetNeedsUpdate(true);
332 int wxListBox::FindString(const wxString& s, bool bCase) const
334 // FIXME: use wxItemContainerImmutable::FindString for bCase parameter
335 wxAutoNSAutoreleasePool pool;
336 return [m_cocoaItems indexOfObject:wxNSStringWithWxString(s)];
340 int wxListBox::GetSelection() const
342 return [GetNSTableView() selectedRow];
345 void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
347 m_itemClientData[n] = clientData;
350 void* wxListBox::DoGetItemClientData(unsigned int n) const
352 return m_itemClientData[n];
355 #endif // wxUSE_LISTBOX