1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/mac/carbon/listbox.cpp 
   4 // Author:      Stefan Csomor 
   8 // Copyright:   (c) Stefan Csomor 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 #include "wx/wxprec.h" 
  16 #include "wx/listbox.h" 
  22     #include "wx/settings.h" 
  23     #include "wx/arrstr.h" 
  24     #include "wx/dcclient.h" 
  27 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControlWithItems
) 
  29 BEGIN_EVENT_TABLE(wxListBox
, wxControl
) 
  32 #include "wx/mac/uma.h" 
  34 // ============================================================================ 
  35 // list box control implementation 
  36 // ============================================================================ 
  38 wxListBox::wxListBox() 
  42 bool wxListBox::Create( 
  47     const wxArrayString
& choices
, 
  49     const wxValidator
& validator
, 
  50     const wxString
& name 
) 
  52     wxCArrayString 
chs(choices
); 
  55         parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
  56         style
, validator
, name 
); 
  59 wxMacListControl
* wxListBox::GetPeer() const 
  61     wxMacDataBrowserListControl 
*lb 
= wxDynamicCast(m_peer
,wxMacDataBrowserListControl
); 
  62     return lb 
? wx_static_cast(wxMacListControl
*,lb
) : 0 ; 
  65 bool wxListBox::Create( 
  71     const wxString choices
[], 
  73     const wxValidator
& validator
, 
  74     const wxString
& name 
) 
  76     m_macIsUserPane 
= false; 
  78     wxASSERT_MSG( !(style 
& wxLB_MULTIPLE
) || !(style 
& wxLB_EXTENDED
), 
  79                   wxT("only a single listbox selection mode can be specified") ); 
  81     if ( !wxListBoxBase::Create( parent
, id
, pos
, size
, style 
& ~(wxHSCROLL 
| wxVSCROLL
), validator
, name 
) ) 
  84     wxMacDataBrowserListControl
* control 
= new wxMacDataBrowserListControl( this, pos
, size
, style 
); 
  85     // TODO CHECK control->SetClientDataType( m_clientDataItemsType ); 
  88     MacPostControlCreate( pos
, size 
); 
  92    // Needed because it is a wxControlWithItems 
  93     SetInitialSize( size 
); 
  98 wxListBox::~wxListBox() 
 101     m_peer
->SetReference( 0 ); 
 104 void wxListBox::FreeData() 
 106     GetPeer()->MacClear(); 
 109 void wxListBox::DoSetFirstItem(int n
) 
 111     GetPeer()->MacScrollTo( n 
); 
 114 void wxListBox::EnsureVisible(int n
) 
 116     GetPeer()->MacScrollTo( n 
); 
 119 void wxListBox::DoDeleteOneItem(unsigned int n
) 
 121     wxCHECK_RET( IsValid(n
), wxT("invalid index in wxListBox::Delete") ); 
 123     GetPeer()->MacDelete( n 
); 
 126 int wxListBox::DoInsertItems(const wxArrayStringsAdapter
& items
, 
 129                              wxClientDataType type
) 
 131     InvalidateBestSize(); 
 133     GetPeer()->MacInsert( pos
, items 
); 
 134     const unsigned int count 
= items
.GetCount(); 
 137         for (unsigned int i 
= 0; i 
< count
; ++i
) 
 138             AssignNewItemClientData( pos 
+ i
, clientData
, i
, type 
); 
 141     return pos 
