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