1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/cocoa/listbox.mm
4 // Author: David Elliott
7 // Copyright: (c) 2003 David Elliott
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 #include "wx/wxprec.h"
15 #include "wx/listbox.h"
22 #include "wx/cocoa/string.h"
23 #include "wx/cocoa/autorelease.h"
24 #include "wx/cocoa/ObjcRef.h"
25 #include "wx/cocoa/private/scrollview.h"
26 #include "wx/cocoa/NSTableDataSource.h"
28 #import <Foundation/NSArray.h>
29 #import <Foundation/NSEnumerator.h>
30 #import <AppKit/NSTableView.h>
31 #import <AppKit/NSTableColumn.h>
32 #import <AppKit/NSScrollView.h>
33 #import <AppKit/NSCell.h>
36 // ============================================================================
38 // ============================================================================
40 static CGFloat _TableColumnMaxWidthForItems(NSTableColumn *tableColumn, NSArray *items)
42 wxAutoNSAutoreleasePool pool;
44 NSCell *dataCell = [[[tableColumn dataCell] copy] autorelease];
46 NSEnumerator *itemEnum = [items objectEnumerator];
48 while( (item = [itemEnum nextObject]) != nil )
50 [dataCell setStringValue: item];
51 NSSize itemSize = [dataCell cellSize];
52 CGFloat itemWidth = itemSize.width;
59 static void _SetWidthOfTableColumnToFitItems(NSTableColumn *tableColumn, NSArray *items)
61 CGFloat width = _TableColumnMaxWidthForItems(tableColumn, items);
62 [tableColumn setWidth:width];
63 [tableColumn setMinWidth:width];
66 // ============================================================================
68 // ============================================================================
70 BEGIN_EVENT_TABLE(wxListBox, wxListBoxBase)
72 WX_IMPLEMENT_COCOA_OWNER(wxListBox,NSTableView,NSControl,NSView)
74 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
77 const wxArrayString& choices,
79 const wxValidator& validator,
82 wxCArrayString chs(choices);
84 return Create(parent, winid, pos, size, chs.GetCount(), chs.GetStrings(),
85 style, validator, name);
88 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
91 int n, const wxString choices[],
93 const wxValidator& validator,
98 Single-selection list.
101 Multiple-selection list: the user can toggle multiple items on and off.
104 Extended-selection list: the user can select multiple items using the SHIFT key and the mouse or special key combinations.
107 Create horizontal scrollbar if contents are too wide (Windows only).
110 Always show a vertical scrollbar.
113 Only create a vertical scrollbar if needed.
116 The listbox contents are sorted in alphabetical order.
118 wxAutoNSAutoreleasePool pool;
119 if(!CreateControl(parent,winid,pos,size,style,validator,name))
123 m_cocoaItems = wxGCSafeRetain([NSMutableArray arrayWithCapacity:n]);
124 for(int i=0; i < n; i++)
126 [m_cocoaItems addObject: wxNSStringWithWxString(choices[i])];
129 m_itemClientData.Clear();
130 // Initialize n elements to NULL
131 m_itemClientData.SetCount(n,NULL);
133 SetNSTableView([[NSTableView alloc] initWithFrame: MakeDefaultNSRect(size)]);
134 [m_cocoaNSView release];
135 [GetNSTableView() setHeaderView: nil];
137 // Set up the data source
138 m_cocoaDataSource = [[WX_GET_OBJC_CLASS(wxCocoaNSTableDataSource) alloc] init];
139 [GetNSTableView() setDataSource:m_cocoaDataSource];
141 // Add the single column
142 NSTableColumn *tableColumn = [[NSTableColumn alloc] initWithIdentifier:nil];
143 [GetNSTableView() addTableColumn: tableColumn];
144 // By default, entries should not be editable
145 [tableColumn setEditable:NO];
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 if ((style & wxLB_NEEDED_SB) || (style & wxLB_ALWAYS_SB))
159 [m_wxCocoaScrollView->GetNSScrollView() setHasVerticalScroller: YES];
162 if (style & wxLB_HSCROLL)
164 [m_wxCocoaScrollView->GetNSScrollView() setHasHorizontalScroller: YES];
167 // We can't set auto-hiding individually for horizontal/vertical scrollers,
168 // so we have settled on always allowing hiding for both unless the vertical
169 // setting is "always show".
170 if (((style & wxLB_NEEDED_SB) || (style & wxLB_HSCROLL)) && !(style & wxLB_ALWAYS_SB))
172 if ([m_wxCocoaScrollView->GetNSScrollView() respondsToSelector:@selector(setAutohidesScrollers:)])
174 [m_wxCocoaScrollView->GetNSScrollView() setAutohidesScrollers: YES];
178 // Set up extended/multiple selection flags
179 if ((style & wxLB_EXTENDED) || (style & wxLB_MULTIPLE))
180 //diff is that mult requires shift down for multi selection
181 [GetNSTableView() setAllowsMultipleSelection:true];
183 [GetNSTableView() setAllowsColumnSelection:false];
184 _SetWidthOfTableColumnToFitItems(tableColumn, m_cocoaItems);
188 wxSize wxListBox::DoGetBestSize() const
190 wxSize size = wxControlWithItems::DoGetBestSize();
191 // Limit best size to 100x100. It can be smaller if none of the items are very
192 // wide or if there aren't many items, but anything bigger than 100x100 ought
193 // to be asked for by the programmer. The 100x100 size is based on being barely
194 // enough for a scroller to be usable.
195 if(size.GetWidth() > 100)
197 if(size.GetHeight() > 100)
202 wxListBox::~wxListBox()
204 [GetNSTableView() setDataSource: nil];
205 [m_cocoaDataSource release];
206 wxGCSafeRelease(m_cocoaItems);
208 DisassociateNSTableView(GetNSTableView());
211 bool wxListBox::_WxCocoa_GetNeedsUpdate()
213 return m_needsUpdate;
216 void wxListBox::_WxCocoa_SetNeedsUpdate(bool needsUpdate)
218 m_needsUpdate = needsUpdate;
221 void wxListBox::OnInternalIdle()
223 wxControlWithItems::OnInternalIdle();
224 if(_WxCocoa_GetNeedsUpdate())
226 _SetWidthOfTableColumnToFitItems([[GetNSTableView() tableColumns] objectAtIndex:0], m_cocoaItems);
227 [GetNSTableView() tile];
228 [GetNSTableView() reloadData];
229 _WxCocoa_SetNeedsUpdate(false);
233 int wxListBox::CocoaDataSource_numberOfRows()
235 return [m_cocoaItems count];
238 struct objc_object* wxListBox::CocoaDataSource_objectForTableColumn(
239 WX_NSTableColumn tableColumn, int rowIndex)
241 return [m_cocoaItems objectAtIndex:rowIndex];
244 // pure virtuals from wxListBoxBase
245 bool wxListBox::IsSelected(int n) const
247 return [GetNSTableView() isRowSelected: n];
250 void wxListBox::DoSetSelection(int n, bool select)
253 [GetNSTableView() selectRow: n byExtendingSelection:NO];
255 [GetNSTableView() deselectRow: n];
258 int wxListBox::GetSelections(wxArrayInt& aSelections) const
261 NSEnumerator *enumerator = [GetNSTableView() selectedRowEnumerator];
262 while(NSNumber *num = [enumerator nextObject])
264 aSelections.Add([num intValue]);
266 return [GetNSTableView() numberOfSelectedRows];
269 int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items, unsigned int pos, void **clientData, wxClientDataType type)
271 wxAutoNSAutoreleasePool pool;
273 const unsigned int numItems = items.GetCount();
274 for ( unsigned int i = 0; i < numItems; ++i, ++pos )
276 [m_cocoaItems insertObject: wxNSStringWithWxString(items[i])
278 m_itemClientData.Insert(NULL, pos);
279 AssignNewItemClientData(pos, clientData, i, type);
282 _WxCocoa_SetNeedsUpdate(true);
286 void wxListBox::DoSetFirstItem(int n)
288 [m_cocoaItems exchangeObjectAtIndex:0 withObjectAtIndex:n];
289 void* pOld = m_itemClientData[n];
290 m_itemClientData[n] = m_itemClientData[0];
291 m_itemClientData[0] = pOld;
292 _WxCocoa_SetNeedsUpdate(true);
296 // pure virtuals from wxItemContainer
298 void wxListBox::DoClear()
300 [m_cocoaItems removeAllObjects];
301 m_itemClientData.Clear();
302 _WxCocoa_SetNeedsUpdate(true);
305 void wxListBox::DoDeleteOneItem(unsigned int n)
307 [m_cocoaItems removeObjectAtIndex:n];
308 m_itemClientData.RemoveAt(n);
309 _WxCocoa_SetNeedsUpdate(true);
313 unsigned int wxListBox::GetCount() const
315 return (unsigned int)[m_cocoaItems count];
318 wxString wxListBox::GetString(unsigned int n) const
320 return wxStringWithNSString([m_cocoaItems objectAtIndex:n]);
323 void wxListBox::SetString(unsigned int n, const wxString& s)
325 wxAutoNSAutoreleasePool pool;
326 [m_cocoaItems removeObjectAtIndex:n];
327 [m_cocoaItems insertObject: wxNSStringWithWxString(s) atIndex: n];
328 _WxCocoa_SetNeedsUpdate(true);
331 int wxListBox::FindString(const wxString& s, bool bCase) const
333 // FIXME: use wxItemContainerImmutable::FindString for bCase parameter
334 wxAutoNSAutoreleasePool pool;
335 return [m_cocoaItems indexOfObject:wxNSStringWithWxString(s)];
339 int wxListBox::GetSelection() const
341 return [GetNSTableView() selectedRow];
344 void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
346 m_itemClientData[n] = clientData;
349 void* wxListBox::DoGetItemClientData(unsigned int n) const
351 return m_itemClientData[n];
354 #endif // wxUSE_LISTBOX