+ count 
- 1; 
 144 int wxListBox::FindString(const wxString
& s
, bool bCase
) const 
 146     for ( size_t i 
= 0; i 
< GetCount(); ++ i 
) 
 148         if (s
.IsSameAs( GetString( i 
), bCase
) ) 
 155 void wxListBox::DoClear() 
 160 void wxListBox::DoSetSelection(int n
, bool select
) 
 162     wxCHECK_RET( n 
== wxNOT_FOUND 
|| IsValid(n
), 
 163         wxT("invalid index in wxListBox::SetSelection") ); 
 165     if ( n 
== wxNOT_FOUND 
) 
 166         GetPeer()->MacDeselectAll(); 
 168         GetPeer()->MacSetSelection( n
, select
, HasMultipleSelection() ); 
 171 bool wxListBox::IsSelected(int n
) const 
 173     wxCHECK_MSG( IsValid(n
), false, wxT("invalid index in wxListBox::Selected") ); 
 175     return GetPeer()->MacIsSelected( n 
); 
 178 void *wxListBox::DoGetItemClientData(unsigned int n
) const 
 180     wxCHECK_MSG( IsValid(n
), NULL
, wxT("invalid index in wxListBox::GetClientData")); 
 181     return GetPeer()->MacGetClientData( n 
); 
 184 void wxListBox::DoSetItemClientData(unsigned int n
, void *clientData
) 
 186     wxCHECK_RET( IsValid(n
), wxT("invalid index in wxListBox::SetClientData") ); 
 187     GetPeer()->MacSetClientData( n 
, clientData
); 
 190 // Return number of selections and an array of selected integers 
 191 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const 
 193     return GetPeer()->MacGetSelections( aSelections 
); 
 196 // Get single selection, for single choice list items 
 197 int wxListBox::GetSelection() const 
 199     return GetPeer()->MacGetSelection(); 
 202 // Find string for position 
 203 wxString 
wxListBox::GetString(unsigned int n
) const 
 205     wxCHECK_MSG( IsValid(n
), wxEmptyString
, wxT("invalid index in wxListBox::GetString") ); 
 206     return GetPeer()->MacGetString(n
); 
 209 void wxListBox::SetString(unsigned int n
, const wxString
& s
) 
 211     GetPeer()->MacSetString( n
, s 
); 
 214 wxSize 
wxListBox::DoGetBestSize() const 
 216     int lbWidth 
= 100;  // some defaults 
 221         wxClientDC 
dc(const_cast<wxListBox
*>(this)); 
 222         dc
.SetFont(GetFont()); 
 224         // Find the widest line 
 225         for (unsigned int i 
= 0; i 
< GetCount(); i
++) 
 227             wxString 
str( GetString( i 
) ); 
 229             wxCoord width
, height 
; 
 230             dc
.GetTextExtent( str 
, &width
, &height
); 
 232             lbWidth 
= wxMax( lbWidth
, wLine 
); 
 235         // Add room for the scrollbar 
 236         lbWidth 
+= wxSystemSettings::GetMetric( wxSYS_VSCROLL_X 
); 
 238         // And just a bit more 
 241         wxCoord width
, height 
; 
 242         dc
.GetTextExtent( wxT("XX") , &width
, &height
); 
 246         // don't make the listbox too tall (limit height to around 10 items) 
 247         // but don't make it too small neither 
 248         lbHeight 
= wxMax( (cy 
+ 4) * wxMin( wxMax( GetCount(), 3 ), 10 ), 70 ); 
 251     return wxSize( lbWidth
, lbHeight 
); 
 254 unsigned int wxListBox::GetCount() const 
 256     return GetPeer()->MacGetCount(); 
 259 void wxListBox::Refresh(bool eraseBack
, const wxRect 
*rect
) 
 261     wxControl::Refresh( eraseBack
, rect 
); 
 264 // Some custom controls depend on this 
 265 /* static */ wxVisualAttributes
 
 266 wxListBox::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
 268     wxVisualAttributes attr
; 
 270     attr
.colFg 
= wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT 
); 
 271     attr
.colBg 
= wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOX 
); 
 272     attr
.font 
= wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT 
); 
 277 int wxListBox::DoListHitTest(const wxPoint
& inpoint
) const 
 281     // There are few reasons why this is complicated: 
 282     // 1) There is no native HitTest function for Mac 
 283     // 2) GetDataBrowserItemPartBounds only works on visible items 
 284     // 3) We can't do it through GetDataBrowserTableView[Item]RowHeight 
 285     //    because what it returns is basically inaccurate in the context 
 286     //    of the coordinates we want here, but we use this as a guess 
 287     //    for where the first visible item lies 
 289     wxPoint point 
= inpoint
; 
 291     // get column property ID (req. for call to itempartbounds) 
 292     DataBrowserTableViewColumnID colId 
= 0; 
 293     err 
= GetDataBrowserTableViewColumnProperty(m_peer
->GetControlRef(), 0, &colId
); 
 294     wxCHECK_MSG(err 
== noErr
, wxNOT_FOUND
, wxT("Unexpected error from GetDataBrowserTableViewColumnProperty")); 
 296     // OK, first we need to find the first visible item we have - 
 297     // this will be the "low" for our binary search. There is no real 
 298     // easy way around this, as we will need to do a SLOW linear search 
 299     // until we find a visible item, but we can do a cheap calculation 
 300     // via the row height to speed things up a bit 
 301     UInt32 scrollx
