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