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