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/osx/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_blockEvents
= false;
77 m_macIsUserPane
= false;
79 wxASSERT_MSG( !(style
& wxLB_MULTIPLE
) || !(style
& wxLB_EXTENDED
),
80 wxT("only a single listbox selection mode can be specified") );
82 if ( !wxListBoxBase::Create( parent
, id
, pos
, size
, style
& ~(wxHSCROLL
| wxVSCROLL
), validator
, name
) )
85 wxMacDataBrowserListControl
* control
= new wxMacDataBrowserListControl( this, pos
, size
, style
);
86 // TODO CHECK control->SetClientDataType( m_clientDataItemsType );
89 MacPostControlCreate( pos
, size
);
93 // Needed because it is a wxControlWithItems
94 SetInitialSize( size
);
99 wxListBox::~wxListBox()
102 m_peer
->SetReference( 0 );
105 void wxListBox::FreeData()
107 GetPeer()->MacClear();
110 void wxListBox::DoSetFirstItem(int n
)
112 GetPeer()->MacScrollTo( n
);
115 void wxListBox::EnsureVisible(int n
)
117 GetPeer()->MacScrollTo( n
);
120 void wxListBox::DoDeleteOneItem(unsigned int n
)
122 wxCHECK_RET( IsValid(n
), wxT("invalid index in wxListBox::Delete") );
124 m_blockEvents
= true;
125 GetPeer()->MacDelete( n
);
126 m_blockEvents
= false;
128 UpdateOldSelections();
131 int wxListBox::DoInsertItems(const wxArrayStringsAdapter
& items
,
134 wxClientDataType type
)
136 InvalidateBestSize();
138 GetPeer()->MacInsert( pos
, items
);
139 const unsigned int count
= items
.GetCount();
142 for (unsigned int i
= 0; i
< count
; ++i
)
143 AssignNewItemClientData( pos
+ i
, clientData
, i
, type
);
146 return pos
+ count
- 1;
148 UpdateOldSelections();
151 int wxListBox::FindString(const wxString
& s
, bool bCase
) const
153 for ( size_t i
= 0; i
< GetCount(); ++ i
)
155 if (s
.IsSameAs( GetString( i
), bCase
) )
162 void wxListBox::DoClear()
164 m_blockEvents
= true;
166 m_blockEvents
= false;
168 UpdateOldSelections();
171 void wxListBox::DoSetSelection(int n
, bool select
)
173 wxCHECK_RET( n
== wxNOT_FOUND
|| IsValid(n
),
174 wxT("invalid index in wxListBox::SetSelection") );
176 m_blockEvents
= true;
178 if ( n
== wxNOT_FOUND
)
179 GetPeer()->MacDeselectAll();
181 GetPeer()->MacSetSelection( n
, select
, HasMultipleSelection() );
183 m_blockEvents
= false;
185 UpdateOldSelections();
188 bool wxListBox::IsSelected(int n
) const
190 wxCHECK_MSG( IsValid(n
), false, wxT("invalid index in wxListBox::Selected") );
192 return GetPeer()->MacIsSelected( n
);
195 void *wxListBox::DoGetItemClientData(unsigned int n
) const
197 wxCHECK_MSG( IsValid(n
), NULL
, wxT("invalid index in wxListBox::GetClientData"));
198 return GetPeer()->MacGetClientData( n
);
201 void wxListBox::DoSetItemClientData(unsigned int n
, void *clientData
)
203 wxCHECK_RET( IsValid(n
), wxT("invalid index in wxListBox::SetClientData") );
204 GetPeer()->MacSetClientData( n
, clientData
);
207 // Return number of selections and an array of selected integers
208 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const
210 return GetPeer()->MacGetSelections( aSelections
);
213 // Get single selection, for single choice list items
214 int wxListBox::GetSelection() const
216 return GetPeer()->MacGetSelection();
219 // Find string for position
220 wxString
wxListBox::GetString(unsigned int n
) const
222 wxCHECK_MSG( IsValid(n
), wxEmptyString
, wxT("invalid index in wxListBox::GetString") );
223 return GetPeer()->MacGetString(n
);
226 void wxListBox::SetString(unsigned int n
, const wxString
& s
)
228 GetPeer()->MacSetString( n
, s
);
231 wxSize
wxListBox::DoGetBestSize() const
233 int lbWidth
= 100; // some defaults
238 wxClientDC
dc(const_cast<wxListBox
*>(this));
239 dc
.SetFont(GetFont());
241 // Find the widest line
242 for (unsigned int i
= 0; i
< GetCount(); i
++)
244 wxString
str( GetString( i
) );
246 wxCoord width
, height
;
247 dc
.GetTextExtent( str
, &width
, &height
);
249 lbWidth
= wxMax( lbWidth
, wLine
);
252 // Add room for the scrollbar
253 lbWidth
+= wxSystemSettings::GetMetric( wxSYS_VSCROLL_X
);
255 // And just a bit more
258 wxCoord width
, height
;
259 dc
.GetTextExtent( wxT("XX") , &width
, &height
);
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 );
268 return wxSize( lbWidth
, lbHeight
);
271 unsigned int wxListBox::GetCount() const
273 return GetPeer()->MacGetCount();
276 void wxListBox::Refresh(bool eraseBack
, const wxRect
*rect
)
278 wxControl::Refresh( eraseBack
, rect
);
281 // Some custom controls depend on this
282 /* static */ wxVisualAttributes
283 wxListBox::GetClassDefaultAttributes(wxWindowVariant
WXUNUSED(variant
))
285 wxVisualAttributes attr
;
287 attr
.colFg
= wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT
);
288 attr
.colBg
= wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOX
);
289 attr
.font
.MacCreateFromThemeFont(kThemeViewsFont
);
294 int wxListBox::DoListHitTest(const wxPoint
& inpoint
) const
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
306 wxPoint point
= inpoint
;
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"));
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"));
323 err
= GetDataBrowserTableViewRowHeight(m_peer
->GetControlRef(), &height
);
324 wxCHECK_MSG(err
== noErr
, wxNOT_FOUND
, wxT("Unexpected error from GetDataBrowserTableViewRowHeight"));
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;
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
)
337 err
= GetDataBrowserItemPartBounds(
338 m_peer
->GetControlRef(), low
+ 1, colId
,
339 kDataBrowserPropertyEnclosingPart
,
340 &bounds
); // note +1 to translate to Mac ID
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") );
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
)
356 int mid
= (low
+ high
) / 2;
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
,
365 wxT("Unexpected error from GetDataBrowserItemPartBounds") );
367 if ( err
== errDataBrowserItemNotFound
)
369 // item not visible, attempt to find a visible one
373 else // visible item, do actual hitttest
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
378 if ((point
.x
>= bounds
.left
&& point
.x
<= bounds
.right
) &&
379 (point
.y
>= bounds
.top
&& point
.y
<= bounds
.bottom
) )
385 if ( point
.y
< bounds
.top
)
386 // index(bounds) greater then key(point)
389 // index(bounds) less then key(point)
397 // ============================================================================
398 // data browser based implementation
399 // ============================================================================
401 wxMacListBoxItem::wxMacListBoxItem()
406 wxMacListBoxItem::~wxMacListBoxItem()
410 void wxMacListBoxItem::Notification(wxMacDataItemBrowserControl
*owner
,
411 DataBrowserItemNotification message
,
412 DataBrowserItemDataRef
WXUNUSED(itemData
) ) const
414 wxMacDataBrowserListControl
*lb
= wxDynamicCast(owner
,wxMacDataBrowserListControl
);
416 // we want to depend on as little as possible to make sure tear-down of controls is safe
418 if ( message
== kDataBrowserItemRemoved
)
420 if ( lb
!= NULL
&& lb
->GetClientDataType() == wxClientData_Object
)
422 delete (wxClientData
*) (m_data
);
429 wxListBox
*list
= wxDynamicCast( owner
->GetPeer() , wxListBox
);
430 wxCHECK_RET( list
!= NULL
, wxT("Listbox expected"));
432 if (message
== kDataBrowserItemDoubleClicked
)
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
);
448 IMPLEMENT_DYNAMIC_CLASS( wxMacDataBrowserListControl
, wxMacDataItemBrowserControl
)
450 wxMacDataBrowserListControl::wxMacDataBrowserListControl( wxWindow
*peer
, const wxPoint
& pos
, const wxSize
& size
, long style
)
451 : wxMacDataItemBrowserControl( peer
, pos
, size
, style
)
453 OSStatus err
= noErr
;
454 m_clientDataItemsType
= wxClientData_None
;
455 if ( style
& wxLB_SORT
)
456 m_sortOrder
= SortOrder_Text_Ascending
;
458 DataBrowserSelectionFlags options
= kDataBrowserDragSelect
;
459 if ( style
& wxLB_MULTIPLE
)
461 options
|= kDataBrowserAlwaysExtendSelection
| kDataBrowserCmdTogglesSelection
;
463 else if ( style
& wxLB_EXTENDED
)
465 options
|= kDataBrowserCmdTogglesSelection
;
469 options
|= kDataBrowserSelectOnlyOne
;
471 err
= SetSelectionFlags( options
);
474 DataBrowserListViewColumnDesc columnDesc
;
475 columnDesc
.headerBtnDesc
.titleOffset
= 0;
476 columnDesc
.headerBtnDesc
.version
= kDataBrowserListViewLatestHeaderDesc
;
478 columnDesc
.headerBtnDesc
.btnFontStyle
.flags
=
479 kControlUseFontMask
| kControlUseJustMask
;
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
;
487 columnDesc
.headerBtnDesc
.minimumWidth
= 0;
488 columnDesc
.headerBtnDesc
.maximumWidth
= 10000;
490 columnDesc
.propertyDesc
.propertyID
= kTextColumnId
;
491 columnDesc
.propertyDesc
.propertyType
= kDataBrowserTextType
;
492 columnDesc
.propertyDesc
.propertyFlags
= kDataBrowserTableViewSelectionColumn
;
493 columnDesc
.propertyDesc
.propertyFlags
|= kDataBrowserListViewTypeSelectColumn
;
495 verify_noerr( AddColumn( &columnDesc
, kDataBrowserListViewAppendColumn
) );
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
;
504 verify_noerr( AddColumn( &columnDesc
, kDataBrowserListViewAppendColumn
) );
506 SetDataBrowserSortProperty( m_controlRef
, kTextColumnId
);
507 if ( m_sortOrder
== SortOrder_Text_Ascending
)
509 SetDataBrowserSortProperty( m_controlRef
, kTextColumnId
);
510 SetDataBrowserSortOrder( m_controlRef
, kDataBrowserOrderIncreasing
);
514 SetDataBrowserSortProperty( m_controlRef
, kNumericOrderColumnId
);
515 SetDataBrowserSortOrder( m_controlRef
, kDataBrowserOrderIncreasing
);
518 verify_noerr( AutoSizeColumns() );
519 verify_noerr( SetHiliteStyle(kDataBrowserTableViewFillHilite
) );
520 verify_noerr( SetHeaderButtonHeight( 0 ) );
521 err
= SetHasScrollBars( (style
& wxHSCROLL
) != 0 , true );
523 // shouldn't be necessary anymore under 10.2
524 m_peer
->SetData( kControlNoPart
, kControlDataBrowserIncludesFrameAndFocusTag
, (Boolean
)false );
525 m_peer
->SetNeedsFocusRect( true );
529 wxMacDataBrowserListControl::~wxMacDataBrowserListControl()
533 void wxMacDataBrowserListControl::ItemNotification(
534 const wxMacDataItem
* itemID
,
535 DataBrowserItemNotification message
,
536 DataBrowserItemDataRef itemData
)
538 wxListBox
*list
= wxDynamicCast( GetPeer() , wxListBox
);
539 wxCHECK_RET( list
!= NULL
, wxT("Listbox expected"));
541 if (list
->HasMultipleSelection() && (message
== kDataBrowserSelectionSetChanged
) && (!list
->MacGetBlockEvents()))
543 list
->CalcAndSendEvent();
547 if ((message
== kDataBrowserSelectionSetChanged
) && (!list
->MacGetBlockEvents()))
549 wxCommandEvent
event( wxEVT_COMMAND_LISTBOX_SELECTED
, list
->GetId() );
551 int sel
= list
->GetSelection();
552 if ((sel
< 0) || (sel
> list
->GetCount())) // OS X can select an item below the last item (why?)
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
) );
561 event
.SetExtraLong( 1 );
562 list
->HandleWindowEvent(event
);
566 // call super for item level(wxMacDataItem->Notification) callback processing
567 wxMacDataItemBrowserControl::ItemNotification( itemID
, message
, itemData
);
570 wxWindow
* wxMacDataBrowserListControl::GetPeer() const
572 return wxDynamicCast( wxMacControl::GetPeer() , wxWindow
);
575 wxMacDataItem
* wxMacDataBrowserListControl::CreateItem()
577 return new wxMacListBoxItem();
582 // in case we need that one day
584 // ============================================================================
585 // HIView owner-draw-based implementation
586 // ============================================================================
588 static pascal void ListBoxDrawProc(
589 ControlRef browser
, DataBrowserItemID item
, DataBrowserPropertyID property
,
590 DataBrowserItemState itemState
, const Rect
*itemRect
, SInt16 depth
, Boolean isColorDevice
)
592 CFStringRef cfString
;
593 ThemeDrawingState themeState
;
596 GetThemeDrawingState( &themeState
);
597 cfString
= CFStringCreateWithFormat( NULL
, NULL
, CFSTR("Row %d"), item
);
599 // In this sample we handle the "selected" state; all others fall through to our "active" state
600 if ( itemState
== kDataBrowserItemIsSelected
)
602 ThemeBrush colorBrushID
;
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
;
610 colorBrushID
= kThemeBrushPrimaryHighlightColor
;
612 // First paint the hilite rect, then the text on top
613 SetThemePen( colorBrushID
, 32, true );
614 PaintRect( itemRect
);
615 SetThemeDrawingState( themeState
, false );
618 DrawThemeTextBox( cfString
, kThemeApplicationFont
, kThemeStateActive
, true, itemRect
, teFlushDefault
, NULL
);
619 SetThemeDrawingState( themeState
, true );
621 if ( cfString
!= NULL
)
622 CFRelease( cfString
);
628 #endif // wxUSE_LISTBOX