merging back XTI branch part 2
[wxWidgets.git] / src / cocoa / listbox.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/cocoa/listbox.mm
3 // Purpose:     wxListBox
4 // Author:      David Elliott
5 // Modified by:
6 // Created:     2003/03/18
7 // Id:          $Id$
8 // Copyright:   (c) 2003 David Elliott
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #if wxUSE_LISTBOX
15
16 #include "wx/listbox.h"
17
18 #ifndef WX_PRECOMP
19     #include "wx/log.h"
20     #include "wx/app.h"
21 #endif //WX_PRECOMP
22
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"
28
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>
35   
36
37 // ============================================================================
38 // helper functions
39 // ============================================================================
40
41 static CGFloat _TableColumnMaxWidthForItems(NSTableColumn *tableColumn, NSArray *items)
42 {
43     wxAutoNSAutoreleasePool pool;
44
45     NSCell *dataCell = [[[tableColumn dataCell] copy] autorelease];
46     CGFloat width = 0.0f;
47     NSEnumerator *itemEnum = [items objectEnumerator];
48     NSString *item;
49     while( (item = [itemEnum nextObject]) != nil )
50     {
51         [dataCell setStringValue: item];
52         NSSize itemSize = [dataCell cellSize];
53         CGFloat itemWidth = itemSize.width;
54         if(itemWidth > width)
55             width = itemWidth;
56     }
57     return width;
58 }
59
60 static void _SetWidthOfTableColumnToFitItems(NSTableColumn *tableColumn, NSArray *items)
61 {
62     CGFloat width = _TableColumnMaxWidthForItems(tableColumn, items);
63     [tableColumn setWidth:width];
64     [tableColumn setMinWidth:width];
65 }
66
67 // ============================================================================
68 // class wxListBox
69 // ============================================================================
70
71 BEGIN_EVENT_TABLE(wxListBox, wxListBoxBase)
72 END_EVENT_TABLE()
73 WX_IMPLEMENT_COCOA_OWNER(wxListBox,NSTableView,NSControl,NSView)
74
75 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
76             const wxPoint& pos,
77             const wxSize& size,
78             const wxArrayString& choices,
79             long style,
80             const wxValidator& validator,
81             const wxString& name)
82 {
83     wxCArrayString chs(choices);
84
85     return Create(parent, winid, pos, size, chs.GetCount(), chs.GetStrings(),
86                   style, validator, name);
87 }
88
89 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
90             const wxPoint& pos,
91             const wxSize& size,
92             int n, const wxString choices[],
93             long style,
94             const wxValidator& validator,
95             const wxString& name)
96 {
97 /*
98 wxLB_SINGLE
99 Single-selection list.
100
101 wxLB_MULTIPLE
102 Multiple-selection list: the user can toggle multiple items on and off.
103
104 wxLB_EXTENDED
105 Extended-selection list: the user can select multiple items using the SHIFT key and the mouse or special key combinations.
106
107 wxLB_HSCROLL
108 Create horizontal scrollbar if contents are too wide (Windows only).
109
110 wxLB_ALWAYS_SB
111 Always show a vertical scrollbar.
112
113 wxLB_NEEDED_SB
114 Only create a vertical scrollbar if needed.
115
116 wxLB_SORT
117 The listbox contents are sorted in alphabetical order.
118 */
119     wxAutoNSAutoreleasePool pool;
120     if(!CreateControl(parent,winid,pos,size,style,validator,name))
121         return false;
122
123     // Provide the data
124     m_cocoaItems = wxGCSafeRetain([NSMutableArray arrayWithCapacity:n]);
125     for(int i=0; i < n; i++)
126     {
127         [m_cocoaItems addObject: wxNSStringWithWxString(choices[i])];
128     }
129     // Remove everything
130     m_itemClientData.Clear();
131     // Initialize n elements to NULL
132     m_itemClientData.SetCount(n,NULL);
133
134     SetNSTableView([[NSTableView alloc] initWithFrame: MakeDefaultNSRect(size)]);
135     [m_cocoaNSView release];
136     [GetNSTableView() setHeaderView: nil];
137
138     // Set up the data source
139     m_cocoaDataSource = [[WX_GET_OBJC_CLASS(wxCocoaNSTableDataSource) alloc] init];
140     [GetNSTableView() setDataSource:m_cocoaDataSource];
141
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];
148
149     [GetNSTableView() sizeToFit];
150     // Finish
151     if(m_parent)
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);
157
158     if ((style & wxLB_NEEDED_SB) || (style & wxLB_ALWAYS_SB))
159     {
160         [m_wxCocoaScrollView->GetNSScrollView() setHasVerticalScroller: YES];
161     }
162
163     if (style & wxLB_HSCROLL)
164     {
165         [m_wxCocoaScrollView->GetNSScrollView() setHasHorizontalScroller: YES];
166     }
167
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))
172     {
173         if ([m_wxCocoaScrollView->GetNSScrollView() respondsToSelector:@selector(setAutohidesScrollers:)])
174         {
175             [m_wxCocoaScrollView->GetNSScrollView() setAutohidesScrollers: YES];
176         }
177     }
178
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];
183
184     [GetNSTableView() setAllowsColumnSelection:false];
185     _SetWidthOfTableColumnToFitItems(tableColumn, m_cocoaItems);
186     return true;
187 }
188
189 wxSize wxListBox::DoGetBestSize() const
190 {
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)
197         size.SetWidth(100);
198     if(size.GetHeight() > 100)
199         size.SetHeight(100);
200     return size;
201 }
202
203 wxListBox::~wxListBox()
204 {
205     [GetNSTableView() setDataSource: nil];
206     [m_cocoaDataSource release];
207     wxGCSafeRelease(m_cocoaItems);
208     m_cocoaItems = nil;
209     DisassociateNSTableView(GetNSTableView());
210 }
211
212 bool wxListBox::_WxCocoa_GetNeedsUpdate()
213 {
214     return m_needsUpdate;
215 }
216
217 void wxListBox::_WxCocoa_SetNeedsUpdate(bool needsUpdate)
218 {
219     m_needsUpdate = needsUpdate;
220 }
221
222 void wxListBox::OnInternalIdle()
223 {
224     wxControlWithItems::OnInternalIdle();
225     if(_WxCocoa_GetNeedsUpdate())
226     {
227         _SetWidthOfTableColumnToFitItems([[GetNSTableView() tableColumns] objectAtIndex:0], m_cocoaItems);
228         [GetNSTableView() tile];
229         [GetNSTableView() reloadData];
230         _WxCocoa_SetNeedsUpdate(false);
231     }
232 }
233
234 int wxListBox::CocoaDataSource_numberOfRows()
235 {
236     return [m_cocoaItems count];
237 }
238
239 struct objc_object* wxListBox::CocoaDataSource_objectForTableColumn(
240         WX_NSTableColumn tableColumn, int rowIndex)
241 {
242     return [m_cocoaItems objectAtIndex:rowIndex];
243 }
244
245 // pure virtuals from wxListBoxBase
246 bool wxListBox::IsSelected(int n) const
247 {
248     return [GetNSTableView() isRowSelected: n];
249 }
250
251 void wxListBox::DoSetSelection(int n, bool select)
252 {
253     if(select)
254         [GetNSTableView() selectRow: n byExtendingSelection:NO];
255     else
256         [GetNSTableView() deselectRow: n];
257 }
258
259 int wxListBox::GetSelections(wxArrayInt& aSelections) const
260 {
261     aSelections.Clear();
262     NSEnumerator *enumerator = [GetNSTableView() selectedRowEnumerator];
263     while(NSNumber *num = [enumerator nextObject])
264     {
265         aSelections.Add([num intValue]);
266     }
267     return [GetNSTableView() numberOfSelectedRows];
268 }
269
270 int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items, unsigned int pos, void **clientData, wxClientDataType type)
271 {
272     wxAutoNSAutoreleasePool pool;
273
274     const unsigned int numItems = items.GetCount();
275     for ( unsigned int i = 0; i < numItems; ++i, ++pos )
276     {
277         [m_cocoaItems insertObject: wxNSStringWithWxString(items[i])
278             atIndex: pos];
279         m_itemClientData.Insert(NULL, pos);
280         AssignNewItemClientData(pos, clientData, i, type);
281     }
282
283     _WxCocoa_SetNeedsUpdate(true);
284     return pos - 1;
285 }
286
287 void wxListBox::DoSetFirstItem(int n)
288 {
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);
294 }
295
296
297 // pure virtuals from wxItemContainer
298     // deleting items
299 void wxListBox::DoClear()
300 {
301     [m_cocoaItems removeAllObjects];
302     m_itemClientData.Clear();
303     _WxCocoa_SetNeedsUpdate(true);
304 }
305
306 void wxListBox::DoDeleteOneItem(unsigned int n)
307 {
308     [m_cocoaItems removeObjectAtIndex:n];
309     m_itemClientData.RemoveAt(n);
310     _WxCocoa_SetNeedsUpdate(true);
311 }
312
313     // accessing strings
314 unsigned int wxListBox::GetCount() const
315 {
316     return (unsigned int)[m_cocoaItems count];
317 }
318
319 wxString wxListBox::GetString(unsigned int n) const
320 {
321     return wxStringWithNSString([m_cocoaItems objectAtIndex:n]);
322 }
323
324 void wxListBox::SetString(unsigned int n, const wxString& s)
325 {
326     wxAutoNSAutoreleasePool pool;
327     [m_cocoaItems removeObjectAtIndex:n];
328     [m_cocoaItems insertObject: wxNSStringWithWxString(s) atIndex: n];
329     _WxCocoa_SetNeedsUpdate(true);
330 }
331
332 int wxListBox::FindString(const wxString& s, bool bCase) const
333 {
334     // FIXME: use wxItemContainerImmutable::FindString for bCase parameter
335     wxAutoNSAutoreleasePool pool;
336     return [m_cocoaItems indexOfObject:wxNSStringWithWxString(s)];
337 }
338
339     // selection
340 int wxListBox::GetSelection() const
341 {
342     return [GetNSTableView() selectedRow];
343 }
344
345 void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
346 {
347     m_itemClientData[n] = clientData;
348 }
349
350 void* wxListBox::DoGetItemClientData(unsigned int n) const
351 {
352     return m_itemClientData[n];
353 }
354
355 #endif // wxUSE_LISTBOX