, scrolly
; 
 302     err 
= GetDataBrowserScrollPosition(m_peer
->GetControlRef(), &scrollx
, &scrolly
); 
 303     wxCHECK_MSG(err 
== noErr
, wxNOT_FOUND
, wxT("Unexpected error from GetDataBrowserScrollPosition")); 
 306     err 
= GetDataBrowserTableViewRowHeight(m_peer
->GetControlRef(), &height
); 
 307     wxCHECK_MSG(err 
== noErr
, wxNOT_FOUND
, wxT("Unexpected error from GetDataBrowserTableViewRowHeight")); 
 309     // these indices are 0-based, as usual, so we need to add 1 to them when 
 310     // passing them to data browser functions which use 1-based indices 
 311     int low 
= scrolly 
/ height
, 
 312         high 
= GetCount() - 1; 
 314     // search for the first visible item (note that the scroll guess above 
 315     // is the low bounds of where the item might lie so we only use that as a 
 316     // starting point - we should reach it within 1 or 2 iterations of the loop) 
 317     while ( low 
<= high 
) 
 320         err 
= GetDataBrowserItemPartBounds( 
 321             m_peer
->GetControlRef(), low 
+ 1, colId
, 
 322             kDataBrowserPropertyEnclosingPart
, 
 323             &bounds
); // note +1 to translate to Mac ID 
 327         // errDataBrowserItemNotFound is expected as it simply means that the 
 328         // item is not currently visible -- but other errors are not 
 329         wxCHECK_MSG( err 
== errDataBrowserItemNotFound
, wxNOT_FOUND
, 
 330                      wxT("Unexpected error from GetDataBrowserItemPartBounds") ); 
 335     // NOW do a binary search for where the item lies, searching low again if 
 336     // we hit an item that isn't visible 
 337     while ( low 
<= high 
) 
 339         int mid 
= (low 
+ high
) / 2; 
 342         err 
= GetDataBrowserItemPartBounds( 
 343             m_peer
->GetControlRef(), mid 
+ 1, colId
, 
 344             kDataBrowserPropertyEnclosingPart
, 
 345             &bounds
); //note +1 to trans to mac id 
 346         wxCHECK_MSG( err 
== noErr 
|| err 
== errDataBrowserItemNotFound
, 
 348                      wxT("Unexpected error from GetDataBrowserItemPartBounds") ); 
 350         if ( err 
== errDataBrowserItemNotFound 
) 
 352             // item not visible, attempt to find a visible one 
 356         else // visible item, do actual hitttest 
 358             // if point is within the bounds, return this item (since we assume 
 359             // all x coords of items are equal we only test the x coord in 
 361             if ((point
.x 
>= bounds
.left 
&& point
.x 
<= bounds
.right
) && 
 362                 (point
.y 
>= bounds
.top 
&& point
.y 
<= bounds
.bottom
) ) 
 368             if ( point
.y 
< bounds
.top 
) 
 369                 // index(bounds) greater then key(point) 
 372                 // index(bounds) less then key(point) 
 380 // ============================================================================ 
 381 // data browser based implementation 
 382 // ============================================================================ 
 384 wxMacListBoxItem::wxMacListBoxItem() 
 389 wxMacListBoxItem::~wxMacListBoxItem() 
 393 void wxMacListBoxItem::Notification(wxMacDataItemBrowserControl 
*owner 
, 
 394     DataBrowserItemNotification message
, 
 395     DataBrowserItemDataRef 
WXUNUSED(itemData
) ) const 
 397     wxMacDataBrowserListControl 
*lb 
= wxDynamicCast(owner
,wxMacDataBrowserListControl
); 
 399     // we want to depend on as little as possible to make sure tear-down of controls is safe 
 401     if ( message 
== kDataBrowserItemRemoved
) 
 403         if ( lb 
!= NULL 
&& lb
->GetClientDataType() == wxClientData_Object 
) 
 405             delete (wxClientData
*) (m_data
); 
 412     wxListBox 
*list 
= wxDynamicCast( owner
->GetPeer() , wxListBox 
); 
 413     wxCHECK_RET( list 
!= NULL 
, wxT("Listbox expected")); 
 415     bool trigger 
