listbox cleanup
[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/checklst.h"
19 #include "wx/button.h"
20 #include "wx/settings.h"
21 #include "wx/toplevel.h"
22 #include "wx/dynarray.h"
23 #include "wx/log.h"
24
25 #include "wx/utils.h"
26
27 IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl)
28
29 BEGIN_EVENT_TABLE(wxListBox, wxControl)
30 END_EVENT_TABLE()
31
32 #include "wx/mac/uma.h"
33
34 // common interface for all implementations
35 class wxMacListControl : public wxMacControl
36 {
37 public :
38 wxMacListControl( wxListBox *peer ) :
39 wxMacControl( peer )
40 {
41 }
42 ~wxMacListControl()
43 {
44
45 }
46
47 virtual void UpdateLine( int n ) = 0;
48
49 virtual void MacDelete( int n ) = 0;
50 virtual void MacInsert( int n , const wxString& item)= 0;
51 virtual void MacInsert( int n , const wxArrayString& items)= 0;
52 virtual void MacAppend( const wxString& item)= 0;
53 virtual void MacSet( int n , const wxString& item )= 0;
54 virtual void MacClear()= 0;
55 virtual void MacDeselectAll()= 0;
56 virtual void MacSetSelection( int n , bool select )= 0;
57 virtual int MacGetSelection() const= 0;
58 virtual int MacGetSelections(wxArrayInt& aSelections) const= 0;
59 virtual bool MacIsSelected( int n ) const= 0;
60 virtual void MacScrollTo( int n )= 0;
61
62 wxListBox* GetPeer() const
63 {
64 return (wxListBox*) m_peer;
65 }
66 };
67
68 #if 0
69 // In case we have to replace data browser ...
70 // custom HIView based implementation
71
72 class wxMacCustomHIViewListControl : public wxMacListControl
73 {
74 public :
75 wxMacCustomHIViewListControl( wxListBox *peer , const wxPoint& pos, const wxSize& size, long style );
76 ~wxMacCustomHIViewListControl();
77
78 void MacDelete( int n );
79 void MacInsert( int n , const wxString& item);
80 void MacInsert( int n , const wxArrayString& items);
81 void MacAppend( const wxString& item);
82 void MacSet( int n , const wxString& item );
83 void MacClear();
84 void MacDeselectAll();
85 void MacSetSelection( int n , bool select );
86 int MacGetSelection() const;
87 int MacGetSelections(wxArrayInt& aSelections) const;
88 bool MacIsSelected( int n ) const;
89 void MacScrollTo( int n );
90 };
91 #endif
92
93 // data browser based implementation
94
95 class wxMacDataBrowserListControl : public wxMacListControl
96 {
97 public :
98 wxMacDataBrowserListControl( wxListBox *peer , const wxPoint& pos, const wxSize& size, long style );
99 ~wxMacDataBrowserListControl();
100
101 void UpdateLine( int n );
102
103 void MacDelete( int n );
104 void MacInsert( int n , const wxString& item);
105 void MacInsert( int n , const wxArrayString& items);
106 void MacAppend( const wxString& item);
107 void MacSet( int n , const wxString& item );
108 void MacClear();
109 void MacDeselectAll();
110 void MacSetSelection( int n , bool select );
111 int MacGetSelection() const;
112 int MacGetSelections(wxArrayInt& aSelections) const;
113 bool MacIsSelected( int n ) const;
114 void MacScrollTo( int n );
115
116 virtual OSStatus SetSelectionFlags( DataBrowserSelectionFlags );
117 virtual OSStatus AddListViewColumn( DataBrowserListViewColumnDesc *columnDesc,
118 DataBrowserTableViewColumnIndex position );
119 virtual OSStatus AutoSizeListViewColumns();
120 virtual OSStatus SetHasScrollBars( bool horiz , bool vert );
121 virtual OSStatus SetTableViewHiliteStyle( DataBrowserTableViewHiliteStyle hiliteStyle );
122 virtual OSStatus SetListViewHeaderBtnHeight(UInt16 height);
123 virtual OSStatus SetCallbacks(const DataBrowserCallbacks * callbacks);
124 virtual OSStatus UpdateItems( DataBrowserItemID container, UInt32 numItems,
125 const DataBrowserItemID* items,
126 DataBrowserPropertyID preSortProperty,
127 DataBrowserPropertyID propertyID );
128 virtual OSStatus AddItems( DataBrowserItemID container, UInt32 numItems,
129 const DataBrowserItemID* items,
130 DataBrowserPropertyID preSortProperty );
131 virtual OSStatus RemoveItems( DataBrowserItemID container, UInt32 numItems,
132 const DataBrowserItemID* items,
133 DataBrowserPropertyID preSortProperty );
134 virtual OSStatus RevealItem( DataBrowserItemID item,
135 DataBrowserPropertyID propertyID,
136 DataBrowserRevealOptions options );
137 virtual OSStatus GetSelectionAnchor( DataBrowserItemID * first, DataBrowserItemID * last ) const;
138 virtual bool IsItemSelected( DataBrowserItemID item ) const;
139 virtual OSStatus SetSelectedItems(UInt32 numItems,
140 const DataBrowserItemID * items,
141 DataBrowserSetOption operation );
142
143 private :
144 // as we are getting the same events for human and API selection we have to suppress
145 // events in the latter case
146 bool MacSuppressSelection( bool suppress );
147 bool MacIsSelectionSuppressed() const { return m_suppressSelection; }
148 bool m_suppressSelection;
149
150 #if TARGET_API_MAC_OSX
151 static pascal void DataBrowserItemNotificationProc(ControlRef browser, DataBrowserItemID itemID,
152 DataBrowserItemNotification message, DataBrowserItemDataRef itemData);
153 #else
154 static pascal void DataBrowserItemNotificationProc(ControlRef browser, DataBrowserItemID itemID,
155 DataBrowserItemNotification message);
156 #endif
157 };
158
159 // ============================================================================
160 // data browser based implementation
161 // ============================================================================
162
163 const short kTextColumnId = 1024;
164 const short kCheckboxColumnId = 1025;
165
166 // new databrowserbased version
167 // because of the limited insert
168 // functionality of DataBrowser,
169 // we just introduce id s corresponding
170 // to the line number
171
172 DataBrowserItemDataUPP gDataBrowserItemDataUPP = NULL;
173 DataBrowserItemNotificationUPP gDataBrowserItemNotificationUPP = NULL;
174
175 #if TARGET_API_MAC_OSX
176 pascal void wxMacDataBrowserListControl::DataBrowserItemNotificationProc(ControlRef browser, DataBrowserItemID itemID,
177 DataBrowserItemNotification message, DataBrowserItemDataRef itemData)
178 #else
179 pascal void wxMacDataBrowserListControl::DataBrowserItemNotificationProc(ControlRef browser, DataBrowserItemID itemID,
180 DataBrowserItemNotification message)
181 #endif
182 {
183 long ref = GetControlReference( browser );
184 if ( ref )
185 {
186 wxListBox* list = wxDynamicCast( (wxObject*) ref , wxListBox );
187 wxMacDataBrowserListControl* peer = (wxMacDataBrowserListControl*) list->GetPeer();
188
189 int i = itemID - 1;
190 if (i >= 0 && i < (int)list->GetCount() )
191 {
192 bool trigger = false;
193 wxCommandEvent event( wxEVT_COMMAND_LISTBOX_SELECTED, list->GetId() );
194 switch ( message )
195 {
196 case kDataBrowserItemDeselected :
197 if ( list->HasMultipleSelection() )
198 trigger = !peer->MacIsSelectionSuppressed();
199 break;
200
201 case kDataBrowserItemSelected :
202 trigger = !peer->MacIsSelectionSuppressed();
203 break;
204
205 case kDataBrowserItemDoubleClicked :
206 event.SetEventType( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED );
207 trigger = true;
208 break;
209
210 default :
211 break;
212 }
213
214 if ( trigger )
215 {
216 event.SetEventObject( list );
217 if ( list->HasClientObjectData() )
218 event.SetClientObject( list->GetClientObject( i ) );
219 else if ( list->HasClientUntypedData() )
220 event.SetClientData( list->GetClientData( i ) );
221 event.SetString( list->GetString( i ) );
222 event.SetInt( i );
223 event.SetExtraLong( list->HasMultipleSelection() ? message == kDataBrowserItemSelected : true );
224 wxPostEvent( list->GetEventHandler() , event );
225 // direct notification is not always having the listbox GetSelection() having in synch with event
226 // list->GetEventHandler()->ProcessEvent(event);
227 }
228 }
229 }
230 }
231
232 static pascal OSStatus ListBoxGetSetItemData(ControlRef browser,
233 DataBrowserItemID itemID, DataBrowserPropertyID property,
234 DataBrowserItemDataRef itemData, Boolean changeValue)
235 {
236 OSStatus err = errDataBrowserPropertyNotSupported;
237
238 long ref = GetControlReference( browser );
239
240 if ( ! changeValue )
241 {
242 wxListBox* list = wxDynamicCast( (wxObject*) ref , wxListBox );
243 bool isCheckList = false;
244 if ( list)
245 isCheckList = list->IsKindOf( CLASSINFO(wxCheckListBox));
246
247 switch (property)
248 {
249 case kTextColumnId:
250 {
251 if ( ref )
252 {
253 int i = itemID - 1;
254 if (i >= 0 && i < (int)list->GetCount() )
255 {
256 wxMacCFStringHolder cf( list->GetString( i ) , list->GetFont().GetEncoding() );
257 verify_noerr( ::SetDataBrowserItemDataText( itemData , cf ) );
258 err = noErr;
259 }
260 }
261 }
262 break;
263
264 case kCheckboxColumnId :
265 {
266 if ( ref )
267 {
268 wxCheckListBox* list = wxDynamicCast( (wxObject*) ref , wxCheckListBox );
269 int i = itemID - 1;
270 if (i >= 0 && (unsigned int) i < list->GetCount() )
271 {
272 verify_noerr( ::SetDataBrowserItemDataButtonValue( itemData , list->IsChecked( i ) ? kThemeButtonOn : kThemeButtonOff ) );
273 err = noErr;
274 }
275 }
276 }
277 break;
278 case kDataBrowserItemIsEditableProperty:
279 {
280 if ( isCheckList )
281 err = ::SetDataBrowserItemDataBooleanValue(itemData, true);
282 }
283 break;
284
285 default:
286 break;
287 }
288 }
289 else
290 {
291 switch( property )
292 {
293 case kCheckboxColumnId :
294 {
295 if ( ref )
296 {
297 wxCheckListBox* list = wxDynamicCast( (wxObject*) ref , wxCheckListBox );
298 int i = itemID - 1;
299 if (i >= 0 && (unsigned int)i < list->GetCount() )
300 {
301 // we have to change this behind the back, since Check() would be triggering another update round
302 bool newVal = !list->IsChecked( i );
303 verify_noerr( ::SetDataBrowserItemDataButtonValue( itemData , newVal ? kThemeButtonOn : kThemeButtonOff ) );
304 err = noErr;
305 list->m_checks[ i ] = newVal;
306
307 wxCommandEvent event(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, list->GetId());
308 event.SetInt(i);
309 event.SetEventObject(list);
310 list->GetEventHandler()->ProcessEvent(event);
311 }
312 }
313
314 }
315 break;
316
317 default :
318 break;
319 }
320 }
321
322 return err;
323 }
324
325 wxMacDataBrowserListControl::wxMacDataBrowserListControl( wxListBox *peer , const wxPoint& pos, const wxSize& size, long style) :
326 wxMacListControl( peer )
327 {
328 bool isCheckList = peer->IsKindOf( CLASSINFO(wxCheckListBox));
329
330 m_suppressSelection = false;
331 Rect bounds = wxMacGetBoundsForControl( peer , pos , size );
332 verify_noerr( ::CreateDataBrowserControl( MAC_WXHWND(peer->MacGetTopLevelWindowRef()), &bounds, kDataBrowserListView , &m_controlRef ) );
333
334 DataBrowserSelectionFlags options = kDataBrowserDragSelect;
335 if ( style & wxLB_MULTIPLE )
336 {
337 options += kDataBrowserAlwaysExtendSelection + kDataBrowserCmdTogglesSelection ;
338 }
339 else if ( style & wxLB_EXTENDED )
340 {
341 // default behaviour
342 }
343 else
344 {
345 options += kDataBrowserSelectOnlyOne;
346 }
347 verify_noerr(SetSelectionFlags( options ) );
348
349 if ( gDataBrowserItemDataUPP == NULL ) gDataBrowserItemDataUPP = NewDataBrowserItemDataUPP(ListBoxGetSetItemData);
350 if ( gDataBrowserItemNotificationUPP == NULL )
351 {
352 gDataBrowserItemNotificationUPP =
353 #if TARGET_API_MAC_OSX
354 (DataBrowserItemNotificationUPP) NewDataBrowserItemNotificationWithItemUPP(DataBrowserItemNotificationProc);
355 #else
356 NewDataBrowserItemNotificationUPP(DataBrowserItemNotificationProc);
357 #endif
358 }
359
360 DataBrowserCallbacks callbacks;
361 InitializeDataBrowserCallbacks( &callbacks , kDataBrowserLatestCallbacks );
362
363 callbacks.u.v1.itemDataCallback = gDataBrowserItemDataUPP;
364 callbacks.u.v1.itemNotificationCallback = gDataBrowserItemNotificationUPP;
365 SetCallbacks( &callbacks);
366
367 DataBrowserListViewColumnDesc columnDesc;
368 columnDesc.headerBtnDesc.titleOffset = 0;
369 columnDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc;
370
371 columnDesc.headerBtnDesc.btnFontStyle.flags =
372 kControlUseFontMask | kControlUseJustMask;
373
374 columnDesc.headerBtnDesc.btnContentInfo.contentType = kControlNoContent;
375 columnDesc.headerBtnDesc.btnFontStyle.just = teFlushDefault;
376 columnDesc.headerBtnDesc.btnFontStyle.font = kControlFontViewSystemFont;
377 columnDesc.headerBtnDesc.btnFontStyle.style = normal;
378 columnDesc.headerBtnDesc.titleString = NULL;
379
380 if( isCheckList )
381 {
382 columnDesc.headerBtnDesc.minimumWidth = 30;
383 columnDesc.headerBtnDesc.maximumWidth = 30;
384
385 columnDesc.propertyDesc.propertyID = kCheckboxColumnId;
386 columnDesc.propertyDesc.propertyType = kDataBrowserCheckboxType;
387 columnDesc.propertyDesc.propertyFlags = kDataBrowserPropertyIsMutable | kDataBrowserTableViewSelectionColumn |
388 kDataBrowserDefaultPropertyFlags;
389 verify_noerr(AddListViewColumn( &columnDesc, kDataBrowserListViewAppendColumn) );
390 }
391
392 columnDesc.headerBtnDesc.minimumWidth = 0;
393 columnDesc.headerBtnDesc.maximumWidth = 10000;
394
395
396 columnDesc.propertyDesc.propertyID = kTextColumnId;
397 columnDesc.propertyDesc.propertyType = kDataBrowserTextType;
398 columnDesc.propertyDesc.propertyFlags =
399 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
400 kDataBrowserListViewTypeSelectColumn |
401 #endif
402 kDataBrowserTableViewSelectionColumn;
403
404 verify_noerr(AddListViewColumn( &columnDesc, kDataBrowserListViewAppendColumn) );
405 verify_noerr(AutoSizeListViewColumns() );
406 verify_noerr(SetHasScrollBars(false , true ) );
407 verify_noerr(SetTableViewHiliteStyle(kDataBrowserTableViewFillHilite ) );
408 verify_noerr(SetListViewHeaderBtnHeight( 0 ) );
409
410 #if 0
411 // shouldn't be necessary anymore under 10.2
412 m_peer->SetData( kControlNoPart, kControlDataBrowserIncludesFrameAndFocusTag, (Boolean) false );
413 m_peer->SetNeedsFocusRect( true );
414 #endif
415 }
416 wxMacDataBrowserListControl::~wxMacDataBrowserListControl()
417 {
418
419 }
420
421 void wxMacDataBrowserListControl::MacDelete( int n )
422 {
423 wxArrayInt selectionBefore;
424 MacGetSelections( selectionBefore );
425
426 UInt32 id = GetPeer()->GetCount()+1;
427 verify_noerr( RemoveItems( kDataBrowserNoItem , 1 , (UInt32*) &id , kDataBrowserItemNoProperty ) );
428 for ( size_t i = 0; i < selectionBefore.GetCount(); ++i )
429 {
430 int current = selectionBefore[i];
431 if ( current == n )
432 {
433 // selection was deleted
434 MacSetSelection( current , false );
435 }
436 else if ( current > n )
437 {
438 // something behind the deleted item was selected -> move up
439 MacSetSelection( current - 1 , true );
440 MacSetSelection( current , false );
441 }
442 }
443 // refresh all
444 verify_noerr( UpdateItems( kDataBrowserNoItem , 1 , (UInt32*) kDataBrowserNoItem , kDataBrowserItemNoProperty , kDataBrowserItemNoProperty ) );
445 }
446
447 void wxMacDataBrowserListControl::MacInsert( int n , const wxString& text)
448 {
449 wxArrayInt selectionBefore;
450 MacGetSelections( selectionBefore );
451
452 UInt32 id = GetPeer()->GetCount(); // this has already been increased
453 verify_noerr( AddItems( kDataBrowserNoItem , 1 , (UInt32*) &id , kDataBrowserItemNoProperty ) );
454
455 for ( int i = selectionBefore.GetCount()-1; i >= 0; --i )
456 {
457 int current = selectionBefore[i];
458 if ( current >= n )
459 {
460 MacSetSelection( current + 1 , true );
461 MacSetSelection( current , false );
462 }
463 }
464
465 // refresh all
466 verify_noerr( UpdateItems( kDataBrowserNoItem , 1 , (UInt32*) kDataBrowserNoItem , kDataBrowserItemNoProperty , kDataBrowserItemNoProperty ) );
467 }
468
469 void wxMacDataBrowserListControl::MacInsert( int n , const wxArrayString& items)
470 {
471 wxArrayInt selectionBefore;
472 MacGetSelections( selectionBefore );
473 size_t itemsCount = items.GetCount();
474
475 UInt32 *ids = new UInt32[itemsCount];
476 for ( unsigned int i = 0; i < itemsCount;++i )
477 ids[i] = GetPeer()->GetCount() - itemsCount + i + 1;
478
479 verify_noerr( AddItems( kDataBrowserNoItem , itemsCount , ids , kDataBrowserItemNoProperty ) );
480 delete[] ids;
481
482 for ( int i = selectionBefore.GetCount()-1; i >= 0; --i )
483 {
484 int current = selectionBefore[i];
485 if ( current >= n )
486 {
487 MacSetSelection( current + 1 , true );
488 MacSetSelection( current , false );
489 }
490 }
491
492 // refresh all
493 verify_noerr( UpdateItems( kDataBrowserNoItem , 1 , (UInt32*) kDataBrowserNoItem , kDataBrowserItemNoProperty , kDataBrowserItemNoProperty ) );
494 }
495
496 void wxMacDataBrowserListControl::MacAppend( const wxString& text)
497 {
498 UInt32 id = GetPeer()->GetCount(); // this has already been increased
499 verify_noerr( AddItems( kDataBrowserNoItem , 1 , (UInt32*) &id , kDataBrowserItemNoProperty ) );
500 // no need to deal with selections nor refreshed, as we have appended
501 }
502
503 void wxMacDataBrowserListControl::MacClear()
504 {
505 verify_noerr( RemoveItems( kDataBrowserNoItem , 0 , NULL , kDataBrowserItemNoProperty ) );
506 }
507
508 void wxMacDataBrowserListControl::MacDeselectAll()
509 {
510 bool former = MacSuppressSelection( true );
511 verify_noerr(SetSelectedItems( 0 , NULL , kDataBrowserItemsRemove ) );
512 MacSuppressSelection( former );
513 }
514
515 void wxMacDataBrowserListControl::MacSetSelection( int n , bool select )
516 {
517 bool former = MacSuppressSelection( true );
518 UInt32 id = n + 1;
519
520 if ( IsItemSelected( id ) != select )
521 {
522 if ( select )
523 verify_noerr(SetSelectedItems( 1 , & id , GetPeer()->HasMultipleSelection() ? kDataBrowserItemsAdd : kDataBrowserItemsAssign ) );
524 else
525 verify_noerr(SetSelectedItems( 1 , & id , kDataBrowserItemsRemove ) );
526 }
527 MacScrollTo( n );
528 MacSuppressSelection( former );
529 }
530
531 bool wxMacDataBrowserListControl::MacSuppressSelection( bool suppress )
532 {
533 bool former = m_suppressSelection;
534 m_suppressSelection = suppress;
535 return former;
536 }
537
538 bool wxMacDataBrowserListControl::MacIsSelected( int n ) const
539 {
540 return IsItemSelected( n + 1 );
541 }
542
543 int wxMacDataBrowserListControl::MacGetSelection() const
544 {
545 for ( unsigned int i = 0; i < GetPeer()->GetCount(); ++i )
546 {
547 if ( IsItemSelected( i + 1 ) )
548 {
549 return i;
550 }
551 }
552 return -1;
553 }
554
555 int wxMacDataBrowserListControl::MacGetSelections( wxArrayInt& aSelections ) const
556 {
557 int no_sel = 0;
558
559 aSelections.Empty();
560
561 UInt32 first , last;
562 GetSelectionAnchor( &first , &last );
563 if ( first != kDataBrowserNoItem )
564 {
565 for ( size_t i = first; i <= last; ++i )
566 {
567 if ( IsItemSelected( i ) )
568 {
569 aSelections.Add( i - 1 );
570 no_sel++;
571 }
572 }
573 }
574 return no_sel;
575 }
576
577 void wxMacDataBrowserListControl::MacSet( int n , const wxString& text )
578 {
579 // as we don't store the strings we only have to issue a redraw
580 UInt32 id = n + 1;
581 verify_noerr( UpdateItems( kDataBrowserNoItem , 1 , &id , kDataBrowserItemNoProperty , kDataBrowserItemNoProperty ) );
582 }
583
584 void wxMacDataBrowserListControl::MacScrollTo( int n )
585 {
586 UInt32 id = n + 1;
587 verify_noerr( RevealItem( id , kTextColumnId , kDataBrowserRevealWithoutSelecting ) );
588 }
589
590 void wxMacDataBrowserListControl::UpdateLine( int n )
591 {
592 UInt32 id = n + 1;
593 verify_noerr( UpdateItems(kDataBrowserNoItem , 1 , &id , kDataBrowserItemNoProperty , kDataBrowserItemNoProperty ) );
594 }
595
596 //
597 // Databrowser
598 //
599
600 OSStatus wxMacDataBrowserListControl::SetSelectionFlags( DataBrowserSelectionFlags options )
601 {
602 return SetDataBrowserSelectionFlags( m_controlRef , options );
603 }
604
605 OSStatus wxMacDataBrowserListControl::AddListViewColumn( DataBrowserListViewColumnDesc *columnDesc,
606 DataBrowserTableViewColumnIndex position )
607 {
608 return AddDataBrowserListViewColumn( m_controlRef , columnDesc, position );
609 }
610
611 OSStatus wxMacDataBrowserListControl::AutoSizeListViewColumns()
612 {
613 return AutoSizeDataBrowserListViewColumns(m_controlRef);
614 }
615
616 OSStatus wxMacDataBrowserListControl::SetHasScrollBars( bool horiz , bool vert )
617 {
618 return SetDataBrowserHasScrollBars( m_controlRef , horiz , vert );
619 }
620
621 OSStatus wxMacDataBrowserListControl::SetTableViewHiliteStyle( DataBrowserTableViewHiliteStyle hiliteStyle )
622 {
623 return SetDataBrowserTableViewHiliteStyle( m_controlRef , hiliteStyle );
624 }
625
626 OSStatus wxMacDataBrowserListControl::SetListViewHeaderBtnHeight(UInt16 height)
627 {
628 return SetDataBrowserListViewHeaderBtnHeight( m_controlRef ,height );
629 }
630
631 OSStatus wxMacDataBrowserListControl::SetCallbacks(const DataBrowserCallbacks * callbacks)
632 {
633 return SetDataBrowserCallbacks( m_controlRef , callbacks );
634 }
635
636 OSStatus wxMacDataBrowserListControl::UpdateItems( DataBrowserItemID container, UInt32 numItems,
637 const DataBrowserItemID* items,
638 DataBrowserPropertyID preSortProperty,
639 DataBrowserPropertyID propertyID )
640 {
641 return UpdateDataBrowserItems( m_controlRef , container, numItems, items, preSortProperty, propertyID );
642 }
643
644 bool wxMacDataBrowserListControl::IsItemSelected( DataBrowserItemID item ) const
645 {
646 return IsDataBrowserItemSelected( m_controlRef , item );
647 }
648
649 OSStatus wxMacDataBrowserListControl::AddItems( DataBrowserItemID container, UInt32 numItems,
650 const DataBrowserItemID* items,
651 DataBrowserPropertyID preSortProperty )
652 {
653 return AddDataBrowserItems( m_controlRef , container, numItems, items, preSortProperty );
654 }
655
656 OSStatus wxMacDataBrowserListControl::RemoveItems( DataBrowserItemID container, UInt32 numItems,
657 const DataBrowserItemID* items,
658 DataBrowserPropertyID preSortProperty )
659 {
660 return RemoveDataBrowserItems( m_controlRef , container, numItems, items, preSortProperty );
661 }
662
663 OSStatus wxMacDataBrowserListControl::RevealItem( DataBrowserItemID item,
664 DataBrowserPropertyID propertyID,
665 DataBrowserRevealOptions options )
666 {
667 return RevealDataBrowserItem( m_controlRef , item , propertyID , options );
668 }
669
670 OSStatus wxMacDataBrowserListControl::SetSelectedItems(UInt32 numItems,
671 const DataBrowserItemID * items,
672 DataBrowserSetOption operation )
673 {
674 return SetDataBrowserSelectedItems( m_controlRef , numItems , items, operation );
675 }
676
677 OSStatus wxMacDataBrowserListControl::GetSelectionAnchor( DataBrowserItemID * first, DataBrowserItemID * last ) const
678 {
679 return GetDataBrowserSelectionAnchor( m_controlRef , first , last );
680 }
681
682 #if 0
683
684 // in case we need that one day
685
686 // ============================================================================
687 // HIView owner draw based implementation
688 // ============================================================================
689
690 static pascal void ListBoxDrawProc( ControlRef browser , DataBrowserItemID item , DataBrowserPropertyID property ,
691 DataBrowserItemState itemState , const Rect *itemRect , SInt16 depth , Boolean isColorDevice )
692 {
693 CFStringRef cfString;
694 ThemeDrawingState themeState;
695 long systemVersion;
696
697 GetThemeDrawingState( &themeState );
698 cfString = CFStringCreateWithFormat( NULL, NULL, CFSTR("Row %d"), item );
699
700 // In this sample we handle the "selected" state; all others fall through to our "active" state
701 if ( itemState == kDataBrowserItemIsSelected )
702 {
703 ThemeBrush colorBrushID;
704
705 Gestalt( gestaltSystemVersion, &systemVersion );
706
707 // TODO: switch over to wxSystemSettingsNative::GetColour() when kThemeBrushSecondaryHighlightColor is incorporated
708 // Panther DB starts using kThemeBrushSecondaryHighlightColor for inactive browser hilighting
709 if ( (systemVersion >= 0x00001030) && !IsControlActive( browser ) )
710 colorBrushID = kThemeBrushSecondaryHighlightColor;
711 else
712 colorBrushID = kThemeBrushPrimaryHighlightColor;
713
714 // First paint the hilite rect, then the text on top
715 SetThemePen( colorBrushID, 32, true );
716 PaintRect( itemRect );
717 SetThemeDrawingState( themeState , false );
718 }
719
720 DrawThemeTextBox( cfString, kThemeApplicationFont, kThemeStateActive, true, itemRect, teFlushDefault, NULL );
721 SetThemeDrawingState( themeState , true );
722
723 if ( cfString != NULL )
724 CFRelease( cfString );
725 }
726 #endif
727
728
729 // ============================================================================
730 // list box control implementation
731 // ============================================================================
732
733 wxListBox::wxListBox()
734 {
735 m_noItems = 0;
736 }
737
738 bool wxListBox::Create(wxWindow *parent,
739 wxWindowID id,
740 const wxPoint& pos,
741 const wxSize& size,
742 const wxArrayString& choices,
743 long style,
744 const wxValidator& validator,
745 const wxString& name)
746 {
747 wxCArrayString chs(choices);
748
749 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
750 style, validator, name);
751 }
752
753 bool wxListBox::Create(wxWindow *parent,
754 wxWindowID id,
755 const wxPoint& pos,
756 const wxSize& size,
757 int n,
758 const wxString choices[],
759 long style,
760 const wxValidator& validator,
761 const wxString& name)
762 {
763 m_macIsUserPane = false;
764
765 wxASSERT_MSG( !(style & wxLB_MULTIPLE) || !(style & wxLB_EXTENDED),
766 _T("only one of listbox selection modes can be specified") );
767
768 if ( !wxListBoxBase::Create(parent, id, pos, size, style & ~(wxHSCROLL|wxVSCROLL), validator, name) )
769 return false;
770
771 m_noItems = 0; // this will be increased by our append command
772
773 m_peer = CreateMacListControl(pos , size , style );
774
775 MacPostControlCreate(pos,size);
776
777 InsertItems( n , choices , 0 );
778
779 SetBestSize(size); // Needed because it is a wxControlWithItems
780
781 return TRUE;
782 }
783
784 wxListBox::~wxListBox()
785 {
786 m_peer->SetReference( 0 );
787 FreeData();
788 }
789
790 wxMacListControl* wxListBox::CreateMacListControl(const wxPoint& pos, const wxSize& size, long style)
791 {
792 return new wxMacDataBrowserListControl(this , pos , size , style );
793 }
794
795 void wxListBox::FreeData()
796 {
797 #if wxUSE_OWNER_DRAWN
798 if ( m_windowStyle & wxLB_OWNERDRAW )
799 {
800 size_t uiCount = m_aItems.Count();
801 while ( uiCount-- != 0 ) {
802 delete m_aItems[uiCount];
803 m_aItems[uiCount] = NULL;
804 }
805
806 m_aItems.Clear();
807 }
808 else
809 #endif // wxUSE_OWNER_DRAWN
810 if ( HasClientObjectData() )
811 {
812 for ( unsigned int n = 0; n < m_noItems; n++ )
813 {
814 delete GetClientObject( n );
815 }
816 }
817 }
818
819 void wxListBox::DoSetSize(int x, int y,
820 int width, int height,
821 int sizeFlags )
822 {
823 wxControl::DoSetSize( x , y , width , height , sizeFlags );
824 }
825
826 void wxListBox::DoSetFirstItem(int n)
827 {
828 GetPeer()->MacScrollTo( n );
829 }
830
831 void wxListBox::Delete(unsigned int n)
832 {
833 wxCHECK_RET( IsValid(n),
834 wxT("invalid index in wxListBox::Delete") );
835
836 #if wxUSE_OWNER_DRAWN
837 delete m_aItems[n];
838 m_aItems.RemoveAt(n);
839 #else // !wxUSE_OWNER_DRAWN
840 if ( HasClientObjectData() )
841 {
842 delete GetClientObject(n);
843 }
844 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN
845 m_stringArray.RemoveAt( n );
846 m_dataArray.RemoveAt( n );
847 m_noItems --;
848
849 GetPeer()->MacDelete( n );
850 }
851
852 int wxListBox::DoAppend(const wxString& item)
853 {
854 InvalidateBestSize();
855
856 unsigned int index = m_noItems;
857 m_stringArray.Add( item );
858 m_dataArray.Add( NULL );
859 m_noItems++;
860 DoSetItemClientData( index , NULL );
861 GetPeer()->MacAppend( item );
862
863 return index;
864 }
865
866 void wxListBox::DoSetItems(const wxArrayString& choices, void** clientData)
867 {
868 Clear();
869 unsigned int n = choices.GetCount();
870
871 for ( size_t i = 0; i < n; ++i )
872 {
873 if ( clientData )
874 {
875 #if wxUSE_OWNER_DRAWN
876 wxASSERT_MSG(clientData[i] == NULL,
877 wxT("Can't use client data with owner-drawn listboxes"));
878 #else // !wxUSE_OWNER_DRAWN
879 Append( choices[i] , clientData[i] );
880 #endif
881 }
882 else
883 Append( choices[i] );
884 }
885
886 #if wxUSE_OWNER_DRAWN
887 if ( m_windowStyle & wxLB_OWNERDRAW ) {
888 // first delete old items
889 size_t ui = m_aItems.Count();
890 while ( ui-- != 0 ) {
891 delete m_aItems[ui];
892 m_aItems[ui] = NULL;
893 }
894 m_aItems.Empty();
895
896 // then create new ones
897 for ( ui = 0; ui < (size_t)m_noItems; ui++ ) {
898 wxOwnerDrawn *pNewItem = CreateItem(ui);
899 pNewItem->SetName(choices[ui]);
900 m_aItems.Add(pNewItem);
901 }
902 }
903 #endif // wxUSE_OWNER_DRAWN
904 }
905
906 int wxListBox::FindString(const wxString& s, bool bCase) const
907 {
908 for ( size_t i = 0; i < m_noItems; ++ i )
909 {
910 if (s.IsSameAs(GetString(i), bCase))
911 return (int)i;
912 }
913
914 return wxNOT_FOUND;
915 }
916
917 void wxListBox::Clear()
918 {
919 FreeData();
920 m_noItems = 0;
921 m_stringArray.Empty();
922 m_dataArray.Empty();
923 GetPeer()->MacClear();
924 }
925
926 void wxListBox::DoSetSelection(int n, bool select)
927 {
928 wxCHECK_RET( n == wxNOT_FOUND || IsValid(n) ,
929 wxT("invalid index in wxListBox::SetSelection") );
930
931 if ( n == wxNOT_FOUND )
932 GetPeer()->MacDeselectAll();
933 else
934 GetPeer()->MacSetSelection( n , select );
935 }
936
937 bool wxListBox::IsSelected(int n) const
938 {
939 wxCHECK_MSG( IsValid(n), false,
940 wxT("invalid index in wxListBox::Selected") );
941
942 return GetPeer()->MacIsSelected( n );
943 }
944
945 void *wxListBox::DoGetItemClientData(unsigned int n) const
946 {
947 wxCHECK_MSG( IsValid(n), NULL, wxT("invalid index in wxListBox::GetClientData"));
948
949 wxASSERT_MSG( m_dataArray.GetCount() >= (unsigned int) n , wxT("invalid client_data array") );
950
951 return (void *)m_dataArray[n];
952 }
953
954 wxClientData *wxListBox::DoGetItemClientObject(unsigned int n) const
955 {
956 return (wxClientData *) DoGetItemClientData( n );
957 }
958
959 void wxListBox::DoSetItemClientData(unsigned int n, void *clientData)
960 {
961 wxCHECK_RET( IsValid(n), wxT("invalid index in wxListBox::SetClientData") );
962
963 #if wxUSE_OWNER_DRAWN
964 if ( m_windowStyle & wxLB_OWNERDRAW )
965 {
966 // client data must be pointer to wxOwnerDrawn, otherwise we would crash
967 // in OnMeasure/OnDraw.
968 wxFAIL_MSG(wxT("Can't use client data with owner-drawn listboxes"));
969 }
970 #endif // wxUSE_OWNER_DRAWN
971 wxASSERT_MSG( m_dataArray.GetCount() >= (unsigned int) n , wxT("invalid client_data array") );
972
973 if ( m_dataArray.GetCount() > (unsigned int) n )
974 m_dataArray[n] = (char*)clientData;
975 else
976 m_dataArray.Add( (char*)clientData );
977 }
978
979 void wxListBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
980 {
981 DoSetItemClientData(n, clientData);
982 }
983
984 // Return number of selections and an array of selected integers
985 int wxListBox::GetSelections(wxArrayInt& aSelections) const
986 {
987 return GetPeer()->MacGetSelections( aSelections );
988 }
989
990 // Get single selection, for single choice list items
991 int wxListBox::GetSelection() const
992 {
993 return GetPeer()->MacGetSelection();
994 }
995
996 // Find string for position
997 wxString wxListBox::GetString(unsigned int n) const
998 {
999 wxCHECK_MSG( IsValid(n), wxEmptyString,
1000 wxT("invalid index in wxListBox::GetString") );
1001
1002 return m_stringArray[n] ;
1003 }
1004
1005 void wxListBox::DoInsertItems(const wxArrayString& items, unsigned int pos)
1006 {
1007 wxCHECK_RET( IsValidInsert(pos),
1008 wxT("invalid index in wxListBox::InsertItems") );
1009
1010 InvalidateBestSize();
1011
1012 unsigned int nItems = items.GetCount();
1013
1014 for ( unsigned int i = 0; i < nItems; i++ )
1015 m_stringArray.Insert( items[i] , pos+i );
1016 m_dataArray.Insert( NULL , pos , nItems );
1017 m_noItems += nItems;
1018 GetPeer()->MacInsert( pos , items );
1019 }
1020
1021 void wxListBox::SetString(unsigned int n, const wxString& s)
1022 {
1023 m_stringArray[n] = s;
1024 GetPeer()->MacSet( n , s );
1025 }
1026
1027 wxSize wxListBox::DoGetBestSize() const
1028 {
1029 int lbWidth = 100; // some defaults
1030 int lbHeight = 110;
1031 int wLine;
1032
1033 {
1034 wxMacPortStateHelper st( UMAGetWindowPort( (WindowRef)MacGetTopLevelWindowRef() ) );
1035
1036 // TODO: clean this up
1037 if ( m_font.Ok() )
1038 {
1039 ::TextFont( m_font.MacGetFontNum() );
1040 ::TextSize( m_font.MacGetFontSize() );
1041 ::TextFace( m_font.MacGetFontStyle() );
1042 }
1043 else
1044 {
1045 ::TextFont( kFontIDMonaco );
1046 ::TextSize( 9 );
1047 ::TextFace( 0 );
1048 }
1049
1050 // Find the widest line
1051 for (unsigned int i = 0; i < GetCount(); i++)
1052 {
1053 wxString str(GetString(i));
1054
1055 #if wxUSE_UNICODE
1056 Point bounds = {0, 0};
1057 SInt16 baseline;
1058
1059 // NB: what if m_font.Ok() == false ???
1060 ::GetThemeTextDimensions(
1061 wxMacCFStringHolder( str , m_font.GetEncoding() ) ,
1062 kThemeCurrentPortFont,
1063 kThemeStateActive,
1064 false,
1065 &bounds,
1066 &baseline );
1067 wLine = bounds.h;
1068 #else
1069 wLine = ::TextWidth( str.c_str() , 0 , str.length() );
1070 #endif
1071
1072 lbWidth = wxMax( lbWidth, wLine );
1073 }
1074
1075 // Add room for the scrollbar
1076 lbWidth += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
1077
1078 // And just a bit more
1079 int cy = 12;
1080 int cx = ::TextWidth( "X" , 0 , 1 );
1081 lbWidth += cx;
1082
1083 // don't make the listbox too tall (limit height to around 10 items)
1084 // but don't make it too small neither
1085 lbHeight = wxMax( (cy + 4) * wxMin( wxMax( GetCount(), 3 ), 10 ), 70 );
1086 }
1087
1088 return wxSize( lbWidth, lbHeight );
1089 }
1090
1091 unsigned int wxListBox::GetCount() const
1092 {
1093 return m_noItems;
1094 }
1095
1096 void wxListBox::Refresh(bool eraseBack, const wxRect *rect)
1097 {
1098 wxControl::Refresh( eraseBack , rect );
1099 }
1100
1101 void wxListBox::MacUpdateLine( int n)
1102 {
1103 GetPeer()->UpdateLine(n);
1104 }
1105
1106 #if wxUSE_OWNER_DRAWN
1107
1108 class wxListBoxItem : public wxOwnerDrawn
1109 {
1110 public:
1111 wxListBoxItem(const wxString& str = "");
1112 };
1113
1114 wxListBoxItem::wxListBoxItem(const wxString& str) : wxOwnerDrawn(str, FALSE)
1115 {
1116 // no bitmaps/checkmarks
1117 SetMarginWidth(0);
1118 }
1119
1120 wxOwnerDrawn *wxListBox::CreateItem(size_t n)
1121 {
1122 return new wxListBoxItem();
1123 }
1124
1125 #endif //USE_OWNER_DRAWN
1126
1127
1128 // Some custom controls depend on this
1129 /* static */ wxVisualAttributes
1130 wxListBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
1131 {
1132 wxVisualAttributes attr;
1133
1134 attr.colFg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
1135 attr.colBg = wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOX );
1136 attr.font = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
1137
1138 return attr;
1139 }
1140
1141 int wxListBox::DoListHitTest(const wxPoint& inpoint) const
1142 {
1143 OSErr err;
1144
1145 // There are few reasons why this is complicated:
1146 // 1) There is no native hittest function for mac
1147 // 2) GetDataBrowserItemPartBounds only works on visible items
1148 // 3) We can't do it through GetDataBrowserTableView[Item]RowHeight
1149 // because what it returns is basically inaccurate in the context
1150 // of the coordinates we want here, but we use this as a guess
1151 // for where the first visible item lies
1152
1153 wxPoint point = inpoint;
1154 // interestingly enough 10.2 (and below?) have GetDataBrowserItemPartBounds
1155 // giving root window coordinates but 10.3 and above give client coordinates
1156 // so we only compare using root window coordinates on 10.3 and up
1157 if ( UMAGetSystemVersion() < 0x1030 )
1158 MacClientToRootWindow(&point.x, &point.y);
1159
1160 // get column property id (req. for call to itempartbounds)
1161 DataBrowserTableViewColumnID colId = 0;
1162 err = GetDataBrowserTableViewColumnProperty(m_peer->GetControlRef(), 0, &colId);
1163 wxCHECK_MSG(err == noErr, wxNOT_FOUND, wxT("Unexpected error from GetDataBrowserTableViewColumnProperty"));
1164
1165 // OK, first we need to find the first visible item we have -
1166 // this will be the "low" for our binary search. There is no real
1167 // easy way around this, as we will need to do a SLOW linear search
1168 // until we find a visible item, but we can do a cheap calculation
1169 // via the row height to speed things up a bit
1170 UInt32 scrollx, scrolly;
1171 err = GetDataBrowserScrollPosition(m_peer->GetControlRef(), &scrollx, &scrolly);
1172 wxCHECK_MSG(err == noErr, wxNOT_FOUND, wxT("Unexpected error from GetDataBrowserScrollPosition"));
1173
1174 UInt16 height;
1175 err = GetDataBrowserTableViewRowHeight(m_peer->GetControlRef(), &height);
1176 wxCHECK_MSG(err == noErr, wxNOT_FOUND, wxT("Unexpected error from GetDataBrowserTableViewRowHeight"));
1177
1178 // these indices are 0-based, as usual, so we need to add 1 to them when
1179 // passing them to data browser functions which use 1-based indices
1180 int low = scrolly / height,
1181 high = GetCount() - 1;
1182
1183
1184 // search for the first visible item (note that the scroll guess above
1185 // is the low bounds of where the item might lie so we only use that as a
1186 // starting point - we should reach it within 1 or 2 iterations of the loop)
1187 while ( low <= high )
1188 {
1189 Rect bounds;
1190 err = GetDataBrowserItemPartBounds(m_peer->GetControlRef(), low + 1, colId,
1191 kDataBrowserPropertyEnclosingPart,
1192 &bounds); //note +1 to trans to mac id
1193 if ( err == noErr )
1194 break;
1195
1196 // errDataBrowserItemNotFound is expected as it simply means that the
1197 // item is not currently visible -- but other errors are not
1198 wxCHECK_MSG( err == errDataBrowserItemNotFound, wxNOT_FOUND,
1199 wxT("Unexpected error from GetDataBrowserItemPartBounds") );
1200
1201 low++;
1202 }
1203
1204 // NOW do a binary search for where the item lies, searching low again if
1205 // we hit an item that isn't visible
1206 while ( low <= high )
1207 {
1208 int mid = (low + high) / 2;
1209
1210 Rect bounds;
1211 err = GetDataBrowserItemPartBounds(m_peer->GetControlRef(), mid + 1, colId,
1212 kDataBrowserPropertyEnclosingPart,
1213 &bounds); //note +1 to trans to mac id
1214 wxCHECK_MSG( err == noErr || err == errDataBrowserItemNotFound,
1215 wxNOT_FOUND,
1216 wxT("Unexpected error from GetDataBrowserItemPartBounds") );
1217
1218 if ( err == errDataBrowserItemNotFound )
1219 {
1220 // item not visible, attempt to find a visible one
1221 high = mid - 1; // search lower
1222 }
1223 else // visible item, do actual hitttest
1224 {
1225 // if point is within the bounds, return this item (since we assume
1226 // all x coords of items are equal we only test the x coord in
1227 // equality)
1228 if( (point.x >= bounds.left && point.x <= bounds.right) &&
1229 (point.y >= bounds.top && point.y <= bounds.bottom) )
1230 {
1231 return mid; // found!
1232 }
1233
1234 if ( point.y < bounds.top )
1235 high = mid - 1; // index(bounds) greater then key(point)
1236 else
1237 low = mid + 1; // index(bounds) less then key(point)
1238 }
1239 }
1240
1241 return wxNOT_FOUND;
1242 }
1243
1244 #endif