]> git.saurik.com Git - wxWidgets.git/blame - src/osx/carbon/listbox.cpp
don't set SHOW_FILES, its value is yes by default anyhow and using it provokes a...
[wxWidgets.git] / src / osx / carbon / listbox.cpp
CommitLineData
489468fe
SC
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/mac/carbon/listbox.cpp
3// Purpose: wxListBox
4// Author: Stefan Csomor
5// Modified by:
6// Created: 1998-01-01
7// RCS-ID: $Id$
8// Copyright: (c) Stefan Csomor
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/intl.h"
21 #include "wx/utils.h"
22 #include "wx/settings.h"
23 #include "wx/arrstr.h"
24 #include "wx/dcclient.h"
25#endif
26
27IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControlWithItems)
28
29BEGIN_EVENT_TABLE(wxListBox, wxControl)
30END_EVENT_TABLE()
31
1f0c8f31 32#include "wx/osx/uma.h"
489468fe
SC
33
34// ============================================================================
35// list box control implementation
36// ============================================================================
37
38wxListBox::wxListBox()
39{
40}
41
42bool wxListBox::Create(
43 wxWindow *parent,
44 wxWindowID id,
45 const wxPoint& pos,
46 const wxSize& size,
47 const wxArrayString& choices,
48 long style,
49 const wxValidator& validator,
50 const wxString& name )
51{
52 wxCArrayString chs(choices);
53
54 return Create(
55 parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
56 style, validator, name );
57}
58
59wxMacListControl* wxListBox::GetPeer() const
60{
61 wxMacDataBrowserListControl *lb = wxDynamicCast(m_peer,wxMacDataBrowserListControl);
62 return lb ? wx_static_cast(wxMacListControl*,lb) : 0 ;
63}
64
65bool wxListBox::Create(
66 wxWindow *parent,
67 wxWindowID id,
68 const wxPoint& pos,
69 const wxSize& size,
70 int n,
71 const wxString choices[],
72 long style,
73 const wxValidator& validator,
74 const wxString& name )
75{
76 m_blockEvents = false;
77 m_macIsUserPane = false;
78
79 wxASSERT_MSG( !(style & wxLB_MULTIPLE) || !(style & wxLB_EXTENDED),
80 wxT("only a single listbox selection mode can be specified") );
81
82 if ( !wxListBoxBase::Create( parent, id, pos, size, style & ~(wxHSCROLL | wxVSCROLL), validator, name ) )
83 return false;
84
85 wxMacDataBrowserListControl* control = new wxMacDataBrowserListControl( this, pos, size, style );
86 // TODO CHECK control->SetClientDataType( m_clientDataItemsType );
87 m_peer = control;
88
89 MacPostControlCreate( pos, size );
90
91 Append(n, choices);
92
93 // Needed because it is a wxControlWithItems
94 SetInitialSize( size );
95
96 return true;
97}
98
99wxListBox::~wxListBox()
100{
101 FreeData();
102 m_peer->SetReference( 0 );
103}
104
105void wxListBox::FreeData()
106{
107 GetPeer()->MacClear();
108}
109
110void wxListBox::DoSetFirstItem(int n)
111{
112 GetPeer()->MacScrollTo( n );
113}
114
115void wxListBox::EnsureVisible(int n)
116{
117 GetPeer()->MacScrollTo( n );
118}
119
120void wxListBox::DoDeleteOneItem(unsigned int n)
121{
122 wxCHECK_RET( IsValid(n), wxT("invalid index in wxListBox::Delete") );
123
124 m_blockEvents = true;
125 GetPeer()->MacDelete( n );
126 m_blockEvents = false;
127
128 UpdateOldSelections();
129}
130
131int wxListBox::DoInsertItems(const wxArrayStringsAdapter& items,
132 unsigned int pos,
133 void **clientData,
134 wxClientDataType type)
135{
136 InvalidateBestSize();
137
138 GetPeer()->MacInsert( pos, items );
139 const unsigned int count = items.GetCount();
140 if ( clientData )
141 {
142 for (unsigned int i = 0; i < count; ++i)
143 AssignNewItemClientData( pos + i, clientData, i, type );
144 }
145
146 return pos + count - 1;
147
148 UpdateOldSelections();
149}
150
151int wxListBox::FindString(const wxString& s, bool bCase) const
152{
153 for ( size_t i = 0; i < GetCount(); ++ i )
154 {
155 if (s.IsSameAs( GetString( i ), bCase) )
156 return (int)i;
157 }
158
159 return wxNOT_FOUND;
160}
161
162void wxListBox::DoClear()
163{
164 m_blockEvents = true;
165 FreeData();
166 m_blockEvents = false;
167
168 UpdateOldSelections();
169}
170
171void wxListBox::DoSetSelection(int n, bool select)
172{
173 wxCHECK_RET( n == wxNOT_FOUND || IsValid(n),
174 wxT("invalid index in wxListBox::SetSelection") );
175
176 m_blockEvents = true;
177
178 if ( n == wxNOT_FOUND )
179 GetPeer()->MacDeselectAll();
180 else
181 GetPeer()->MacSetSelection( n, select, HasMultipleSelection() );
182
183 m_blockEvents = false;
184
185 UpdateOldSelections();
186}
187
188bool wxListBox::IsSelected(int n) const
189{
190 wxCHECK_MSG( IsValid(n), false, wxT("invalid index in wxListBox::Selected") );
191
192 return GetPeer()->MacIsSelected( n );
193}
194
195void *wxListBox::DoGetItemClientData(unsigned int n) const
196{
197 wxCHECK_MSG( IsValid(n), NULL, wxT("invalid index in wxListBox::GetClientData"));
198 return GetPeer()->MacGetClientData( n );
199}
200
201void wxListBox::DoSetItemClientData(unsigned int n, void *clientData)
202{
203 wxCHECK_RET( IsValid(n), wxT("invalid index in wxListBox::SetClientData") );
204 GetPeer()->MacSetClientData( n , clientData);
205}
206
207// Return number of selections and an array of selected integers
208int wxListBox::GetSelections(wxArrayInt& aSelections) const
209{
210 return GetPeer()->MacGetSelections( aSelections );
211}
212
213// Get single selection, for single choice list items
214int wxListBox::GetSelection() const
215{
216 return GetPeer()->MacGetSelection();
217}
218
219// Find string for position
220wxString wxListBox::GetString(unsigned int n) const
221{
222 wxCHECK_MSG( IsValid(n), wxEmptyString, wxT("invalid index in wxListBox::GetString") );
223 return GetPeer()->MacGetString(n);
224}
225
226void wxListBox::SetString(unsigned int n, const wxString& s)
227{
228 GetPeer()->MacSetString( n, s );
229}
230
231wxSize wxListBox::DoGetBestSize() const
232{
233 int lbWidth = 100; // some defaults
234 int lbHeight = 110;
235 int wLine;
236
237 {
238 wxClientDC dc(const_cast<wxListBox*>(this));
239 dc.SetFont(GetFont());
240
241 // Find the widest line
242 for (unsigned int i = 0; i < GetCount(); i++)
243 {
244 wxString str( GetString( i ) );
245
246 wxCoord width, height ;
247 dc.GetTextExtent( str , &width, &height);
248 wLine = width ;
249 lbWidth = wxMax( lbWidth, wLine );
250 }
251
252 // Add room for the scrollbar
253 lbWidth += wxSystemSettings::GetMetric( wxSYS_VSCROLL_X );
254
255 // And just a bit more
256 int cy = 12;
257
258 wxCoord width, height ;
259 dc.GetTextExtent( wxT("XX") , &width, &height);
260 int cx = width ;
261 lbWidth += cx;
262
263 // don't make the listbox too tall (limit height to around 10 items)
264 // but don't make it too small neither
265 lbHeight = wxMax( (cy + 4) * wxMin( wxMax( GetCount(), 3 ), 10 ), 70 );
266 }
267
268 return wxSize( lbWidth, lbHeight );
269}
270
271unsigned int wxListBox::GetCount() const
272{
273 return GetPeer()->MacGetCount();
274}
275
276void wxListBox::Refresh(bool eraseBack, const wxRect *rect)
277{
278 wxControl::Refresh( eraseBack, rect );
279}
280
281// Some custom controls depend on this
282/* static */ wxVisualAttributes
283wxListBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
284{
285 wxVisualAttributes attr;
286
287 attr.colFg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
288 attr.colBg = wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOX );
289 attr.font.MacCreateFromThemeFont(kThemeViewsFont);
290
291 return attr;
292}
293
294int wxListBox::DoListHitTest(const wxPoint& inpoint) const
295{
296 OSStatus err;
297
298 // There are few reasons why this is complicated:
299 // 1) There is no native HitTest function for Mac
300 // 2) GetDataBrowserItemPartBounds only works on visible items
301 // 3) We can't do it through GetDataBrowserTableView[Item]RowHeight
302 // because what it returns is basically inaccurate in the context
303 // of the coordinates we want here, but we use this as a guess
304 // for where the first visible item lies
305
306 wxPoint point = inpoint;
307
308 // get column property ID (req. for call to itempartbounds)
309 DataBrowserTableViewColumnID colId = 0;
310 err = GetDataBrowserTableViewColumnProperty(m_peer->GetControlRef(), 0, &colId);
311 wxCHECK_MSG(err == noErr, wxNOT_FOUND, wxT("Unexpected error from GetDataBrowserTableViewColumnProperty"));
312
313 // OK, first we need to find the first visible item we have -
314 // this will be the "low" for our binary search. There is no real
315 // easy way around this, as we will need to do a SLOW linear search
316 // until we find a visible item, but we can do a cheap calculation
317 // via the row height to speed things up a bit
318 UInt32 scrollx, scrolly;
319 err = GetDataBrowserScrollPosition(m_peer->GetControlRef(), &scrollx, &scrolly);
320 wxCHECK_MSG(err == noErr, wxNOT_FOUND, wxT("Unexpected error from GetDataBrowserScrollPosition"));
321
322 UInt16 height;
323 err = GetDataBrowserTableViewRowHeight(m_peer->GetControlRef(), &height);
324 wxCHECK_MSG(err == noErr, wxNOT_FOUND, wxT("Unexpected error from GetDataBrowserTableViewRowHeight"));
325
326 // these indices are 0-based, as usual, so we need to add 1 to them when
327 // passing them to data browser functions which use 1-based indices
328 int low = scrolly / height,
329 high = GetCount() - 1;
330
331 // search for the first visible item (note that the scroll guess above
332 // is the low bounds of where the item might lie so we only use that as a
333 // starting point - we should reach it within 1 or 2 iterations of the loop)
334 while ( low <= high )
335 {
336 Rect bounds;
337 err = GetDataBrowserItemPartBounds(
338 m_peer->GetControlRef(), low + 1, colId,
339 kDataBrowserPropertyEnclosingPart,
340 &bounds); // note +1 to translate to Mac ID
341 if ( err == noErr )
342 break;
343
344 // errDataBrowserItemNotFound is expected as it simply means that the
345 // item is not currently visible -- but other errors are not
346 wxCHECK_MSG( err == errDataBrowserItemNotFound, wxNOT_FOUND,
347 wxT("Unexpected error from GetDataBrowserItemPartBounds") );
348
349 low++;
350 }
351
352 // NOW do a binary search for where the item lies, searching low again if
353 // we hit an item that isn't visible
354 while ( low <= high )
355 {
356 int mid = (low + high) / 2;
357
358 Rect bounds;
359 err = GetDataBrowserItemPartBounds(
360 m_peer->GetControlRef(), mid + 1, colId,
361 kDataBrowserPropertyEnclosingPart,
362 &bounds); //note +1 to trans to mac id
363 wxCHECK_MSG( err == noErr || err == errDataBrowserItemNotFound,
364 wxNOT_FOUND,
365 wxT("Unexpected error from GetDataBrowserItemPartBounds") );
366
367 if ( err == errDataBrowserItemNotFound )
368 {
369 // item not visible, attempt to find a visible one
370 // search lower
371 high = mid - 1;
372 }
373 else // visible item, do actual hitttest
374 {
375 // if point is within the bounds, return this item (since we assume
376 // all x coords of items are equal we only test the x coord in
377 // equality)
378 if ((point.x >= bounds.left && point.x <= bounds.right) &&
379 (point.y >= bounds.top && point.y <= bounds.bottom) )
380 {
381 // found!
382 return mid;
383 }
384
385 if ( point.y < bounds.top )
386 // index(bounds) greater then key(point)
387 high = mid - 1;
388 else
389 // index(bounds) less then key(point)
390 low = mid + 1;
391 }
392 }
393
394 return wxNOT_FOUND;
395}
396
397// ============================================================================
398// data browser based implementation
399// ============================================================================
400
401wxMacListBoxItem::wxMacListBoxItem()
402 :wxMacDataItem()
403{
404}
405
406wxMacListBoxItem::~wxMacListBoxItem()
407{
408}
409
410void wxMacListBoxItem::Notification(wxMacDataItemBrowserControl *owner ,
411 DataBrowserItemNotification message,
412 DataBrowserItemDataRef WXUNUSED(itemData) ) const
413{
414 wxMacDataBrowserListControl *lb = wxDynamicCast(owner,wxMacDataBrowserListControl);
415
416 // we want to depend on as little as possible to make sure tear-down of controls is safe
417
418 if ( message == kDataBrowserItemRemoved)
419 {
420 if ( lb != NULL && lb->GetClientDataType() == wxClientData_Object )
421 {
422 delete (wxClientData*) (m_data);
423 }
424
425 delete this;
426 return;
427 }
428
b2680ced 429 wxListBox *list = wxDynamicCast( owner->GetWXPeer() , wxListBox );
489468fe
SC
430 wxCHECK_RET( list != NULL , wxT("Listbox expected"));
431
432 if (message == kDataBrowserItemDoubleClicked)
433 {
434 wxCommandEvent event( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, list->GetId() );
435 event.SetEventObject( list );
436 if ( list->HasClientObjectData() )
437 event.SetClientObject( (wxClientData*) m_data );
438 else if ( list->HasClientUntypedData() )
439 event.SetClientData( m_data );
440 event.SetString( m_label );
441 event.SetInt( owner->GetLineFromItem( this ) );
442 event.SetExtraLong( 1 );
443 list->HandleWindowEvent(event);
444 return;
445 }
446}
447
448IMPLEMENT_DYNAMIC_CLASS( wxMacDataBrowserListControl , wxMacDataItemBrowserControl )
449
450wxMacDataBrowserListControl::wxMacDataBrowserListControl( wxWindow *peer, const wxPoint& pos, const wxSize& size, long style)
451 : wxMacDataItemBrowserControl( peer, pos, size, style )
452{
453 OSStatus err = noErr;
454 m_clientDataItemsType = wxClientData_None;
455 if ( style & wxLB_SORT )
456 m_sortOrder = SortOrder_Text_Ascending;
457
458 DataBrowserSelectionFlags options = kDataBrowserDragSelect;
459 if ( style & wxLB_MULTIPLE )
460 {
461 options |= kDataBrowserAlwaysExtendSelection | kDataBrowserCmdTogglesSelection;
462 }
463 else if ( style & wxLB_EXTENDED )
464 {
465 options |= kDataBrowserCmdTogglesSelection;
466 }
467 else
468 {
469 options |= kDataBrowserSelectOnlyOne;
470 }
471 err = SetSelectionFlags( options );
472 verify_noerr( err );
473
474 DataBrowserListViewColumnDesc columnDesc;
475 columnDesc.headerBtnDesc.titleOffset = 0;
476 columnDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc;
477
478 columnDesc.headerBtnDesc.btnFontStyle.flags =
479 kControlUseFontMask | kControlUseJustMask;
480
481 columnDesc.headerBtnDesc.btnContentInfo.contentType = kControlNoContent;
482 columnDesc.headerBtnDesc.btnFontStyle.just = teFlushDefault;
483 columnDesc.headerBtnDesc.btnFontStyle.font = kControlFontViewSystemFont;
484 columnDesc.headerBtnDesc.btnFontStyle.style = normal;
485 columnDesc.headerBtnDesc.titleString = NULL;
486
487 columnDesc.headerBtnDesc.minimumWidth = 0;
488 columnDesc.headerBtnDesc.maximumWidth = 10000;
489
490 columnDesc.propertyDesc.propertyID = kTextColumnId;
491 columnDesc.propertyDesc.propertyType = kDataBrowserTextType;
492 columnDesc.propertyDesc.propertyFlags = kDataBrowserTableViewSelectionColumn;
493 columnDesc.propertyDesc.propertyFlags |= kDataBrowserListViewTypeSelectColumn;
494
495 verify_noerr( AddColumn( &columnDesc, kDataBrowserListViewAppendColumn ) );
496
497 columnDesc.headerBtnDesc.minimumWidth = 0;
498 columnDesc.headerBtnDesc.maximumWidth = 0;
499 columnDesc.propertyDesc.propertyID = kNumericOrderColumnId;
500 columnDesc.propertyDesc.propertyType = kDataBrowserPropertyRelevanceRankPart;
501 columnDesc.propertyDesc.propertyFlags = kDataBrowserTableViewSelectionColumn;
502 columnDesc.propertyDesc.propertyFlags |= kDataBrowserListViewTypeSelectColumn;
503
504 verify_noerr( AddColumn( &columnDesc, kDataBrowserListViewAppendColumn ) );
505
506 SetDataBrowserSortProperty( m_controlRef , kTextColumnId);
507 if ( m_sortOrder == SortOrder_Text_Ascending )
508 {
509 SetDataBrowserSortProperty( m_controlRef , kTextColumnId);
510 SetDataBrowserSortOrder( m_controlRef , kDataBrowserOrderIncreasing);
511 }
512 else
513 {
514 SetDataBrowserSortProperty( m_controlRef , kNumericOrderColumnId);
515 SetDataBrowserSortOrder( m_controlRef , kDataBrowserOrderIncreasing);
516 }
517
518 verify_noerr( AutoSizeColumns() );
519 verify_noerr( SetHiliteStyle(kDataBrowserTableViewFillHilite ) );
520 verify_noerr( SetHeaderButtonHeight( 0 ) );
521 err = SetHasScrollBars( (style & wxHSCROLL) != 0 , true );
522#if 0
523 // shouldn't be necessary anymore under 10.2
524 m_peer->SetData( kControlNoPart, kControlDataBrowserIncludesFrameAndFocusTag, (Boolean)false );
525 m_peer->SetNeedsFocusRect( true );
526#endif
527}
528
529wxMacDataBrowserListControl::~wxMacDataBrowserListControl()
530{
531}
532
533void wxMacDataBrowserListControl::ItemNotification(
534 const wxMacDataItem* itemID,
535 DataBrowserItemNotification message,
536 DataBrowserItemDataRef itemData)
537{
b2680ced 538 wxListBox *list = wxDynamicCast( GetWXPeer() , wxListBox );
489468fe
SC
539 wxCHECK_RET( list != NULL , wxT("Listbox expected"));
540
541 if (list->HasMultipleSelection() && (message == kDataBrowserSelectionSetChanged) && (!list->MacGetBlockEvents()))
542 {
543 list->CalcAndSendEvent();
544 return;
545 }
546
547 if ((message == kDataBrowserSelectionSetChanged) && (!list->MacGetBlockEvents()))
548 {
549 wxCommandEvent event( wxEVT_COMMAND_LISTBOX_SELECTED, list->GetId() );
550
551 int sel = list->GetSelection();
b2680ced 552 if ((sel < 0) || (sel > (int) list->GetCount())) // OS X can select an item below the last item (why?)
489468fe
SC
553 return;
554 event.SetEventObject( list );
555 if ( list->HasClientObjectData() )
556 event.SetClientObject( list->GetClientObject( sel ) );
557 else if ( list->HasClientUntypedData() )
558 event.SetClientData( list->GetClientData( sel ) );
559 event.SetString( list->GetString( sel ) );
560 event.SetInt( sel );
561 event.SetExtraLong( 1 );
562 list->HandleWindowEvent(event);
563 return;
564 }
565
566 // call super for item level(wxMacDataItem->Notification) callback processing
567 wxMacDataItemBrowserControl::ItemNotification( itemID, message, itemData);
568}
b2680ced 569/*
489468fe
SC
570wxWindow * wxMacDataBrowserListControl::GetPeer() const
571{
b2680ced 572 return wxDynamicCast( wxMacControl::GetWX() , wxWindow );
489468fe 573}
b2680ced 574*/
489468fe
SC
575wxMacDataItem* wxMacDataBrowserListControl::CreateItem()
576{
577 return new wxMacListBoxItem();
578}
579
580#if 0
581
582// in case we need that one day
583
584// ============================================================================
585// HIView owner-draw-based implementation
586// ============================================================================
587
588static pascal void ListBoxDrawProc(
589 ControlRef browser, DataBrowserItemID item, DataBrowserPropertyID property,
590 DataBrowserItemState itemState, const Rect *itemRect, SInt16 depth, Boolean isColorDevice )
591{
592 CFStringRef cfString;
593 ThemeDrawingState themeState;
594 long systemVersion;
595
596 GetThemeDrawingState( &themeState );
597 cfString = CFStringCreateWithFormat( NULL, NULL, CFSTR("Row %d"), item );
598
599 // In this sample we handle the "selected" state; all others fall through to our "active" state
600 if ( itemState == kDataBrowserItemIsSelected )
601 {
602 ThemeBrush colorBrushID;
603
604 // TODO: switch over to wxSystemSettingsNative::GetColour() when kThemeBrushSecondaryHighlightColor
605 // is incorporated Panther DB starts using kThemeBrushSecondaryHighlightColor
606 // for inactive browser highlighting
607 if ( !IsControlActive( browser ) )
608 colorBrushID = kThemeBrushSecondaryHighlightColor;
609 else
610 colorBrushID = kThemeBrushPrimaryHighlightColor;
611
612 // First paint the hilite rect, then the text on top
613 SetThemePen( colorBrushID, 32, true );
614 PaintRect( itemRect );
615 SetThemeDrawingState( themeState, false );
616 }
617
618 DrawThemeTextBox( cfString, kThemeApplicationFont, kThemeStateActive, true, itemRect, teFlushDefault, NULL );
619 SetThemeDrawingState( themeState, true );
620
621 if ( cfString != NULL )
622 CFRelease( cfString );
623}
624
625#endif
626
627
628#endif // wxUSE_LISTBOX