= false; 
 416     wxCommandEvent 
event( wxEVT_COMMAND_LISTBOX_SELECTED
, list
->GetId() ); 
 419         case kDataBrowserItemDeselected
: 
 420             if ( list
->HasMultipleSelection() ) 
 421                 trigger 
= !lb
->IsSelectionSuppressed(); 
 424         case kDataBrowserItemSelected
: 
 425             trigger 
= !lb
->IsSelectionSuppressed(); 
 428         case kDataBrowserItemDoubleClicked
: 
 429             event
.SetEventType( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED 
); 
 439         event
.SetEventObject( list 
); 
 440         if ( list
->HasClientObjectData() ) 
 441             event
.SetClientObject( (wxClientData
*) m_data 
); 
 442         else if ( list
->HasClientUntypedData() ) 
 443             event
.SetClientData( m_data 
); 
 444         event
.SetString( m_label 
); 
 445         event
.SetInt( owner
->GetLineFromItem( this ) ); 
 446         event
.SetExtraLong( list
->HasMultipleSelection() ? message 
== kDataBrowserItemSelected 
: true ); 
 448         // direct notification is not always having the listbox GetSelection() 
 449         // having in synch with event, so use wxPostEvent instead 
 450         // list->HandleWindowEvent(event); 
 452         wxPostEvent( list
->GetEventHandler(), event 
); 
 456 IMPLEMENT_DYNAMIC_CLASS( wxMacDataBrowserListControl 
, wxMacDataItemBrowserControl 
) 
 458 wxMacDataBrowserListControl::wxMacDataBrowserListControl( wxWindow 
*peer
, const wxPoint
& pos
, const wxSize
& size
, long style
) 
 459     : wxMacDataItemBrowserControl( peer
, pos
, size
, style 
) 
 461     OSStatus err 
= noErr
; 
 462     m_clientDataItemsType 
= wxClientData_None
; 
 463     if ( style 
& wxLB_SORT 
) 
 464         m_sortOrder 
= SortOrder_Text_Ascending
; 
 466     DataBrowserSelectionFlags  options 
= kDataBrowserDragSelect
; 
 467     if ( style 
& wxLB_MULTIPLE 
) 
 469         options 
|= kDataBrowserAlwaysExtendSelection 
| kDataBrowserCmdTogglesSelection
; 
 471     else if ( style 
& wxLB_EXTENDED 
) 
 473         options 
|= kDataBrowserCmdTogglesSelection
; 
 477         options 
|= kDataBrowserSelectOnlyOne
; 
 479     err 
= SetSelectionFlags( options 
); 
 482     DataBrowserListViewColumnDesc columnDesc
; 
 483     columnDesc
.headerBtnDesc
.titleOffset 
= 0; 
 484     columnDesc
.headerBtnDesc
.version 
= kDataBrowserListViewLatestHeaderDesc
; 
 486     columnDesc
.headerBtnDesc
.btnFontStyle
.flags 
= 
 487         kControlUseFontMask 
| kControlUseJustMask
; 
 489     columnDesc
.headerBtnDesc
.btnContentInfo
.contentType 
= kControlNoContent
; 
 490     columnDesc
.headerBtnDesc
.btnFontStyle
.just 
= teFlushDefault
; 
 491     columnDesc
.headerBtnDesc
.btnFontStyle
.font 
= kControlFontViewSystemFont
; 
 492     columnDesc
.headerBtnDesc
.btnFontStyle
.style 
= normal
; 
 493     columnDesc
.headerBtnDesc
.titleString 
= NULL
; 
 495     columnDesc
.headerBtnDesc
.minimumWidth 
= 0; 
 496     columnDesc
.headerBtnDesc
.maximumWidth 
= 10000; 
 498     columnDesc
.propertyDesc
.propertyID 
= kTextColumnId
; 
 499     columnDesc
.propertyDesc
.propertyType 
= kDataBrowserTextType
; 
 500     columnDesc
.propertyDesc
.propertyFlags 
= kDataBrowserTableViewSelectionColumn
; 
 501     columnDesc
.propertyDesc
.propertyFlags 
|= kDataBrowserListViewTypeSelectColumn
; 
 503     verify_noerr( AddColumn( &columnDesc
, kDataBrowserListViewAppendColumn 
) ); 
 505     columnDesc
