1/////////////////////////////////////////////////////////////////////////////// 2// Name: src/osx/cocoa/dataview.mm 3// Purpose: wxDataView 4// Author: 5// Modified by: 6// Created: 2009-01-31 7// Copyright: 8// Licence: wxWindows licence 9/////////////////////////////////////////////////////////////////////////////// 10 11#include "wx/wxprec.h" 12 13#if (wxUSE_DATAVIEWCTRL == 1) && !defined(wxUSE_GENERICDATAVIEWCTRL) 14 15#ifndef WX_PRECOMP 16 #include "wx/app.h" 17 #include "wx/toplevel.h" 18 #include "wx/font.h" 19 #include "wx/settings.h" 20 #include "wx/utils.h" 21#endif 22 23#include "wx/osx/private.h" 24#include "wx/osx/cocoa/dataview.h" 25#include "wx/renderer.h" 26#include "wx/stopwatch.h" 27#include "wx/dcgraph.h" 28 29// ============================================================================ 30// Constants used locally 31// ============================================================================ 32 33#define DataViewPboardType @"OutlineViewItem" 34 35// ============================================================================ 36// Classes used locally in dataview.mm 37// ============================================================================ 38 39// ============================================================================ 40// wxPointerObject 41// ============================================================================ 42 43@implementation wxPointerObject 44 45-(id) init 46{ 47 self = [super init]; 48 if (self != nil) 49 self->pointer = NULL; 50 return self; 51} 52 53-(id) initWithPointer:(void*) initPointer 54{ 55 self = [super init]; 56 if (self != nil) 57 self->pointer = initPointer; 58 return self; 59} 60 61// 62// inherited methods from NSObject 63// 64-(BOOL) isEqual:(id)object 65{ 66 return (object != nil) && 67 ([object isKindOfClass:[wxPointerObject class]]) && 68 (pointer == [((wxPointerObject*) object) pointer]); 69} 70 71-(NSUInteger) hash 72{ 73 return (NSUInteger) pointer; 74} 75 76-(void*) pointer 77{ 78 return pointer; 79} 80 81-(void) setPointer:(void*) newPointer 82{ 83 pointer = newPointer; 84} 85 86@end 87 88namespace 89{ 90 91inline wxDataViewItem wxDataViewItemFromItem(id item) 92{ 93 return wxDataViewItem([static_cast<wxPointerObject *>(item) pointer]); 94} 95 96inline wxDataViewItem wxDataViewItemFromMaybeNilItem(id item) 97{ 98 return item == nil ? wxDataViewItem() : wxDataViewItemFromItem(item); 99} 100 101} // anonymous namespace 102 103// ---------------------------------------------------------------------------- 104// wxCustomRendererObject 105// ---------------------------------------------------------------------------- 106 107@interface wxCustomRendererObject : NSObject <NSCopying> 108{ 109@public 110 wxDataViewCustomRenderer* customRenderer; // not owned by the class 111} 112 113 -(id) init; 114 -(id) initWithRenderer:(wxDataViewCustomRenderer*)renderer; 115@end 116 117@implementation wxCustomRendererObject 118 119-(id) init 120{ 121 self = [super init]; 122 if (self != nil) 123 { 124 customRenderer = NULL; 125 } 126 return self; 127} 128 129-(id) initWithRenderer:(wxDataViewCustomRenderer*)renderer 130{ 131 self = [super init]; 132 if (self != nil) 133 { 134 customRenderer = renderer; 135 } 136 return self; 137} 138 139-(id) copyWithZone:(NSZone*)zone 140{ 141 wxCustomRendererObject* copy; 142 143 copy = [[[self class] allocWithZone:zone] init]; 144 copy->customRenderer = customRenderer; 145 146 return copy; 147} 148@end 149 150// ---------------------------------------------------------------------------- 151// wxDVCNSTableColumn: exists only to override NSTableColumn:dataCellForRow: 152// ---------------------------------------------------------------------------- 153 154@interface wxDVCNSTableColumn : NSTableColumn 155{ 156} 157 158 // Get the identifier we use for the specified column. This should be used 159 // for finding columns from identifier only, to initialize the identifier 160 // of a new column use initWithColumnPointer below instead. 161 +(NSString*) identifierForColumnPointer:(const wxDataViewColumn*)column; 162 163 // Initialize the column with the given pointer to the associated 164 // wxDataViewColumn. This pointer can later be retrieved using 165 // getColumnPointer. 166 -(id) initWithColumnPointer:(const wxDataViewColumn*)column; 167 168 // Retrieve the associated column. 169 -(wxDataViewColumn*) getColumnPointer; 170 171 -(id) dataCellForRow:(NSInteger)row; 172@end 173 174@implementation wxDVCNSTableColumn 175 176+(NSString*) identifierForColumnPointer:(const wxDataViewColumn*)column 177{ 178 // Starting from OS X 10.7 the column identifier must be an NSString and 179 // not just some arbitrary object, so we serialize the pointer into the 180 // string. Notice the use of NSInteger which is big enough to store a 181 // pointer in both 32 and 64 bit builds. 182 return [NSString stringWithFormat:@"%lu", 183 (unsigned long)reinterpret_cast<NSUInteger>(column)]; 184} 185 186-(id) initWithColumnPointer:(const wxDataViewColumn*)column 187{ 188 [self initWithIdentifier: [wxDVCNSTableColumn identifierForColumnPointer:column]]; 189 return self; 190} 191 192-(wxDataViewColumn*) getColumnPointer 193{ 194 // The case to NSString is needed for OS X < 10.7. 195 return reinterpret_cast<wxDataViewColumn*>( 196 [static_cast<NSString*>([self identifier]) integerValue]); 197} 198 199-(id) dataCellForRow:(NSInteger)row 200{ 201 // what we want to do here is to simply return nil for the cells which 202 // shouldn't show anything as otherwise we would show e.g. empty combo box 203 // or progress cells in the columns using the corresponding types even for 204 // the container rows which is wrong 205 206 const wxDataViewColumn * const dvCol = [self getColumnPointer]; 207 208 const wxDataViewCtrl * const dvc = dvCol->GetOwner(); 209 const wxCocoaDataViewControl * const 210 peer = static_cast<wxCocoaDataViewControl *>(dvc->GetPeer()); 211 212 213 // once we do have everything, simply ask NSOutlineView for the item... 214 const id item = peer->GetItemAtRow(row); 215 if ( item ) 216 { 217 // ... and if it succeeded, ask the model whether it has any value 218 wxDataViewItem dvItem(wxDataViewItemFromItem(item)); 219 220 if ( !dvc->GetModel()->HasValue(dvItem, dvCol->GetModelColumn()) ) 221 return nil; 222 } 223 224 return [super dataCellForRow:row]; 225} 226 227@end 228 229// ============================================================================ 230// local helpers 231// ============================================================================ 232 233namespace 234{ 235 236// convert from NSObject to different C++ types: all these functions check 237// that the conversion really makes sense and assert if it doesn't 238wxString ObjectToString(NSObject *object) 239{ 240 wxCHECK_MSG( [object isKindOfClass:[NSString class]], "", 241 wxString::Format 242 ( 243 "string expected but got %s", 244 wxCFStringRef::AsString([object className]) 245 )); 246 247 return wxCFStringRef([((NSString*) object) retain]).AsString(); 248} 249 250bool ObjectToBool(NSObject *object) 251{ 252 // actually the value must be of NSCFBoolean class but it's private so we 253 // can't check for it directly 254 wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], false, 255 wxString::Format 256 ( 257 "number expected but got %s", 258 wxCFStringRef::AsString([object className]) 259 )); 260 261 return [(NSNumber *)object boolValue]; 262} 263 264long ObjectToLong(NSObject *object) 265{ 266 wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], -1, 267 wxString::Format 268 ( 269 "number expected but got %s", 270 wxCFStringRef::AsString([object className]) 271 )); 272 273 return [(NSNumber *)object longValue]; 274} 275 276wxDateTime ObjectToDate(NSObject *object) 277{ 278 wxCHECK_MSG( [object isKindOfClass:[NSDate class]], wxInvalidDateTime, 279 wxString::Format 280 ( 281 "date expected but got %s", 282 wxCFStringRef::AsString([object className]) 283 )); 284 285 // get the number of seconds since 1970-01-01 UTC and this is the only 286 // way to convert a double to a wxLongLong 287 const wxLongLong seconds = [((NSDate*) object) timeIntervalSince1970]; 288 289 wxDateTime dt(1, wxDateTime::Jan, 1970); 290 dt.Add(wxTimeSpan(0,0,seconds)); 291 292 // the user has entered a date in the local timezone but seconds 293 // contains the number of seconds from date in the local timezone 294 // since 1970-01-01 UTC; therefore, the timezone information has to be 295 // transferred to wxWidgets, too: 296 dt.MakeFromTimezone(wxDateTime::UTC); 297 298 return dt; 299} 300 301NSInteger CompareItems(id item1, id item2, void* context) 302{ 303 NSArray* const sortDescriptors = (NSArray*) context; 304 305 NSUInteger const count = [sortDescriptors count]; 306 307 NSInteger result = NSOrderedSame; 308 for ( NSUInteger i = 0; i < count && result == NSOrderedSame; ++i ) 309 { 310 wxSortDescriptorObject* const 311 sortDescriptor = (wxSortDescriptorObject*) 312 [sortDescriptors objectAtIndex:i]; 313 314 int rc = [sortDescriptor modelPtr]->Compare 315 ( 316 wxDataViewItemFromItem(item1), 317 wxDataViewItemFromItem(item2), 318 [sortDescriptor columnPtr]->GetModelColumn(), 319 [sortDescriptor ascending] == YES 320 ); 321 322 if ( rc < 0 ) 323 result = NSOrderedAscending; 324 else if ( rc > 0 ) 325 result = NSOrderedDescending; 326 } 327 328 return result; 329} 330 331NSTextAlignment ConvertToNativeHorizontalTextAlignment(int alignment) 332{ 333 if (alignment & wxALIGN_CENTER_HORIZONTAL) 334 return NSCenterTextAlignment; 335 else if (alignment & wxALIGN_RIGHT) 336 return NSRightTextAlignment; 337 else 338 return NSLeftTextAlignment; 339} 340 341NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column) 342{ 343 wxDataViewRenderer * const renderer = column->GetRenderer(); 344 345 wxCHECK_MSG( renderer, NULL, "column should have a renderer" ); 346 347 wxDVCNSTableColumn * const nativeColumn( 348 [[wxDVCNSTableColumn alloc] initWithColumnPointer: column] 349 ); 350 351 // setting the size related parameters: 352 int resizingMask; 353 if (column->IsResizeable()) 354 { 355 resizingMask = NSTableColumnUserResizingMask; 356 [nativeColumn setMinWidth:column->GetMinWidth()]; 357 [nativeColumn setMaxWidth:column->GetMaxWidth()]; 358 } 359 else // column is not resizable [by user] 360 { 361 // if the control doesn't show a header, make the columns resize 362 // automatically, this is particularly important for the single column 363 // controls (such as wxDataViewTreeCtrl) as their unique column should 364 // always take up all the available splace 365 resizingMask = column->GetOwner()->HasFlag(wxDV_NO_HEADER) 366 ? NSTableColumnAutoresizingMask 367 : NSTableColumnNoResizing; 368 } 369 [nativeColumn setResizingMask:resizingMask]; 370 371 // setting the visibility: 372 [nativeColumn setHidden:static_cast<BOOL>(column->IsHidden())]; 373 374 wxDataViewRendererNativeData * const renderData = renderer->GetNativeData(); 375 376 // setting the header: 377 [[nativeColumn headerCell] setAlignment: 378 ConvertToNativeHorizontalTextAlignment(column->GetAlignment())]; 379 [[nativeColumn headerCell] setStringValue: 380 [[wxCFStringRef(column->GetTitle()).AsNSString() retain] autorelease]]; 381 renderData->ApplyLineBreakMode([nativeColumn headerCell]); 382 383 // setting data cell's properties: 384 [[nativeColumn dataCell] setWraps:NO]; 385 // setting the default data cell: 386 [nativeColumn setDataCell:renderData->GetColumnCell()]; 387 // setting the editablility: 388 const bool isEditable = renderer->GetMode() == wxDATAVIEW_CELL_EDITABLE; 389 390 [nativeColumn setEditable:isEditable]; 391 [[nativeColumn dataCell] setEditable:isEditable]; 392 393 return nativeColumn; 394} 395 396} // anonymous namespace 397 398// ============================================================================ 399// Public helper functions for dataview implementation on OSX 400// ============================================================================ 401 402wxWidgetImplType* CreateDataView(wxWindowMac* wxpeer, 403 wxWindowMac* WXUNUSED(parent), 404 wxWindowID WXUNUSED(id), 405 const wxPoint& pos, 406 const wxSize& size, 407 long style, 408 long WXUNUSED(extraStyle)) 409{ 410 return new wxCocoaDataViewControl(wxpeer,pos,size,style); 411} 412 413// ============================================================================ 414// wxSortDescriptorObject 415// ============================================================================ 416 417@implementation wxSortDescriptorObject 418-(id) init 419{ 420 self = [super init]; 421 if (self != nil) 422 { 423 columnPtr = NULL; 424 modelPtr = NULL; 425 } 426 return self; 427} 428 429-(id) 430initWithModelPtr:(wxDataViewModel*)initModelPtr 431 sortingColumnPtr:(wxDataViewColumn*)initColumnPtr 432 ascending:(BOOL)sortAscending 433{ 434 self = [super initWithKey:@"dummy" ascending:sortAscending]; 435 if (self != nil) 436 { 437 columnPtr = initColumnPtr; 438 modelPtr = initModelPtr; 439 } 440 return self; 441} 442 443-(id) copyWithZone:(NSZone*)zone 444{ 445 wxSortDescriptorObject* copy; 446 447 448 copy = [super copyWithZone:zone]; 449 copy->columnPtr = columnPtr; 450 copy->modelPtr = modelPtr; 451 452 return copy; 453} 454 455// 456// access to model column's index 457// 458-(wxDataViewColumn*) columnPtr 459{ 460 return columnPtr; 461} 462 463-(wxDataViewModel*) modelPtr 464{ 465 return modelPtr; 466} 467 468-(void) setColumnPtr:(wxDataViewColumn*)newColumnPtr 469{ 470 columnPtr = newColumnPtr; 471} 472 473-(void) setModelPtr:(wxDataViewModel*)newModelPtr 474{ 475 modelPtr = newModelPtr; 476} 477 478@end 479 480// ============================================================================ 481// wxCocoaOutlineDataSource 482// ============================================================================ 483@implementation wxCocoaOutlineDataSource 484 485// 486// constructors / destructor 487// 488-(id) init 489{ 490 self = [super init]; 491 if (self != nil) 492 { 493 implementation = NULL; 494 model = NULL; 495 496 currentParentItem = nil; 497 498 sortDescriptors = nil; 499 500 children = [[NSMutableArray alloc] init]; 501 items = [[NSMutableSet alloc] init]; 502 } 503 return self; 504} 505 506-(void) dealloc 507{ 508 [sortDescriptors release]; 509 510 [currentParentItem release]; 511 512 [children release]; 513 [items release]; 514 515 [super dealloc]; 516} 517 518// 519// methods of informal protocol: 520// 521-(BOOL) 522outlineView:(NSOutlineView*)outlineView 523 acceptDrop:(id<NSDraggingInfo>)info 524 item:(id)item childIndex:(NSInteger)index 525{ 526 wxUnusedVar(outlineView); 527 wxUnusedVar(index); 528 529 NSArray* supportedTypes( 530 [NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil] 531 ); 532 533 NSPasteboard* pasteboard([info draggingPasteboard]); 534 535 NSString* bestType([pasteboard availableTypeFromArray:supportedTypes]); 536 537 if ( bestType == nil ) 538 return FALSE; 539 540 wxDataViewCtrl * const dvc(implementation->GetDataViewCtrl()); 541 542 wxCHECK_MSG( dvc, false, 543 "Pointer to data view control not set correctly." ); 544 wxCHECK_MSG( dvc->GetModel(), false, 545 "Pointer to model not set correctly." ); 546 547 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_DROP, dvc->GetId()); 548 event.SetEventObject(dvc); 549 event.SetItem(wxDataViewItemFromItem(item)); 550 event.SetModel(dvc->GetModel()); 551 552 BOOL dragSuccessful = false; 553 if ( [bestType compare:DataViewPboardType] == NSOrderedSame ) 554 { 555 NSArray* dataArray((NSArray*) 556 [pasteboard propertyListForType:DataViewPboardType]); 557 NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]); 558 559 indexDraggedItem = 0; 560 while (indexDraggedItem < noOfDraggedItems) 561 { 562 wxDataObjectComposite* dataObjects( 563 implementation->GetDnDDataObjects((NSData*) 564 [dataArray objectAtIndex:indexDraggedItem])); 565 566 if (dataObjects && (dataObjects->GetFormatCount() > 0)) 567 { 568 wxMemoryBuffer buffer; 569 570 // copy data into data object: 571 event.SetDataObject(dataObjects); 572 event.SetDataFormat( 573 implementation->GetDnDDataFormat(dataObjects)); 574 // copy data into buffer: 575 dataObjects->GetDataHere( 576 event.GetDataFormat().GetType(), 577 buffer.GetWriteBuf(event.GetDataSize())); 578 buffer.UngetWriteBuf(event.GetDataSize()); 579 event.SetDataBuffer(buffer.GetData()); 580 // finally, send event: 581 if (dvc->HandleWindowEvent(event) && event.IsAllowed()) 582 { 583 dragSuccessful = true; 584 ++indexDraggedItem; 585 } 586 else 587 { 588 dragSuccessful = true; 589 indexDraggedItem = noOfDraggedItems; // stop loop 590 } 591 } 592 else 593 { 594 dragSuccessful = false; 595 indexDraggedItem = noOfDraggedItems; // stop loop 596 } 597 // clean-up: 598 delete dataObjects; 599 } 600 } 601 else 602 { 603 // needed to convert internally used UTF-16 representation to a UTF-8 604 // representation 605 CFDataRef osxData; 606 wxDataObjectComposite* dataObjects (new wxDataObjectComposite()); 607 wxTextDataObject* textDataObject(new wxTextDataObject()); 608 609 osxData = ::CFStringCreateExternalRepresentation 610 ( 611 kCFAllocatorDefault, 612 (CFStringRef)[pasteboard stringForType:NSStringPboardType], 613 kCFStringEncodingUTF8, 614 32 615 ); 616 if (textDataObject->SetData(::CFDataGetLength(osxData), 617 ::CFDataGetBytePtr(osxData))) 618 dataObjects->Add(textDataObject); 619 else 620 delete textDataObject; 621 // send event if data could be copied: 622 if (dataObjects->GetFormatCount() > 0) 623 { 624 event.SetDataObject(dataObjects); 625 event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects)); 626 if (dvc->HandleWindowEvent(event) && event.IsAllowed()) 627 dragSuccessful = true; 628 else 629 dragSuccessful = false; 630 } 631 else 632 dragSuccessful = false; 633 // clean up: 634 ::CFRelease(osxData); 635 delete dataObjects; 636 } 637 return dragSuccessful; 638} 639 640-(id) outlineView:(NSOutlineView*)outlineView 641 child:(NSInteger)index 642 ofItem:(id)item 643{ 644 wxUnusedVar(outlineView); 645 646 if ((item == currentParentItem) && 647 (index < ((NSInteger) [self getChildCount]))) 648 return [self getChild:index]; 649 650 wxDataViewItemArray dataViewChildren; 651 652 wxCHECK_MSG( model, 0, "Valid model in data source does not exist." ); 653 model->GetChildren(wxDataViewItemFromMaybeNilItem(item), dataViewChildren); 654 [self bufferItem:item withChildren:&dataViewChildren]; 655 if ([sortDescriptors count] > 0) 656 [children sortUsingFunction:CompareItems context:sortDescriptors]; 657 return [self getChild:index]; 658} 659 660-(BOOL) outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item 661{ 662 wxUnusedVar(outlineView); 663 664 wxCHECK_MSG( model, 0, "Valid model in data source does not exist." ); 665 return model->IsContainer(wxDataViewItemFromItem(item)); 666} 667 668-(NSInteger) outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item 669{ 670 wxUnusedVar(outlineView); 671 672 NSInteger noOfChildren; 673 674 wxDataViewItemArray dataViewChildren; 675 676 677 wxCHECK_MSG( model, 0, "Valid model in data source does not exist." ); 678 noOfChildren = model->GetChildren(wxDataViewItemFromMaybeNilItem(item), 679 dataViewChildren); 680 [self bufferItem:item withChildren:&dataViewChildren]; 681 if ([sortDescriptors count] > 0) 682 [children sortUsingFunction:CompareItems context:sortDescriptors]; 683 return noOfChildren; 684} 685 686-(id) 687outlineView:(NSOutlineView*)outlineView 688 objectValueForTableColumn:(NSTableColumn*)tableColumn 689 byItem:(id)item 690{ 691 wxUnusedVar(outlineView); 692 693 wxCHECK_MSG( model, nil, "Valid model in data source does not exist." ); 694 695 wxDataViewColumn* const 696 col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]); 697 const unsigned colIdx = col->GetModelColumn(); 698 699 wxDataViewItem dataViewItem(wxDataViewItemFromItem(item)); 700 701 if ( model->HasValue(dataViewItem, colIdx) ) 702 { 703 wxVariant value; 704 model->GetValue(value,dataViewItem, colIdx); 705 col->GetRenderer()->SetValue(value); 706 } 707 708 return nil; 709} 710 711-(void) 712outlineView:(NSOutlineView*)outlineView 713 setObjectValue:(id)object 714 forTableColumn:(NSTableColumn*)tableColumn 715 byItem:(id)item 716{ 717 wxUnusedVar(outlineView); 718 719 wxDataViewColumn* const 720 col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]); 721 722 col->GetRenderer()-> 723 OSXOnCellChanged(object, wxDataViewItemFromItem(item), col->GetModelColumn()); 724} 725 726-(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)oldDescriptors 727{ 728 wxUnusedVar(oldDescriptors); 729 730 // Warning: the new sort descriptors are guaranteed to be only of type 731 // NSSortDescriptor! Therefore, the sort descriptors for the data source 732 // have to be converted. 733 NSArray* newDescriptors; 734 735 NSMutableArray* wxSortDescriptors; 736 737 NSUInteger noOfDescriptors; 738 739 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 740 741 742 // convert NSSortDescriptors to wxSortDescriptorObjects: 743 newDescriptors = [outlineView sortDescriptors]; 744 noOfDescriptors = [newDescriptors count]; 745 wxSortDescriptors = [NSMutableArray arrayWithCapacity:noOfDescriptors]; 746 for (NSUInteger i=0; i<noOfDescriptors; ++i) 747 { 748 NSSortDescriptor* const newDescriptor = [newDescriptors objectAtIndex:i]; 749 750 [wxSortDescriptors addObject:[[[wxSortDescriptorObject alloc] initWithModelPtr:model 751 sortingColumnPtr:dvc->GetColumn([[newDescriptor key] intValue]) 752 ascending:[newDescriptor ascending]] autorelease]]; 753 } 754 [(wxCocoaOutlineDataSource*)[outlineView dataSource] setSortDescriptors:wxSortDescriptors]; 755 756 // send first the event to wxWidgets that the sorting has changed so that 757 // the program can do special actions before the sorting actually starts: 758 wxDataViewEvent event(wxEVT_DATAVIEW_COLUMN_SORTED,dvc->GetId()); // variable definition 759 760 event.SetEventObject(dvc); 761 if (noOfDescriptors > 0) 762 { 763 wxDataViewColumn* const col = [[wxSortDescriptors objectAtIndex:0] columnPtr]; 764 765 event.SetColumn(dvc->GetColumnPosition(col)); 766 event.SetDataViewColumn(col); 767 } 768 dvc->GetEventHandler()->ProcessEvent(event); 769 770 // start re-ordering the data; 771 // children's buffer must be cleared first because it contains the old order: 772 [self clearChildren]; 773 // sorting is done while reloading the data: 774 [outlineView reloadData]; 775} 776 777-(NSDragOperation) outlineView:(NSOutlineView*)outlineView validateDrop:(id<NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index 778{ 779 wxUnusedVar(outlineView); 780 wxUnusedVar(index); 781 782 NSArray* supportedTypes([NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]); 783 784 NSPasteboard* pasteboard([info draggingPasteboard]); 785 786 NSString* bestType([pasteboard availableTypeFromArray:supportedTypes]); 787 if (bestType == nil) 788 return NSDragOperationNone; 789 790 NSDragOperation dragOperation = NSDragOperationNone; 791 wxDataViewCtrl* const dvc(implementation->GetDataViewCtrl()); 792 793 wxCHECK_MSG(dvc, false, "Pointer to data view control not set correctly."); 794 wxCHECK_MSG(dvc->GetModel(), false, "Pointer to model not set correctly."); 795 796 wxDataViewEvent 797 event(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE,dvc->GetId()); 798 799 event.SetEventObject(dvc); 800 event.SetItem(wxDataViewItemFromItem(item)); 801 event.SetModel(dvc->GetModel()); 802 if ([bestType compare:DataViewPboardType] == NSOrderedSame) 803 { 804 NSArray* dataArray((NSArray*)[pasteboard propertyListForType:DataViewPboardType]); 805 NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]); 806 807 indexDraggedItem = 0; 808 while (indexDraggedItem < noOfDraggedItems) 809 { 810 wxDataObjectComposite* dataObjects(implementation->GetDnDDataObjects((NSData*)[dataArray objectAtIndex:indexDraggedItem])); 811 812 if (dataObjects && (dataObjects->GetFormatCount() > 0)) 813 { 814 wxMemoryBuffer buffer; 815 816 // copy data into data object: 817 event.SetDataObject(dataObjects); 818 event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects)); 819 // copy data into buffer: 820 dataObjects->GetDataHere(event.GetDataFormat().GetType(),buffer.GetWriteBuf(event.GetDataSize())); 821 buffer.UngetWriteBuf(event.GetDataSize()); 822 event.SetDataBuffer(buffer.GetData()); 823 // finally, send event: 824 if (dvc->HandleWindowEvent(event) && event.IsAllowed()) 825 { 826 dragOperation = NSDragOperationEvery; 827 ++indexDraggedItem; 828 } 829 else 830 { 831 dragOperation = NSDragOperationNone; 832 indexDraggedItem = noOfDraggedItems; // stop loop 833 } 834 } 835 else 836 { 837 dragOperation = NSDragOperationNone; 838 indexDraggedItem = noOfDraggedItems; // stop loop 839 } 840 // clean-up: 841 delete dataObjects; 842 } 843 } 844 else 845 { 846 // needed to convert internally used UTF-16 representation to a UTF-8 847 // representation 848 CFDataRef osxData; 849 wxDataObjectComposite* dataObjects (new wxDataObjectComposite()); 850 wxTextDataObject* textDataObject(new wxTextDataObject()); 851 852 osxData = ::CFStringCreateExternalRepresentation(kCFAllocatorDefault,(CFStringRef)[pasteboard stringForType:NSStringPboardType],kCFStringEncodingUTF8,32); 853 if (textDataObject->SetData(::CFDataGetLength(osxData),::CFDataGetBytePtr(osxData))) 854 dataObjects->Add(textDataObject); 855 else 856 delete textDataObject; 857 // send event if data could be copied: 858 if (dataObjects->GetFormatCount() > 0) 859 { 860 event.SetDataObject(dataObjects); 861 event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects)); 862 if (dvc->HandleWindowEvent(event) && event.IsAllowed()) 863 dragOperation = NSDragOperationEvery; 864 else 865 dragOperation = NSDragOperationNone; 866 } 867 else 868 dragOperation = NSDragOperationNone; 869 // clean up: 870 ::CFRelease(osxData); 871 delete dataObjects; 872 } 873 874 return dragOperation; 875} 876 877-(BOOL) outlineView:(NSOutlineView*)outlineView writeItems:(NSArray*)writeItems toPasteboard:(NSPasteboard*)pasteboard 878{ 879 wxUnusedVar(outlineView); 880 881 // the pasteboard will be filled up with an array containing the data as 882 // returned by the events (including the data type) and a concatenation of 883 // text (string) data; the text data will only be put onto the pasteboard 884 // if for all items a string representation exists 885 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 886 887 wxDataViewItemArray dataViewItems; 888 889 890 wxCHECK_MSG(dvc, false,"Pointer to data view control not set correctly."); 891 wxCHECK_MSG(dvc->GetModel(),false,"Pointer to model not set correctly."); 892 893 if ([writeItems count] > 0) 894 { 895 bool dataStringAvailable(true); // a flag indicating if for all items a data string is available 896 NSMutableArray* dataArray = [NSMutableArray arrayWithCapacity:[writeItems count]]; // data of all items 897 wxString dataString; // contains the string data of all items 898 899 // send a begin drag event for all selected items and proceed with 900 // dragging unless the event is vetoed: 901 wxDataViewEvent 902 event(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG,dvc->GetId()); 903 904 event.SetEventObject(dvc); 905 event.SetModel(dvc->GetModel()); 906 for (size_t itemCounter=0; itemCounter<[writeItems count]; ++itemCounter) 907 { 908 bool itemStringAvailable(false); // a flag indicating if for the current item a string is available 909 wxDataObjectComposite* itemObject(new wxDataObjectComposite()); // data object for current item 910 wxString itemString; // contains the TAB concatenated data of an item 911 912 event.SetItem( 913 wxDataViewItemFromItem([writeItems objectAtIndex:itemCounter])); 914 itemString = ::ConcatenateDataViewItemValues(dvc,event.GetItem()); 915 itemObject->Add(new wxTextDataObject(itemString)); 916 event.SetDataObject(itemObject); 917 // check if event has not been vetoed: 918 if (dvc->HandleWindowEvent(event) && event.IsAllowed() && (event.GetDataObject()->GetFormatCount() > 0)) 919 { 920 size_t const noOfFormats = event.GetDataObject()->GetFormatCount(); 921 wxDataFormat* dataFormats(new wxDataFormat[noOfFormats]); 922 923 event.GetDataObject()->GetAllFormats(dataFormats,wxDataObject::Get); 924 for (size_t formatCounter=0; formatCounter<noOfFormats; ++formatCounter) 925 { 926 // constant definitions for abbreviational purposes: 927 wxDataFormatId const idDataFormat = dataFormats[formatCounter].GetType(); 928 size_t const dataSize = event.GetDataObject()->GetDataSize(idDataFormat); 929 size_t const dataBufferSize = sizeof(wxDataFormatId)+dataSize; 930 // variable definitions (used in all case statements): 931 // give additional headroom for trailing NULL 932 wxMemoryBuffer dataBuffer(dataBufferSize+4); 933 934 dataBuffer.AppendData(&idDataFormat,sizeof(wxDataFormatId)); 935 switch (idDataFormat) 936 { 937 case wxDF_TEXT: 938 // otherwise wxDF_UNICODETEXT already filled up 939 // the string; and the UNICODE representation has 940 // priority 941 if (!itemStringAvailable) 942 { 943 event.GetDataObject()->GetDataHere(wxDF_TEXT,dataBuffer.GetAppendBuf(dataSize)); 944 dataBuffer.UngetAppendBuf(dataSize); 945 [dataArray addObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]]; 946 itemString = wxString(static_cast<char const*>(dataBuffer.GetData())+sizeof(wxDataFormatId),wxConvLocal); 947 itemStringAvailable = true; 948 } 949 break; 950 case wxDF_UNICODETEXT: 951 { 952 event.GetDataObject()->GetDataHere(wxDF_UNICODETEXT,dataBuffer.GetAppendBuf(dataSize)); 953 dataBuffer.UngetAppendBuf(dataSize); 954 if (itemStringAvailable) // does an object already exist as an ASCII text (see wxDF_TEXT case statement)? 955 [dataArray replaceObjectAtIndex:itemCounter withObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]]; 956 else 957 [dataArray addObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]]; 958 itemString = wxString::FromUTF8(static_cast<char const*>(dataBuffer.GetData())+sizeof(wxDataFormatId),dataSize); 959 itemStringAvailable = true; 960 } /* block */ 961 break; 962 default: 963 wxFAIL_MSG("Data object has invalid or unsupported data format"); 964 return NO; 965 } 966 } 967 delete[] dataFormats; 968 delete itemObject; 969 if (dataStringAvailable) 970 { 971 if (itemStringAvailable) 972 { 973 if (itemCounter > 0) 974 dataString << wxT('\n'); 975 dataString << itemString; 976 } 977 else 978 dataStringAvailable = false; 979 } 980 } 981 else 982 { 983 delete itemObject; 984 return NO; // dragging was vetoed or no data available 985 } 986 } 987 if (dataStringAvailable) 988 { 989 wxCFStringRef osxString(dataString); 990 991 [pasteboard declareTypes:[NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil] owner:nil]; 992 [pasteboard setPropertyList:dataArray forType:DataViewPboardType]; 993 [pasteboard setString:osxString.AsNSString() forType:NSStringPboardType]; 994 } 995 else 996 { 997 [pasteboard declareTypes:[NSArray arrayWithObject:DataViewPboardType] owner:nil]; 998 [pasteboard setPropertyList:dataArray forType:DataViewPboardType]; 999 } 1000 return YES; 1001 } 1002 else 1003 return NO; // no items to drag (should never occur) 1004} 1005 1006// 1007// buffer handling 1008// 1009-(void) addToBuffer:(wxPointerObject*)item 1010{ 1011 [items addObject:item]; 1012} 1013 1014-(void) clearBuffer 1015{ 1016 [items removeAllObjects]; 1017} 1018 1019-(wxPointerObject*) getDataViewItemFromBuffer:(const wxDataViewItem&)item 1020{ 1021 return [items member:[[[wxPointerObject alloc] initWithPointer:item.GetID()] autorelease]]; 1022} 1023 1024-(wxPointerObject*) getItemFromBuffer:(wxPointerObject*)item 1025{ 1026 return [items member:item]; 1027} 1028 1029-(BOOL) isInBuffer:(wxPointerObject*)item 1030{ 1031 return [items containsObject:item]; 1032} 1033 1034-(void) removeFromBuffer:(wxPointerObject*)item 1035{ 1036 [items removeObject:item]; 1037} 1038 1039// 1040// children handling 1041// 1042-(void) appendChild:(wxPointerObject*)item 1043{ 1044 [children addObject:item]; 1045} 1046 1047-(void) clearChildren 1048{ 1049 [children removeAllObjects]; 1050} 1051 1052-(wxPointerObject*) getChild:(NSUInteger)index 1053{ 1054 return [children objectAtIndex:index]; 1055} 1056 1057-(NSUInteger) getChildCount 1058{ 1059 return [children count]; 1060} 1061 1062-(void) removeChild:(NSUInteger)index 1063{ 1064 [children removeObjectAtIndex:index]; 1065} 1066 1067// 1068// buffer handling 1069// 1070-(void) clearBuffers 1071{ 1072 [self clearBuffer]; 1073 [self clearChildren]; 1074 [self setCurrentParentItem:nil]; 1075} 1076 1077// 1078// sorting 1079// 1080-(NSArray*) sortDescriptors 1081{ 1082 return sortDescriptors; 1083} 1084 1085-(void) setSortDescriptors:(NSArray*)newSortDescriptors 1086{ 1087 [newSortDescriptors retain]; 1088 [sortDescriptors release]; 1089 sortDescriptors = newSortDescriptors; 1090} 1091 1092// 1093// access to wxWidget's implementation 1094// 1095-(wxPointerObject*) currentParentItem 1096{ 1097 return currentParentItem; 1098} 1099 1100-(wxCocoaDataViewControl*) implementation 1101{ 1102 return implementation; 1103} 1104 1105-(wxDataViewModel*) model 1106{ 1107 return model; 1108} 1109 1110-(void) setCurrentParentItem:(wxPointerObject*)newCurrentParentItem 1111{ 1112 [newCurrentParentItem retain]; 1113 [currentParentItem release]; 1114 currentParentItem = newCurrentParentItem; 1115} 1116 1117-(void) setImplementation:(wxCocoaDataViewControl*) newImplementation 1118{ 1119 implementation = newImplementation; 1120} 1121 1122-(void) setModel:(wxDataViewModel*) newModel 1123{ 1124 model = newModel; 1125} 1126 1127// 1128// other methods 1129// 1130-(void) bufferItem:(wxPointerObject*)parentItem withChildren:(wxDataViewItemArray*)dataViewChildrenPtr 1131{ 1132 NSInteger const noOfChildren = (*dataViewChildrenPtr).GetCount(); 1133 1134 [self setCurrentParentItem:parentItem]; 1135 [self clearChildren]; 1136 for (NSInteger indexChild=0; indexChild<noOfChildren; ++indexChild) 1137 { 1138 wxPointerObject* bufferedPointerObject; 1139 wxPointerObject* newPointerObject([[wxPointerObject alloc] initWithPointer:(*dataViewChildrenPtr)[indexChild].GetID()]); 1140 1141 // The next statement and test looks strange but there is 1142 // unfortunately no workaround: due to the fact that two pointer 1143 // objects are identical if their pointers are identical - because the 1144 // method isEqual has been overloaded - the set operation will only 1145 // add a new pointer object if there is not already one in the set 1146 // having the same pointer. On the other side the children's array 1147 // would always add the new pointer object. This means that different 1148 // pointer objects are stored in the set and array. This will finally 1149 // lead to a crash as objects diverge. To solve this issue it is first 1150 // tested if the child already exists in the set and if it is the case 1151 // the sets object is going to be appended to the array, otheriwse the 1152 // new pointer object is added to the set and array: 1153 bufferedPointerObject = [self getItemFromBuffer:newPointerObject]; 1154 if (bufferedPointerObject == nil) 1155 { 1156 [items addObject:newPointerObject]; 1157 [children addObject:newPointerObject]; 1158 } 1159 else 1160 [children addObject:bufferedPointerObject]; 1161 [newPointerObject release]; 1162 } 1163} 1164 1165@end 1166 1167// ============================================================================ 1168// wxCustomCell 1169// ============================================================================ 1170 1171@implementation wxCustomCell 1172 1173#if 0 // starting implementation for custom cell clicks 1174 1175- (id)init 1176{ 1177 self = [super init]; 1178 [self setAction:@selector(clickedAction)]; 1179 [self setTarget:self]; 1180 return self; 1181} 1182 1183- (void) clickedAction: (id) sender 1184{ 1185 wxUnusedVar(sender); 1186} 1187 1188#endif 1189 1190-(NSSize) cellSize 1191{ 1192 wxCustomRendererObject * const 1193 obj = static_cast<wxCustomRendererObject *>([self objectValue]); 1194 1195 1196 const wxSize size = obj->customRenderer->GetSize(); 1197 return NSMakeSize(size.x, size.y); 1198} 1199 1200// 1201// implementations 1202// 1203-(void) drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView 1204{ 1205 wxCustomRendererObject * const 1206 obj = static_cast<wxCustomRendererObject *>([self objectValue]); 1207 if ( !obj ) 1208 { 1209 // this may happen for the custom cells in container rows: they don't 1210 // have any values 1211 return; 1212 } 1213 1214 wxDataViewCustomRenderer * const renderer = obj->customRenderer; 1215 1216 // if this method is called everything is already setup correctly, 1217 CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort]; 1218 CGContextSaveGState( context ); 1219 1220 if ( ![controlView isFlipped] ) 1221 { 1222 CGContextTranslateCTM( context, 0, [controlView bounds].size.height ); 1223 CGContextScaleCTM( context, 1, -1 ); 1224 } 1225 1226 wxGCDC dc; 1227 wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(context); 1228 dc.SetGraphicsContext(gc); 1229 1230 int state = 0; 1231 if ( [self isHighlighted] ) 1232 state |= wxDATAVIEW_CELL_SELECTED; 1233 1234 renderer->WXCallRender(wxFromNSRect(controlView, cellFrame), &dc, state); 1235 1236 CGContextRestoreGState( context ); 1237} 1238 1239-(NSRect) imageRectForBounds:(NSRect)cellFrame 1240{ 1241 return cellFrame; 1242} 1243 1244-(NSRect) titleRectForBounds:(NSRect)cellFrame 1245{ 1246 return cellFrame; 1247} 1248 1249@end 1250 1251// ============================================================================ 1252// wxImageTextCell 1253// ============================================================================ 1254@implementation wxImageTextCell 1255// 1256// initialization 1257// 1258-(id) init 1259{ 1260 self = [super init]; 1261 if (self != nil) 1262 { 1263 // initializing the text part: 1264 [self setSelectable:YES]; 1265 // initializing the image part: 1266 image = nil; 1267 imageSize = NSMakeSize(16,16); 1268 spaceImageText = 5.0; 1269 xImageShift = 5.0; 1270 } 1271 return self; 1272} 1273 1274-(id) copyWithZone:(NSZone*)zone 1275{ 1276 wxImageTextCell* cell; 1277 1278 1279 cell = (wxImageTextCell*) [super copyWithZone:zone]; 1280 cell->image = [image retain]; 1281 cell->imageSize = imageSize; 1282 cell->spaceImageText = spaceImageText; 1283 cell->xImageShift = xImageShift; 1284 1285 return cell; 1286} 1287 1288-(void) dealloc 1289{ 1290 [image release]; 1291 1292 [super dealloc]; 1293} 1294 1295// 1296// alignment 1297// 1298-(NSTextAlignment) alignment 1299{ 1300 return cellAlignment; 1301} 1302 1303-(void) setAlignment:(NSTextAlignment)newAlignment 1304{ 1305 cellAlignment = newAlignment; 1306 switch (newAlignment) 1307 { 1308 case NSCenterTextAlignment: 1309 case NSLeftTextAlignment: 1310 case NSJustifiedTextAlignment: 1311 case NSNaturalTextAlignment: 1312 [super setAlignment:NSLeftTextAlignment]; 1313 break; 1314 case NSRightTextAlignment: 1315 [super setAlignment:NSRightTextAlignment]; 1316 break; 1317 default: 1318 wxFAIL_MSG("Unknown alignment type."); 1319 } 1320} 1321 1322// 1323// image access 1324// 1325-(NSImage*) image 1326{ 1327 return image; 1328} 1329 1330-(void) setImage:(NSImage*)newImage 1331{ 1332 [newImage retain]; 1333 [image release]; 1334 image = newImage; 1335} 1336 1337-(NSSize) imageSize 1338{ 1339 return imageSize; 1340} 1341 1342-(void) setImageSize:(NSSize) newImageSize 1343{ 1344 imageSize = newImageSize; 1345} 1346 1347// 1348// other methods 1349// 1350-(NSSize) cellImageSize 1351{ 1352 return NSMakeSize(imageSize.width+xImageShift+spaceImageText,imageSize.height); 1353} 1354 1355-(NSSize) cellSize 1356{ 1357 NSSize cellSize([super cellSize]); 1358 1359 1360 if (imageSize.height > cellSize.height) 1361 cellSize.height = imageSize.height; 1362 cellSize.width += imageSize.width+xImageShift+spaceImageText; 1363 1364 return cellSize; 1365} 1366 1367-(NSSize) cellTextSize 1368{ 1369 return [super cellSize]; 1370} 1371 1372// 1373// implementations 1374// 1375-(void) determineCellParts:(NSRect)cellFrame imagePart:(NSRect*)imageFrame textPart:(NSRect*)textFrame 1376{ 1377 switch (cellAlignment) 1378 { 1379 case NSCenterTextAlignment: 1380 { 1381 CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width; 1382 1383 // if the cell's frame is smaller than its contents (at least 1384 // in x-direction) make sure that the image is visible: 1385 if (cellSpace <= 0) 1386 NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge); 1387 else // otherwise center the image and text in the cell's frame 1388 NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText+0.5*cellSpace,NSMinXEdge); 1389 } 1390 break; 1391 case NSJustifiedTextAlignment: 1392 case NSLeftTextAlignment: 1393 case NSNaturalTextAlignment: // how to determine the natural writing direction? TODO 1394 NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge); 1395 break; 1396 case NSRightTextAlignment: 1397 { 1398 CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width; 1399 1400 // if the cell's frame is smaller than its contents (at least 1401 // in x-direction) make sure that the image is visible: 1402 if (cellSpace <= 0) 1403 NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge); 1404 else // otherwise right align the image and text in the cell's frame 1405 NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText+cellSpace,NSMinXEdge); 1406 } 1407 break; 1408 default: 1409 *imageFrame = NSZeroRect; 1410 *textFrame = NSZeroRect; 1411 wxFAIL_MSG("Unhandled alignment type."); 1412 } 1413} 1414 1415-(void) drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView 1416{ 1417 NSRect textFrame, imageFrame; 1418 1419 1420 [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame]; 1421 // draw the image part by ourselves; 1422 // check if the cell has to draw its own background (checking is done by 1423 // the parameter of the textfield's cell): 1424 if ([self drawsBackground]) 1425 { 1426 [[self backgroundColor] set]; 1427 NSRectFill(imageFrame); 1428 } 1429 if (image != nil) 1430 { 1431 // the image is slightly shifted (xImageShift) and has a fixed size 1432 // but the image's frame might be larger and starts currently on the 1433 // left side of the cell's frame; therefore, the origin and the 1434 // image's frame size have to be adjusted: 1435 if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText) 1436 { 1437 imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText; 1438 imageFrame.size.width = imageSize.width; 1439 } 1440 else 1441 { 1442 imageFrame.origin.x += xImageShift; 1443 imageFrame.size.width -= xImageShift+spaceImageText; 1444 } 1445 // ...and the image has to be centered in the y-direction: 1446 if (imageFrame.size.height > imageSize.height) 1447 imageFrame.size.height = imageSize.height; 1448 imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height)); 1449 1450 // according to the documentation the coordinate system should be 1451 // flipped for NSTableViews (y-coordinate goes from top to bottom); to 1452 // draw an image correctly the coordinate system has to be transformed 1453 // to a bottom-top coordinate system, otherwise the image's 1454 // content is flipped: 1455 NSAffineTransform* coordinateTransform([NSAffineTransform transform]); 1456 1457 if ([controlView isFlipped]) 1458 { 1459 [coordinateTransform scaleXBy: 1.0 yBy:-1.0]; // first the coordinate system is brought back to bottom-top orientation 1460 [coordinateTransform translateXBy:0.0 yBy:(-2.0)*imageFrame.origin.y-imageFrame.size.height]; // the coordinate system has to be moved to compensate for the 1461 [coordinateTransform concat]; // other orientation and the position of the image's frame 1462 } 1463 [image drawInRect:imageFrame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; // suggested method to draw the image 1464 // instead of compositeToPoint:operation: 1465 // take back previous transformation (if the view is not flipped the 1466 // coordinate transformation matrix contains the identity matrix and 1467 // the next two operations do not change the content's transformation 1468 // matrix): 1469 [coordinateTransform invert]; 1470 [coordinateTransform concat]; 1471 } 1472 // let the textfield cell draw the text part: 1473 if (textFrame.size.width > [self cellTextSize].width) 1474 { 1475 // for unknown reasons the alignment of the text cell is ignored; 1476 // therefore change the size so that alignment does not influence the 1477 // visualization anymore 1478 textFrame.size.width = [self cellTextSize].width; 1479 } 1480 [super drawWithFrame:textFrame inView:controlView]; 1481} 1482 1483-(void) editWithFrame:(NSRect)aRect inView:(NSView*)controlView editor:(NSText*)textObj delegate:(id)anObject event:(NSEvent*)theEvent 1484{ 1485 NSRect textFrame, imageFrame; 1486 1487 1488 [self determineCellParts:aRect imagePart:&imageFrame textPart:&textFrame]; 1489 [super editWithFrame:textFrame inView:controlView editor:textObj delegate:anObject event:theEvent]; 1490} 1491 1492-(NSUInteger) hitTestForEvent:(NSEvent*)event inRect:(NSRect)cellFrame ofView:(NSView*)controlView 1493{ 1494 NSPoint point = [controlView convertPoint:[event locationInWindow] fromView:nil]; 1495 1496 NSRect imageFrame, textFrame; 1497 1498 1499 [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame]; 1500 if (image != nil) 1501 { 1502 // the image is shifted... 1503 if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText) 1504 { 1505 imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText; 1506 imageFrame.size.width = imageSize.width; 1507 } 1508 else 1509 { 1510 imageFrame.origin.x += xImageShift; 1511 imageFrame.size.width -= xImageShift+spaceImageText; 1512 } 1513 // ...and centered: 1514 if (imageFrame.size.height > imageSize.height) 1515 imageFrame.size.height = imageSize.height; 1516 imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height)); 1517 // If the point is in the image rect, then it is a content hit (see 1518 // documentation for hitTestForEvent:inRect:ofView): 1519 if (NSMouseInRect(point, imageFrame, [controlView isFlipped])) 1520 return NSCellHitContentArea; 1521 } 1522 // if the image was not hit let's try the text part: 1523 if (textFrame.size.width > [self cellTextSize].width) 1524 { 1525 // for unknown reasons the alignment of the text cell is ignored; 1526 // therefore change the size so that alignment does not influence the 1527 // visualization anymore 1528 textFrame.size.width = [self cellTextSize].width; 1529 } 1530 1531 return [super hitTestForEvent:event inRect:textFrame ofView:controlView]; 1532} 1533 1534-(NSRect) imageRectForBounds:(NSRect)cellFrame 1535{ 1536 NSRect textFrame, imageFrame; 1537 1538 1539 [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame]; 1540 if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText) 1541 { 1542 imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText; 1543 imageFrame.size.width = imageSize.width; 1544 } 1545 else 1546 { 1547 imageFrame.origin.x += xImageShift; 1548 imageFrame.size.width -= xImageShift+spaceImageText; 1549 } 1550 // ...and centered: 1551 if (imageFrame.size.height > imageSize.height) 1552 imageFrame.size.height = imageSize.height; 1553 imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height)); 1554 1555 return imageFrame; 1556} 1557 1558-(void) selectWithFrame:(NSRect)aRect inView:(NSView*)controlView editor:(NSText*)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength 1559{ 1560 NSRect textFrame, imageFrame; 1561 1562 1563 [self determineCellParts:aRect imagePart:&imageFrame textPart:&textFrame]; 1564 [super selectWithFrame:textFrame inView:controlView editor:textObj delegate:anObject start:selStart length:selLength]; 1565} 1566 1567-(NSRect) titleRectForBounds:(NSRect)cellFrame 1568{ 1569 NSRect textFrame, imageFrame; 1570 1571 1572 [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame]; 1573 return textFrame; 1574} 1575 1576@end 1577 1578// ============================================================================ 1579// wxCocoaOutlineView 1580// ============================================================================ 1581@implementation wxCocoaOutlineView 1582 1583// 1584// initializers / destructor 1585// 1586-(id) init 1587{ 1588 self = [super init]; 1589 if (self != nil) 1590 { 1591 currentlyEditedColumn = 1592 currentlyEditedRow = -1; 1593 1594 [self registerForDraggedTypes:[NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]]; 1595 [self setDelegate:self]; 1596 [self setDoubleAction:@selector(actionDoubleClick:)]; 1597 [self setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO]; 1598 [self setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES]; 1599 [self setTarget:self]; 1600 } 1601 return self; 1602} 1603 1604// 1605// access to wxWidget's implementation 1606// 1607-(wxCocoaDataViewControl*) implementation 1608{ 1609 return implementation; 1610} 1611 1612-(void) setImplementation:(wxCocoaDataViewControl*) newImplementation 1613{ 1614 implementation = newImplementation; 1615} 1616 1617// 1618// actions 1619// 1620-(void) actionDoubleClick:(id)sender 1621{ 1622 wxUnusedVar(sender); 1623 1624 // actually the documentation (NSTableView 2007-10-31) for doubleAction: 1625 // and setDoubleAction: seems to be wrong as this action message is always 1626 // sent whether the cell is editable or not 1627 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 1628 1629 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_ACTIVATED,dvc->GetId()); 1630 1631 1632 event.SetEventObject(dvc); 1633 event.SetItem(wxDataViewItemFromItem([self itemAtRow:[self clickedRow]])); 1634 dvc->GetEventHandler()->ProcessEvent(event); 1635} 1636 1637 1638// 1639// contextual menus 1640// 1641-(NSMenu*) menuForEvent:(NSEvent*)theEvent 1642{ 1643 wxUnusedVar(theEvent); 1644 1645 // this method does not do any special menu event handling but only sends 1646 // an event message; therefore, the user has full control if a context 1647 // menu should be shown or not 1648 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 1649 1650 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU,dvc->GetId()); 1651 1652 wxDataViewItemArray selectedItems; 1653 1654 1655 event.SetEventObject(dvc); 1656 event.SetModel(dvc->GetModel()); 1657 // get the item information; 1658 // theoretically more than one ID can be returned but the event can only 1659 // handle one item, therefore only the first item of the array is 1660 // returned: 1661 if (dvc->GetSelections(selectedItems) > 0) 1662 event.SetItem(selectedItems[0]); 1663 dvc->GetEventHandler()->ProcessEvent(event); 1664 // nothing is done: 1665 return nil; 1666} 1667 1668// 1669// delegate methods 1670// 1671-(void) outlineView:(NSOutlineView*)outlineView didClickTableColumn:(NSTableColumn*)tableColumn 1672{ 1673 wxDataViewColumn* const 1674 col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]); 1675 1676 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 1677 1678 wxDataViewEvent 1679 event(wxEVT_DATAVIEW_COLUMN_HEADER_CLICK,dvc->GetId()); 1680 1681 1682 // first, send an event that the user clicked into a column's header: 1683 event.SetEventObject(dvc); 1684 event.SetColumn(dvc->GetColumnPosition(col)); 1685 event.SetDataViewColumn(col); 1686 dvc->HandleWindowEvent(event); 1687 1688 // now, check if the click may have had an influence on sorting, too; 1689 // the sorting setup has to be done only if the clicked table column is 1690 // sortable and has not been used for sorting before the click; if the 1691 // column is already responsible for sorting the native control changes 1692 // the sorting direction automatically and informs the data source via 1693 // outlineView:sortDescriptorsDidChange: 1694 if (col->IsSortable() && ([tableColumn sortDescriptorPrototype] == nil)) 1695 { 1696 // remove the sort order from the previously sorted column table (it 1697 // can also be that no sorted column table exists): 1698 UInt32 const noOfColumns = [outlineView numberOfColumns]; 1699 1700 for (UInt32 i=0; i<noOfColumns; ++i) 1701 [[[outlineView tableColumns] objectAtIndex:i] setSortDescriptorPrototype:nil]; 1702 // make column table sortable: 1703 NSArray* sortDescriptors; 1704 NSSortDescriptor* sortDescriptor; 1705 1706 sortDescriptor = [[NSSortDescriptor alloc] initWithKey:[NSString stringWithFormat:@"%ld",(long)[outlineView columnWithIdentifier:[tableColumn identifier]]] 1707 ascending:YES]; 1708 sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; 1709 [tableColumn setSortDescriptorPrototype:sortDescriptor]; 1710 [outlineView setSortDescriptors:sortDescriptors]; 1711 [sortDescriptor release]; 1712 } 1713} 1714 1715-(BOOL) outlineView:(NSOutlineView*)outlineView shouldCollapseItem:(id)item 1716{ 1717 wxUnusedVar(outlineView); 1718 1719 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 1720 1721 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_COLLAPSING,dvc->GetId()); 1722 1723 1724 event.SetEventObject(dvc); 1725 event.SetItem (wxDataViewItemFromItem(item)); 1726 event.SetModel (dvc->GetModel()); 1727 // finally send the equivalent wxWidget event: 1728 dvc->GetEventHandler()->ProcessEvent(event); 1729 // opening the container is allowed if not vetoed: 1730 return event.IsAllowed(); 1731} 1732 1733-(BOOL) outlineView:(NSOutlineView*)outlineView shouldExpandItem:(id)item 1734{ 1735 wxUnusedVar(outlineView); 1736 1737 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 1738 1739 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_EXPANDING,dvc->GetId()); 1740 1741 1742 event.SetEventObject(dvc); 1743 event.SetItem (wxDataViewItemFromItem(item)); 1744 event.SetModel (dvc->GetModel()); 1745 // finally send the equivalent wxWidget event: 1746 dvc->GetEventHandler()->ProcessEvent(event); 1747 // opening the container is allowed if not vetoed: 1748 return event.IsAllowed(); 1749} 1750 1751-(BOOL) outlineView:(NSOutlineView*)outlineView shouldSelectTableColumn:(NSTableColumn*)tableColumn 1752{ 1753 wxUnusedVar(tableColumn); 1754 wxUnusedVar(outlineView); 1755 1756 return NO; 1757} 1758 1759-(void) outlineView:(wxCocoaOutlineView*)outlineView 1760 willDisplayCell:(id)cell 1761 forTableColumn:(NSTableColumn*)tableColumn 1762 item:(id)item 1763{ 1764 wxUnusedVar(outlineView); 1765 1766 wxDataViewCtrl * const dvc = implementation->GetDataViewCtrl(); 1767 wxDataViewModel * const model = dvc->GetModel(); 1768 1769 wxDataViewColumn* const 1770 dvCol([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]); 1771 const unsigned colIdx = dvCol->GetModelColumn(); 1772 1773 wxDataViewItem dvItem(wxDataViewItemFromItem(item)); 1774 1775 if ( !model->HasValue(dvItem, colIdx) ) 1776 return; 1777 1778 wxDataViewRenderer * const renderer = dvCol->GetRenderer(); 1779 wxDataViewRendererNativeData * const data = renderer->GetNativeData(); 1780 1781 // let the renderer know about what it's going to render next 1782 data->SetColumnPtr(tableColumn); 1783 data->SetItem(item); 1784 data->SetItemCell(cell); 1785 1786 // use the attributes: notice that we need to do this whether we have them 1787 // or not as even if this cell doesn't have any attributes, the previous 1788 // one might have had some and then we need to reset them back to default 1789 wxDataViewItemAttr attr; 1790 model->GetAttr(dvItem, colIdx, attr); 1791 renderer->OSXApplyAttr(attr); 1792 1793 // set the state (enabled/disabled) of the item 1794 renderer->OSXApplyEnabled(model->IsEnabled(dvItem, colIdx)); 1795 1796 // and finally do draw it 1797 renderer->MacRender(); 1798} 1799 1800// 1801// notifications 1802// 1803-(void) outlineViewColumnDidMove:(NSNotification*)notification 1804{ 1805 int const newColumnPosition = [[[notification userInfo] objectForKey:@"NSNewColumn"] intValue]; 1806 1807 NSTableColumn* 1808 tableColumn = [[self tableColumns] objectAtIndex:newColumnPosition]; 1809 wxDataViewColumn* const 1810 col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]); 1811 1812 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 1813 1814 wxDataViewEvent event(wxEVT_DATAVIEW_COLUMN_REORDERED,dvc->GetId()); 1815 1816 1817 event.SetEventObject(dvc); 1818 event.SetColumn(dvc->GetColumnPosition(col)); 1819 event.SetDataViewColumn(col); 1820 dvc->GetEventHandler()->ProcessEvent(event); 1821} 1822 1823-(void) outlineViewItemDidCollapse:(NSNotification*)notification 1824{ 1825 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 1826 1827 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_COLLAPSED,dvc->GetId()); 1828 1829 1830 event.SetEventObject(dvc); 1831 event.SetItem(wxDataViewItemFromItem( 1832 [[notification userInfo] objectForKey:@"NSObject"])); 1833 dvc->GetEventHandler()->ProcessEvent(event); 1834} 1835 1836-(void) outlineViewItemDidExpand:(NSNotification*)notification 1837{ 1838 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 1839 1840 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_EXPANDED,dvc->GetId()); 1841 1842 1843 event.SetEventObject(dvc); 1844 event.SetItem(wxDataViewItemFromItem( 1845 [[notification userInfo] objectForKey:@"NSObject"])); 1846 dvc->GetEventHandler()->ProcessEvent(event); 1847} 1848 1849-(void) outlineViewSelectionDidChange:(NSNotification*)notification 1850{ 1851 wxUnusedVar(notification); 1852 1853 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 1854 1855 wxDataViewEvent event(wxEVT_DATAVIEW_SELECTION_CHANGED,dvc->GetId()); 1856 1857 event.SetEventObject(dvc); 1858 event.SetModel(dvc->GetModel()); 1859 event.SetItem(dvc->GetSelection()); 1860 dvc->GetEventHandler()->ProcessEvent(event); 1861} 1862 1863-(void) textDidBeginEditing:(NSNotification*)notification 1864{ 1865 // this notification is only sent if the user started modifying the cell 1866 // (not when the user clicked into the cell and the cell's editor is 1867 // called!) 1868 1869 // call method of superclass (otherwise editing does not work correctly - 1870 // the outline data source class is not informed about a change of data): 1871 [super textDidBeginEditing:notification]; 1872 1873 // remember the column being edited, it will be used in textDidEndEditing: 1874 currentlyEditedColumn = [self editedColumn]; 1875 currentlyEditedRow = [self editedRow]; 1876 1877 NSTableColumn* 1878 tableColumn = [[self tableColumns] objectAtIndex:currentlyEditedColumn]; 1879 wxDataViewColumn* const 1880 col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]); 1881 1882 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 1883 1884 1885 // stop editing of a custom item first (if necessary) 1886 dvc->FinishCustomItemEditing(); 1887 1888 // now, send the event: 1889 wxDataViewEvent 1890 event(wxEVT_DATAVIEW_ITEM_EDITING_STARTED,dvc->GetId()); 1891 1892 event.SetEventObject(dvc); 1893 event.SetItem( 1894 wxDataViewItemFromItem([self itemAtRow:currentlyEditedRow])); 1895 event.SetColumn(dvc->GetColumnPosition(col)); 1896 event.SetDataViewColumn(col); 1897 dvc->GetEventHandler()->ProcessEvent(event); 1898} 1899 1900-(void) textDidEndEditing:(NSNotification*)notification 1901{ 1902 // call method of superclass (otherwise editing does not work correctly - 1903 // the outline data source class is not informed about a change of data): 1904 [super textDidEndEditing:notification]; 1905 1906 // under OSX an event indicating the end of an editing session can be sent 1907 // even if no event indicating a start of an editing session has been sent 1908 // (see Documentation for NSControl controlTextDidEndEditing:); this is 1909 // not expected by a user of the wxWidgets library and therefore an 1910 // wxEVT_DATAVIEW_ITEM_EDITING_DONE event is only sent if a 1911 // corresponding wxEVT_DATAVIEW_ITEM_EDITING_STARTED has been sent 1912 // before; to check if a wxEVT_DATAVIEW_ITEM_EDITING_STARTED has 1913 // been sent the last edited column/row are valid: 1914 if ( currentlyEditedColumn != -1 && currentlyEditedRow != -1 ) 1915 { 1916 NSTableColumn* 1917 tableColumn = [[self tableColumns] objectAtIndex:currentlyEditedColumn]; 1918 wxDataViewColumn* const 1919 col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]); 1920 1921 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); 1922 1923 // send event to wxWidgets: 1924 wxDataViewEvent 1925 event(wxEVT_DATAVIEW_ITEM_EDITING_DONE,dvc->GetId()); 1926 1927 event.SetEventObject(dvc); 1928 event.SetItem( 1929 wxDataViewItemFromItem([self itemAtRow:currentlyEditedRow])); 1930 event.SetColumn(dvc->GetColumnPosition(col)); 1931 event.SetDataViewColumn(col); 1932 dvc->GetEventHandler()->ProcessEvent(event); 1933 1934 1935 // we're not editing any more 1936 currentlyEditedColumn = 1937 currentlyEditedRow = -1; 1938 } 1939} 1940 1941@end 1942 1943// ============================================================================ 1944// wxCocoaDataViewControl 1945// ============================================================================ 1946 1947wxCocoaDataViewControl::wxCocoaDataViewControl(wxWindow* peer, 1948 const wxPoint& pos, 1949 const wxSize& size, 1950 long style) 1951 : wxWidgetCocoaImpl 1952 ( 1953 peer, 1954 [[NSScrollView alloc] initWithFrame:wxOSXGetFrameForControl(peer,pos,size)] 1955 ), 1956 m_DataSource(NULL), 1957 m_OutlineView([[wxCocoaOutlineView alloc] init]) 1958{ 1959 // initialize scrollview (the outline view is part of a scrollview): 1960 NSScrollView* scrollview = (NSScrollView*) GetWXWidget(); 1961 1962 [scrollview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 1963 [scrollview setBorderType:NSNoBorder]; 1964 [scrollview setHasVerticalScroller:YES]; 1965 [scrollview setHasHorizontalScroller:YES]; 1966 [scrollview setAutohidesScrollers:YES]; 1967 [scrollview setDocumentView:m_OutlineView]; 1968 1969 // we cannot call InstallHandler(m_OutlineView) here, because we are handling 1970 // our action:s ourselves, only associate the view with this impl 1971 Associate(m_OutlineView,this); 1972 // initialize the native control itself too 1973 InitOutlineView(style); 1974} 1975 1976void wxCocoaDataViewControl::InitOutlineView(long style) 1977{ 1978 [m_OutlineView setImplementation:this]; 1979 [m_OutlineView setColumnAutoresizingStyle:NSTableViewSequentialColumnAutoresizingStyle]; 1980 [m_OutlineView setIndentationPerLevel:GetDataViewCtrl()->GetIndent()]; 1981 NSUInteger maskGridStyle(NSTableViewGridNone); 1982 if (style & wxDV_HORIZ_RULES) 1983 maskGridStyle |= NSTableViewSolidHorizontalGridLineMask; 1984 if (style & wxDV_VERT_RULES) 1985 maskGridStyle |= NSTableViewSolidVerticalGridLineMask; 1986 [m_OutlineView setGridStyleMask:maskGridStyle]; 1987 [m_OutlineView setAllowsMultipleSelection: (style & wxDV_MULTIPLE) != 0]; 1988 [m_OutlineView setUsesAlternatingRowBackgroundColors:(style & wxDV_ROW_LINES) != 0]; 1989 1990 if ( style & wxDV_NO_HEADER ) 1991 [m_OutlineView setHeaderView:nil]; 1992} 1993 1994wxCocoaDataViewControl::~wxCocoaDataViewControl() 1995{ 1996 [m_DataSource release]; 1997 [m_OutlineView release]; 1998} 1999 2000// 2001// column related methods (inherited from wxDataViewWidgetImpl) 2002// 2003bool wxCocoaDataViewControl::ClearColumns() 2004{ 2005 // as there is a bug in NSOutlineView version (OSX 10.5.6 #6555162) the 2006 // columns cannot be deleted if there is an outline column in the view; 2007 // therefore, the whole view is deleted and newly constructed: 2008 [m_OutlineView release]; 2009 m_OutlineView = [[wxCocoaOutlineView alloc] init]; 2010 [((NSScrollView*) GetWXWidget()) setDocumentView:m_OutlineView]; 2011 [m_OutlineView setDataSource:m_DataSource]; 2012 2013 InitOutlineView(GetDataViewCtrl()->GetWindowStyle()); 2014 2015 return true; 2016} 2017 2018bool wxCocoaDataViewControl::DeleteColumn(wxDataViewColumn* columnPtr) 2019{ 2020 if ([m_OutlineView outlineTableColumn] == columnPtr->GetNativeData()->GetNativeColumnPtr()) 2021 [m_OutlineView setOutlineTableColumn:nil]; // due to a bug this does not work 2022 [m_OutlineView removeTableColumn:columnPtr->GetNativeData()->GetNativeColumnPtr()]; // due to a confirmed bug #6555162 the deletion does not work for 2023 // outline table columns (... and there is no workaround) 2024 return (([m_OutlineView columnWithIdentifier:[wxDVCNSTableColumn identifierForColumnPointer:columnPtr]]) == -1); 2025} 2026 2027void wxCocoaDataViewControl::DoSetExpanderColumn(const wxDataViewColumn *columnPtr) 2028{ 2029 [m_OutlineView setOutlineTableColumn:columnPtr->GetNativeData()->GetNativeColumnPtr()]; 2030} 2031 2032wxDataViewColumn* wxCocoaDataViewControl::GetColumn(unsigned int pos) const 2033{ 2034 NSTableColumn* tableColumn = [[m_OutlineView tableColumns] objectAtIndex:pos]; 2035 return [static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]; 2036} 2037 2038int wxCocoaDataViewControl::GetColumnPosition(const wxDataViewColumn *columnPtr) const 2039{ 2040 return [m_OutlineView columnWithIdentifier:[wxDVCNSTableColumn identifierForColumnPointer:columnPtr]]; 2041} 2042 2043bool wxCocoaDataViewControl::InsertColumn(unsigned int pos, wxDataViewColumn* columnPtr) 2044{ 2045 // create column and set the native data of the dataview column: 2046 NSTableColumn *nativeColumn = ::CreateNativeColumn(columnPtr); 2047 columnPtr->GetNativeData()->SetNativeColumnPtr(nativeColumn); 2048 // as the native control does not allow the insertion of a column at a 2049 // specified position the column is first appended and - if necessary - 2050 // moved to its final position: 2051 [m_OutlineView addTableColumn:nativeColumn]; 2052 2053 // it is owned, and kepy alive, by m_OutlineView now 2054 [nativeColumn release]; 2055 2056 if (pos != static_cast<unsigned int>([m_OutlineView numberOfColumns]-1)) 2057 [m_OutlineView moveColumn:[m_OutlineView numberOfColumns]-1 toColumn:pos]; 2058 2059 // set columns width now that it can be computed even for autosized columns: 2060 columnPtr->SetWidth(columnPtr->GetWidthVariable()); 2061 2062 // done: 2063 return true; 2064} 2065 2066void wxCocoaDataViewControl::FitColumnWidthToContent(unsigned int pos) 2067{ 2068 const int count = GetCount(); 2069 NSTableColumn *column = GetColumn(pos)->GetNativeData()->GetNativeColumnPtr(); 2070 2071 class MaxWidthCalculator 2072 { 2073 public: 2074 MaxWidthCalculator(wxCocoaOutlineView *view, 2075 NSTableColumn *column, unsigned columnIndex) 2076 : m_width(0), 2077 m_view(view), 2078 m_column(columnIndex), 2079 m_indent(0) 2080 { 2081 // account for indentation in the column with expander 2082 if ( column == [m_view outlineTableColumn] ) 2083 m_indent = [m_view indentationPerLevel]; 2084 } 2085 2086 void UpdateWithWidth(int width) 2087 { 2088 m_width = wxMax(m_width, width); 2089 } 2090 2091 void UpdateWithRow(int row) 2092 { 2093 NSCell *cell = [m_view preparedCellAtColumn:m_column row:row]; 2094 unsigned cellWidth = [cell cellSize].width + 1/*round the float up*/; 2095 2096 if ( m_indent ) 2097 cellWidth += m_indent * ([m_view levelForRow:row] + 1); 2098 2099 m_width = wxMax(m_width, cellWidth); 2100 } 2101 2102 int GetMaxWidth() const { return m_width; } 2103 2104 private: 2105 int m_width; 2106 wxCocoaOutlineView *m_view; 2107 unsigned m_column; 2108 int m_indent; 2109 }; 2110 2111 MaxWidthCalculator calculator(m_OutlineView, column, pos); 2112 2113 if ( [column headerCell] ) 2114 { 2115 calculator.UpdateWithWidth([[column headerCell] cellSize].width + 1/*round the float up*/); 2116 } 2117 2118 // The code below deserves some explanation. For very large controls, we 2119 // simply can't afford to calculate sizes for all items, it takes too 2120 // long. So the best we can do is to check the first and the last N/2 2121 // items in the control for some sufficiently large N and calculate best 2122 // sizes from that. That can result in the calculated best width being too 2123 // small for some outliers, but it's better to get slightly imperfect 2124 // result than to wait several seconds after every update. To avoid highly 2125 // visible miscalculations, we also include all currently visible items 2126 // no matter what. Finally, the value of N is determined dynamically by 2127 // measuring how much time we spent on the determining item widths so far. 2128 2129#if wxUSE_STOPWATCH 2130 int top_part_end = count; 2131 static const long CALC_TIMEOUT = 20/*ms*/; 2132 // don't call wxStopWatch::Time() too often 2133 static const unsigned CALC_CHECK_FREQ = 100; 2134 wxStopWatch timer; 2135#else 2136 // use some hard-coded limit, that's the best we can do without timer 2137 int top_part_end = wxMin(500, count); 2138#endif // wxUSE_STOPWATCH/!wxUSE_STOPWATCH 2139 2140 int row = 0; 2141 2142 for ( row = 0; row < top_part_end; row++ ) 2143 { 2144#if wxUSE_STOPWATCH 2145 if ( row % CALC_CHECK_FREQ == CALC_CHECK_FREQ-1 && 2146 timer.Time() > CALC_TIMEOUT ) 2147 break; 2148#endif // wxUSE_STOPWATCH 2149 calculator.UpdateWithRow(row); 2150 } 2151 2152 // row is the first unmeasured item now; that's our value of N/2 2153 2154 if ( row < count ) 2155 { 2156 top_part_end = row; 2157 2158 // add bottom N/2 items now: 2159 const int bottom_part_start = wxMax(row, count - row); 2160 for ( row = bottom_part_start; row < count; row++ ) 2161 calculator.UpdateWithRow(row); 2162 2163 // finally, include currently visible items in the calculation: 2164 const NSRange visible = [m_OutlineView rowsInRect:[m_OutlineView visibleRect]]; 2165 const int first_visible = wxMax(visible.location, top_part_end); 2166 const int last_visible = wxMin(first_visible + visible.length, bottom_part_start); 2167 2168 for ( row = first_visible; row < last_visible; row++ ) 2169 calculator.UpdateWithRow(row); 2170 2171 wxLogTrace("dataview", 2172 "determined best size from %d top, %d bottom plus %d more visible items out of %d total", 2173 top_part_end, 2174 count - bottom_part_start, 2175 wxMax(0, last_visible - first_visible), 2176 count); 2177 } 2178 2179 [column setWidth:calculator.GetMaxWidth()]; 2180} 2181 2182// 2183// item related methods (inherited from wxDataViewWidgetImpl) 2184// 2185bool wxCocoaDataViewControl::Add(const wxDataViewItem& parent, const wxDataViewItem& WXUNUSED(item)) 2186{ 2187 if (parent.IsOk()) 2188 [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:parent] reloadChildren:YES]; 2189 else 2190 [m_OutlineView reloadData]; 2191 return true; 2192} 2193 2194bool wxCocoaDataViewControl::Add(const wxDataViewItem& parent, const wxDataViewItemArray& WXUNUSED(items)) 2195{ 2196 if (parent.IsOk()) 2197 [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:parent] reloadChildren:YES]; 2198 else 2199 [m_OutlineView reloadData]; 2200 return true; 2201} 2202 2203void wxCocoaDataViewControl::Collapse(const wxDataViewItem& item) 2204{ 2205 [m_OutlineView collapseItem:[m_DataSource getDataViewItemFromBuffer:item]]; 2206} 2207 2208void wxCocoaDataViewControl::EnsureVisible(const wxDataViewItem& item, const wxDataViewColumn *columnPtr) 2209{ 2210 if (item.IsOk()) 2211 { 2212 [m_OutlineView scrollRowToVisible:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]]; 2213 if (columnPtr) 2214 [m_OutlineView scrollColumnToVisible:GetColumnPosition(columnPtr)]; 2215 } 2216} 2217 2218void wxCocoaDataViewControl::Expand(const wxDataViewItem& item) 2219{ 2220 [m_OutlineView expandItem:[m_DataSource getDataViewItemFromBuffer:item]]; 2221} 2222 2223unsigned int wxCocoaDataViewControl::GetCount() const 2224{ 2225 return [m_OutlineView numberOfRows]; 2226} 2227 2228wxRect wxCocoaDataViewControl::GetRectangle(const wxDataViewItem& item, const wxDataViewColumn *columnPtr) 2229{ 2230 return wxFromNSRect([m_osxView superview],[m_OutlineView frameOfCellAtColumn:GetColumnPosition(columnPtr) 2231 row:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]]); 2232} 2233 2234bool wxCocoaDataViewControl::IsExpanded(const wxDataViewItem& item) const 2235{ 2236 return [m_OutlineView isItemExpanded:[m_DataSource getDataViewItemFromBuffer:item]]; 2237} 2238 2239bool wxCocoaDataViewControl::Reload() 2240{ 2241 [m_DataSource clearBuffers]; 2242 [m_OutlineView reloadData]; 2243 [m_OutlineView scrollColumnToVisible:0]; 2244 [m_OutlineView scrollRowToVisible:0]; 2245 return true; 2246} 2247 2248bool wxCocoaDataViewControl::Remove(const wxDataViewItem& parent, const wxDataViewItem& WXUNUSED(item)) 2249{ 2250 if (parent.IsOk()) 2251 [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:parent] reloadChildren:YES]; 2252 else 2253 [m_OutlineView reloadData]; 2254 return true; 2255} 2256 2257bool wxCocoaDataViewControl::Remove(const wxDataViewItem& parent, const wxDataViewItemArray& WXUNUSED(item)) 2258{ 2259 if (parent.IsOk()) 2260 [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:parent] reloadChildren:YES]; 2261 else 2262 [m_OutlineView reloadData]; 2263 return true; 2264} 2265 2266bool wxCocoaDataViewControl::Update(const wxDataViewColumn *columnPtr) 2267{ 2268 wxUnusedVar(columnPtr); 2269 2270 return false; 2271} 2272 2273bool wxCocoaDataViewControl::Update(const wxDataViewItem& WXUNUSED(parent), const wxDataViewItem& item) 2274{ 2275 [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:item]]; 2276 return true; 2277} 2278 2279bool wxCocoaDataViewControl::Update(const wxDataViewItem& WXUNUSED(parent), const wxDataViewItemArray& items) 2280{ 2281 for (size_t i=0; i<items.GetCount(); ++i) 2282 [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:items[i]]]; 2283 return true; 2284} 2285 2286// 2287// model related methods 2288// 2289bool wxCocoaDataViewControl::AssociateModel(wxDataViewModel* model) 2290{ 2291 [m_DataSource release]; 2292 if (model) 2293 { 2294 m_DataSource = [[wxCocoaOutlineDataSource alloc] init]; 2295 [m_DataSource setImplementation:this]; 2296 [m_DataSource setModel:model]; 2297 } 2298 else 2299 m_DataSource = NULL; 2300 [m_OutlineView setDataSource:m_DataSource]; // if there is a data source the data is immediately going to be requested 2301 return true; 2302} 2303 2304// 2305// selection related methods (inherited from wxDataViewWidgetImpl) 2306// 2307 2308wxDataViewItem wxCocoaDataViewControl::GetCurrentItem() const 2309{ 2310 return wxDataViewItem([[m_OutlineView itemAtRow:[m_OutlineView selectedRow]] pointer]); 2311} 2312 2313wxDataViewColumn *wxCocoaDataViewControl::GetCurrentColumn() const 2314{ 2315 int col = [m_OutlineView selectedColumn]; 2316 if ( col == -1 ) 2317 return NULL; 2318 return GetColumn(col); 2319} 2320 2321void wxCocoaDataViewControl::SetCurrentItem(const wxDataViewItem& item) 2322{ 2323 // We can't have unselected current item in a NSTableView, as the 2324 // documentation of its deselectRow method explains, the control will 2325 // automatically change the current item to the closest still selected item 2326 // if the current item is deselected. So we have no choice but to select 2327 // the item in the same time as making it current. 2328 Select(item); 2329} 2330 2331int wxCocoaDataViewControl::GetSelectedItemsCount() const 2332{ 2333 return [m_OutlineView numberOfSelectedRows]; 2334} 2335 2336int wxCocoaDataViewControl::GetSelections(wxDataViewItemArray& sel) const 2337{ 2338 NSIndexSet* selectedRowIndexes([m_OutlineView selectedRowIndexes]); 2339 2340 NSUInteger indexRow; 2341 2342 2343 sel.Empty(); 2344 sel.Alloc([selectedRowIndexes count]); 2345 indexRow = [selectedRowIndexes firstIndex]; 2346 while (indexRow != NSNotFound) 2347 { 2348 sel.Add(wxDataViewItem([[m_OutlineView itemAtRow:indexRow] pointer])); 2349 indexRow = [selectedRowIndexes indexGreaterThanIndex:indexRow]; 2350 } 2351 return sel.GetCount(); 2352} 2353 2354bool wxCocoaDataViewControl::IsSelected(const wxDataViewItem& item) const 2355{ 2356 return [m_OutlineView isRowSelected:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]]; 2357} 2358 2359void wxCocoaDataViewControl::Select(const wxDataViewItem& item) 2360{ 2361 if (item.IsOk()) 2362 [m_OutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]] 2363 byExtendingSelection:GetDataViewCtrl()->HasFlag(wxDV_MULTIPLE) ? YES : NO]; 2364} 2365 2366void wxCocoaDataViewControl::SelectAll() 2367{ 2368 [m_OutlineView selectAll:m_OutlineView]; 2369} 2370 2371void wxCocoaDataViewControl::Unselect(const wxDataViewItem& item) 2372{ 2373 if (item.IsOk()) 2374 [m_OutlineView deselectRow:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]]; 2375} 2376 2377void wxCocoaDataViewControl::UnselectAll() 2378{ 2379 [m_OutlineView deselectAll:m_OutlineView]; 2380} 2381 2382// 2383// sorting related methods 2384// 2385wxDataViewColumn* wxCocoaDataViewControl::GetSortingColumn() const 2386{ 2387 NSArray* const columns = [m_OutlineView tableColumns]; 2388 2389 UInt32 const noOfColumns = [columns count]; 2390 2391 2392 for (UInt32 i=0; i<noOfColumns; ++i) 2393 if ([[columns objectAtIndex:i] sortDescriptorPrototype] != nil) 2394 return GetColumn(i); 2395 return NULL; 2396} 2397 2398void wxCocoaDataViewControl::Resort() 2399{ 2400 [m_DataSource clearChildren]; 2401 [m_OutlineView reloadData]; 2402} 2403 2404void wxCocoaDataViewControl::StartEditor( const wxDataViewItem & item, unsigned int column ) 2405{ 2406 [m_OutlineView editColumn:column row:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]] withEvent:nil select:YES]; 2407} 2408 2409// 2410// other methods (inherited from wxDataViewWidgetImpl) 2411// 2412void wxCocoaDataViewControl::DoSetIndent(int indent) 2413{ 2414 [m_OutlineView setIndentationPerLevel:static_cast<CGFloat>(indent)]; 2415} 2416 2417void wxCocoaDataViewControl::HitTest(const wxPoint& point, wxDataViewItem& item, wxDataViewColumn*& columnPtr) const 2418{ 2419 NSPoint const nativePoint = wxToNSPoint((NSScrollView*) GetWXWidget(),point); 2420 2421 int indexColumn; 2422 int indexRow; 2423 2424 2425 indexColumn = [m_OutlineView columnAtPoint:nativePoint]; 2426 indexRow = [m_OutlineView rowAtPoint: nativePoint]; 2427 if ((indexColumn >= 0) && (indexRow >= 0)) 2428 { 2429 columnPtr = GetColumn(indexColumn); 2430 item = wxDataViewItem([[m_OutlineView itemAtRow:indexRow] pointer]); 2431 } 2432 else 2433 { 2434 columnPtr = NULL; 2435 item = wxDataViewItem(); 2436 } 2437} 2438 2439void wxCocoaDataViewControl::SetRowHeight(const wxDataViewItem& WXUNUSED(item), unsigned int WXUNUSED(height)) 2440 // Not supported by the native control 2441{ 2442} 2443 2444void wxCocoaDataViewControl::OnSize() 2445{ 2446 if ([m_OutlineView numberOfColumns] == 1) 2447 [m_OutlineView sizeLastColumnToFit]; 2448} 2449 2450// 2451// drag & drop helper methods 2452// 2453wxDataFormat wxCocoaDataViewControl::GetDnDDataFormat(wxDataObjectComposite* dataObjects) 2454{ 2455 wxDataFormat resultFormat; 2456 if ( !dataObjects ) 2457 return resultFormat; 2458 2459 bool compatible(true); 2460 2461 size_t const noOfFormats = dataObjects->GetFormatCount(); 2462 size_t indexFormat; 2463 2464 wxDataFormat* formats; 2465 2466 // get all formats and check afterwards if the formats are compatible; if 2467 // they are compatible the preferred format is returned otherwise 2468 // wxDF_INVALID is returned; 2469 // currently compatible types (ordered by priority are): 2470 // - wxDF_UNICODETEXT - wxDF_TEXT 2471 formats = new wxDataFormat[noOfFormats]; 2472 dataObjects->GetAllFormats(formats); 2473 indexFormat = 0; 2474 while ((indexFormat < noOfFormats) && compatible) 2475 { 2476 switch (resultFormat.GetType()) 2477 { 2478 case wxDF_INVALID: 2479 resultFormat.SetType(formats[indexFormat].GetType()); // first format (should only be reached if indexFormat == 0) 2480 break; 2481 case wxDF_TEXT: 2482 if (formats[indexFormat].GetType() == wxDF_UNICODETEXT) 2483 resultFormat.SetType(wxDF_UNICODETEXT); 2484 else // incompatible 2485 { 2486 resultFormat.SetType(wxDF_INVALID); 2487 compatible = false; 2488 } 2489 break; 2490 case wxDF_UNICODETEXT: 2491 if (formats[indexFormat].GetType() != wxDF_TEXT) 2492 { 2493 resultFormat.SetType(wxDF_INVALID); 2494 compatible = false; 2495 } 2496 break; 2497 default: 2498 resultFormat.SetType(wxDF_INVALID); // not (yet) supported format 2499 compatible = false; 2500 } 2501 ++indexFormat; 2502 } 2503 2504 delete[] formats; 2505 2506 return resultFormat; 2507} 2508 2509wxDataObjectComposite* wxCocoaDataViewControl::GetDnDDataObjects(NSData* dataObject) const 2510{ 2511 wxDataFormatId dataFormatID; 2512 2513 2514 [dataObject getBytes:&dataFormatID length:sizeof(wxDataFormatId)]; 2515 switch (dataFormatID) 2516 { 2517 case wxDF_TEXT: 2518 case wxDF_UNICODETEXT: 2519 { 2520 wxTextDataObject* textDataObject(new wxTextDataObject()); 2521 2522 if (textDataObject->SetData(wxDataFormat(dataFormatID),[dataObject length]-sizeof(wxDataFormatId),static_cast<char const*>([dataObject bytes])+sizeof(wxDataFormatId))) 2523 { 2524 wxDataObjectComposite* dataObjectComposite(new wxDataObjectComposite()); 2525 2526 dataObjectComposite->Add(textDataObject); 2527 return dataObjectComposite; 2528 } 2529 else 2530 { 2531 delete textDataObject; 2532 return NULL; 2533 } 2534 } 2535 break; 2536 default: 2537 return NULL; 2538 } 2539} 2540 2541id wxCocoaDataViewControl::GetItemAtRow(int row) const 2542{ 2543 return [m_OutlineView itemAtRow:row]; 2544} 2545 2546// ---------------------------------------------------------------------------- 2547// wxDataViewRendererNativeData 2548// ---------------------------------------------------------------------------- 2549 2550void wxDataViewRendererNativeData::Init() 2551{ 2552 m_origFont = NULL; 2553 m_origTextColour = NULL; 2554 m_ellipsizeMode = wxELLIPSIZE_MIDDLE; 2555 2556 if ( m_ColumnCell ) 2557 ApplyLineBreakMode(m_ColumnCell); 2558} 2559 2560void wxDataViewRendererNativeData::ApplyLineBreakMode(NSCell *cell) 2561{ 2562 NSLineBreakMode nsMode = NSLineBreakByWordWrapping; 2563 switch ( m_ellipsizeMode ) 2564 { 2565 case wxELLIPSIZE_NONE: 2566 nsMode = NSLineBreakByClipping; 2567 break; 2568 2569 case wxELLIPSIZE_START: 2570 nsMode = NSLineBreakByTruncatingHead; 2571 break; 2572 2573 case wxELLIPSIZE_MIDDLE: 2574 nsMode = NSLineBreakByTruncatingMiddle; 2575 break; 2576 2577 case wxELLIPSIZE_END: 2578 nsMode = NSLineBreakByTruncatingTail; 2579 break; 2580 } 2581 2582 wxASSERT_MSG( nsMode != NSLineBreakByWordWrapping, "unknown wxEllipsizeMode" ); 2583 2584 [cell setLineBreakMode: nsMode]; 2585} 2586 2587// --------------------------------------------------------- 2588// wxDataViewRenderer 2589// --------------------------------------------------------- 2590 2591wxDataViewRenderer::wxDataViewRenderer(const wxString& varianttype, 2592 wxDataViewCellMode mode, 2593 int align) 2594 : wxDataViewRendererBase(varianttype, mode, align), 2595 m_alignment(align), 2596 m_mode(mode), 2597 m_NativeDataPtr(NULL) 2598{ 2599} 2600 2601wxDataViewRenderer::~wxDataViewRenderer() 2602{ 2603 delete m_NativeDataPtr; 2604} 2605 2606void wxDataViewRenderer::SetAlignment(int align) 2607{ 2608 m_alignment = align; 2609 [GetNativeData()->GetColumnCell() setAlignment:ConvertToNativeHorizontalTextAlignment(align)]; 2610} 2611 2612void wxDataViewRenderer::SetMode(wxDataViewCellMode mode) 2613{ 2614 m_mode = mode; 2615 if ( GetOwner() ) 2616 [GetOwner()->GetNativeData()->GetNativeColumnPtr() setEditable:(mode == wxDATAVIEW_CELL_EDITABLE)]; 2617} 2618 2619void wxDataViewRenderer::SetNativeData(wxDataViewRendererNativeData* newNativeDataPtr) 2620{ 2621 delete m_NativeDataPtr; 2622 m_NativeDataPtr = newNativeDataPtr; 2623} 2624 2625void wxDataViewRenderer::EnableEllipsize(wxEllipsizeMode mode) 2626{ 2627 // we need to store this value to apply it to the columns headerCell in 2628 // CreateNativeColumn() 2629 GetNativeData()->SetEllipsizeMode(mode); 2630 2631 // but we may already apply it to the column cell which will be used for 2632 // this column 2633 GetNativeData()->ApplyLineBreakMode(GetNativeData()->GetColumnCell()); 2634} 2635 2636wxEllipsizeMode wxDataViewRenderer::GetEllipsizeMode() const 2637{ 2638 return GetNativeData()->GetEllipsizeMode(); 2639} 2640 2641void 2642wxDataViewRenderer::OSXOnCellChanged(NSObject *object, 2643 const wxDataViewItem& item, 2644 unsigned col) 2645{ 2646 // TODO: This code should really be removed and this function be made pure 2647 // virtual. We just need to decide what to do with custom renderers 2648 // (i.e. wxDataViewCustomRenderer), currently OS X "in place" editing 2649 // which doesn't really create an editor control is not compatible 2650 // with the in place editing under other platforms. 2651 2652 wxVariant value; 2653 if ( [object isKindOfClass:[NSString class]] ) 2654 value = ObjectToString(object); 2655 else if ( [object isKindOfClass:[NSNumber class]] ) 2656 value = ObjectToLong(object); 2657 else if ( [object isKindOfClass:[NSDate class]] ) 2658 value = ObjectToDate(object); 2659 else 2660 { 2661 wxFAIL_MSG( wxString::Format 2662 ( 2663 "unknown value type %s", 2664 wxCFStringRef::AsString([object className]) 2665 )); 2666 return; 2667 } 2668 2669 if ( !Validate(value) ) 2670 return; 2671 2672 wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); 2673 model->ChangeValue(value, item, col); 2674} 2675 2676void wxDataViewRenderer::OSXApplyAttr(const wxDataViewItemAttr& attr) 2677{ 2678 wxDataViewRendererNativeData * const data = GetNativeData(); 2679 NSCell * const cell = data->GetItemCell(); 2680 2681 // set the font and text colour to use: we need to do it if we had ever 2682 // changed them before, even if this item itself doesn't have any special 2683 // attributes as otherwise it would reuse the attributes from the previous 2684 // cell rendered using the same renderer 2685 NSFont *font = NULL; 2686 NSColor *colText = NULL; 2687 2688 if ( attr.HasFont() ) 2689 { 2690 font = data->GetOriginalFont(); 2691 if ( !font ) 2692 { 2693 // this is the first time we're setting the font, remember the 2694 // original one before changing it 2695 font = [cell font]; 2696 data->SaveOriginalFont(font); 2697 } 2698 2699 if ( font ) 2700 { 2701 // FIXME: using wxFont methods here doesn't work for some reason 2702 NSFontManager * const fm = [NSFontManager sharedFontManager]; 2703 if ( attr.GetBold() ) 2704 font = [fm convertFont:font toHaveTrait:NSBoldFontMask]; 2705 if ( attr.GetItalic() ) 2706 font = [fm convertFont:font toHaveTrait:NSItalicFontMask]; 2707 } 2708 //else: can't change font if the cell doesn't have any 2709 } 2710 2711 if ( attr.HasColour() ) 2712 { 2713 // we can set font for any cell but only NSTextFieldCell provides 2714 // a method for setting text colour so check that this method is 2715 // available before using it 2716 if ( [cell respondsToSelector:@selector(setTextColor:)] && 2717 [cell respondsToSelector:@selector(textColor)] ) 2718 { 2719 if ( !data->GetOriginalTextColour() ) 2720 { 2721 // the cast to (untyped) id is safe because of the check above 2722 data->SaveOriginalTextColour([(id)cell textColor]); 2723 } 2724 2725 const wxColour& c = attr.GetColour(); 2726 colText = [NSColor colorWithCalibratedRed:c.Red() / 255. 2727 green:c.Green() / 255. 2728 blue:c.Blue() / 255. 2729 alpha:c.Alpha() / 255.]; 2730 } 2731 } 2732 2733 if ( !font ) 2734 font = data->GetOriginalFont(); 2735 if ( !colText ) 2736 colText = data->GetOriginalTextColour(); 2737 2738 if ( font ) 2739 [cell setFont:font]; 2740 2741 if ( colText ) 2742 [(id)cell setTextColor:colText]; 2743} 2744 2745void wxDataViewRenderer::OSXApplyEnabled(bool enabled) 2746{ 2747 [GetNativeData()->GetItemCell() setEnabled:enabled]; 2748} 2749 2750IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer,wxDataViewRendererBase) 2751 2752// --------------------------------------------------------- 2753// wxDataViewCustomRenderer 2754// --------------------------------------------------------- 2755wxDataViewCustomRenderer::wxDataViewCustomRenderer(const wxString& varianttype, 2756 wxDataViewCellMode mode, 2757 int align) 2758 : wxDataViewCustomRendererBase(varianttype, mode, align), 2759 m_editorCtrlPtr(NULL), 2760 m_DCPtr(NULL) 2761{ 2762 wxCustomCell* cell = [[wxCustomCell alloc] init]; 2763 SetNativeData(new wxDataViewRendererNativeData(cell)); 2764 [cell release]; 2765} 2766 2767bool wxDataViewCustomRenderer::MacRender() 2768{ 2769 [GetNativeData()->GetItemCell() setObjectValue:[[[wxCustomRendererObject alloc] initWithRenderer:this] autorelease]]; 2770 return true; 2771} 2772 2773void wxDataViewCustomRenderer::OSXApplyAttr(const wxDataViewItemAttr& attr) 2774{ 2775 // simply save the attribute so that it could be reused from our Render() 2776 SetAttr(attr); 2777 2778 // it's not necessary to call the base class version which sets the cell 2779 // properties to correspond to this attribute because we currently don't 2780 // use any NSCell methods in custom renderers anyhow but if we ever start 2781 // doing this (e.g. override RenderText() here to use NSTextFieldCell 2782 // methods), then we should pass it on to wxDataViewRenderer here 2783} 2784 2785IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer) 2786 2787// --------------------------------------------------------- 2788// wxDataViewTextRenderer 2789// --------------------------------------------------------- 2790wxDataViewTextRenderer::wxDataViewTextRenderer(const wxString& varianttype, 2791 wxDataViewCellMode mode, 2792 int align) 2793 : wxDataViewRenderer(varianttype,mode,align) 2794{ 2795 NSTextFieldCell* cell; 2796 2797 2798 cell = [[NSTextFieldCell alloc] init]; 2799 [cell setAlignment:ConvertToNativeHorizontalTextAlignment(align)]; 2800 SetNativeData(new wxDataViewRendererNativeData(cell)); 2801 [cell release]; 2802} 2803 2804bool wxDataViewTextRenderer::MacRender() 2805{ 2806 if (GetValue().GetType() == GetVariantType()) 2807 { 2808 [GetNativeData()->GetItemCell() setObjectValue:wxCFStringRef(GetValue().GetString()).AsNSString()]; 2809 return true; 2810 } 2811 else 2812 { 2813 wxFAIL_MSG(wxString("Text renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType()); 2814 return false; 2815 } 2816} 2817 2818void 2819wxDataViewTextRenderer::OSXOnCellChanged(NSObject *value, 2820 const wxDataViewItem& item, 2821 unsigned col) 2822{ 2823 wxVariant valueText(ObjectToString(value)); 2824 if ( !Validate(valueText) ) 2825 return; 2826 2827 wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); 2828 model->ChangeValue(valueText, item, col); 2829} 2830 2831IMPLEMENT_CLASS(wxDataViewTextRenderer,wxDataViewRenderer) 2832 2833// --------------------------------------------------------- 2834// wxDataViewBitmapRenderer 2835// --------------------------------------------------------- 2836wxDataViewBitmapRenderer::wxDataViewBitmapRenderer(const wxString& varianttype, 2837 wxDataViewCellMode mode, 2838 int align) 2839 : wxDataViewRenderer(varianttype,mode,align) 2840{ 2841 NSImageCell* cell; 2842 2843 2844 cell = [[NSImageCell alloc] init]; 2845 SetNativeData(new wxDataViewRendererNativeData(cell)); 2846 [cell release]; 2847} 2848 2849// This method returns 'true' if 2850// - the passed bitmap is valid and it could be assigned to the native data 2851// browser; 2852// - the passed bitmap is invalid (or is not initialized); this case 2853// simulates a non-existing bitmap. 2854// In all other cases the method returns 'false'. 2855bool wxDataViewBitmapRenderer::MacRender() 2856{ 2857 wxCHECK_MSG(GetValue().GetType() == GetVariantType(),false,wxString("Bitmap renderer cannot render value; value type: ") << GetValue().GetType()); 2858 2859 wxBitmap bitmap; 2860 2861 bitmap << GetValue(); 2862 if (bitmap.IsOk()) 2863 [GetNativeData()->GetItemCell() setObjectValue:[[bitmap.GetNSImage() retain] autorelease]]; 2864 return true; 2865} 2866 2867IMPLEMENT_CLASS(wxDataViewBitmapRenderer,wxDataViewRenderer) 2868 2869// ------------------------------------- 2870// wxDataViewChoiceRenderer 2871// ------------------------------------- 2872wxDataViewChoiceRenderer::wxDataViewChoiceRenderer(const wxArrayString& choices, 2873 wxDataViewCellMode mode, 2874 int alignment) 2875 : wxDataViewRenderer(wxT("string"), mode, alignment), 2876 m_choices(choices) 2877{ 2878 NSPopUpButtonCell* cell; 2879 2880 2881 cell = [[NSPopUpButtonCell alloc] init]; 2882 [cell setControlSize:NSMiniControlSize]; 2883 [cell setFont:[NSFont fontWithName:[[cell font] fontName] size:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]]; 2884 for (size_t i=0; i<choices.GetCount(); ++i) 2885 [cell addItemWithTitle:wxCFStringRef(choices[i]).AsNSString()]; 2886 SetNativeData(new wxDataViewRendererNativeData(cell)); 2887 [cell release]; 2888} 2889 2890void 2891wxDataViewChoiceRenderer::OSXOnCellChanged(NSObject *value, 2892 const wxDataViewItem& item, 2893 unsigned col) 2894{ 2895 // At least under OS X 10.7 we get the index of the item selected and not 2896 // its string. 2897 const long choiceIndex = ObjectToLong(value); 2898 2899 // We can receive -1 if the selection was cancelled, just ignore it. 2900 if ( choiceIndex == -1 ) 2901 return; 2902 2903 // If it's not -1, it must be valid, but avoid crashing in GetChoice() 2904 // below if it isn't, for some reason. 2905 wxCHECK_RET( choiceIndex >= 0 && (size_t)choiceIndex < GetChoices().size(), 2906 wxS("Choice index out of range.") ); 2907 2908 wxVariant valueChoice(GetChoice(choiceIndex)); 2909 if ( !Validate(valueChoice) ) 2910 return; 2911 2912 wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); 2913 model->ChangeValue(valueChoice, item, col); 2914} 2915 2916bool wxDataViewChoiceRenderer::MacRender() 2917{ 2918 if (GetValue().GetType() == GetVariantType()) 2919 { 2920 [((NSPopUpButtonCell*) GetNativeData()->GetItemCell()) selectItemWithTitle:[[wxCFStringRef(GetValue().GetString()).AsNSString() retain] autorelease]]; 2921 return true; 2922 } 2923 else 2924 { 2925 wxFAIL_MSG(wxString("Choice renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType()); 2926 return false; 2927 } 2928} 2929 2930IMPLEMENT_CLASS(wxDataViewChoiceRenderer,wxDataViewRenderer) 2931 2932// --------------------------------------------------------- 2933// wxDataViewDateRenderer 2934// --------------------------------------------------------- 2935 2936wxDataViewDateRenderer::wxDataViewDateRenderer(const wxString& varianttype, 2937 wxDataViewCellMode mode, 2938 int align) 2939 : wxDataViewRenderer(varianttype,mode,align) 2940{ 2941 NSTextFieldCell* cell; 2942 2943 NSDateFormatter* dateFormatter; 2944 2945 2946 dateFormatter = [[NSDateFormatter alloc] init]; 2947 [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; 2948 [dateFormatter setDateStyle:NSDateFormatterShortStyle]; 2949 cell = [[NSTextFieldCell alloc] init]; 2950 [cell setFormatter:dateFormatter]; 2951 SetNativeData(new wxDataViewRendererNativeData(cell,[NSDate dateWithString:@"2000-12-30 20:00:00 +0000"])); 2952 [cell release]; 2953 [dateFormatter release]; 2954} 2955 2956bool wxDataViewDateRenderer::MacRender() 2957{ 2958 if (GetValue().GetType() == GetVariantType()) 2959 { 2960 if (GetValue().GetDateTime().IsValid()) 2961 { 2962 // -- find best fitting style to show the date -- 2963 // as the style should be identical for all cells a reference date 2964 // instead of the actual cell's date value is used for all cells; 2965 // this reference date is stored in the renderer's native data 2966 // section for speed purposes; otherwise, the reference date's 2967 // string has to be recalculated for each item that may become 2968 // timewise long if a lot of rows using dates exist; the algorithm 2969 // has the preference to display as much information as possible 2970 // in the first instance; but as this is often impossible due to 2971 // space restrictions the style is shortened per loop; finally, if 2972 // the shortest time and date format does not fit into the cell 2973 // the time part is dropped; remark: the time part itself is not 2974 // modified per iteration loop and only uses the short style, 2975 // means that only the hours and minutes are being shown 2976 2977 // GetObject() returns a date for testing the size of a date object 2978 [GetNativeData()->GetItemCell() setObjectValue:GetNativeData()->GetObject()]; 2979 [[GetNativeData()->GetItemCell() formatter] setTimeStyle:NSDateFormatterShortStyle]; 2980 for (int dateFormatterStyle=4; dateFormatterStyle>0; --dateFormatterStyle) 2981 { 2982 [[GetNativeData()->GetItemCell() formatter] setDateStyle:(NSDateFormatterStyle)dateFormatterStyle]; 2983 if (dateFormatterStyle == 1) 2984 { 2985 // if the shortest style for displaying the date and time 2986 // is too long to be fully visible remove the time part of 2987 // the date: 2988 if ([GetNativeData()->GetItemCell() cellSize].width > [GetNativeData()->GetColumnPtr() width]) 2989 [[GetNativeData()->GetItemCell() formatter] setTimeStyle:NSDateFormatterNoStyle]; 2990 { 2991 // basically not necessary as the loop would end anyway 2992 // but let's save the last comparison 2993 break; 2994 } 2995 } 2996 else if ([GetNativeData()->GetItemCell() cellSize].width <= [GetNativeData()->GetColumnPtr() width]) 2997 break; 2998 } 2999 // set data (the style is set by the previous loop); on OSX the 3000 // date has to be specified with respect to UTC; in wxWidgets the 3001 // date is always entered in the local timezone; so, we have to do 3002 // a conversion from the local to UTC timezone when adding the 3003 // seconds to 1970-01-01 UTC: 3004 [GetNativeData()->GetItemCell() setObjectValue:[NSDate dateWithTimeIntervalSince1970:GetValue().GetDateTime().ToUTC().Subtract(wxDateTime(1,wxDateTime::Jan,1970)).GetSeconds().ToDouble()]]; 3005 } 3006 return true; 3007 } 3008 else 3009 { 3010 wxFAIL_MSG(wxString("Date renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType()); 3011 return false; 3012 } 3013} 3014 3015void 3016wxDataViewDateRenderer::OSXOnCellChanged(NSObject *value, 3017 const wxDataViewItem& item, 3018 unsigned col) 3019{ 3020 wxVariant valueDate(ObjectToDate(value)); 3021 if ( !Validate(valueDate) ) 3022 return; 3023 3024 wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); 3025 model->ChangeValue(valueDate, item, col); 3026} 3027 3028IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer,wxDataViewRenderer) 3029 3030// --------------------------------------------------------- 3031// wxDataViewIconTextRenderer 3032// --------------------------------------------------------- 3033wxDataViewIconTextRenderer::wxDataViewIconTextRenderer(const wxString& varianttype, 3034 wxDataViewCellMode mode, 3035 int align) 3036 : wxDataViewRenderer(varianttype,mode) 3037{ 3038 wxImageTextCell* cell; 3039 3040 3041 cell = [[wxImageTextCell alloc] init]; 3042 [cell setAlignment:ConvertToNativeHorizontalTextAlignment(align)]; 3043 SetNativeData(new wxDataViewRendererNativeData(cell)); 3044 [cell release]; 3045} 3046 3047bool wxDataViewIconTextRenderer::MacRender() 3048{ 3049 if (GetValue().GetType() == GetVariantType()) 3050 { 3051 wxDataViewIconText iconText; 3052 3053 wxImageTextCell* cell; 3054 3055 cell = (wxImageTextCell*) GetNativeData()->GetItemCell(); 3056 iconText << GetValue(); 3057 if (iconText.GetIcon().IsOk()) 3058 [cell setImage:[[wxBitmap(iconText.GetIcon()).GetNSImage() retain] autorelease]]; 3059 else 3060 [cell setImage:nil]; 3061 [cell setStringValue:[[wxCFStringRef(iconText.GetText()).AsNSString() retain] autorelease]]; 3062 return true; 3063 } 3064 else 3065 { 3066 wxFAIL_MSG(wxString("Icon & text renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType()); 3067 return false; 3068 } 3069} 3070 3071void 3072wxDataViewIconTextRenderer::OSXOnCellChanged(NSObject *value, 3073 const wxDataViewItem& item, 3074 unsigned col) 3075{ 3076 wxVariant valueIconText; 3077 valueIconText << wxDataViewIconText(ObjectToString(value)); 3078 3079 if ( !Validate(valueIconText) ) 3080 return; 3081 3082 wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); 3083 model->ChangeValue(valueIconText, item, col); 3084} 3085 3086IMPLEMENT_ABSTRACT_CLASS(wxDataViewIconTextRenderer,wxDataViewRenderer) 3087 3088// --------------------------------------------------------- 3089// wxDataViewToggleRenderer 3090// --------------------------------------------------------- 3091wxDataViewToggleRenderer::wxDataViewToggleRenderer(const wxString& varianttype, 3092 wxDataViewCellMode mode, 3093 int align) 3094 : wxDataViewRenderer(varianttype,mode) 3095{ 3096 NSButtonCell* cell; 3097 3098 3099 cell = [[NSButtonCell alloc] init]; 3100 [cell setAlignment:ConvertToNativeHorizontalTextAlignment(align)]; 3101 [cell setButtonType:NSSwitchButton]; 3102 [cell setImagePosition:NSImageOnly]; 3103 SetNativeData(new wxDataViewRendererNativeData(cell)); 3104 [cell release]; 3105} 3106 3107bool wxDataViewToggleRenderer::MacRender() 3108{ 3109 if (GetValue().GetType() == GetVariantType()) 3110 { 3111 [GetNativeData()->GetItemCell() setIntValue:GetValue().GetLong()]; 3112 return true; 3113 } 3114 else 3115 { 3116 wxFAIL_MSG(wxString("Toggle renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType()); 3117 return false; 3118 } 3119} 3120 3121void 3122wxDataViewToggleRenderer::OSXOnCellChanged(NSObject *value, 3123 const wxDataViewItem& item, 3124 unsigned col) 3125{ 3126 wxVariant valueToggle(ObjectToBool(value)); 3127 if ( !Validate(valueToggle) ) 3128 return; 3129 3130 wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); 3131 model->ChangeValue(valueToggle, item, col); 3132} 3133 3134IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer,wxDataViewRenderer) 3135 3136// --------------------------------------------------------- 3137// wxDataViewProgressRenderer 3138// --------------------------------------------------------- 3139wxDataViewProgressRenderer::wxDataViewProgressRenderer(const wxString& label, 3140 const wxString& varianttype, 3141 wxDataViewCellMode mode, 3142 int align) 3143 : wxDataViewRenderer(varianttype,mode,align) 3144{ 3145 wxUnusedVar(label); 3146 3147 NSLevelIndicatorCell* cell; 3148 3149 cell = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]; 3150 [cell setMinValue:0]; 3151 [cell setMaxValue:100]; 3152 SetNativeData(new wxDataViewRendererNativeData(cell)); 3153 [cell release]; 3154} 3155 3156bool wxDataViewProgressRenderer::MacRender() 3157{ 3158 if (GetValue().GetType() == GetVariantType()) 3159 { 3160 [GetNativeData()->GetItemCell() setIntValue:GetValue().GetLong()]; 3161 return true; 3162 } 3163 else 3164 { 3165 wxFAIL_MSG(wxString("Progress renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType()); 3166 return false; 3167 } 3168} 3169 3170void 3171wxDataViewProgressRenderer::OSXOnCellChanged(NSObject *value, 3172 const wxDataViewItem& item, 3173 unsigned col) 3174{ 3175 wxVariant valueProgress(ObjectToLong(value)); 3176 if ( !Validate(valueProgress) ) 3177 return; 3178 3179 wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); 3180 model->ChangeValue(valueProgress, item, col); 3181} 3182 3183IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer,wxDataViewRenderer) 3184 3185// --------------------------------------------------------- 3186// wxDataViewColumn 3187// --------------------------------------------------------- 3188 3189wxDataViewColumn::wxDataViewColumn(const wxString& title, 3190 wxDataViewRenderer* renderer, 3191 unsigned int model_column, 3192 int width, 3193 wxAlignment align, 3194 int flags) 3195 : wxDataViewColumnBase(renderer, model_column), 3196 m_NativeDataPtr(new wxDataViewColumnNativeData()), 3197 m_title(title) 3198{ 3199 InitCommon(width, align, flags); 3200 if (renderer && !renderer->IsCustomRenderer() && 3201 (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT)) 3202 renderer->SetAlignment(align); 3203} 3204 3205wxDataViewColumn::wxDataViewColumn(const wxBitmap& bitmap, 3206 wxDataViewRenderer* renderer, 3207 unsigned int model_column, 3208 int width, 3209 wxAlignment align, 3210 int flags) 3211 : wxDataViewColumnBase(bitmap, renderer, model_column), 3212 m_NativeDataPtr(new wxDataViewColumnNativeData()) 3213{ 3214 InitCommon(width, align, flags); 3215 if (renderer && !renderer->IsCustomRenderer() && 3216 (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT)) 3217 renderer->SetAlignment(align); 3218} 3219 3220wxDataViewColumn::~wxDataViewColumn() 3221{ 3222 delete m_NativeDataPtr; 3223} 3224 3225int wxDataViewColumn::GetWidth() const 3226{ 3227 return [m_NativeDataPtr->GetNativeColumnPtr() width]; 3228} 3229 3230bool wxDataViewColumn::IsSortKey() const 3231{ 3232 NSTableColumn *nsCol = GetNativeData()->GetNativeColumnPtr(); 3233 return nsCol && ([nsCol sortDescriptorPrototype] != nil); 3234} 3235 3236void wxDataViewColumn::SetAlignment(wxAlignment align) 3237{ 3238 m_alignment = align; 3239 [[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setAlignment:ConvertToNativeHorizontalTextAlignment(align)]; 3240 if (m_renderer && !m_renderer->IsCustomRenderer() && 3241 (m_renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT)) 3242 m_renderer->SetAlignment(align); 3243} 3244 3245void wxDataViewColumn::SetBitmap(const wxBitmap& bitmap) 3246{ 3247 // bitmaps and titles cannot exist at the same time - if the bitmap is set 3248 // the title is removed: 3249 m_title = wxEmptyString; 3250 wxDataViewColumnBase::SetBitmap(bitmap); 3251 [[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setImage:[[bitmap.GetNSImage() retain] autorelease]]; 3252} 3253 3254void wxDataViewColumn::SetMaxWidth(int maxWidth) 3255{ 3256 m_maxWidth = maxWidth; 3257 [m_NativeDataPtr->GetNativeColumnPtr() setMaxWidth:maxWidth]; 3258} 3259 3260void wxDataViewColumn::SetMinWidth(int minWidth) 3261{ 3262 m_minWidth = minWidth; 3263 [m_NativeDataPtr->GetNativeColumnPtr() setMinWidth:minWidth]; 3264} 3265 3266void wxDataViewColumn::SetReorderable(bool reorderable) 3267{ 3268 wxUnusedVar(reorderable); 3269} 3270 3271void wxDataViewColumn::SetHidden(bool hidden) 3272{ 3273 // How to set flag here? 3274 3275 [m_NativeDataPtr->GetNativeColumnPtr() setHidden:hidden]; 3276} 3277 3278bool wxDataViewColumn::IsHidden() const 3279{ 3280 return [m_NativeDataPtr->GetNativeColumnPtr() isHidden]; 3281} 3282 3283void wxDataViewColumn::SetResizeable(bool resizable) 3284{ 3285 wxDataViewColumnBase::SetResizeable(resizable); 3286 if (resizable) 3287 [m_NativeDataPtr->GetNativeColumnPtr() setResizingMask:NSTableColumnUserResizingMask]; 3288 else 3289 [m_NativeDataPtr->GetNativeColumnPtr() setResizingMask:NSTableColumnNoResizing]; 3290} 3291 3292void wxDataViewColumn::SetSortable(bool sortable) 3293{ 3294 // wxDataViewColumnBase::SetSortable(sortable); 3295 // Avoid endless recursion and just set the flag here 3296 if (sortable) 3297 m_flags |= wxDATAVIEW_COL_SORTABLE; 3298 else 3299 m_flags &= ~wxDATAVIEW_COL_SORTABLE; 3300} 3301 3302void wxDataViewColumn::SetSortOrder(bool ascending) 3303{ 3304 if (m_ascending != ascending) 3305 { 3306 m_ascending = ascending; 3307 if (IsSortKey()) 3308 { 3309 // change sorting order: 3310 NSArray* sortDescriptors; 3311 NSSortDescriptor* sortDescriptor; 3312 NSTableColumn* tableColumn; 3313 3314 tableColumn = m_NativeDataPtr->GetNativeColumnPtr(); 3315 sortDescriptor = [[NSSortDescriptor alloc] initWithKey:[[tableColumn sortDescriptorPrototype] key] ascending:m_ascending]; 3316 sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; 3317 [tableColumn setSortDescriptorPrototype:sortDescriptor]; 3318 [[tableColumn tableView] setSortDescriptors:sortDescriptors]; 3319 [sortDescriptor release]; 3320 } 3321 } 3322} 3323 3324void wxDataViewColumn::SetTitle(const wxString& title) 3325{ 3326 // bitmaps and titles cannot exist at the same time - if the title is set 3327 // the bitmap is removed: 3328 wxDataViewColumnBase::SetBitmap(wxBitmap()); 3329 m_title = title; 3330 [[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setStringValue:[[wxCFStringRef(title).AsNSString() retain] autorelease]]; 3331} 3332 3333void wxDataViewColumn::SetWidth(int width) 3334{ 3335 m_width = width; 3336 3337 switch ( width ) 3338 { 3339 case wxCOL_WIDTH_AUTOSIZE: 3340 if ( GetOwner() ) 3341 { 3342 wxCocoaDataViewControl *peer = static_cast<wxCocoaDataViewControl*>(GetOwner()->GetPeer()); 3343 peer->FitColumnWidthToContent(GetOwner()->GetColumnPosition(this)); 3344 break; 3345 } 3346 // fall through if not yet settable 3347 3348 case wxCOL_WIDTH_DEFAULT: 3349 width = wxDVC_DEFAULT_WIDTH; 3350 // fall through 3351 3352 default: 3353 [m_NativeDataPtr->GetNativeColumnPtr() setWidth:width]; 3354 break; 3355 } 3356} 3357 3358void wxDataViewColumn::SetNativeData(wxDataViewColumnNativeData* newNativeDataPtr) 3359{ 3360 delete m_NativeDataPtr; 3361 m_NativeDataPtr = newNativeDataPtr; 3362} 3363 3364#endif // (wxUSE_DATAVIEWCTRL == 1) && !defined(wxUSE_GENERICDATAVIEWCTRL) 3365