added wxListBox::HitTest() from Ryan (patch 1446207)
[wxWidgets.git] / src / mac / carbon / listbox.cpp
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/app.h"
17 #include "wx/listbox.h"
18 #include "wx/button.h"
19 #include "wx/settings.h"
20 #include "wx/toplevel.h"
21 #include "wx/dynarray.h"
22 #include "wx/log.h"
23
24 #include "wx/utils.h"
25
26 #include "wx/mac/uma.h"
27
28 const short kTextColumnId = 1024 ;
29
30 // new DataBrowser-based version:
31 // because of the limited insert functionality of DataBrowser,
32 // we just introduce IDs corresponding to the line number
33
34
35 IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl)
36
37 BEGIN_EVENT_TABLE(wxListBox, wxControl)
38 #ifndef __WXMAC_OSX__
39 // EVT_SIZE( wxListBox::OnSize )
40 EVT_CHAR( wxListBox::OnChar )
41 #endif
42 END_EVENT_TABLE()
43
44
45 DataBrowserItemDataUPP gDataBrowserItemDataUPP = NULL ;
46 DataBrowserItemNotificationUPP gDataBrowserItemNotificationUPP = NULL ;
47 DataBrowserDrawItemUPP gDataBrowserDrawItemUPP = NULL ;
48
49
50 #if TARGET_API_MAC_OSX
51 static pascal void DataBrowserItemNotificationProc(ControlRef browser, DataBrowserItemID itemID,
52 DataBrowserItemNotification message, DataBrowserItemDataRef itemData)
53 #else
54 static pascal void DataBrowserItemNotificationProc(ControlRef browser, DataBrowserItemID itemID,
55 DataBrowserItemNotification message)
56 #endif
57 {
58 long ref = GetControlReference( browser ) ;
59 if ( ref )
60 {
61 wxListBox* list = wxDynamicCast( (wxObject*) ref , wxListBox ) ;
62 int i = itemID - 1 ;
63 if (i >= 0 && i < list->GetCount() )
64 {
65 bool trigger = false ;
66 wxCommandEvent event( wxEVT_COMMAND_LISTBOX_SELECTED, list->GetId() );
67 switch ( message )
68 {
69 case kDataBrowserItemDeselected :
70 if ( list->HasMultipleSelection() )
71 trigger = !list->MacIsSelectionSuppressed() ;
72 break ;
73
74 case kDataBrowserItemSelected :
75 trigger = !list->MacIsSelectionSuppressed() ;
76 break ;
77
78 case kDataBrowserItemDoubleClicked :
79 event.SetEventType( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED ) ;
80 trigger = true ;
81 break ;
82
83 default :
84 break ;
85 }
86
87 if ( trigger )
88 {
89 event.SetEventObject( list );
90 if ( list->HasClientObjectData() )
91 event.SetClientObject( list->GetClientObject( i ) );
92 else if ( list->HasClientUntypedData() )
93 event.SetClientData( list->GetClientData( i ) );
94 event.SetString( list->GetString( i ) );
95 event.SetInt( i ) ;
96 event.SetExtraLong( list->HasMultipleSelection() ? message == kDataBrowserItemSelected : true );
97 wxPostEvent( list->GetEventHandler() , event ) ;
98 // direct notification is not always having the listbox GetSelection() having in synch with event
99 // list->GetEventHandler()->ProcessEvent(event) ;
100 }
101 }
102 }
103 }
104
105 static pascal OSStatus ListBoxGetSetItemData(ControlRef browser,
106 DataBrowserItemID itemID, DataBrowserPropertyID property,
107 DataBrowserItemDataRef itemData, Boolean changeValue)
108 {
109 OSStatus err = errDataBrowserPropertyNotSupported;
110
111 if ( ! changeValue )
112 {
113 switch (property)
114 {
115 case kTextColumnId:
116 {
117 long ref = GetControlReference( browser ) ;
118 if ( ref )
119 {
120 wxListBox* list = wxDynamicCast( (wxObject*) ref , wxListBox ) ;
121 int i = itemID - 1 ;
122 if (i >= 0 && i < list->GetCount() )
123 {
124 wxMacCFStringHolder cf( list->GetString( i ) , list->GetFont().GetEncoding() ) ;
125 verify_noerr( ::SetDataBrowserItemDataText( itemData , cf ) ) ;
126 err = noErr ;
127 }
128 }
129 }
130 break;
131
132 default:
133 break;
134 }
135 }
136
137 return err;
138 }
139
140 static pascal void ListBoxDrawProc( ControlRef browser , DataBrowserItemID item , DataBrowserPropertyID property ,
141 DataBrowserItemState itemState , const Rect *itemRect , SInt16 depth , Boolean isColorDevice )
142 {
143 CFStringRef cfString;
144 ThemeDrawingState themeState ;
145 long systemVersion;
146
147 GetThemeDrawingState( &themeState ) ;
148 cfString = CFStringCreateWithFormat( NULL, NULL, CFSTR("Row %d"), item );
149
150 // In this sample we handle the "selected" state; all others fall through to our "active" state
151 if ( itemState == kDataBrowserItemIsSelected )
152 {
153 ThemeBrush colorBrushID;
154
155 Gestalt( gestaltSystemVersion, &systemVersion );
156
157 // TODO: switch over to wxSystemSettingsNative::GetColour() when kThemeBrushSecondaryHighlightColor is incorporated
158 // Panther DB starts using kThemeBrushSecondaryHighlightColor for inactive browser hilighting
159 if ( (systemVersion >= 0x00001030) && !IsControlActive( browser ) )
160 colorBrushID = kThemeBrushSecondaryHighlightColor;
161 else
162 colorBrushID = kThemeBrushPrimaryHighlightColor;
163
164 // First paint the hilite rect, then the text on top
165 SetThemePen( colorBrushID, 32, true );
166 PaintRect( itemRect );
167 SetThemeDrawingState( themeState , false ) ;
168 }
169
170 DrawThemeTextBox( cfString, kThemeApplicationFont, kThemeStateActive, true, itemRect, teFlushDefault, NULL );
171 SetThemeDrawingState( themeState , true ) ;
172
173 if ( cfString != NULL )
174 CFRelease( cfString );
175 }
176
177 // Listbox item
178 wxListBox::wxListBox()
179 {
180 m_noItems = 0;
181 m_selected = 0;
182 m_macList = NULL ;
183 m_suppressSelection = false ;
184 }
185
186 bool wxListBox::Create(wxWindow *parent,
187 wxWindowID id,
188 const wxPoint& pos,
189 const wxSize& size,
190 const wxArrayString& choices,
191 long style,
192 const wxValidator& validator,
193 const wxString& name)
194 {
195 wxCArrayString chs(choices);
196
197 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
198 style, validator, name);
199 }
200
201 bool wxListBox::Create(wxWindow *parent,
202 wxWindowID id,
203 const wxPoint& pos,
204 const wxSize& size,
205 int n,
206 const wxString choices[],
207 long style,
208 const wxValidator& validator,
209 const wxString& name)
210 {
211 m_macIsUserPane = false ;
212
213 wxASSERT_MSG( !(style & wxLB_MULTIPLE) || !(style & wxLB_EXTENDED),
214 _T("only one of listbox selection modes can be specified") );
215
216 if ( !wxListBoxBase::Create(parent, id, pos, size, style & ~(wxHSCROLL|wxVSCROLL), validator, name) )
217 return false;
218
219 m_noItems = 0 ; // this will be increased by our append command
220 m_selected = 0;
221
222 Rect bounds = wxMacGetBoundsForControl( this , pos , size ) ;
223
224 m_peer = new wxMacControl( this ) ;
225 verify_noerr(
226 ::CreateDataBrowserControl(
227 MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds,
228 kDataBrowserListView, m_peer->GetControlRefAddr() ) );
229
230 DataBrowserSelectionFlags options = kDataBrowserDragSelect ;
231 if ( style & wxLB_MULTIPLE )
232 options |= kDataBrowserAlwaysExtendSelection | kDataBrowserCmdTogglesSelection ;
233 else if ( style & wxLB_EXTENDED )
234 ; // default behaviour
235 else
236 options |= kDataBrowserSelectOnlyOne ;
237
238 verify_noerr( m_peer->SetSelectionFlags( options ) );
239
240 if ( gDataBrowserItemDataUPP == NULL )
241 gDataBrowserItemDataUPP = NewDataBrowserItemDataUPP(ListBoxGetSetItemData) ;
242 if ( gDataBrowserItemNotificationUPP == NULL )
243 {
244 gDataBrowserItemNotificationUPP =
245 #if TARGET_API_MAC_OSX
246 (DataBrowserItemNotificationUPP) NewDataBrowserItemNotificationWithItemUPP(DataBrowserItemNotificationProc) ;
247 #else
248 NewDataBrowserItemNotificationUPP(DataBrowserItemNotificationProc) ;
249 #endif
250 }
251
252 if ( gDataBrowserDrawItemUPP == NULL )
253 gDataBrowserDrawItemUPP = NewDataBrowserDrawItemUPP(ListBoxDrawProc) ;
254
255 DataBrowserCallbacks callbacks ;
256 InitializeDataBrowserCallbacks( &callbacks , kDataBrowserLatestCallbacks ) ;
257
258 callbacks.u.v1.itemDataCallback = gDataBrowserItemDataUPP;
259 callbacks.u.v1.itemNotificationCallback = gDataBrowserItemNotificationUPP;
260 m_peer->SetCallbacks( &callbacks);
261
262 DataBrowserCustomCallbacks customCallbacks ;
263 InitializeDataBrowserCustomCallbacks( &customCallbacks , kDataBrowserLatestCustomCallbacks ) ;
264
265 customCallbacks.u.v1.drawItemCallback = gDataBrowserDrawItemUPP ;
266
267 SetDataBrowserCustomCallbacks( m_peer->GetControlRef() , &customCallbacks ) ;
268
269 DataBrowserListViewColumnDesc columnDesc ;
270 columnDesc.headerBtnDesc.titleOffset = 0;
271 columnDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc;
272 columnDesc.headerBtnDesc.btnFontStyle.flags = kControlUseFontMask | kControlUseJustMask;
273 columnDesc.headerBtnDesc.btnContentInfo.contentType = kControlNoContent;
274 columnDesc.headerBtnDesc.btnFontStyle.just = teFlushDefault;
275 columnDesc.headerBtnDesc.minimumWidth = 0;
276 columnDesc.headerBtnDesc.maximumWidth = 10000;
277
278 columnDesc.headerBtnDesc.btnFontStyle.font = kControlFontViewSystemFont;
279 columnDesc.headerBtnDesc.btnFontStyle.style = normal;
280 columnDesc.headerBtnDesc.titleString = NULL ; // CFSTR( "" );
281
282 columnDesc.propertyDesc.propertyID = kTextColumnId;
283 columnDesc.propertyDesc.propertyType = kDataBrowserTextType ; // kDataBrowserCustomType;
284 columnDesc.propertyDesc.propertyFlags = kDataBrowserTableViewSelectionColumn;
285
286 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
287 columnDesc.propertyDesc.propertyFlags |= kDataBrowserListViewTypeSelectColumn;
288 #endif
289
290 verify_noerr( m_peer->AddListViewColumn( &columnDesc, kDataBrowserListViewAppendColumn ) ) ;
291 verify_noerr( m_peer->AutoSizeListViewColumns() ) ;
292 verify_noerr( m_peer->SetHasScrollBars( false, true ) ) ;
293 verify_noerr( m_peer->SetTableViewHiliteStyle( kDataBrowserTableViewFillHilite ) ) ;
294 verify_noerr( m_peer->SetListViewHeaderBtnHeight( 0 ) ) ;
295
296 #if 0
297 // shouldn't be necessary anymore under 10.2
298 m_peer->SetData( kControlNoPart, kControlDataBrowserIncludesFrameAndFocusTag, (Boolean) false ) ;
299 m_peer->SetNeedsFocusRect( true ) ;
300 #endif
301
302 MacPostControlCreate( pos, size ) ;
303
304 for ( int i = 0 ; i < n ; i++ )
305 {
306 Append( choices[i] ) ;
307 }
308
309 // Needed because it is a wxControlWithItems
310 SetBestSize(size);
311
312 return true;
313 }
314
315 wxListBox::~wxListBox()
316 {
317 m_peer->SetReference( 0 ) ;
318 FreeData() ;
319
320 // avoid access during destruction
321 if ( m_macList )
322 m_macList = NULL ;
323 }
324
325 void wxListBox::FreeData()
326 {
327 if ( HasClientObjectData() )
328 {
329 for ( size_t n = 0; n < (size_t)m_noItems; n++ )
330 {
331 delete GetClientObject( n );
332 }
333 }
334 }
335
336 void wxListBox::DoSetSize(int x, int y,
337 int width, int height,
338 int sizeFlags )
339 {
340 wxControl::DoSetSize( x , y , width , height , sizeFlags ) ;
341 }
342
343 void wxListBox::DoSetFirstItem(int n)
344 {
345 MacScrollTo( n ) ;
346 }
347
348 void wxListBox::Delete(int n)
349 {
350 wxCHECK_RET( n >= 0 && n < m_noItems,
351 wxT("invalid index in wxListBox::Delete") );
352
353 if ( HasClientObjectData() )
354 delete GetClientObject( n );
355
356 m_stringArray.RemoveAt( n ) ;
357 m_dataArray.RemoveAt( n ) ;
358 m_noItems--;
359
360 MacDelete( n ) ;
361 }
362
363 int wxListBox::DoAppend(const wxString& item)
364 {
365 InvalidateBestSize();
366
367 int index = m_noItems ;
368 m_stringArray.Add( item ) ;
369 m_dataArray.Add( NULL );
370 m_noItems++;
371 DoSetItemClientData( index , NULL ) ;
372 MacAppend( item ) ;
373
374 return index ;
375 }
376
377 void wxListBox::DoSetItems(const wxArrayString& choices, void** clientData)
378 {
379 Clear() ;
380 int n = choices.GetCount();
381
382 for ( int i = 0 ; i < n ; ++i )
383 {
384 if ( clientData )
385 Append( choices[i] , clientData[i] ) ;
386 else
387 Append( choices[i] ) ;
388 }
389 }
390
391 int wxListBox::FindString(const wxString& s, bool bCase) const
392 {
393 if ( s.Right(1) == wxT("*") )
394 {
395 wxString search = s.Left( s.Length() - 1 ) ;
396 int len = search.Length() ;
397 Str255 s1 , s2 ;
398 wxMacStringToPascal( search , s2 ) ;
399
400 for ( int i = 0 ; i < m_noItems ; ++ i )
401 {
402 wxMacStringToPascal( m_stringArray[i].Left( len ) , s1 ) ;
403
404 if ( EqualString( s1 , s2 , bCase , false ) )
405 return i ;
406 }
407
408 if ( s.Left(1) == wxT("*") && s.Length() > 1 )
409 {
410 wxString st = s ;
411 st.MakeLower() ;
412
413 for ( int i = 0 ; i < m_noItems ; ++i )
414 {
415 if ( GetString( i ).Lower().Matches(st) )
416 return i ;
417 }
418 }
419 }
420 else
421 {
422 Str255 s1 , s2 ;
423
424 wxMacStringToPascal( s , s2 ) ;
425
426 for ( int i = 0 ; i < m_noItems ; ++ i )
427 {
428 wxMacStringToPascal( m_stringArray[i] , s1 ) ;
429
430 if ( EqualString( s1 , s2 , bCase , false ) )
431 return i ;
432 }
433 }
434
435 return wxNOT_FOUND;
436 }
437
438 void wxListBox::Clear()
439 {
440 FreeData();
441 m_noItems = 0;
442 m_stringArray.Empty() ;
443 m_dataArray.Empty() ;
444 MacClear() ;
445 }
446
447 void wxListBox::DoSetSelection(int n, bool select)
448 {
449 wxCHECK_RET( n == wxNOT_FOUND || (n >= 0 && n < m_noItems) ,
450 wxT("invalid index in wxListBox::SetSelection") );
451
452 if ( n == wxNOT_FOUND )
453 MacDeselectAll() ;
454 else
455 MacSetSelection( n , select ) ;
456 }
457
458 bool wxListBox::IsSelected(int n) const
459 {
460 wxCHECK_MSG( n >= 0 && n < m_noItems, false,
461 wxT("invalid index in wxListBox::Selected") );
462
463 return MacIsSelected( n ) ;
464 }
465
466 void *wxListBox::DoGetItemClientData(int n) const
467 {
468 wxCHECK_MSG( n >= 0 && n < m_noItems, NULL,
469 wxT("invalid index in wxListBox::GetClientData"));
470
471 return (void *)m_dataArray[n];
472 }
473
474 wxClientData *wxListBox::DoGetItemClientObject(int n) const
475 {
476 return (wxClientData *) DoGetItemClientData( n ) ;
477 }
478
479 void wxListBox::DoSetItemClientData(int n, void *clientData)
480 {
481 wxCHECK_RET( n >= 0 && n < m_noItems,
482 wxT("invalid index in wxListBox::SetClientData") );
483
484 wxASSERT_MSG( m_dataArray.GetCount() >= (size_t) n , wxT("invalid client_data array") ) ;
485
486 if ( m_dataArray.GetCount() > (size_t) n )
487 m_dataArray[n] = (char*)clientData ;
488 else
489 m_dataArray.Add( (char*)clientData ) ;
490 }
491
492 void wxListBox::DoSetItemClientObject(int n, wxClientData* clientData)
493 {
494 DoSetItemClientData(n, clientData);
495 }
496
497 // Return number of selections and an array of selected integers
498 int wxListBox::GetSelections(wxArrayInt& aSelections) const
499 {
500 return MacGetSelections( aSelections ) ;
501 }
502
503 // Get single selection, for single choice list items
504 int wxListBox::GetSelection() const
505 {
506 return MacGetSelection() ;
507 }
508
509 // Find string for position
510 wxString wxListBox::GetString(int n) const
511 {
512 wxCHECK_MSG( n >= 0 && n < m_noItems, wxEmptyString,
513 wxT("invalid index in wxListBox::GetString") );
514
515 return m_stringArray[n] ;
516 }
517
518 void wxListBox::DoInsertItems(const wxArrayString& items, int pos)
519 {
520 wxCHECK_RET( pos >= 0 && pos <= m_noItems,
521 wxT("invalid index in wxListBox::InsertItems") );
522
523 InvalidateBestSize();
524
525 int nItems = items.GetCount();
526
527 for ( int i = 0 ; i < nItems ; i++ )
528 {
529 m_stringArray.Insert( items[i] , pos + i ) ;
530 m_dataArray.Insert( NULL , pos + i ) ;
531 m_noItems++ ;
532 MacInsert( pos + i , items[i] ) ;
533 }
534 }
535
536 void wxListBox::SetString(int n, const wxString& s)
537 {
538 m_stringArray[n] = s ;
539 MacSet( n , s ) ;
540 }
541
542 wxSize wxListBox::DoGetBestSize() const
543 {
544 int lbWidth = 100; // some defaults
545 int lbHeight = 110;
546 int wLine;
547
548 {
549 wxMacPortStateHelper st( UMAGetWindowPort( (WindowRef)MacGetTopLevelWindowRef() ) ) ;
550
551 // TODO: clean this up
552 if ( m_font.Ok() )
553 {
554 ::TextFont( m_font.MacGetFontNum() ) ;
555 ::TextSize( m_font.MacGetFontSize() ) ;
556 ::TextFace( m_font.MacGetFontStyle() ) ;
557 }
558 else
559 {
560 ::TextFont( kFontIDMonaco ) ;
561 ::TextSize( 9 );
562 ::TextFace( 0 ) ;
563 }
564
565 // Find the widest line
566 for (int i = 0; i < GetCount(); i++)
567 {
568 wxString str( GetString( i ) );
569
570 #if wxUSE_UNICODE
571 Point bounds = {0, 0} ;
572 SInt16 baseline ;
573
574 // NB: what if m_font.Ok() == false ???
575 ::GetThemeTextDimensions(
576 wxMacCFStringHolder( str , m_font.GetEncoding() ) ,
577 kThemeCurrentPortFont,
578 kThemeStateActive,
579 false,
580 &bounds,
581 &baseline );
582 wLine = bounds.h ;
583 #else
584 wLine = ::TextWidth( str.c_str() , 0 , str.Length() ) ;
585 #endif
586
587 lbWidth = wxMax( lbWidth, wLine );
588 }
589
590 // Add room for the scrollbar
591 lbWidth += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
592
593 // And just a bit more
594 int cy = 12 ;
595 int cx = ::TextWidth( "X" , 0 , 1 ) ;
596 lbWidth += cx ;
597
598 // don't make the listbox too tall (limit height to around 10 items)
599 // but don't make it too small neither
600 lbHeight = wxMax( (cy + 4) * wxMin( wxMax( GetCount(), 3 ), 10 ), 70 );
601 }
602
603 return wxSize( lbWidth, lbHeight );
604 }
605
606 int wxListBox::GetCount() const
607 {
608 return m_noItems;
609 }
610
611 void wxListBox::Refresh(bool eraseBack, const wxRect *rect)
612 {
613 wxControl::Refresh( eraseBack , rect ) ;
614 }
615
616 // Some custom controls depend on this
617 /* static */ wxVisualAttributes
618 wxListBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
619 {
620 wxVisualAttributes attr;
621
622 attr.colFg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
623 attr.colBg = wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOX );
624 attr.font = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
625
626 return attr;
627 }
628
629 // ============================================================================
630 // list box control implementation
631 // ============================================================================
632
633 void wxListBox::MacDelete( int n )
634 {
635 wxArrayInt selectionBefore ;
636 MacGetSelections( selectionBefore ) ;
637
638 UInt32 id = m_noItems + 1 ;
639
640 verify_noerr( m_peer->RemoveItems( kDataBrowserNoItem , 1 , (UInt32*) &id , kDataBrowserItemNoProperty ) ) ;
641 for ( size_t i = 0 ; i < selectionBefore.GetCount() ; ++i )
642 {
643 int current = selectionBefore[i] ;
644 if ( current == n )
645 {
646 // selection was deleted
647 MacSetSelection( current , false ) ;
648 }
649 else if ( current > n )
650 {
651 // something behind the deleted item was selected -> move up
652 MacSetSelection( current - 1 , true ) ;
653 MacSetSelection( current , false ) ;
654 }
655 }
656
657 // refresh all
658 verify_noerr(
659 m_peer->UpdateItems(
660 kDataBrowserNoItem, 1, (UInt32*)kDataBrowserNoItem,
661 kDataBrowserItemNoProperty, kDataBrowserItemNoProperty ) ) ;
662 }
663
664 void wxListBox::MacInsert( int n , const wxString& text )
665 {
666 wxArrayInt selectionBefore ;
667 MacGetSelections( selectionBefore ) ;
668
669 // this has already been increased
670 UInt32 id = m_noItems ;
671 verify_noerr( m_peer->AddItems( kDataBrowserNoItem , 1 , (UInt32*) &id , kDataBrowserItemNoProperty ) ) ;
672
673 for ( int i = selectionBefore.GetCount()-1 ; i >= 0 ; --i )
674 {
675 int current = selectionBefore[i] ;
676 if ( current >= n )
677 {
678 MacSetSelection( current + 1 , true ) ;
679 MacSetSelection( current , false ) ;
680 }
681 }
682
683 // refresh all
684 verify_noerr(
685 m_peer->UpdateItems(
686 kDataBrowserNoItem, 1, (UInt32*)kDataBrowserNoItem,
687 kDataBrowserItemNoProperty, kDataBrowserItemNoProperty ) ) ;
688 }
689
690 void wxListBox::MacAppend( const wxString& text )
691 {
692 UInt32 id = m_noItems ; // this has already been increased
693 verify_noerr( m_peer->AddItems( kDataBrowserNoItem , 1 , (UInt32*) &id , kDataBrowserItemNoProperty ) ) ;
694 // no need to deal with selections nor refreshed, as we have appended
695 }
696
697 void wxListBox::MacClear()
698 {
699 verify_noerr( m_peer->RemoveItems( kDataBrowserNoItem , 0 , NULL , kDataBrowserItemNoProperty ) ) ;
700 }
701
702 void wxListBox::MacDeselectAll()
703 {
704 bool former = MacSuppressSelection( true ) ;
705 verify_noerr(m_peer->SetSelectedItems( 0 , NULL , kDataBrowserItemsRemove ) ) ;
706 MacSuppressSelection( former ) ;
707 }
708
709 void wxListBox::MacSetSelection( int n , bool select )
710 {
711 bool former = MacSuppressSelection( true ) ;
712 UInt32 id = n + 1 ;
713
714 if ( m_peer->IsItemSelected( id ) != select )
715 {
716 if ( select )
717 verify_noerr( m_peer->SetSelectedItems( 1 , & id , HasMultipleSelection() ? kDataBrowserItemsAdd : kDataBrowserItemsAssign ) ) ;
718 else
719 verify_noerr( m_peer->SetSelectedItems( 1 , & id , kDataBrowserItemsRemove ) ) ;
720 }
721
722 MacScrollTo( n ) ;
723 MacSuppressSelection( former ) ;
724 }
725
726 bool wxListBox::MacSuppressSelection( bool suppress )
727 {
728 bool former = m_suppressSelection ;
729 m_suppressSelection = suppress ;
730 return former ;
731 }
732
733 bool wxListBox::MacIsSelected( int n ) const
734 {
735 return m_peer->IsItemSelected( n + 1 ) ;
736 }
737
738 int wxListBox::MacGetSelection() const
739 {
740 for ( int i = 0 ; i < GetCount() ; ++i )
741 {
742 if ( m_peer->IsItemSelected( i + 1 ) )
743 return i ;
744 }
745
746 return -1 ;
747 }
748
749 int wxListBox::MacGetSelections( wxArrayInt& aSelections ) const
750 {
751 int no_sel = 0 ;
752
753 aSelections.Empty();
754
755 UInt32 first , last ;
756 m_peer->GetSelectionAnchor( &first , &last ) ;
757 if ( first != kDataBrowserNoItem )
758 {
759 for ( size_t i = first ; i <= last ; ++i )
760 {
761 if ( m_peer->IsItemSelected( i ) )
762 {
763 aSelections.Add( i - 1 ) ;
764 no_sel++ ;
765 }
766 }
767 }
768
769 return no_sel ;
770 }
771
772 void wxListBox::MacSet( int n , const wxString& text )
773 {
774 // as we don't store the strings we only have to issue a redraw
775 UInt32 id = n + 1 ;
776 verify_noerr( m_peer->UpdateItems( kDataBrowserNoItem , 1 , &id , kDataBrowserItemNoProperty , kDataBrowserItemNoProperty ) ) ;
777 }
778
779 void wxListBox::MacScrollTo( int n )
780 {
781 UInt32 id = n + 1 ;
782 verify_noerr( m_peer->RevealItem( id , kTextColumnId , kDataBrowserRevealWithoutSelecting ) ) ;
783 }
784
785 int wxListBox::DoListHitTest(const wxPoint& point) const
786 {
787 //Yuck - there is no easy way to get a databrowseritem from a point
788 //so we need to iterate through our items to see which one this falls under
789 int count = GetCount();
790 DataBrowserTableViewColumnID colId = 0;
791
792 //Get column property id (req. for call to itempartbounds)
793 GetDataBrowserTableViewColumnProperty(m_peer->GetControlRef(), 0, &colId);
794
795 for(int i = 1; i <= count; ++i)
796 {
797 Rect bounds;
798 GetDataBrowserItemPartBounds(m_peer->GetControlRef(), i, colId,
799 kDataBrowserPropertyEnclosingPart,
800 &bounds);
801
802 //translate to client coords
803 MacRootWindowToWindow(&bounds.left, &bounds.top);
804 MacRootWindowToWindow(&bounds.right, &bounds.bottom);
805
806 #if 0 //debugging :)
807 wxPrintf(wxT("L:%i R:%i T:%i B:%i HT:%i,%i\n"),
808 bounds.left, bounds.right,
809 bounds.top, bounds.bottom,
810 point.x, point.y);
811 fflush(stdout);
812 #endif
813 //if point is within the bounds, return this item
814 if( (point.x >= bounds.left && point.x <= bounds.right) &&
815 (point.y >= bounds.top && point.y <= bounds.bottom) )
816 {
817 return i - 1; //found
818 }
819 }
820
821 return wxNOT_FOUND;
822 }
823
824 int wxListBox::MacHitTest(const wxPoint& point)
825 {
826 //Yuck - there is no easy way to get a databrowseritem from a point
827 //so we need to iterate through our items to see which one this falls under
828
829 // TODO: binary search would be faster
830 int count = GetCount();
831 DataBrowserTableViewColumnID colId = 0;
832
833 //Get column property id (req. for call to itempartbounds)
834 GetDataBrowserTableViewColumnProperty(m_peer->GetControlRef(), 0, &colId);
835
836 for(int i = 1; i <= count; ++i)
837 {
838 Rect bounds;
839 GetDataBrowserItemPartBounds(m_peer->GetControlRef(), i, colId,
840 kDataBrowserPropertyEnclosingPart,
841 &bounds);
842
843 //translate to client coords
844 //
845 // TODO: it would probably be more efficient to translate point to
846 // screen coordinates once outside of the loop
847 MacRootWindowToWindow(&bounds.left, &bounds.top);
848 MacRootWindowToWindow(&bounds.right, &bounds.bottom);
849
850 //if point is within the bounds, return this item
851 if( (point.x >= bounds.left && point.x <= bounds.right) &&
852 (point.y >= bounds.top && point.y <= bounds.bottom) )
853 {
854 return i - 1; //found
855 }
856 }
857
858 return wxNOT_FOUND;
859 }
860
861 #if !TARGET_API_MAC_OSX
862
863 void wxListBox::OnChar(wxKeyEvent& event)
864 {
865 // TODO: trigger proper events here
866 event.Skip() ;
867 return ;
868
869 if ( event.GetKeyCode() == WXK_RETURN || event.GetKeyCode() == WXK_NUMPAD_ENTER)
870 {
871 wxWindow* parent = GetParent() ;
872
873 while ( parent && !parent->IsTopLevel() && parent->GetDefaultItem() == NULL )
874 parent = parent->GetParent() ;
875
876 if ( parent && parent->GetDefaultItem() )
877 {
878 wxButton *def = wxDynamicCast(parent->GetDefaultItem(), wxButton);
879 if ( def && def->IsEnabled() )
880 {
881 wxCommandEvent event( wxEVT_COMMAND_BUTTON_CLICKED, def->GetId() );
882 event.SetEventObject( def );
883 def->Command( event );
884
885 return ;
886 }
887 }
888
889 event.Skip() ;
890 }
891
892 /* generate wxID_CANCEL if command-. or <esc> has been pressed (typically in dialogs) */
893 else if (event.GetKeyCode() == WXK_ESCAPE || (event.GetKeyCode() == '.' && event.MetaDown() ) )
894 {
895 // FIXME: look in ancestors, not just parent.
896 wxWindow* win = GetParent()->FindWindow( wxID_CANCEL ) ;
897 if (win)
898 {
899 wxCommandEvent new_event(wxEVT_COMMAND_BUTTON_CLICKED,wxID_CANCEL);
900 new_event.SetEventObject( win );
901 win->GetEventHandler()->ProcessEvent( new_event );
902 }
903 }
904 else if ( event.GetKeyCode() == WXK_TAB )
905 {
906 wxNavigationKeyEvent new_event;
907 new_event.SetEventObject( this );
908 new_event.SetDirection( !event.ShiftDown() );
909 /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
910 new_event.SetWindowChange( event.ControlDown() );
911 new_event.SetCurrentFocus( this );
912 if ( !GetEventHandler()->ProcessEvent( new_event ) )
913 event.Skip() ;
914 }
915 else if ( event.GetKeyCode() == WXK_DOWN || event.GetKeyCode() == WXK_UP )
916 {
917 // perform the default key handling first
918 wxControl::OnKeyDown( event ) ;
919
920 wxCommandEvent event( wxEVT_COMMAND_LISTBOX_SELECTED, m_windowId );
921 event.SetEventObject( this );
922
923 wxArrayInt aSelections;
924 int n, count = GetSelections(aSelections);
925 if ( count > 0 )
926 {
927 n = aSelections[0];
928 if ( HasClientObjectData() )
929 event.SetClientObject( GetClientObject( n ) );
930 else if ( HasClientUntypedData() )
931 event.SetClientData( GetClientData( n ) );
932 event.SetString( GetString( n ) );
933 }
934 else
935 {
936 n = -1;
937 }
938
939 event.SetInt( n );
940
941 GetEventHandler()->ProcessEvent(event);
942 }
943 else
944 {
945 if ( event.GetTimestamp() > m_lastTypeIn + 60 )
946 m_typeIn = wxEmptyString ;
947
948 m_lastTypeIn = event.GetTimestamp() ;
949 m_typeIn += (char) event.GetKeyCode() ;
950 int line = FindString( wxT("*") + m_typeIn + wxT("*") ) ;
951 if ( line >= 0 )
952 {
953 if ( GetSelection() != line )
954 {
955 SetSelection( line ) ;
956
957 wxCommandEvent event( wxEVT_COMMAND_LISTBOX_SELECTED, m_windowId );
958 event.SetEventObject( this );
959
960 if ( HasClientObjectData() )
961 event.SetClientObject( GetClientObject( line ) );
962 else if ( HasClientUntypedData() )
963 event.SetClientData( GetClientData( line ) );
964 event.SetString( GetString( line ) );
965 event.SetInt( line );
966
967 GetEventHandler()->ProcessEvent(event);
968 }
969 }
970 }
971 }
972
973 #endif // !TARGET_API_MAC_OSX
974
975 #endif