.headerBtnDesc
.minimumWidth 
= 0; 
 506     columnDesc
.headerBtnDesc
.maximumWidth 
= 0; 
 507     columnDesc
.propertyDesc
.propertyID 
= kNumericOrderColumnId
; 
 508     columnDesc
.propertyDesc
.propertyType 
= kDataBrowserPropertyRelevanceRankPart
; 
 509     columnDesc
.propertyDesc
.propertyFlags 
= kDataBrowserTableViewSelectionColumn
; 
 510     columnDesc
.propertyDesc
.propertyFlags 
|= kDataBrowserListViewTypeSelectColumn
; 
 512     verify_noerr( AddColumn( &columnDesc
, kDataBrowserListViewAppendColumn 
) ); 
 514     SetDataBrowserSortProperty( m_controlRef 
, kTextColumnId
); 
 515     if ( m_sortOrder 
== SortOrder_Text_Ascending 
) 
 517         SetDataBrowserSortProperty( m_controlRef 
, kTextColumnId
); 
 518         SetDataBrowserSortOrder( m_controlRef 
, kDataBrowserOrderIncreasing
); 
 522         SetDataBrowserSortProperty( m_controlRef 
, kNumericOrderColumnId
); 
 523         SetDataBrowserSortOrder( m_controlRef 
, kDataBrowserOrderIncreasing
); 
 526     verify_noerr( AutoSizeColumns() ); 
 527     verify_noerr( SetHiliteStyle(kDataBrowserTableViewFillHilite 
) ); 
 528     verify_noerr( SetHeaderButtonHeight( 0 ) ); 
 529     err 
= SetHasScrollBars( (style 
& wxHSCROLL
) != 0 , true ); 
 531     // shouldn't be necessary anymore under 10.2 
 532     m_peer
->SetData( kControlNoPart
, kControlDataBrowserIncludesFrameAndFocusTag
, (Boolean
)false ); 
 533     m_peer
->SetNeedsFocusRect( true ); 
 537 wxMacDataBrowserListControl::~wxMacDataBrowserListControl() 
 541 wxWindow 
* wxMacDataBrowserListControl::GetPeer() const 
 543     return wxDynamicCast( wxMacControl::GetPeer() , wxWindow 
); 
 546 wxMacDataItem
* wxMacDataBrowserListControl::CreateItem() 
 548     return new wxMacListBoxItem(); 
 553 // in case we need that one day 
 555 // ============================================================================ 
 556 // HIView owner-draw-based implementation 
 557 // ============================================================================ 
 559 static pascal void ListBoxDrawProc( 
 560     ControlRef browser
, DataBrowserItemID item
, DataBrowserPropertyID property
, 
 561     DataBrowserItemState itemState
, const Rect 
*itemRect
, SInt16 depth
, Boolean isColorDevice 
) 
 563     CFStringRef cfString
; 
 564     ThemeDrawingState themeState
; 
 567     GetThemeDrawingState( &themeState 
); 
 568     cfString 
= CFStringCreateWithFormat( NULL
, NULL
, CFSTR("Row %d"), item 
); 
 570     //  In this sample we handle the "selected" state; all others fall through to our "active" state 
 571     if ( itemState 
== kDataBrowserItemIsSelected 
) 
 573         ThemeBrush colorBrushID
; 
 575         // TODO: switch over to wxSystemSettingsNative::GetColour() when kThemeBrushSecondaryHighlightColor 
 576         // is incorporated Panther DB starts using kThemeBrushSecondaryHighlightColor 
 577         // for inactive browser highlighting 
 578         if ( !IsControlActive( browser 
) ) 
 579             colorBrushID 
= kThemeBrushSecondaryHighlightColor
; 
 581             colorBrushID 
= kThemeBrushPrimaryHighlightColor
; 
 583         // First paint the hilite rect, then the text on top 
 584         SetThemePen( colorBrushID
, 32, true ); 
 585         PaintRect( itemRect 
); 
 586         SetThemeDrawingState( themeState
, false ); 
 589     DrawThemeTextBox( cfString
, kThemeApplicationFont
, kThemeStateActive
, true, itemRect
, teFlushDefault
, NULL 
); 
 590     SetThemeDrawingState( themeState
, true ); 
 592     if ( cfString 
!= NULL 
) 
 593         CFRelease( cfString 
); 
 599 #endif // wxUSE_LISTBOX