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