1/////////////////////////////////////////////////////////////////////////////// 2// Name: src/osx/cocoa/listbox.mm 3// Purpose: wxListBox 4// Author: Stefan Csomor 5// Modified by: 6// Created: 1998-01-01 7// Copyright: (c) Stefan Csomor 8// Licence: wxWindows licence 9/////////////////////////////////////////////////////////////////////////////// 10 11#include "wx/wxprec.h" 12 13#if wxUSE_LISTBOX 14 15#include "wx/listbox.h" 16#include "wx/dnd.h" 17 18#ifndef WX_PRECOMP 19 #include "wx/log.h" 20 #include "wx/intl.h" 21 #include "wx/utils.h" 22 #include "wx/settings.h" 23 #include "wx/arrstr.h" 24 #include "wx/dcclient.h" 25#endif 26 27#include "wx/osx/private.h" 28 29#include <vector> 30 31// forward decls 32 33class wxListWidgetCocoaImpl; 34 35@interface wxNSTableDataSource : NSObject wxOSX_10_6_AND_LATER(<NSTableViewDataSource>) 36{ 37 wxListWidgetCocoaImpl* impl; 38} 39 40- (id)tableView:(NSTableView *)aTableView 41 objectValueForTableColumn:(NSTableColumn *)aTableColumn 42 row:(NSInteger)rowIndex; 43 44- (void)tableView:(NSTableView *)aTableView 45 setObjectValue:(id)value forTableColumn:(NSTableColumn *)aTableColumn 46 row:(NSInteger)rowIndex; 47 48- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView; 49 50- (void)setImplementation: (wxListWidgetCocoaImpl *) theImplementation; 51- (wxListWidgetCocoaImpl*) implementation; 52 53@end 54 55@interface wxNSTableView : NSTableView wxOSX_10_6_AND_LATER(<NSTableViewDelegate>) 56{ 57} 58 59@end 60 61// 62// table column 63// 64 65class wxCocoaTableColumn; 66 67@interface wxNSTableColumn : NSTableColumn 68{ 69 wxCocoaTableColumn* column; 70} 71 72- (void) setColumn: (wxCocoaTableColumn*) col; 73 74- (wxCocoaTableColumn*) column; 75 76@end 77 78class WXDLLIMPEXP_CORE wxCocoaTableColumn : public wxListWidgetColumn 79{ 80public : 81 wxCocoaTableColumn( wxNSTableColumn* column, bool editable ) 82 : m_column( column ), m_editable(editable) 83 { 84 } 85 86 ~wxCocoaTableColumn() 87 { 88 } 89 90 wxNSTableColumn* GetNSTableColumn() const { return m_column ; } 91 92 bool IsEditable() const { return m_editable; } 93 94protected : 95 wxNSTableColumn* m_column; 96 bool m_editable; 97} ; 98 99NSString* column1 = @"1"; 100 101class wxListWidgetCocoaImpl : public wxWidgetCocoaImpl, public wxListWidgetImpl 102{ 103public : 104 wxListWidgetCocoaImpl( wxWindowMac* peer, NSScrollView* view, wxNSTableView* tableview, wxNSTableDataSource* data ); 105 106 ~wxListWidgetCocoaImpl(); 107 108 virtual wxListWidgetColumn* InsertTextColumn( unsigned pos, const wxString& title, bool editable = false, 109 wxAlignment just = wxALIGN_LEFT , int defaultWidth = -1) ; 110 virtual wxListWidgetColumn* InsertCheckColumn( unsigned pos , const wxString& title, bool editable = false, 111 wxAlignment just = wxALIGN_LEFT , int defaultWidth = -1) ; 112 113 // add and remove 114 115 virtual void ListDelete( unsigned int n ) ; 116 virtual void ListInsert( unsigned int n ) ; 117 virtual void ListClear() ; 118 119 // selecting 120 121 virtual void ListDeselectAll(); 122 123 virtual void ListSetSelection( unsigned int n, bool select, bool multi ) ; 124 virtual int ListGetSelection() const ; 125 126 virtual int ListGetSelections( wxArrayInt& aSelections ) const ; 127 128 virtual bool ListIsSelected( unsigned int n ) const ; 129 130 // display 131 132 virtual void ListScrollTo( unsigned int n ) ; 133 134 // accessing content 135 136 virtual unsigned int ListGetCount() const ; 137 virtual int DoListHitTest( const wxPoint& inpoint ) const; 138 139 int ListGetColumnType( int col ) 140 { 141 return col; 142 } 143 virtual void UpdateLine( unsigned int n, wxListWidgetColumn* col = NULL ) ; 144 virtual void UpdateLineToEnd( unsigned int n); 145 146 virtual void controlDoubleAction(WXWidget slf, void* _cmd, void *sender); 147 148 149protected : 150 wxNSTableView* m_tableView ; 151 152 wxNSTableDataSource* m_dataSource; 153} ; 154 155// 156// implementations 157// 158 159@implementation wxNSTableColumn 160 161- (id) init 162{ 163 self = [super init]; 164 column = nil; 165 return self; 166} 167 168- (void) setColumn: (wxCocoaTableColumn*) col 169{ 170 column = col; 171} 172 173- (wxCocoaTableColumn*) column 174{ 175 return column; 176} 177 178@end 179 180class wxNSTableViewCellValue : public wxListWidgetCellValue 181{ 182public : 183 wxNSTableViewCellValue( id &v ) : value(v) 184 { 185 } 186 187 virtual ~wxNSTableViewCellValue() {} 188 189 virtual void Set( CFStringRef v ) 190 { 191 value = [[(NSString*)v retain] autorelease]; 192 } 193 virtual void Set( const wxString& value ) 194 { 195 Set( (CFStringRef) wxCFStringRef( value ) ); 196 } 197 virtual void Set( int v ) 198 { 199 value = [NSNumber numberWithInt:v]; 200 } 201 202 virtual int GetIntValue() const 203 { 204 if ( [value isKindOfClass:[NSNumber class]] ) 205 return [ (NSNumber*) value intValue ]; 206 207 return 0; 208 } 209 210 virtual wxString GetStringValue() const 211 { 212 if ( [value isKindOfClass:[NSString class]] ) 213 return wxCFStringRef::AsString( (NSString*) value ); 214 215 return wxEmptyString; 216 } 217 218protected: 219 id& value; 220} ; 221 222@implementation wxNSTableDataSource 223 224- (id) init 225{ 226 self = [super init]; 227 impl = nil; 228 return self; 229} 230 231- (void)setImplementation: (wxListWidgetCocoaImpl *) theImplementation 232{ 233 impl = theImplementation; 234} 235 236- (wxListWidgetCocoaImpl*) implementation 237{ 238 return impl; 239} 240 241- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView 242{ 243 wxUnusedVar(aTableView); 244 if ( impl ) 245 return impl->ListGetCount(); 246 return 0; 247} 248 249- (id)tableView:(NSTableView *)aTableView 250 objectValueForTableColumn:(NSTableColumn *)aTableColumn 251 row:(NSInteger)rowIndex 252{ 253 wxUnusedVar(aTableView); 254 wxNSTableColumn* tablecol = (wxNSTableColumn *)aTableColumn; 255 wxListBox* lb = dynamic_cast<wxListBox*>(impl->GetWXPeer()); 256 wxCocoaTableColumn* col = [tablecol column]; 257 id value = nil; 258 wxNSTableViewCellValue cellvalue(value); 259 lb->GetValueCallback(rowIndex, col, cellvalue); 260 return value; 261} 262 263- (void)tableView:(NSTableView *)aTableView 264 setObjectValue:(id)value forTableColumn:(NSTableColumn *)aTableColumn 265 row:(NSInteger)rowIndex 266{ 267 wxUnusedVar(aTableView); 268 wxNSTableColumn* tablecol = (wxNSTableColumn *)aTableColumn; 269 wxListBox* lb = dynamic_cast<wxListBox*>(impl->GetWXPeer()); 270 wxCocoaTableColumn* col = [tablecol column]; 271 wxNSTableViewCellValue cellvalue(value); 272 lb->SetValueCallback(rowIndex, col, cellvalue); 273} 274 275@end 276 277@implementation wxNSTableView 278 279+ (void)initialize 280{ 281 static BOOL initialized = NO; 282 if (!initialized) 283 { 284 initialized = YES; 285 wxOSXCocoaClassAddWXMethods( self ); 286 } 287} 288 289- (void) tableViewSelectionDidChange: (NSNotification *) notification 290{ 291 wxUnusedVar(notification); 292 293 int row = [self selectedRow]; 294 295 wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); 296 wxListBox* const list = wxDynamicCast(impl->GetWXPeer(), wxListBox); 297 wxCHECK_RET( list != NULL , "Associated control should be a wxListBox" ); 298 299 list->MacHandleSelectionChange(row); 300} 301 302- (void)setFont:(NSFont *)aFont 303{ 304 NSArray *tableColumns = [self tableColumns]; 305 unsigned int columnIndex = [tableColumns count]; 306 while (columnIndex--) 307 [[(NSTableColumn *)[tableColumns objectAtIndex:columnIndex] dataCell] setFont:aFont]; 308 309 [self setRowHeight:[gNSLayoutManager defaultLineHeightForFont:aFont]+2]; 310} 311 312- (void) setControlSize:(NSControlSize) size 313{ 314 NSArray *tableColumns = [self tableColumns]; 315 unsigned int columnIndex = [tableColumns count]; 316 while (columnIndex--) 317 [[(NSTableColumn *)[tableColumns objectAtIndex:columnIndex] dataCell] setControlSize:size]; 318} 319 320@end 321 322// 323// 324// 325 326wxListWidgetCocoaImpl::wxListWidgetCocoaImpl( wxWindowMac* peer, NSScrollView* view, wxNSTableView* tableview, wxNSTableDataSource* data ) : 327 wxWidgetCocoaImpl( peer, view ), m_tableView(tableview), m_dataSource(data) 328{ 329 InstallEventHandler( tableview ); 330} 331 332wxListWidgetCocoaImpl::~wxListWidgetCocoaImpl() 333{ 334 [m_dataSource release]; 335} 336 337unsigned int wxListWidgetCocoaImpl::ListGetCount() const 338{ 339 wxListBox* lb = dynamic_cast<wxListBox*> ( GetWXPeer() ); 340 return lb->GetCount(); 341} 342 343// 344// columns 345// 346 347wxListWidgetColumn* wxListWidgetCocoaImpl::InsertTextColumn( unsigned pos, const wxString& WXUNUSED(title), bool editable, 348 wxAlignment WXUNUSED(just), int defaultWidth) 349{ 350 wxNSTableColumn* col1 = [[wxNSTableColumn alloc] init]; 351 [col1 setEditable:editable]; 352 353 unsigned formerColCount = [m_tableView numberOfColumns]; 354 355 // there's apparently no way to insert at a specific position 356 [m_tableView addTableColumn:col1 ]; 357 if ( pos < formerColCount ) 358 [m_tableView moveColumn:formerColCount toColumn:pos]; 359 360 if ( defaultWidth >= 0 ) 361 { 362 [col1 setMaxWidth:defaultWidth]; 363 [col1 setMinWidth:defaultWidth]; 364 [col1 setWidth:defaultWidth]; 365 } 366 else 367 { 368 [col1 setMaxWidth:1000]; 369 [col1 setMinWidth:10]; 370 // temporary hack, because I cannot get the automatic column resizing 371 // to work properly 372 [col1 setWidth:1000]; 373 } 374 [col1 setResizingMask: NSTableColumnAutoresizingMask]; 375 376 wxListBox *list = static_cast<wxListBox*> ( GetWXPeer()); 377 if ( list != NULL ) 378 [[col1 dataCell] setFont:list->GetFont().OSXGetNSFont()]; 379 380 wxCocoaTableColumn* wxcol = new wxCocoaTableColumn( col1, editable ); 381 [col1 setColumn:wxcol]; 382 383 // owned by the tableview 384 [col1 release]; 385 return wxcol; 386} 387 388wxListWidgetColumn* wxListWidgetCocoaImpl::InsertCheckColumn( unsigned pos , const wxString& WXUNUSED(title), bool editable, 389 wxAlignment WXUNUSED(just), int defaultWidth ) 390{ 391 wxNSTableColumn* col1 = [[wxNSTableColumn alloc] init]; 392 [col1 setEditable:editable]; 393 394 // set your custom cell & set it up 395 NSButtonCell* checkbox = [[NSButtonCell alloc] init]; 396 [checkbox setTitle:@""]; 397 [checkbox setButtonType:NSSwitchButton]; 398 [col1 setDataCell:checkbox] ; 399 400 wxListBox *list = static_cast<wxListBox*> ( GetWXPeer()); 401 if ( list != NULL ) 402 { 403 NSControlSize size = NSRegularControlSize; 404 405 switch ( list->GetWindowVariant() ) 406 { 407 case wxWINDOW_VARIANT_NORMAL : 408 size = NSRegularControlSize; 409 break ; 410 411 case wxWINDOW_VARIANT_SMALL : 412 size = NSSmallControlSize; 413 break ; 414 415 case wxWINDOW_VARIANT_MINI : 416 size = NSMiniControlSize; 417 break ; 418 419 case wxWINDOW_VARIANT_LARGE : 420 size = NSRegularControlSize; 421 break ; 422 423 default: 424 break ; 425 } 426 427 [[col1 dataCell] setControlSize:size]; 428 // although there is no text, it may help to get the correct vertical layout 429 [[col1 dataCell] setFont:list->GetFont().OSXGetNSFont()]; 430 } 431 432 [checkbox release]; 433 434 unsigned formerColCount = [m_tableView numberOfColumns]; 435 436 // there's apparently no way to insert at a specific position 437 [m_tableView addTableColumn:col1 ]; 438 if ( pos < formerColCount ) 439 [m_tableView moveColumn:formerColCount toColumn:pos]; 440 441 if ( defaultWidth >= 0 ) 442 { 443 [col1 setMaxWidth:defaultWidth]; 444 [col1 setMinWidth:defaultWidth]; 445 [col1 setWidth:defaultWidth]; 446 } 447 448 [col1 setResizingMask: NSTableColumnNoResizing]; 449 wxCocoaTableColumn* wxcol = new wxCocoaTableColumn( col1, editable ); 450 [col1 setColumn:wxcol]; 451 452 // owned by the tableview 453 [col1 release]; 454 return wxcol; 455} 456 457 458// 459// inserting / removing lines 460// 461 462void wxListWidgetCocoaImpl::ListInsert( unsigned int WXUNUSED(n) ) 463{ 464 [m_tableView reloadData]; 465} 466 467void wxListWidgetCocoaImpl::ListDelete( unsigned int WXUNUSED(n) ) 468{ 469 [m_tableView reloadData]; 470} 471 472void wxListWidgetCocoaImpl::ListClear() 473{ 474 [m_tableView reloadData]; 475} 476 477// selecting 478 479void wxListWidgetCocoaImpl::ListDeselectAll() 480{ 481 [m_tableView deselectAll:nil]; 482} 483 484void wxListWidgetCocoaImpl::ListSetSelection( unsigned int n, bool select, bool multi ) 485{ 486 // TODO 487 if ( select ) 488 [m_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:n] 489 byExtendingSelection:multi]; 490 else 491 [m_tableView deselectRow: n]; 492 493} 494 495int wxListWidgetCocoaImpl::ListGetSelection() const 496{ 497 return [m_tableView selectedRow]; 498} 499 500int wxListWidgetCocoaImpl::ListGetSelections( wxArrayInt& aSelections ) const 501{ 502 aSelections.Empty(); 503 504 int count = ListGetCount(); 505 506 for ( int i = 0; i < count; ++i) 507 { 508 if ([m_tableView isRowSelected:i]) 509 aSelections.Add(i); 510 } 511 512 return aSelections.Count(); 513} 514 515bool wxListWidgetCocoaImpl::ListIsSelected( unsigned int n ) const 516{ 517 return [m_tableView isRowSelected:n]; 518} 519 520// display 521 522void wxListWidgetCocoaImpl::ListScrollTo( unsigned int n ) 523{ 524 [m_tableView scrollRowToVisible:n]; 525} 526 527 528void wxListWidgetCocoaImpl::UpdateLine( unsigned int WXUNUSED(n), wxListWidgetColumn* WXUNUSED(col) ) 529{ 530 // TODO optimize 531 [m_tableView reloadData]; 532} 533 534void wxListWidgetCocoaImpl::UpdateLineToEnd( unsigned int WXUNUSED(n)) 535{ 536 // TODO optimize 537 [m_tableView reloadData]; 538} 539 540void wxListWidgetCocoaImpl::controlDoubleAction(WXWidget WXUNUSED(slf),void* WXUNUSED(_cmd), void *WXUNUSED(sender)) 541{ 542 wxListBox *list = static_cast<wxListBox*> ( GetWXPeer()); 543 wxCHECK_RET( list != NULL , wxT("Listbox expected")); 544 545 int sel = [m_tableView clickedRow]; 546 if ((sel < 0) || (sel > (int) list->GetCount())) // OS X can select an item below the last item (why?) 547 return; 548 549 list->HandleLineEvent( sel, true ); 550} 551 552// accessing content 553 554 555wxWidgetImplType* wxWidgetImpl::CreateListBox( wxWindowMac* wxpeer, 556 wxWindowMac* WXUNUSED(parent), 557 wxWindowID WXUNUSED(id), 558 const wxPoint& pos, 559 const wxSize& size, 560 long style, 561 long WXUNUSED(extraStyle)) 562{ 563 NSRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ; 564 NSScrollView* scrollview = [[NSScrollView alloc] initWithFrame:r]; 565 566 // use same scroll flags logic as msw 567 568 [scrollview setHasVerticalScroller:YES]; 569 570 if ( style & wxLB_HSCROLL ) 571 [scrollview setHasHorizontalScroller:YES]; 572 573 [scrollview setAutohidesScrollers: ((style & wxLB_ALWAYS_SB) ? NO : YES)]; 574 575 // setting up the true table 576 577 wxNSTableView* tableview = [[wxNSTableView alloc] init]; 578 [tableview setDelegate:tableview]; 579 // only one multi-select mode available 580 if ( (style & wxLB_EXTENDED) || (style & wxLB_MULTIPLE) ) 581 [tableview setAllowsMultipleSelection:YES]; 582 583 // simple listboxes have no header row 584 [tableview setHeaderView:nil]; 585 586 if ( style & wxLB_HSCROLL ) 587 [tableview setColumnAutoresizingStyle:NSTableViewNoColumnAutoresizing]; 588 else 589 [tableview setColumnAutoresizingStyle:NSTableViewLastColumnOnlyAutoresizingStyle]; 590 591 wxNSTableDataSource* ds = [[ wxNSTableDataSource alloc] init]; 592 [tableview setDataSource:ds]; 593 [scrollview setDocumentView:tableview]; 594 [tableview release]; 595 596 wxListWidgetCocoaImpl* c = new wxListWidgetCocoaImpl( wxpeer, scrollview, tableview, ds ); 597 598 // temporary hook for dnd 599 // [tableview registerForDraggedTypes:[NSArray arrayWithObjects: 600 // NSStringPboardType, NSFilenamesPboardType, (NSString*) kPasteboardTypeFileURLPromise, NSTIFFPboardType, NSPICTPboardType, NSPDFPboardType, nil]]; 601 602 [ds setImplementation:c]; 603 return c; 604} 605 606int wxListWidgetCocoaImpl::DoListHitTest(const wxPoint& inpoint) const 607{ 608 // translate inpoint to listpoint via scrollview 609 NSPoint p = wxToNSPoint( m_osxView, inpoint ); 610 p = [m_osxView convertPoint:p toView:m_tableView]; 611 // hittest using new point 612 NSInteger i = [m_tableView rowAtPoint:p]; 613 return i; 614} 615 616#endif // wxUSE_LISTBOX 617