Revert the 2.8 hacks from r51892 on the trunk.
[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:     wxWidgets 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 IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControlWithItems)
72 BEGIN_EVENT_TABLE(wxListBox, wxListBoxBase)
73 END_EVENT_TABLE()
74 WX_IMPLEMENT_COCOA_OWNER(wxListBox,NSTableView,NSControl,NSView)
75
76 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
77             const wxPoint& pos,
78             const wxSize& size,
79             const wxArrayString& choices,
80             long style,
81             const wxValidator& validator,
82             const wxString& name)
83 {
84     wxCArrayString chs(choices);
85
86     return Create(parent, winid, pos, size, chs.GetCount(), chs.GetStrings(),
87                   style, validator, name);
88 }
89
90 bool wxListBox::Create(wxWindow *parent, wxWindowID winid,
91             const wxPoint& pos,
92             const wxSize& size,
93             int n, const wxString choices[],
94             long style,
95             const wxValidator& validator,
96             const wxString& name)
97 {
98 /*
99 wxLB_SINGLE
100 Single-selection list.
101
102 wxLB_MULTIPLE
103 Multiple-selection list: the user can toggle multiple items on and off.
104
105 wxLB_EXTENDED
106 Extended-selection list: the user can select multiple items using the SHIFT key and the mouse or special key combinations.
107
108 wxLB_HSCROLL
109 Create horizontal scrollbar if contents are too wide (Windows only).
110
111 wxLB_ALWAYS_SB
112 Always show a vertical scrollbar.
113
114 wxLB_NEEDED_SB
115 Only create a vertical scrollbar if needed.
116
117 wxLB_SORT
118 The listbox contents are sorted in alphabetical order.
119 */
120     wxAutoNSAutoreleasePool pool;
121     if(!CreateControl(parent,winid,pos,size,style,validator,name))
122         return false;
123
124     // Provide the data
125     m_cocoaItems = wxGCSafeRetain([NSMutableArray arrayWithCapacity:n]);
126     for(int i=0; i < n; i++)
127     {
128         [m_cocoaItems addObject: wxNSStringWithWxString(choices[i])];
129     }
130     // Remove everything
131     m_itemClientData.Clear();
132     // Initialize n elements to NULL
133     m_itemClientData.SetCount(n,NULL);
134
135     SetNSTableView([[NSTableView alloc] initWithFrame: MakeDefaultNSRect(size)]);
136     [m_cocoaNSView release];
137     [GetNSTableView() setHeaderView: nil];
138
139     // Set up the data source
140     m_cocoaDataSource = [[WX_GET_OBJC_CLASS(wxCocoaNSTableDataSource) alloc] init];
141     [GetNSTableView() setDataSource:m_cocoaDataSource];
142
143     // Add the single column
144     NSTableColumn *tableColumn = [[NSTableColumn alloc] initWithIdentifier:nil];
145     [GetNSTableView() addTableColumn: tableColumn];
146     [tableColumn release];
147
148     [GetNSTableView() sizeToFit];
149     // Finish
150     if(m_parent)
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);
156
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:)])
161     {
162         [m_wxCocoaScrollView->GetNSScrollView() setHasHorizontalScroller:YES];
163         [m_wxCocoaScrollView->GetNSScrollView() setAutohidesScrollers:YES];
164     }
165
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];
170
171     [GetNSTableView() setAllowsColumnSelection:false];
172     _SetWidthOfTableColumnToFitItems(tableColumn, m_cocoaItems);
173     return true;
174 }
175
176 wxListBox::~wxListBox()
177 {
178     [GetNSTableView() setDataSource: nil];
179     [m_cocoaDataSource release];
180     wxGCSafeRelease(m_cocoaItems);
181     m_cocoaItems = nil;
182     DisassociateNSTableView(GetNSTableView());
183 }
184
185 bool wxListBox::_WxCocoa_GetNeedsUpdate()
186 {
187     return m_needsUpdate;
188 }
189
190 void wxListBox::_WxCocoa_SetNeedsUpdate(bool needsUpdate)
191 {
192     m_needsUpdate = needsUpdate;
193 }
194
195 void wxListBox::OnInternalIdle()
196 {
197     wxControlWithItems::OnInternalIdle();
198     if(_WxCocoa_GetNeedsUpdate())
199     {
200         _SetWidthOfTableColumnToFitItems([[GetNSTableView() tableColumns] objectAtIndex:0], m_cocoaItems);
201         [GetNSTableView() tile];
202         [GetNSTableView() reloadData];
203         _WxCocoa_SetNeedsUpdate(false);
204     }
205 }
206
207 int wxListBox::CocoaDataSource_numberOfRows()
208 {
209     return [m_cocoaItems count];
210 }
211
212 struct objc_object* wxListBox::CocoaDataSource_objectForTableColumn(
213         WX_NSTableColumn tableColumn, int rowIndex)
214 {
215     return [m_cocoaItems objectAtIndex:rowIndex];
216 }
217
218 // pure virtuals from wxListBoxBase
219 bool wxListBox::IsSelected(int n) const
220 {
221     return [GetNSTableView() isRowSelected: n];
222 }
223
224 void wxListBox::DoSetSelection(int n, bool select)
225 {
226     if(select)
227         [GetNSTableView() selectRow: n byExtendingSelection:NO];
228     else
229         [GetNSTableView() deselectRow: n];
230 }
231
232 int wxListBox::GetSelections(wxArrayInt& aSelections) const
233 {
234     aSelections.Clear();
235     NSEnumerator *enumerator = [GetNSTableView() selectedRowEnumerator];
236     while(NSNumber *num = [enumerator nextObject])
237     {
238         aSelections.Add([num intValue]);
239     }
240     return [GetNSTableView() numberOfSelectedRows];
241 }
242
243 int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items, unsigned int pos, void **clientData, wxClientDataType type)
244 {
245     wxAutoNSAutoreleasePool pool;
246
247     const unsigned int numItems = items.GetCount();
248     for ( unsigned int i = 0; i < numItems; ++i, ++pos )
249     {
250         [m_cocoaItems insertObject: wxNSStringWithWxString(items[i])
251             atIndex: pos];
252         m_itemClientData.Insert(NULL, pos);
253         AssignNewItemClientData(pos, clientData, i, type);
254     }
255
256     _WxCocoa_SetNeedsUpdate(true);
257     return pos - 1;
258 }
259
260 void wxListBox::DoSetFirstItem(int n)
261 {
262     [m_cocoaItems exchangeObjectAtIndex:0 withObjectAtIndex:n];
263     void* pOld = m_itemClientData[n];
264     m_itemClientData[n] = m_itemClientData[0];
265     m_itemClientData[0] = pOld;
266     _WxCocoa_SetNeedsUpdate(true);
267 }
268
269
270 // pure virtuals from wxItemContainer
271     // deleting items
272 void wxListBox::DoClear()
273 {
274     [m_cocoaItems removeAllObjects];
275     m_itemClientData.Clear();
276     _WxCocoa_SetNeedsUpdate(true);
277 }
278
279 void wxListBox::DoDeleteOneItem(unsigned int n)
280 {
281     [m_cocoaItems removeObjectAtIndex:n];
282     m_itemClientData.RemoveAt(n);
283     _WxCocoa_SetNeedsUpdate(true);
284 }
285
286     // accessing strings
287 unsigned int wxListBox::GetCount() const
288 {
289     return (unsigned int)[m_cocoaItems count];
290 }
291
292 wxString wxListBox::GetString(unsigned int n) const
293 {
294     return wxStringWithNSString([m_cocoaItems objectAtIndex:n]);
295 }
296
297 void wxListBox::SetString(unsigned int n, const wxString& s)
298 {
299     wxAutoNSAutoreleasePool pool;
300     [m_cocoaItems removeObjectAtIndex:n];
301     [m_cocoaItems insertObject: wxNSStringWithWxString(s) atIndex: n];
302     _WxCocoa_SetNeedsUpdate(true);
303 }
304
305 int wxListBox::FindString(const wxString& s, bool bCase) const
306 {
307     // FIXME: use wxItemContainerImmutable::FindString for bCase parameter
308     wxAutoNSAutoreleasePool pool;
309     return [m_cocoaItems indexOfObject:wxNSStringWithWxString(s)];
310 }
311
312     // selection
313 int wxListBox::GetSelection() const
314 {
315     return [GetNSTableView() selectedRow];
316 }
317
318 void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
319 {
320     m_itemClientData[n] = clientData;
321 }
322
323 void* wxListBox::DoGetItemClientData(unsigned int n) const
324 {
325     return m_itemClientData[n];
326 }
327
328 #endif // wxUSE_LISTBOX