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 // By default, entries should not be editable
147 [tableColumn setEditable:NO];
148 [tableColumn release];
150 [GetNSTableView() sizeToFit];
153 m_parent->CocoaAddChild(this);
154 // NSTableView does WEIRD things with sizes. Wrapping it in an
155 // NSScrollView seems to be the only reasonable solution.
156 CocoaCreateNSScrollView();
157 SetInitialFrameRect(pos,size);
159 if ((style & wxLB_NEEDED_SB) || (style & wxLB_ALWAYS_SB))
161 [m_wxCocoaScrollView->GetNSScrollView() setHasVerticalScroller: YES];
164 if (style & wxLB_HSCROLL)
166 [m_wxCocoaScrollView->GetNSScrollView() setHasHorizontalScroller: YES];
169 // We can't set auto-hiding individually for horizontal/vertical scrollers,
170 // so we have settled on always allowing hiding for both unless the vertical
171 // setting is "always show".
172 if (((style & wxLB_NEEDED_SB) || (style & wxLB_HSCROLL)) && !(style & wxLB_ALWAYS_SB))
174 if ([m_wxCocoaScrollView->GetNSScrollView() respondsToSelector:@selector(setAutohidesScrollers:)])
176 [m_wxCocoaScrollView->GetNSScrollView() setAutohidesScrollers: YES];
180 // Set up extended/multiple selection flags
181 if ((style & wxLB_EXTENDED) || (style & wxLB_MULTIPLE))
182 //diff is that mult requires shift down for multi selection
183 [GetNSTableView() setAllowsMultipleSelection:true];
185 [GetNSTableView() setAllowsColumnSelection:false];
186 _SetWidthOfTableColumnToFitItems(tableColumn, m_cocoaItems);
190 wxSize wxListBox::DoGetBestSize() const
192 wxSize size = wxControlWithItems::DoGetBestSize();
193 // Limit best size to 100x100. It can be smaller if none of the items are very
194 // wide or if there aren't many items, but anything bigger than 100x100 ought
195 // to be asked for by the programmer. The 100x100 size is based on being barely
196 // enough for a scroller to be usable.
197 if(size.GetWidth() > 100)
199 if(size.GetHeight() > 100)
204 wxListBox::~wxListBox()
206 [GetNSTableView() setDataSource: nil];
207 [m_cocoaDataSource release];
208 wxGCSafeRelease(m_cocoaItems);
210 DisassociateNSTableView(GetNSTableView());
213 bool wxListBox::_WxCocoa_GetNeedsUpdate()
215 return m_needsUpdate;
218 void wxListBox::_WxCocoa_SetNeedsUpdate(bool needsUpdate)
220 m_needsUpdate = needsUpdate;
223 void wxListBox::OnInternalIdle()
225 wxControlWithItems::OnInternalIdle();
226 if(_WxCocoa_GetNeedsUpdate())
228 _SetWidthOfTableColumnToFitItems([[GetNSTableView() tableColumns] objectAtIndex:0], m_cocoaItems);
229 [GetNSTableView() tile];
230 [GetNSTableView() reloadData];
231 _WxCocoa_SetNeedsUpdate(false);
235 int wxListBox::CocoaDataSource_numberOfRows()
237 return [m_cocoaItems count];
240 struct objc_object* wxListBox::CocoaDataSource_objectForTableColumn(
241 WX_NSTableColumn tableColumn, int rowIndex)
243 return [m_cocoaItems objectAtIndex:rowIndex];
246 // pure virtuals from wxListBoxBase
247 bool wxListBox::IsSelected(int n) const
249 return [GetNSTableView() isRowSelected: n];
252 void wxListBox::DoSetSelection(int n, bool select)
255 [GetNSTableView() selectRow: n byExtendingSelection:NO];
257 [GetNSTableView() deselectRow: n];
260 int wxListBox::GetSelections(wxArrayInt& aSelections) const
263 NSEnumerator *enumerator = [GetNSTableView() selectedRowEnumerator];
264 while(NSNumber *num = [enumerator nextObject])
266 aSelections.Add([num intValue]);
268 return [GetNSTableView() numberOfSelectedRows];
271 int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items, unsigned int pos, void **clientData, wxClientDataType type)
273 wxAutoNSAutoreleasePool pool;
275 const unsigned int numItems = items.GetCount();
276 for ( unsigned int i = 0; i < numItems; ++i, ++pos )
278 [m_cocoaItems insertObject: wxNSStringWithWxString(items[i])
280 m_itemClientData.Insert(NULL, pos);
281 AssignNewItemClientData(pos, clientData, i, type);
284 _WxCocoa_SetNeedsUpdate(true);
288 void wxListBox::DoSetFirstItem(int n)
290 [m_cocoaItems exchangeObjectAtIndex:0 withObjectAtIndex:n];
291 void* pOld = m_itemClientData[n];
292 m_itemClientData[n] = m_itemClientData[0];
293 m_itemClientData[0] = pOld;
294 _WxCocoa_SetNeedsUpdate(true);
298 // pure virtuals from wxItemContainer
300 void wxListBox::DoClear()
302 [m_cocoaItems removeAllObjects];
303 m_itemClientData.Clear();
304 _WxCocoa_SetNeedsUpdate(true);
307 void wxListBox::DoDeleteOneItem(unsigned int n)
309 [m_cocoaItems removeObjectAtIndex:n];
310 m_itemClientData.RemoveAt(n);
311 _WxCocoa_SetNeedsUpdate(true);
315 unsigned int wxListBox::GetCount() const
317 return (unsigned int)[m_cocoaItems count];
320 wxString wxListBox::GetString(unsigned int n) const
322 return wxStringWithNSString([m_cocoaItems objectAtIndex:n]);
325 void wxListBox::SetString(unsigned int n, const wxString& s)
327 wxAutoNSAutoreleasePool pool;
328 [m_cocoaItems removeObjectAtIndex:n];
329 [m_cocoaItems insertObject: wxNSStringWithWxString(s) atIndex: n];
330 _WxCocoa_SetNeedsUpdate(true);
333 int wxListBox::FindString(const wxString& s, bool bCase) const
335 // FIXME: use wxItemContainerImmutable::FindString for bCase parameter
336 wxAutoNSAutoreleasePool pool;
337 return [m_cocoaItems indexOfObject:wxNSStringWithWxString(s)];
341 int wxListBox::GetSelection() const
343 return [GetNSTableView() selectedRow];
346 void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
348 m_itemClientData[n] = clientData;
351 void* wxListBox::DoGetItemClientData(unsigned int n) const
353 return m_itemClientData[n];
356 #endif // wxUSE_LISTBOX