1 ///////////////////////////////////////////////////////////////////////////////
 
   2 // Name:        src/osx/cocoa/dataview.mm
 
   8 // Licence:     wxWindows licence
 
   9 ///////////////////////////////////////////////////////////////////////////////
 
  11 #include "wx/wxprec.h"
 
  13 #if (wxUSE_DATAVIEWCTRL == 1) && !defined(wxUSE_GENERICDATAVIEWCTRL)
 
  17     #include "wx/toplevel.h"
 
  19     #include "wx/settings.h"
 
  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"
 
  29 // ============================================================================
 
  30 // Constants used locally
 
  31 // ============================================================================
 
  33 #define DataViewPboardType @"OutlineViewItem"
 
  35 // ============================================================================
 
  36 // Classes used locally in dataview.mm
 
  37 // ============================================================================
 
  39 // ============================================================================
 
  41 // ============================================================================
 
  43 @implementation wxPointerObject
 
  53 -(id) initWithPointer:(void*) initPointer
 
  57         self->pointer = initPointer;
 
  62 // inherited methods from NSObject
 
  64 -(BOOL) isEqual:(id)object
 
  66     return (object != nil) &&
 
  67              ([object isKindOfClass:[wxPointerObject class]]) &&
 
  68                  (pointer == [((wxPointerObject*) object) pointer]);
 
  73     return (NSUInteger) pointer;
 
  81 -(void) setPointer:(void*) newPointer
 
  91 inline wxDataViewItem wxDataViewItemFromItem(id item)
 
  93     return wxDataViewItem([static_cast<wxPointerObject *>(item) pointer]);
 
  96 inline wxDataViewItem wxDataViewItemFromMaybeNilItem(id item)
 
  98     return item == nil ? wxDataViewItem() : wxDataViewItemFromItem(item);
 
 101 } // anonymous namespace
 
 103 // ----------------------------------------------------------------------------
 
 104 // wxCustomRendererObject
 
 105 // ----------------------------------------------------------------------------
 
 107 @interface wxCustomRendererObject : NSObject <NSCopying>
 
 110     wxDataViewCustomRenderer* customRenderer; // not owned by the class
 
 114     -(id) initWithRenderer:(wxDataViewCustomRenderer*)renderer;
 
 117 @implementation wxCustomRendererObject
 
 124         customRenderer = NULL;
 
 129 -(id) initWithRenderer:(wxDataViewCustomRenderer*)renderer
 
 134         customRenderer = renderer;
 
 139 -(id) copyWithZone:(NSZone*)zone
 
 141     wxCustomRendererObject* copy;
 
 143     copy = [[[self class] allocWithZone:zone] init];
 
 144     copy->customRenderer = customRenderer;
 
 150 // ----------------------------------------------------------------------------
 
 151 // wxDVCNSTableColumn: exists only to override NSTableColumn:dataCellForRow:
 
 152 // ----------------------------------------------------------------------------
 
 154 @interface wxDVCNSTableColumn : NSTableColumn
 
 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;
 
 163     // Initialize the column with the given pointer to the associated
 
 164     // wxDataViewColumn. This pointer can later be retrieved using
 
 166     -(id) initWithColumnPointer:(const wxDataViewColumn*)column;
 
 168     // Retrieve the associated column.
 
 169     -(wxDataViewColumn*) getColumnPointer;
 
 171     -(id) dataCellForRow:(NSInteger)row;
 
 174 @implementation wxDVCNSTableColumn
 
 176 +(NSString*) identifierForColumnPointer:(const wxDataViewColumn*)column
 
 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", reinterpret_cast<NSUInteger>(column)];
 
 185 -(id) initWithColumnPointer:(const wxDataViewColumn*)column
 
 187     [self initWithIdentifier: [wxDVCNSTableColumn identifierForColumnPointer:column]];
 
 191 -(wxDataViewColumn*) getColumnPointer
 
 193     // The case to NSString is needed for OS X < 10.7.
 
 194     return reinterpret_cast<wxDataViewColumn*>(
 
 195             [static_cast<NSString*>([self identifier]) integerValue]);
 
 198 -(id) dataCellForRow:(NSInteger)row
 
 200     // what we want to do here is to simply return nil for the cells which
 
 201     // shouldn't show anything as otherwise we would show e.g. empty combo box
 
 202     // or progress cells in the columns using the corresponding types even for
 
 203     // the container rows which is wrong
 
 205     const wxDataViewColumn * const dvCol = [self getColumnPointer];
 
 207     const wxDataViewCtrl * const dvc = dvCol->GetOwner();
 
 208     const wxCocoaDataViewControl * const
 
 209         peer = static_cast<wxCocoaDataViewControl *>(dvc->GetPeer());
 
 212     // once we do have everything, simply ask NSOutlineView for the item...
 
 213     const id item = peer->GetItemAtRow(row);
 
 216         // ... and if it succeeded, ask the model whether it has any value
 
 217         wxDataViewItem dvItem(wxDataViewItemFromItem(item));
 
 219         if ( !dvc->GetModel()->HasValue(dvItem, dvCol->GetModelColumn()) )
 
 223     return [super dataCellForRow:row];
 
 228 // ============================================================================
 
 230 // ============================================================================
 
 235 // convert from NSObject to different C++ types: all these functions check
 
 236 // that the conversion really makes sense and assert if it doesn't
 
 237 wxString ObjectToString(NSObject *object)
 
 239     wxCHECK_MSG( [object isKindOfClass:[NSString class]], "",
 
 242                     "string expected but got %s",
 
 243                     wxCFStringRef::AsString([object className])
 
 246     return wxCFStringRef([((NSString*) object) retain]).AsString();
 
 249 bool ObjectToBool(NSObject *object)
 
 251     // actually the value must be of NSCFBoolean class but it's private so we
 
 252     // can't check for it directly
 
 253     wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], false,
 
 256                     "number expected but got %s",
 
 257                     wxCFStringRef::AsString([object className])
 
 260     return [(NSNumber *)object boolValue];
 
 263 long ObjectToLong(NSObject *object)
 
 265     wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], -1,
 
 268                     "number expected but got %s",
 
 269                     wxCFStringRef::AsString([object className])
 
 272     return [(NSNumber *)object longValue];
 
 275 wxDateTime ObjectToDate(NSObject *object)
 
 277     wxCHECK_MSG( [object isKindOfClass:[NSDate class]], wxInvalidDateTime,
 
 280                     "date expected but got %s",
 
 281                     wxCFStringRef::AsString([object className])
 
 284     // get the number of seconds since 1970-01-01 UTC and this is the only
 
 285     // way to convert a double to a wxLongLong
 
 286     const wxLongLong seconds = [((NSDate*) object) timeIntervalSince1970];
 
 288     wxDateTime dt(1, wxDateTime::Jan, 1970);
 
 289     dt.Add(wxTimeSpan(0,0,seconds));
 
 291     // the user has entered a date in the local timezone but seconds
 
 292     // contains the number of seconds from date in the local timezone
 
 293     // since 1970-01-01 UTC; therefore, the timezone information has to be
 
 294     // transferred to wxWidgets, too:
 
 295     dt.MakeFromTimezone(wxDateTime::UTC);
 
 300 NSInteger CompareItems(id item1, id item2, void* context)
 
 302     NSArray* const sortDescriptors = (NSArray*) context;
 
 304     NSUInteger const count = [sortDescriptors count];
 
 306     NSInteger result = NSOrderedSame;
 
 307     for ( NSUInteger i = 0; i < count && result == NSOrderedSame; ++i )
 
 309         wxSortDescriptorObject* const
 
 310             sortDescriptor = (wxSortDescriptorObject*)
 
 311                 [sortDescriptors objectAtIndex:i];
 
 313         int rc = [sortDescriptor modelPtr]->Compare
 
 315                      wxDataViewItemFromItem(item1),
 
 316                      wxDataViewItemFromItem(item2),
 
 317                      [sortDescriptor columnPtr]->GetModelColumn(),
 
 318                      [sortDescriptor ascending] == YES
 
 322             result = NSOrderedAscending;
 
 324             result = NSOrderedDescending;
 
 330 NSTextAlignment ConvertToNativeHorizontalTextAlignment(int alignment)
 
 332     if (alignment & wxALIGN_CENTER_HORIZONTAL)
 
 333         return NSCenterTextAlignment;
 
 334     else if (alignment & wxALIGN_RIGHT)
 
 335         return NSRightTextAlignment;
 
 337         return NSLeftTextAlignment;
 
 340 NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column)
 
 342     wxDataViewRenderer * const renderer = column->GetRenderer();
 
 344     wxCHECK_MSG( renderer, NULL, "column should have a renderer" );
 
 346     wxDVCNSTableColumn * const nativeColumn(
 
 347         [[wxDVCNSTableColumn alloc] initWithColumnPointer: column]
 
 350     // setting the size related parameters:
 
 352     if (column->IsResizeable())
 
 354         resizingMask = NSTableColumnUserResizingMask;
 
 355         [nativeColumn setMinWidth:column->GetMinWidth()];
 
 356         [nativeColumn setMaxWidth:column->GetMaxWidth()];
 
 358     else // column is not resizable [by user]
 
 360         // if the control doesn't show a header, make the columns resize
 
 361         // automatically, this is particularly important for the single column
 
 362         // controls (such as wxDataViewTreeCtrl) as their unique column should
 
 363         // always take up all the available splace
 
 364         resizingMask = column->GetOwner()->HasFlag(wxDV_NO_HEADER)
 
 365                             ? NSTableColumnAutoresizingMask
 
 366                             : NSTableColumnNoResizing;
 
 368     [nativeColumn setResizingMask:resizingMask];
 
 370 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
 
 371     // setting the visibility:
 
 372     [nativeColumn setHidden:static_cast<BOOL>(column->IsHidden())];
 
 375     wxDataViewRendererNativeData * const renderData = renderer->GetNativeData();
 
 377     // setting the header:
 
 378     [[nativeColumn headerCell] setAlignment:
 
 379         ConvertToNativeHorizontalTextAlignment(column->GetAlignment())];
 
 380     [[nativeColumn headerCell] setStringValue:
 
 381         [[wxCFStringRef(column->GetTitle()).AsNSString() retain] autorelease]];
 
 382     renderData->ApplyLineBreakMode([nativeColumn headerCell]);
 
 384     // setting data cell's properties:
 
 385     [[nativeColumn dataCell] setWraps:NO];
 
 386     // setting the default data cell:
 
 387     [nativeColumn setDataCell:renderData->GetColumnCell()];
 
 388     // setting the editablility:
 
 389     const bool isEditable = renderer->GetMode() == wxDATAVIEW_CELL_EDITABLE;
 
 391     [nativeColumn setEditable:isEditable];
 
 392     [[nativeColumn dataCell] setEditable:isEditable];
 
 397 } // anonymous namespace
 
 399 // ============================================================================
 
 400 // Public helper functions for dataview implementation on OSX
 
 401 // ============================================================================
 
 403 wxWidgetImplType* CreateDataView(wxWindowMac* wxpeer,
 
 404                                  wxWindowMac* WXUNUSED(parent),
 
 405                                  wxWindowID WXUNUSED(id),
 
 409                                  long WXUNUSED(extraStyle))
 
 411     return new wxCocoaDataViewControl(wxpeer,pos,size,style);
 
 414 // ============================================================================
 
 415 // wxSortDescriptorObject
 
 416 // ============================================================================
 
 418 @implementation wxSortDescriptorObject
 
 431 initWithModelPtr:(wxDataViewModel*)initModelPtr
 
 432     sortingColumnPtr:(wxDataViewColumn*)initColumnPtr
 
 433     ascending:(BOOL)sortAscending
 
 435     self = [super initWithKey:@"dummy" ascending:sortAscending];
 
 438         columnPtr = initColumnPtr;
 
 439         modelPtr  = initModelPtr;
 
 444 -(id) copyWithZone:(NSZone*)zone
 
 446     wxSortDescriptorObject* copy;
 
 449     copy = [super copyWithZone:zone];
 
 450     copy->columnPtr = columnPtr;
 
 451     copy->modelPtr  = modelPtr;
 
 457 // access to model column's index
 
 459 -(wxDataViewColumn*) columnPtr
 
 464 -(wxDataViewModel*) modelPtr
 
 469 -(void) setColumnPtr:(wxDataViewColumn*)newColumnPtr
 
 471     columnPtr = newColumnPtr;
 
 474 -(void) setModelPtr:(wxDataViewModel*)newModelPtr
 
 476     modelPtr = newModelPtr;
 
 481 // ============================================================================
 
 482 // wxCocoaOutlineDataSource
 
 483 // ============================================================================
 
 484 @implementation wxCocoaOutlineDataSource
 
 487 // constructors / destructor
 
 494         implementation = NULL;
 
 497         currentParentItem = nil;
 
 499         children = [[NSMutableArray alloc] init];
 
 500         items    = [[NSMutableSet   alloc] init];
 
 507     [currentParentItem release];
 
 516 // methods of informal protocol:
 
 519 outlineView:(NSOutlineView*)outlineView
 
 520     acceptDrop:(id<NSDraggingInfo>)info
 
 521     item:(id)item childIndex:(NSInteger)index
 
 523     wxUnusedVar(outlineView);
 
 526     NSArray* supportedTypes(
 
 527         [NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]
 
 530     NSPasteboard* pasteboard([info draggingPasteboard]);
 
 532     NSString* bestType([pasteboard availableTypeFromArray:supportedTypes]);
 
 534     if ( bestType == nil )
 
 537     wxDataViewCtrl * const dvc(implementation->GetDataViewCtrl());
 
 539     wxCHECK_MSG( dvc, false,
 
 540                      "Pointer to data view control not set correctly." );
 
 541     wxCHECK_MSG( dvc->GetModel(), false,
 
 542                     "Pointer to model not set correctly." );
 
 544     wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_DROP, dvc->GetId());
 
 545     event.SetEventObject(dvc);
 
 546     event.SetItem(wxDataViewItemFromItem(item));
 
 547     event.SetModel(dvc->GetModel());
 
 549     BOOL dragSuccessful = false;
 
 550     if ( [bestType compare:DataViewPboardType] == NSOrderedSame )
 
 552         NSArray* dataArray((NSArray*)
 
 553                       [pasteboard propertyListForType:DataViewPboardType]);
 
 554         NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]);
 
 556         indexDraggedItem = 0;
 
 557         while (indexDraggedItem < noOfDraggedItems)
 
 559             wxDataObjectComposite* dataObjects(
 
 560                 implementation->GetDnDDataObjects((NSData*)
 
 561                     [dataArray objectAtIndex:indexDraggedItem]));
 
 563             if (dataObjects && (dataObjects->GetFormatCount() > 0))
 
 565                 wxMemoryBuffer buffer;
 
 567                 // copy data into data object:
 
 568                 event.SetDataObject(dataObjects);
 
 570                     implementation->GetDnDDataFormat(dataObjects));
 
 571                 // copy data into buffer:
 
 572                 dataObjects->GetDataHere(
 
 573                     event.GetDataFormat().GetType(),
 
 574                     buffer.GetWriteBuf(event.GetDataSize()));
 
 575                 buffer.UngetWriteBuf(event.GetDataSize());
 
 576                 event.SetDataBuffer(buffer.GetData());
 
 577                 // finally, send event:
 
 578                 if (dvc->HandleWindowEvent(event) && event.IsAllowed())
 
 580                     dragSuccessful = true;
 
 585                     dragSuccessful   = true;
 
 586                     indexDraggedItem = noOfDraggedItems; // stop loop
 
 591                 dragSuccessful   = false;
 
 592                 indexDraggedItem = noOfDraggedItems; // stop loop
 
 600         // needed to convert internally used UTF-16 representation to a UTF-8
 
 603         wxDataObjectComposite* dataObjects   (new wxDataObjectComposite());
 
 604         wxTextDataObject*      textDataObject(new wxTextDataObject());
 
 606         osxData = ::CFStringCreateExternalRepresentation
 
 609                      (CFStringRef)[pasteboard stringForType:NSStringPboardType],
 
 610                      kCFStringEncodingUTF8,
 
 613         if (textDataObject->SetData(::CFDataGetLength(osxData),
 
 614                                     ::CFDataGetBytePtr(osxData)))
 
 615             dataObjects->Add(textDataObject);
 
 617             delete textDataObject;
 
 618         // send event if data could be copied:
 
 619         if (dataObjects->GetFormatCount() > 0)
 
 621             event.SetDataObject(dataObjects);
 
 622             event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects));
 
 623             if (dvc->HandleWindowEvent(event) && event.IsAllowed())
 
 624                 dragSuccessful = true;
 
 626                 dragSuccessful = false;
 
 629             dragSuccessful = false;
 
 631         ::CFRelease(osxData);
 
 634     return dragSuccessful;
 
 637 -(id) outlineView:(NSOutlineView*)outlineView
 
 638     child:(NSInteger)index
 
 641     wxUnusedVar(outlineView);
 
 643     if ((item == currentParentItem) &&
 
 644             (index < ((NSInteger) [self getChildCount])))
 
 645         return [self getChild:index];
 
 647     wxDataViewItemArray dataViewChildren;
 
 649     wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
 
 650     model->GetChildren(wxDataViewItemFromMaybeNilItem(item), dataViewChildren);
 
 651     [self bufferItem:item withChildren:&dataViewChildren];
 
 652     if ([sortDescriptors count] > 0)
 
 653         [children sortUsingFunction:CompareItems context:sortDescriptors];
 
 654     return [self getChild:index];
 
 657 -(BOOL) outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item
 
 659     wxUnusedVar(outlineView);
 
 661     wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
 
 662     return model->IsContainer(wxDataViewItemFromItem(item));
 
 665 -(NSInteger) outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item
 
 667     wxUnusedVar(outlineView);
 
 669     NSInteger noOfChildren;
 
 671     wxDataViewItemArray dataViewChildren;
 
 674     wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
 
 675     noOfChildren = model->GetChildren(wxDataViewItemFromMaybeNilItem(item),
 
 677     [self bufferItem:item withChildren:&dataViewChildren];
 
 678     if ([sortDescriptors count] > 0)
 
 679         [children sortUsingFunction:CompareItems context:sortDescriptors];
 
 684 outlineView:(NSOutlineView*)outlineView
 
 685     objectValueForTableColumn:(NSTableColumn*)tableColumn
 
 688     wxUnusedVar(outlineView);
 
 690     wxCHECK_MSG( model, nil, "Valid model in data source does not exist." );
 
 692     wxDataViewColumn* const
 
 693         col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]);
 
 694     const unsigned colIdx = col->GetModelColumn();
 
 696     wxDataViewItem dataViewItem(wxDataViewItemFromItem(item));
 
 698     if ( model->HasValue(dataViewItem, colIdx) )
 
 701         model->GetValue(value,dataViewItem, colIdx);
 
 702         col->GetRenderer()->SetValue(value);
 
 709 outlineView:(NSOutlineView*)outlineView
 
 710     setObjectValue:(id)object
 
 711     forTableColumn:(NSTableColumn*)tableColumn
 
 714     wxUnusedVar(outlineView);
 
 716     wxDataViewColumn* const
 
 717         col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]);
 
 720         OSXOnCellChanged(object, wxDataViewItemFromItem(item), col->GetModelColumn());
 
 723 -(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)oldDescriptors
 
 725     wxUnusedVar(oldDescriptors);
 
 727     // Warning: the new sort descriptors are guaranteed to be only of type
 
 728     // NSSortDescriptor! Therefore, the sort descriptors for the data source
 
 729     // have to be converted.
 
 730     NSArray* newDescriptors;
 
 732     NSMutableArray* wxSortDescriptors;
 
 734     NSUInteger noOfDescriptors;
 
 736     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
 739     // convert NSSortDescriptors to wxSortDescriptorObjects:
 
 740     newDescriptors    = [outlineView sortDescriptors];
 
 741     noOfDescriptors   = [newDescriptors count];
 
 742     wxSortDescriptors = [NSMutableArray arrayWithCapacity:noOfDescriptors];
 
 743     for (NSUInteger i=0; i<noOfDescriptors; ++i)
 
 745         NSSortDescriptor* const newDescriptor = [newDescriptors objectAtIndex:i];
 
 747         [wxSortDescriptors addObject:[[[wxSortDescriptorObject alloc] initWithModelPtr:model
 
 748             sortingColumnPtr:dvc->GetColumn([[newDescriptor key] intValue])
 
 749             ascending:[newDescriptor ascending]] autorelease]];
 
 751     [(wxCocoaOutlineDataSource*)[outlineView dataSource] setSortDescriptors:wxSortDescriptors];
 
 753     // send first the event to wxWidgets that the sorting has changed so that
 
 754     // the program can do special actions before the sorting actually starts:
 
 755     wxDataViewEvent event(wxEVT_DATAVIEW_COLUMN_SORTED,dvc->GetId()); // variable definition
 
 757     event.SetEventObject(dvc);
 
 758     if (noOfDescriptors > 0)
 
 760         wxDataViewColumn* const col = [[wxSortDescriptors objectAtIndex:0] columnPtr];
 
 762         event.SetColumn(dvc->GetColumnPosition(col));
 
 763         event.SetDataViewColumn(col);
 
 765     dvc->GetEventHandler()->ProcessEvent(event);
 
 767     // start re-ordering the data;
 
 768     // children's buffer must be cleared first because it contains the old order:
 
 769     [self clearChildren];
 
 770     // sorting is done while reloading the data:
 
 771     [outlineView reloadData];
 
 774 -(NSDragOperation) outlineView:(NSOutlineView*)outlineView validateDrop:(id<NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
 
 776     wxUnusedVar(outlineView);
 
 779     NSArray* supportedTypes([NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]);
 
 781     NSPasteboard* pasteboard([info draggingPasteboard]);
 
 783     NSString* bestType([pasteboard availableTypeFromArray:supportedTypes]);
 
 785         return NSDragOperationNone;
 
 787     NSDragOperation dragOperation = NSDragOperationNone;
 
 788     wxDataViewCtrl* const dvc(implementation->GetDataViewCtrl());
 
 790     wxCHECK_MSG(dvc, false, "Pointer to data view control not set correctly.");
 
 791     wxCHECK_MSG(dvc->GetModel(), false, "Pointer to model not set correctly.");
 
 794         event(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE,dvc->GetId());
 
 796     event.SetEventObject(dvc);
 
 797     event.SetItem(wxDataViewItemFromItem(item));
 
 798     event.SetModel(dvc->GetModel());
 
 799     if ([bestType compare:DataViewPboardType] == NSOrderedSame)
 
 801         NSArray*               dataArray((NSArray*)[pasteboard propertyListForType:DataViewPboardType]);
 
 802         NSUInteger             indexDraggedItem, noOfDraggedItems([dataArray count]);
 
 804         indexDraggedItem = 0;
 
 805         while (indexDraggedItem < noOfDraggedItems)
 
 807             wxDataObjectComposite* dataObjects(implementation->GetDnDDataObjects((NSData*)[dataArray objectAtIndex:indexDraggedItem]));
 
 809             if (dataObjects && (dataObjects->GetFormatCount() > 0))
 
 811                 wxMemoryBuffer buffer;
 
 813                 // copy data into data object:
 
 814                 event.SetDataObject(dataObjects);
 
 815                 event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects));
 
 816                 // copy data into buffer:
 
 817                 dataObjects->GetDataHere(event.GetDataFormat().GetType(),buffer.GetWriteBuf(event.GetDataSize()));
 
 818                 buffer.UngetWriteBuf(event.GetDataSize());
 
 819                 event.SetDataBuffer(buffer.GetData());
 
 820                 // finally, send event:
 
 821                 if (dvc->HandleWindowEvent(event) && event.IsAllowed())
 
 823                     dragOperation = NSDragOperationEvery;
 
 828                     dragOperation    = NSDragOperationNone;
 
 829                     indexDraggedItem = noOfDraggedItems; // stop loop
 
 834                 dragOperation    = NSDragOperationNone;
 
 835                 indexDraggedItem = noOfDraggedItems; // stop loop
 
 843         // needed to convert internally used UTF-16 representation to a UTF-8
 
 846         wxDataObjectComposite* dataObjects   (new wxDataObjectComposite());
 
 847         wxTextDataObject*      textDataObject(new wxTextDataObject());
 
 849         osxData = ::CFStringCreateExternalRepresentation(kCFAllocatorDefault,(CFStringRef)[pasteboard stringForType:NSStringPboardType],kCFStringEncodingUTF8,32);
 
 850         if (textDataObject->SetData(::CFDataGetLength(osxData),::CFDataGetBytePtr(osxData)))
 
 851             dataObjects->Add(textDataObject);
 
 853             delete textDataObject;
 
 854         // send event if data could be copied:
 
 855         if (dataObjects->GetFormatCount() > 0)
 
 857             event.SetDataObject(dataObjects);
 
 858             event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects));
 
 859             if (dvc->HandleWindowEvent(event) && event.IsAllowed())
 
 860                 dragOperation = NSDragOperationEvery;
 
 862                 dragOperation = NSDragOperationNone;
 
 865             dragOperation = NSDragOperationNone;
 
 867         ::CFRelease(osxData);
 
 871     return dragOperation;
 
 874 -(BOOL) outlineView:(NSOutlineView*)outlineView writeItems:(NSArray*)writeItems toPasteboard:(NSPasteboard*)pasteboard
 
 876     wxUnusedVar(outlineView);
 
 878     // the pasteboard will be filled up with an array containing the data as
 
 879     // returned by the events (including the data type) and a concatenation of
 
 880     // text (string) data; the text data will only be put onto the pasteboard
 
 881     // if for all items a string representation exists
 
 882     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
 884     wxDataViewItemArray dataViewItems;
 
 887     wxCHECK_MSG(dvc, false,"Pointer to data view control not set correctly.");
 
 888     wxCHECK_MSG(dvc->GetModel(),false,"Pointer to model not set correctly.");
 
 890     if ([writeItems count] > 0)
 
 892         bool            dataStringAvailable(true); // a flag indicating if for all items a data string is available
 
 893         NSMutableArray* dataArray = [NSMutableArray arrayWithCapacity:[writeItems count]]; // data of all items
 
 894         wxString        dataString; // contains the string data of all items
 
 896         // send a begin drag event for all selected items and proceed with
 
 897         // dragging unless the event is vetoed:
 
 899             event(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG,dvc->GetId());
 
 901         event.SetEventObject(dvc);
 
 902         event.SetModel(dvc->GetModel());
 
 903         for (size_t itemCounter=0; itemCounter<[writeItems count]; ++itemCounter)
 
 905             bool                   itemStringAvailable(false);              // a flag indicating if for the current item a string is available
 
 906             wxDataObjectComposite* itemObject(new wxDataObjectComposite()); // data object for current item
 
 907             wxString               itemString;                              // contains the TAB concatenated data of an item
 
 910                 wxDataViewItemFromItem([writeItems objectAtIndex:itemCounter]));
 
 911             itemString = ::ConcatenateDataViewItemValues(dvc,event.GetItem());
 
 912             itemObject->Add(new wxTextDataObject(itemString));
 
 913             event.SetDataObject(itemObject);
 
 914             // check if event has not been vetoed:
 
 915             if (dvc->HandleWindowEvent(event) && event.IsAllowed() && (event.GetDataObject()->GetFormatCount() > 0))
 
 917                 size_t const noOfFormats = event.GetDataObject()->GetFormatCount();
 
 918                 wxDataFormat* dataFormats(new wxDataFormat[noOfFormats]);
 
 920                 event.GetDataObject()->GetAllFormats(dataFormats,wxDataObject::Get);
 
 921                 for (size_t formatCounter=0; formatCounter<noOfFormats; ++formatCounter)
 
 923                     // constant definitions for abbreviational purposes:
 
 924                     wxDataFormatId const idDataFormat = dataFormats[formatCounter].GetType();
 
 925                     size_t const dataSize       = event.GetDataObject()->GetDataSize(idDataFormat);
 
 926                     size_t const dataBufferSize = sizeof(wxDataFormatId)+dataSize;
 
 927                     // variable definitions (used in all case statements):
 
 928                     // give additional headroom for trailing NULL
 
 929                     wxMemoryBuffer dataBuffer(dataBufferSize+4);
 
 931                     dataBuffer.AppendData(&idDataFormat,sizeof(wxDataFormatId));
 
 932                     switch (idDataFormat)
 
 935                             // otherwise wxDF_UNICODETEXT already filled up
 
 936                             // the string; and the UNICODE representation has
 
 938                             if (!itemStringAvailable)
 
 940                                 event.GetDataObject()->GetDataHere(wxDF_TEXT,dataBuffer.GetAppendBuf(dataSize));
 
 941                                 dataBuffer.UngetAppendBuf(dataSize);
 
 942                                 [dataArray addObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]];
 
 943                                 itemString = wxString(static_cast<char const*>(dataBuffer.GetData())+sizeof(wxDataFormatId),wxConvLocal);
 
 944                                 itemStringAvailable = true;
 
 947                         case wxDF_UNICODETEXT:
 
 949                                 event.GetDataObject()->GetDataHere(wxDF_UNICODETEXT,dataBuffer.GetAppendBuf(dataSize));
 
 950                                 dataBuffer.UngetAppendBuf(dataSize);
 
 951                                 if (itemStringAvailable) // does an object already exist as an ASCII text (see wxDF_TEXT case statement)?
 
 952                                     [dataArray replaceObjectAtIndex:itemCounter withObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]];
 
 954                                     [dataArray addObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]];
 
 955                                 itemString = wxString::FromUTF8(static_cast<char const*>(dataBuffer.GetData())+sizeof(wxDataFormatId),dataSize);
 
 956                                 itemStringAvailable = true;
 
 960                             wxFAIL_MSG("Data object has invalid or unsupported data format");
 
 964                 delete[] dataFormats;
 
 966                 if (dataStringAvailable)
 
 968                     if (itemStringAvailable)
 
 971                             dataString << wxT('\n');
 
 972                         dataString << itemString;
 
 975                         dataStringAvailable = false;
 
 981                 return NO; // dragging was vetoed or no data available
 
 984         if (dataStringAvailable)
 
 986             wxCFStringRef osxString(dataString);
 
 988             [pasteboard declareTypes:[NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil] owner:nil];
 
 989             [pasteboard setPropertyList:dataArray forType:DataViewPboardType];
 
 990             [pasteboard setString:osxString.AsNSString() forType:NSStringPboardType];
 
 994             [pasteboard declareTypes:[NSArray arrayWithObject:DataViewPboardType] owner:nil];
 
 995             [pasteboard setPropertyList:dataArray forType:DataViewPboardType];
 
1000         return NO; // no items to drag (should never occur)
 
1006 -(void) addToBuffer:(wxPointerObject*)item
 
1008     [items addObject:item];
 
1013     [items removeAllObjects];
 
1016 -(wxPointerObject*) getDataViewItemFromBuffer:(const wxDataViewItem&)item
 
1018     return [items member:[[[wxPointerObject alloc] initWithPointer:item.GetID()] autorelease]];
 
1021 -(wxPointerObject*) getItemFromBuffer:(wxPointerObject*)item
 
1023     return [items member:item];
 
1026 -(BOOL) isInBuffer:(wxPointerObject*)item
 
1028     return [items containsObject:item];
 
1031 -(void) removeFromBuffer:(wxPointerObject*)item
 
1033     [items removeObject:item];
 
1037 // children handling
 
1039 -(void) appendChild:(wxPointerObject*)item
 
1041     [children addObject:item];
 
1044 -(void) clearChildren
 
1046     [children removeAllObjects];
 
1049 -(wxPointerObject*) getChild:(NSUInteger)index
 
1051     return [children objectAtIndex:index];
 
1054 -(NSUInteger) getChildCount
 
1056     return [children count];
 
1059 -(void) removeChild:(NSUInteger)index
 
1061     [children removeObjectAtIndex:index];
 
1067 -(void) clearBuffers
 
1070     [self clearChildren];
 
1071     [self setCurrentParentItem:nil];
 
1077 -(NSArray*) sortDescriptors
 
1079     return sortDescriptors;
 
1082 -(void) setSortDescriptors:(NSArray*)newSortDescriptors
 
1084     [newSortDescriptors retain];
 
1085     [sortDescriptors release];
 
1086     sortDescriptors = newSortDescriptors;
 
1090 // access to wxWidget's implementation
 
1092 -(wxPointerObject*) currentParentItem
 
1094     return currentParentItem;
 
1097 -(wxCocoaDataViewControl*) implementation
 
1099     return implementation;
 
1102 -(wxDataViewModel*) model
 
1107 -(void) setCurrentParentItem:(wxPointerObject*)newCurrentParentItem
 
1109     [newCurrentParentItem retain];
 
1110     [currentParentItem release];
 
1111     currentParentItem = newCurrentParentItem;
 
1114 -(void) setImplementation:(wxCocoaDataViewControl*) newImplementation
 
1116     implementation = newImplementation;
 
1119 -(void) setModel:(wxDataViewModel*) newModel
 
1127 -(void) bufferItem:(wxPointerObject*)parentItem withChildren:(wxDataViewItemArray*)dataViewChildrenPtr
 
1129     NSInteger const noOfChildren = (*dataViewChildrenPtr).GetCount();
 
1131     [self setCurrentParentItem:parentItem];
 
1132     [self clearChildren];
 
1133     for (NSInteger indexChild=0; indexChild<noOfChildren; ++indexChild)
 
1135         wxPointerObject* bufferedPointerObject;
 
1136         wxPointerObject* newPointerObject([[wxPointerObject alloc] initWithPointer:(*dataViewChildrenPtr)[indexChild].GetID()]);
 
1138         // The next statement and test looks strange but there is
 
1139         // unfortunately no workaround: due to the fact that two pointer
 
1140         // objects are identical if their pointers are identical - because the
 
1141         // method isEqual has been overloaded - the set operation will only
 
1142         // add a new pointer object if there is not already one in the set
 
1143         // having the same pointer. On the other side the children's array
 
1144         // would always add the new pointer object. This means that different
 
1145         // pointer objects are stored in the set and array. This will finally
 
1146         // lead to a crash as objects diverge. To solve this issue it is first
 
1147         // tested if the child already exists in the set and if it is the case
 
1148         // the sets object is going to be appended to the array, otheriwse the
 
1149         // new pointer object is added to the set and array:
 
1150         bufferedPointerObject = [self getItemFromBuffer:newPointerObject];
 
1151         if (bufferedPointerObject == nil)
 
1153             [items    addObject:newPointerObject];
 
1154             [children addObject:newPointerObject];
 
1157             [children addObject:bufferedPointerObject];
 
1158         [newPointerObject release];
 
1164 // ============================================================================
 
1166 // ============================================================================
 
1168 @implementation wxCustomCell
 
1170 #if 0 // starting implementation for custom cell clicks
 
1174     self = [super init];
 
1175     [self setAction:@selector(clickedAction)];
 
1176     [self setTarget:self];
 
1180 - (void) clickedAction: (id) sender
 
1182     wxUnusedVar(sender);
 
1189     wxCustomRendererObject * const
 
1190         obj = static_cast<wxCustomRendererObject *>([self objectValue]);
 
1193     const wxSize size = obj->customRenderer->GetSize();
 
1194     return NSMakeSize(size.x, size.y);
 
1200 -(void) drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
 
1202     wxCustomRendererObject * const
 
1203         obj = static_cast<wxCustomRendererObject *>([self objectValue]);
 
1206         // this may happen for the custom cells in container rows: they don't
 
1211     wxDataViewCustomRenderer * const renderer = obj->customRenderer;
 
1213     // if this method is called everything is already setup correctly, 
 
1214     CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
 
1215     CGContextSaveGState( context );
 
1217     if ( ![controlView isFlipped] )
 
1219         CGContextTranslateCTM( context, 0,  [controlView bounds].size.height );
 
1220         CGContextScaleCTM( context, 1, -1 );
 
1224     wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(context);
 
1225     dc.SetGraphicsContext(gc);
 
1228     if ( [self isHighlighted] )
 
1229         state |= wxDATAVIEW_CELL_SELECTED;
 
1231     renderer->WXCallRender(wxFromNSRect(controlView, cellFrame), &dc, state);
 
1233     CGContextRestoreGState( context );
 
1236 -(NSRect) imageRectForBounds:(NSRect)cellFrame
 
1241 -(NSRect) titleRectForBounds:(NSRect)cellFrame
 
1248 // ============================================================================
 
1250 // ============================================================================
 
1251 @implementation wxImageTextCell
 
1257     self = [super init];
 
1260         // initializing the text part:
 
1261         [self setSelectable:YES];
 
1262         // initializing the image part:
 
1264         imageSize   = NSMakeSize(16,16);
 
1265         spaceImageText = 5.0;
 
1271 -(id) copyWithZone:(NSZone*)zone
 
1273     wxImageTextCell* cell;
 
1276     cell = (wxImageTextCell*) [super copyWithZone:zone];
 
1277     cell->image          = [image retain];
 
1278     cell->imageSize      = imageSize;
 
1279     cell->spaceImageText = spaceImageText;
 
1280     cell->xImageShift    = xImageShift;
 
1295 -(NSTextAlignment) alignment
 
1297     return cellAlignment;
 
1300 -(void) setAlignment:(NSTextAlignment)newAlignment
 
1302     cellAlignment = newAlignment;
 
1303     switch (newAlignment)
 
1305         case NSCenterTextAlignment:
 
1306         case NSLeftTextAlignment:
 
1307         case NSJustifiedTextAlignment:
 
1308         case NSNaturalTextAlignment:
 
1309             [super setAlignment:NSLeftTextAlignment];
 
1311         case NSRightTextAlignment:
 
1312             [super setAlignment:NSRightTextAlignment];
 
1315             wxFAIL_MSG("Unknown alignment type.");
 
1327 -(void) setImage:(NSImage*)newImage
 
1339 -(void) setImageSize:(NSSize) newImageSize
 
1341     imageSize = newImageSize;
 
1347 -(NSSize) cellImageSize
 
1349     return NSMakeSize(imageSize.width+xImageShift+spaceImageText,imageSize.height);
 
1354     NSSize cellSize([super cellSize]);
 
1357     if (imageSize.height > cellSize.height)
 
1358         cellSize.height = imageSize.height;
 
1359     cellSize.width += imageSize.width+xImageShift+spaceImageText;
 
1364 -(NSSize) cellTextSize
 
1366     return [super cellSize];
 
1372 -(void) determineCellParts:(NSRect)cellFrame imagePart:(NSRect*)imageFrame textPart:(NSRect*)textFrame
 
1374     switch (cellAlignment)
 
1376         case NSCenterTextAlignment:
 
1378                 CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width;
 
1380                 // if the cell's frame is smaller than its contents (at least
 
1381                 // in x-direction) make sure that the image is visible:
 
1383                     NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge);
 
1384                 else // otherwise center the image and text in the cell's frame
 
1385                     NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText+0.5*cellSpace,NSMinXEdge);
 
1388         case NSJustifiedTextAlignment:
 
1389         case NSLeftTextAlignment:
 
1390         case NSNaturalTextAlignment: // how to determine the natural writing direction? TODO
 
1391             NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge);
 
1393         case NSRightTextAlignment:
 
1395                 CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width;
 
1397                 // if the cell's frame is smaller than its contents (at least
 
1398                 // in x-direction) make sure that the image is visible:
 
1400                     NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge);
 
1401                 else // otherwise right align the image and text in the cell's frame
 
1402                     NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText+cellSpace,NSMinXEdge);
 
1406             *imageFrame = NSZeroRect;
 
1407             *textFrame  = NSZeroRect;
 
1408             wxFAIL_MSG("Unhandled alignment type.");
 
1412 -(void) drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
 
1414     NSRect textFrame, imageFrame;
 
1417     [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
 
1418     // draw the image part by ourselves;
 
1419     // check if the cell has to draw its own background (checking is done by
 
1420     // the parameter of the textfield's cell):
 
1421     if ([self drawsBackground])
 
1423         [[self backgroundColor] set];
 
1424         NSRectFill(imageFrame);
 
1428         // the image is slightly shifted (xImageShift) and has a fixed size
 
1429         // but the image's frame might be larger and starts currently on the
 
1430         // left side of the cell's frame; therefore, the origin and the
 
1431         // image's frame size have to be adjusted:
 
1432         if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText)
 
1434             imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText;
 
1435             imageFrame.size.width = imageSize.width;
 
1439             imageFrame.origin.x   += xImageShift;
 
1440             imageFrame.size.width -= xImageShift+spaceImageText;
 
1442         // ...and the image has to be centered in the y-direction:
 
1443         if (imageFrame.size.height > imageSize.height)
 
1444             imageFrame.size.height = imageSize.height;
 
1445         imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height));
 
1447         // according to the documentation the coordinate system should be
 
1448         // flipped for NSTableViews (y-coordinate goes from top to bottom); to
 
1449         // draw an image correctly the coordinate system has to be transformed
 
1450         // to a bottom-top coordinate system, otherwise the image's
 
1451         // content is flipped:
 
1452         NSAffineTransform* coordinateTransform([NSAffineTransform transform]);
 
1454         if ([controlView isFlipped])
 
1456             [coordinateTransform scaleXBy: 1.0 yBy:-1.0]; // first the coordinate system is brought back to bottom-top orientation
 
1457             [coordinateTransform translateXBy:0.0 yBy:(-2.0)*imageFrame.origin.y-imageFrame.size.height]; // the coordinate system has to be moved to compensate for the
 
1458             [coordinateTransform concat];                                                                 // other orientation and the position of the image's frame
 
1460         [image drawInRect:imageFrame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; // suggested method to draw the image
 
1461         // instead of compositeToPoint:operation:
 
1462         // take back previous transformation (if the view is not flipped the
 
1463         // coordinate transformation matrix contains the identity matrix and
 
1464         // the next two operations do not change the content's transformation
 
1466         [coordinateTransform invert];
 
1467         [coordinateTransform concat];
 
1469     // let the textfield cell draw the text part:
 
1470     if (textFrame.size.width > [self cellTextSize].width)
 
1472         // for unknown reasons the alignment of the text cell is ignored;
 
1473         // therefore change the size so that alignment does not influence the
 
1474         // visualization anymore
 
1475         textFrame.size.width = [self cellTextSize].width;
 
1477     [super drawWithFrame:textFrame inView:controlView];
 
1480 -(void) editWithFrame:(NSRect)aRect inView:(NSView*)controlView editor:(NSText*)textObj delegate:(id)anObject event:(NSEvent*)theEvent
 
1482     NSRect textFrame, imageFrame;
 
1485     [self determineCellParts:aRect imagePart:&imageFrame textPart:&textFrame];
 
1486     [super editWithFrame:textFrame inView:controlView editor:textObj delegate:anObject event:theEvent];
 
1489 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
 
1490 -(NSUInteger) hitTestForEvent:(NSEvent*)event inRect:(NSRect)cellFrame ofView:(NSView*)controlView
 
1492     NSPoint point = [controlView convertPoint:[event locationInWindow] fromView:nil];
 
1494     NSRect imageFrame, textFrame;
 
1497     [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
 
1500         // the image is shifted...
 
1501         if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText)
 
1503             imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText;
 
1504             imageFrame.size.width = imageSize.width;
 
1508             imageFrame.origin.x   += xImageShift;
 
1509             imageFrame.size.width -= xImageShift+spaceImageText;
 
1512         if (imageFrame.size.height > imageSize.height)
 
1513             imageFrame.size.height = imageSize.height;
 
1514         imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height));
 
1515         // If the point is in the image rect, then it is a content hit (see
 
1516         // documentation for hitTestForEvent:inRect:ofView):
 
1517         if (NSMouseInRect(point, imageFrame, [controlView isFlipped]))
 
1518             return NSCellHitContentArea;
 
1520     // if the image was not hit let's try the text part:
 
1521     if (textFrame.size.width > [self cellTextSize].width)
 
1523         // for unknown reasons the alignment of the text cell is ignored;
 
1524         // therefore change the size so that alignment does not influence the
 
1525         // visualization anymore
 
1526         textFrame.size.width = [self cellTextSize].width;
 
1529     return [super hitTestForEvent:event inRect:textFrame ofView:controlView];
 
1533 -(NSRect) imageRectForBounds:(NSRect)cellFrame
 
1535     NSRect textFrame, imageFrame;
 
1538     [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
 
1539     if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText)
 
1541         imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText;
 
1542         imageFrame.size.width = imageSize.width;
 
1546         imageFrame.origin.x   += xImageShift;
 
1547         imageFrame.size.width -= xImageShift+spaceImageText;
 
1550     if (imageFrame.size.height > imageSize.height)
 
1551         imageFrame.size.height = imageSize.height;
 
1552     imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height));
 
1557 -(void) selectWithFrame:(NSRect)aRect inView:(NSView*)controlView editor:(NSText*)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength
 
1559     NSRect textFrame, imageFrame;
 
1562     [self determineCellParts:aRect imagePart:&imageFrame textPart:&textFrame];
 
1563     [super selectWithFrame:textFrame inView:controlView editor:textObj delegate:anObject start:selStart length:selLength];
 
1566 -(NSRect) titleRectForBounds:(NSRect)cellFrame
 
1568     NSRect textFrame, imageFrame;
 
1571     [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
 
1577 // ============================================================================
 
1578 // wxCocoaOutlineView
 
1579 // ============================================================================
 
1580 @implementation wxCocoaOutlineView
 
1583 // initializers / destructor
 
1587     self = [super init];
 
1590         currentlyEditedColumn =
 
1591             currentlyEditedRow = -1;
 
1593         [self registerForDraggedTypes:[NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]];
 
1594         [self setDelegate:self];
 
1595         [self setDoubleAction:@selector(actionDoubleClick:)];
 
1596         [self setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO];
 
1597         [self setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
 
1598         [self setTarget:self];
 
1604 // access to wxWidget's implementation
 
1606 -(wxCocoaDataViewControl*) implementation
 
1608     return implementation;
 
1611 -(void) setImplementation:(wxCocoaDataViewControl*) newImplementation
 
1613     implementation = newImplementation;
 
1619 -(void) actionDoubleClick:(id)sender
 
1621     wxUnusedVar(sender);
 
1623     // actually the documentation (NSTableView 2007-10-31) for doubleAction:
 
1624     // and setDoubleAction: seems to be wrong as this action message is always
 
1625     // sent whether the cell is editable or not
 
1626     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
1628     wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_ACTIVATED,dvc->GetId());
 
1631     event.SetEventObject(dvc);
 
1632     event.SetItem(wxDataViewItemFromItem([self itemAtRow:[self clickedRow]]));
 
1633     dvc->GetEventHandler()->ProcessEvent(event);
 
1640 -(NSMenu*) menuForEvent:(NSEvent*)theEvent
 
1642     wxUnusedVar(theEvent);
 
1644     // this method does not do any special menu event handling but only sends
 
1645     // an event message; therefore, the user has full control if a context
 
1646     // menu should be shown or not
 
1647     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
1649     wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU,dvc->GetId());
 
1651     wxDataViewItemArray selectedItems;
 
1654     event.SetEventObject(dvc);
 
1655     event.SetModel(dvc->GetModel());
 
1656     // get the item information;
 
1657     // theoretically more than one ID can be returned but the event can only
 
1658     // handle one item, therefore only the first item of the array is
 
1660     if (dvc->GetSelections(selectedItems) > 0)
 
1661         event.SetItem(selectedItems[0]);
 
1662     dvc->GetEventHandler()->ProcessEvent(event);
 
1670 -(void) outlineView:(NSOutlineView*)outlineView didClickTableColumn:(NSTableColumn*)tableColumn
 
1672     wxDataViewColumn* const
 
1673         col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]);
 
1675     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
1678         event(wxEVT_DATAVIEW_COLUMN_HEADER_CLICK,dvc->GetId());
 
1681     // first, send an event that the user clicked into a column's header:
 
1682     event.SetEventObject(dvc);
 
1683     event.SetColumn(dvc->GetColumnPosition(col));
 
1684     event.SetDataViewColumn(col);
 
1685     dvc->HandleWindowEvent(event);
 
1687     // now, check if the click may have had an influence on sorting, too;
 
1688     // the sorting setup has to be done only if the clicked table column is
 
1689     // sortable and has not been used for sorting before the click; if the
 
1690     // column is already responsible for sorting the native control changes
 
1691     // the sorting direction automatically and informs the data source via
 
1692     // outlineView:sortDescriptorsDidChange:
 
1693     if (col->IsSortable() && ([tableColumn sortDescriptorPrototype] == nil))
 
1695         // remove the sort order from the previously sorted column table (it
 
1696         // can also be that no sorted column table exists):
 
1697         UInt32 const noOfColumns = [outlineView numberOfColumns];
 
1699         for (UInt32 i=0; i<noOfColumns; ++i)
 
1700             [[[outlineView tableColumns] objectAtIndex:i] setSortDescriptorPrototype:nil];
 
1701         // make column table sortable:
 
1702         NSArray*          sortDescriptors;
 
1703         NSSortDescriptor* sortDescriptor;
 
1705         sortDescriptor = [[NSSortDescriptor alloc] initWithKey:[NSString stringWithFormat:@"%ld",(long)[outlineView columnWithIdentifier:[tableColumn identifier]]]
 
1707         sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
 
1708         [tableColumn setSortDescriptorPrototype:sortDescriptor];
 
1709         [outlineView setSortDescriptors:sortDescriptors];
 
1710         [sortDescriptor release];
 
1714 -(BOOL) outlineView:(NSOutlineView*)outlineView shouldCollapseItem:(id)item
 
1716     wxUnusedVar(outlineView);
 
1718     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
1720     wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_COLLAPSING,dvc->GetId());
 
1723     event.SetEventObject(dvc);
 
1724     event.SetItem       (wxDataViewItemFromItem(item));
 
1725     event.SetModel      (dvc->GetModel());
 
1726     // finally send the equivalent wxWidget event:
 
1727     dvc->GetEventHandler()->ProcessEvent(event);
 
1728     // opening the container is allowed if not vetoed:
 
1729     return event.IsAllowed();
 
1732 -(BOOL) outlineView:(NSOutlineView*)outlineView shouldExpandItem:(id)item
 
1734     wxUnusedVar(outlineView);
 
1736     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
1738     wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_EXPANDING,dvc->GetId());
 
1741     event.SetEventObject(dvc);
 
1742     event.SetItem       (wxDataViewItemFromItem(item));
 
1743     event.SetModel      (dvc->GetModel());
 
1744     // finally send the equivalent wxWidget event:
 
1745     dvc->GetEventHandler()->ProcessEvent(event);
 
1746     // opening the container is allowed if not vetoed:
 
1747     return event.IsAllowed();
 
1750 -(BOOL) outlineView:(NSOutlineView*)outlineView shouldSelectTableColumn:(NSTableColumn*)tableColumn
 
1752     wxUnusedVar(tableColumn);
 
1753     wxUnusedVar(outlineView);
 
1758 -(void) outlineView:(wxCocoaOutlineView*)outlineView
 
1759     willDisplayCell:(id)cell
 
1760     forTableColumn:(NSTableColumn*)tableColumn
 
1763     wxUnusedVar(outlineView);
 
1765     wxDataViewCtrl * const dvc = implementation->GetDataViewCtrl();
 
1766     wxDataViewModel * const model = dvc->GetModel();
 
1768     wxDataViewColumn* const
 
1769         dvCol([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]);
 
1770     const unsigned colIdx = dvCol->GetModelColumn();
 
1772     wxDataViewItem dvItem(wxDataViewItemFromItem(item));
 
1774     if ( !model->HasValue(dvItem, colIdx) )
 
1777     wxDataViewRenderer * const renderer = dvCol->GetRenderer();
 
1778     wxDataViewRendererNativeData * const data = renderer->GetNativeData();
 
1780     // let the renderer know about what it's going to render next
 
1781     data->SetColumnPtr(tableColumn);
 
1782     data->SetItem(item);
 
1783     data->SetItemCell(cell);
 
1785     // use the attributes: notice that we need to do this whether we have them
 
1786     // or not as even if this cell doesn't have any attributes, the previous
 
1787     // one might have had some and then we need to reset them back to default
 
1788     wxDataViewItemAttr attr;
 
1789     model->GetAttr(dvItem, colIdx, attr);
 
1790     renderer->OSXApplyAttr(attr);
 
1792     // set the state (enabled/disabled) of the item
 
1793     renderer->OSXApplyEnabled(model->IsEnabled(dvItem, colIdx));
 
1795     // and finally do draw it
 
1796     renderer->MacRender();
 
1802 -(void) outlineViewColumnDidMove:(NSNotification*)notification
 
1804     int const newColumnPosition = [[[notification userInfo] objectForKey:@"NSNewColumn"] intValue];
 
1807         tableColumn = [[self tableColumns] objectAtIndex:newColumnPosition];
 
1808     wxDataViewColumn* const
 
1809         col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]);
 
1811     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
1813     wxDataViewEvent event(wxEVT_DATAVIEW_COLUMN_REORDERED,dvc->GetId());
 
1816     event.SetEventObject(dvc);
 
1817     event.SetColumn(dvc->GetColumnPosition(col));
 
1818     event.SetDataViewColumn(col);
 
1819     dvc->GetEventHandler()->ProcessEvent(event);
 
1822 -(void) outlineViewItemDidCollapse:(NSNotification*)notification
 
1824     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
1826     wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_COLLAPSED,dvc->GetId());
 
1829     event.SetEventObject(dvc);
 
1830     event.SetItem(wxDataViewItemFromItem(
 
1831                     [[notification userInfo] objectForKey:@"NSObject"]));
 
1832     dvc->GetEventHandler()->ProcessEvent(event);
 
1835 -(void) outlineViewItemDidExpand:(NSNotification*)notification
 
1837     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
1839     wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_EXPANDED,dvc->GetId());
 
1842     event.SetEventObject(dvc);
 
1843     event.SetItem(wxDataViewItemFromItem(
 
1844                     [[notification userInfo] objectForKey:@"NSObject"]));
 
1845     dvc->GetEventHandler()->ProcessEvent(event);
 
1848 -(void) outlineViewSelectionDidChange:(NSNotification*)notification
 
1850     wxUnusedVar(notification);
 
1852     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
1854     wxDataViewEvent event(wxEVT_DATAVIEW_SELECTION_CHANGED,dvc->GetId());
 
1856     event.SetEventObject(dvc);
 
1857     event.SetModel(dvc->GetModel());
 
1858     event.SetItem(dvc->GetSelection());
 
1859     dvc->GetEventHandler()->ProcessEvent(event);
 
1862 -(void) textDidBeginEditing:(NSNotification*)notification
 
1864     // this notification is only sent if the user started modifying the cell
 
1865     // (not when the user clicked into the cell and the cell's editor is
 
1868     // call method of superclass (otherwise editing does not work correctly -
 
1869     // the outline data source class is not informed about a change of data):
 
1870     [super textDidBeginEditing:notification];
 
1872     // remember the column being edited, it will be used in textDidEndEditing:
 
1873     currentlyEditedColumn = [self editedColumn];
 
1874     currentlyEditedRow = [self editedRow];
 
1877         tableColumn = [[self tableColumns] objectAtIndex:currentlyEditedColumn];
 
1878     wxDataViewColumn* const
 
1879         col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]);
 
1881     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
1884     // stop editing of a custom item first (if necessary)
 
1885     dvc->FinishCustomItemEditing();
 
1887     // now, send the event:
 
1889         event(wxEVT_DATAVIEW_ITEM_EDITING_STARTED,dvc->GetId());
 
1891     event.SetEventObject(dvc);
 
1893             wxDataViewItemFromItem([self itemAtRow:currentlyEditedRow]));
 
1894     event.SetColumn(dvc->GetColumnPosition(col));
 
1895     event.SetDataViewColumn(col);
 
1896     dvc->GetEventHandler()->ProcessEvent(event);
 
1899 -(void) textDidEndEditing:(NSNotification*)notification
 
1901     // call method of superclass (otherwise editing does not work correctly -
 
1902     // the outline data source class is not informed about a change of data):
 
1903     [super textDidEndEditing:notification];
 
1905     // under OSX an event indicating the end of an editing session can be sent
 
1906     // even if no event indicating a start of an editing session has been sent
 
1907     // (see Documentation for NSControl controlTextDidEndEditing:); this is
 
1908     // not expected by a user of the wxWidgets library and therefore an
 
1909     // wxEVT_DATAVIEW_ITEM_EDITING_DONE event is only sent if a
 
1910     // corresponding wxEVT_DATAVIEW_ITEM_EDITING_STARTED has been sent
 
1911     // before; to check if a wxEVT_DATAVIEW_ITEM_EDITING_STARTED has
 
1912     // been sent the last edited column/row are valid:
 
1913     if ( currentlyEditedColumn != -1 && currentlyEditedRow != -1 )
 
1916             tableColumn = [[self tableColumns] objectAtIndex:currentlyEditedColumn];
 
1917         wxDataViewColumn* const
 
1918             col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]);
 
1920         wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
1922         // send event to wxWidgets:
 
1924             event(wxEVT_DATAVIEW_ITEM_EDITING_DONE,dvc->GetId());
 
1926         event.SetEventObject(dvc);
 
1928                 wxDataViewItemFromItem([self itemAtRow:currentlyEditedRow]));
 
1929         event.SetColumn(dvc->GetColumnPosition(col));
 
1930         event.SetDataViewColumn(col);
 
1931         dvc->GetEventHandler()->ProcessEvent(event);
 
1934         // we're not editing any more
 
1935         currentlyEditedColumn =
 
1936             currentlyEditedRow = -1;
 
1942 // ============================================================================
 
1943 // wxCocoaDataViewControl
 
1944 // ============================================================================
 
1946 wxCocoaDataViewControl::wxCocoaDataViewControl(wxWindow* peer,
 
1953         [[NSScrollView alloc] initWithFrame:wxOSXGetFrameForControl(peer,pos,size)]
 
1956       m_OutlineView([[wxCocoaOutlineView alloc] init])
 
1958     // initialize scrollview (the outline view is part of a scrollview):
 
1959     NSScrollView* scrollview = (NSScrollView*) GetWXWidget();
 
1961     [scrollview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
 
1962     [scrollview setBorderType:NSNoBorder];
 
1963     [scrollview setHasVerticalScroller:YES];
 
1964     [scrollview setHasHorizontalScroller:YES];
 
1965     [scrollview setAutohidesScrollers:YES];
 
1966     [scrollview setDocumentView:m_OutlineView];
 
1968     // we cannot call InstallHandler(m_OutlineView) here, because we are handling
 
1969     // our action:s ourselves, only associate the view with this impl
 
1970     Associate(m_OutlineView,this);
 
1971     // initialize the native control itself too
 
1972     InitOutlineView(style);
 
1975 void wxCocoaDataViewControl::InitOutlineView(long style)
 
1977     [m_OutlineView setImplementation:this];
 
1978     [m_OutlineView setColumnAutoresizingStyle:NSTableViewSequentialColumnAutoresizingStyle];
 
1979     [m_OutlineView setIndentationPerLevel:GetDataViewCtrl()->GetIndent()];
 
1980     NSUInteger maskGridStyle(NSTableViewGridNone);
 
1981     if (style & wxDV_HORIZ_RULES)
 
1982         maskGridStyle |= NSTableViewSolidHorizontalGridLineMask;
 
1983     if (style & wxDV_VERT_RULES)
 
1984         maskGridStyle |= NSTableViewSolidVerticalGridLineMask;
 
1985     [m_OutlineView setGridStyleMask:maskGridStyle];
 
1986     [m_OutlineView setAllowsMultipleSelection:           (style & wxDV_MULTIPLE)  != 0];
 
1987     [m_OutlineView setUsesAlternatingRowBackgroundColors:(style & wxDV_ROW_LINES) != 0];
 
1989     if ( style & wxDV_NO_HEADER )
 
1990         [m_OutlineView setHeaderView:nil];
 
1993 wxCocoaDataViewControl::~wxCocoaDataViewControl()
 
1995     [m_DataSource  release];
 
1996     [m_OutlineView release];
 
2000 // column related methods (inherited from wxDataViewWidgetImpl)
 
2002 bool wxCocoaDataViewControl::ClearColumns()
 
2004     // as there is a bug in NSOutlineView version (OSX 10.5.6 #6555162) the
 
2005     // columns cannot be deleted if there is an outline column in the view;
 
2006     // therefore, the whole view is deleted and newly constructed:
 
2007     [m_OutlineView release];
 
2008     m_OutlineView = [[wxCocoaOutlineView alloc] init];
 
2009     [((NSScrollView*) GetWXWidget()) setDocumentView:m_OutlineView];
 
2010     [m_OutlineView setDataSource:m_DataSource];
 
2012     InitOutlineView(GetDataViewCtrl()->GetWindowStyle());
 
2017 bool wxCocoaDataViewControl::DeleteColumn(wxDataViewColumn* columnPtr)
 
2019     if ([m_OutlineView outlineTableColumn] == columnPtr->GetNativeData()->GetNativeColumnPtr())
 
2020         [m_OutlineView setOutlineTableColumn:nil]; // due to a bug this does not work
 
2021     [m_OutlineView removeTableColumn:columnPtr->GetNativeData()->GetNativeColumnPtr()]; // due to a confirmed bug #6555162 the deletion does not work for
 
2022     // outline table columns (... and there is no workaround)
 
2023     return (([m_OutlineView columnWithIdentifier:[wxDVCNSTableColumn identifierForColumnPointer:columnPtr]]) == -1);
 
2026 void wxCocoaDataViewControl::DoSetExpanderColumn(const wxDataViewColumn *columnPtr)
 
2028     [m_OutlineView setOutlineTableColumn:columnPtr->GetNativeData()->GetNativeColumnPtr()];
 
2031 wxDataViewColumn* wxCocoaDataViewControl::GetColumn(unsigned int pos) const
 
2033     NSTableColumn* tableColumn = [[m_OutlineView tableColumns] objectAtIndex:pos];
 
2034     return [static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer];
 
2037 int wxCocoaDataViewControl::GetColumnPosition(const wxDataViewColumn *columnPtr) const
 
2039     return [m_OutlineView columnWithIdentifier:[wxDVCNSTableColumn identifierForColumnPointer:columnPtr]];
 
2042 bool wxCocoaDataViewControl::InsertColumn(unsigned int pos, wxDataViewColumn* columnPtr)
 
2044     // create column and set the native data of the dataview column:
 
2045     NSTableColumn *nativeColumn = ::CreateNativeColumn(columnPtr);
 
2046     columnPtr->GetNativeData()->SetNativeColumnPtr(nativeColumn);
 
2047     // as the native control does not allow the insertion of a column at a
 
2048     // specified position the column is first appended and - if necessary -
 
2049     // moved to its final position:
 
2050     [m_OutlineView addTableColumn:nativeColumn];
 
2051     if (pos != static_cast<unsigned int>([m_OutlineView numberOfColumns]-1))
 
2052         [m_OutlineView moveColumn:[m_OutlineView numberOfColumns]-1 toColumn:pos];
 
2054     // set columns width now that it can be computed even for autosized columns:
 
2055     columnPtr->SetWidth(columnPtr->GetWidthVariable());
 
2061 void wxCocoaDataViewControl::FitColumnWidthToContent(unsigned int pos)
 
2063 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
 
2064     const int count = GetCount();
 
2065     NSTableColumn *column = GetColumn(pos)->GetNativeData()->GetNativeColumnPtr();
 
2067     class MaxWidthCalculator
 
2070         MaxWidthCalculator(wxCocoaOutlineView *view,
 
2071                            NSTableColumn *column, unsigned columnIndex)
 
2074               m_column(columnIndex),
 
2077             // account for indentation in the column with expander
 
2078             if ( column == [m_view outlineTableColumn] )
 
2079                 m_indent = [m_view indentationPerLevel];
 
2082         void UpdateWithWidth(int width)
 
2084             m_width = wxMax(m_width, width);
 
2087         void UpdateWithRow(int row)
 
2089             NSCell *cell = [m_view preparedCellAtColumn:m_column row:row];
 
2090             unsigned cellWidth = [cell cellSize].width + 1/*round the float up*/;
 
2093                 cellWidth += m_indent * ([m_view levelForRow:row] + 1);
 
2095             m_width = wxMax(m_width, cellWidth);
 
2098         int GetMaxWidth() const { return m_width; }
 
2102         wxCocoaOutlineView *m_view;
 
2107     MaxWidthCalculator calculator(m_OutlineView, column, pos);
 
2109     if ( [column headerCell] )
 
2111         calculator.UpdateWithWidth([[column headerCell] cellSize].width + 1/*round the float up*/);
 
2114     // The code below deserves some explanation. For very large controls, we
 
2115     // simply can't afford to calculate sizes for all items, it takes too
 
2116     // long. So the best we can do is to check the first and the last N/2
 
2117     // items in the control for some sufficiently large N and calculate best
 
2118     // sizes from that. That can result in the calculated best width being too
 
2119     // small for some outliers, but it's better to get slightly imperfect
 
2120     // result than to wait several seconds after every update. To avoid highly
 
2121     // visible miscalculations, we also include all currently visible items
 
2122     // no matter what.  Finally, the value of N is determined dynamically by
 
2123     // measuring how much time we spent on the determining item widths so far.
 
2126     int top_part_end = count;
 
2127     static const long CALC_TIMEOUT = 20/*ms*/;
 
2128     // don't call wxStopWatch::Time() too often
 
2129     static const unsigned CALC_CHECK_FREQ = 100;
 
2132     // use some hard-coded limit, that's the best we can do without timer
 
2133     int top_part_end = wxMin(500, count);
 
2134 #endif // wxUSE_STOPWATCH/!wxUSE_STOPWATCH
 
2138     for ( row = 0; row < top_part_end; row++ )
 
2141         if ( row % CALC_CHECK_FREQ == CALC_CHECK_FREQ-1 &&
 
2142              timer.Time() > CALC_TIMEOUT )
 
2144 #endif // wxUSE_STOPWATCH
 
2145         calculator.UpdateWithRow(row);
 
2148     // row is the first unmeasured item now; that's our value of N/2
 
2154         // add bottom N/2 items now:
 
2155         const int bottom_part_start = wxMax(row, count - row);
 
2156         for ( row = bottom_part_start; row < count; row++ )
 
2157             calculator.UpdateWithRow(row);
 
2159         // finally, include currently visible items in the calculation:
 
2160         const NSRange visible = [m_OutlineView rowsInRect:[m_OutlineView visibleRect]];
 
2161         const int first_visible = wxMax(visible.location, top_part_end);
 
2162         const int last_visible = wxMin(first_visible + visible.length, bottom_part_start);
 
2164         for ( row = first_visible; row < last_visible; row++ )
 
2165             calculator.UpdateWithRow(row);
 
2167         wxLogTrace("dataview",
 
2168                    "determined best size from %d top, %d bottom plus %d more visible items out of %d total",
 
2170                    count - bottom_part_start,
 
2171                    wxMax(0, last_visible - first_visible),
 
2175     [column setWidth:calculator.GetMaxWidth()];
 
2176 #endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
 
2180 // item related methods (inherited from wxDataViewWidgetImpl)
 
2182 bool wxCocoaDataViewControl::Add(const wxDataViewItem& parent, const wxDataViewItem& WXUNUSED(item))
 
2185         [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:parent] reloadChildren:YES];
 
2187         [m_OutlineView reloadData];
 
2191 bool wxCocoaDataViewControl::Add(const wxDataViewItem& parent, const wxDataViewItemArray& WXUNUSED(items))
 
2194         [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:parent] reloadChildren:YES];
 
2196         [m_OutlineView reloadData];
 
2200 void wxCocoaDataViewControl::Collapse(const wxDataViewItem& item)
 
2202     [m_OutlineView collapseItem:[m_DataSource getDataViewItemFromBuffer:item]];
 
2205 void wxCocoaDataViewControl::EnsureVisible(const wxDataViewItem& item, const wxDataViewColumn *columnPtr)
 
2209         [m_OutlineView scrollRowToVisible:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]];
 
2211             [m_OutlineView scrollColumnToVisible:GetColumnPosition(columnPtr)];
 
2215 void wxCocoaDataViewControl::Expand(const wxDataViewItem& item)
 
2217     [m_OutlineView expandItem:[m_DataSource getDataViewItemFromBuffer:item]];
 
2220 unsigned int wxCocoaDataViewControl::GetCount() const
 
2222     return [m_OutlineView numberOfRows];
 
2225 wxRect wxCocoaDataViewControl::GetRectangle(const wxDataViewItem& item, const wxDataViewColumn *columnPtr)
 
2227     return wxFromNSRect([m_osxView superview],[m_OutlineView frameOfCellAtColumn:GetColumnPosition(columnPtr)
 
2228             row:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]]);
 
2231 bool wxCocoaDataViewControl::IsExpanded(const wxDataViewItem& item) const
 
2233     return [m_OutlineView isItemExpanded:[m_DataSource getDataViewItemFromBuffer:item]];
 
2236 bool wxCocoaDataViewControl::Reload()
 
2238     [m_DataSource clearBuffers];
 
2239     [m_OutlineView reloadData];
 
2240     [m_OutlineView scrollColumnToVisible:0];
 
2241     [m_OutlineView scrollRowToVisible:0];
 
2245 bool wxCocoaDataViewControl::Remove(const wxDataViewItem& parent, const wxDataViewItem& WXUNUSED(item))
 
2248         [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:parent] reloadChildren:YES];
 
2250         [m_OutlineView reloadData];
 
2254 bool wxCocoaDataViewControl::Remove(const wxDataViewItem& parent, const wxDataViewItemArray& WXUNUSED(item))
 
2257         [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:parent] reloadChildren:YES];
 
2259         [m_OutlineView reloadData];
 
2263 bool wxCocoaDataViewControl::Update(const wxDataViewColumn *columnPtr)
 
2265     wxUnusedVar(columnPtr);
 
2270 bool wxCocoaDataViewControl::Update(const wxDataViewItem& WXUNUSED(parent), const wxDataViewItem& item)
 
2272     [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:item]];
 
2276 bool wxCocoaDataViewControl::Update(const wxDataViewItem& WXUNUSED(parent), const wxDataViewItemArray& items)
 
2278     for (size_t i=0; i<items.GetCount(); ++i)
 
2279         [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:items[i]]];
 
2284 // model related methods
 
2286 bool wxCocoaDataViewControl::AssociateModel(wxDataViewModel* model)
 
2288     [m_DataSource release];
 
2291         m_DataSource = [[wxCocoaOutlineDataSource alloc] init];
 
2292         [m_DataSource setImplementation:this];
 
2293         [m_DataSource setModel:model];
 
2296         m_DataSource = NULL;
 
2297     [m_OutlineView setDataSource:m_DataSource]; // if there is a data source the data is immediately going to be requested
 
2302 // selection related methods (inherited from wxDataViewWidgetImpl)
 
2305 wxDataViewItem wxCocoaDataViewControl::GetCurrentItem() const
 
2307     return wxDataViewItem([[m_OutlineView itemAtRow:[m_OutlineView selectedRow]] pointer]);
 
2310 wxDataViewColumn *wxCocoaDataViewControl::GetCurrentColumn() const
 
2312     int col = [m_OutlineView selectedColumn];
 
2315     return GetColumn(col);
 
2318 void wxCocoaDataViewControl::SetCurrentItem(const wxDataViewItem& item)
 
2320     // We can't have unselected current item in a NSTableView, as the
 
2321     // documentation of its deselectRow method explains, the control will
 
2322     // automatically change the current item to the closest still selected item
 
2323     // if the current item is deselected. So we have no choice but to select
 
2324     // the item in the same time as making it current.
 
2328 int wxCocoaDataViewControl::GetSelectedItemsCount() const
 
2330     return [m_OutlineView numberOfSelectedRows];
 
2333 int wxCocoaDataViewControl::GetSelections(wxDataViewItemArray& sel) const
 
2335     NSIndexSet* selectedRowIndexes([m_OutlineView selectedRowIndexes]);
 
2337     NSUInteger indexRow;
 
2341     sel.Alloc([selectedRowIndexes count]);
 
2342     indexRow = [selectedRowIndexes firstIndex];
 
2343     while (indexRow != NSNotFound)
 
2345         sel.Add(wxDataViewItem([[m_OutlineView itemAtRow:indexRow] pointer]));
 
2346         indexRow = [selectedRowIndexes indexGreaterThanIndex:indexRow];
 
2348     return sel.GetCount();
 
2351 bool wxCocoaDataViewControl::IsSelected(const wxDataViewItem& item) const
 
2353     return [m_OutlineView isRowSelected:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]];
 
2356 void wxCocoaDataViewControl::Select(const wxDataViewItem& item)
 
2359         [m_OutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]]
 
2360             byExtendingSelection:GetDataViewCtrl()->HasFlag(wxDV_MULTIPLE) ? YES : NO];
 
2363 void wxCocoaDataViewControl::SelectAll()
 
2365     [m_OutlineView selectAll:m_OutlineView];
 
2368 void wxCocoaDataViewControl::Unselect(const wxDataViewItem& item)
 
2371         [m_OutlineView deselectRow:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]];
 
2374 void wxCocoaDataViewControl::UnselectAll()
 
2376     [m_OutlineView deselectAll:m_OutlineView];
 
2380 // sorting related methods
 
2382 wxDataViewColumn* wxCocoaDataViewControl::GetSortingColumn() const
 
2384     NSArray* const columns = [m_OutlineView tableColumns];
 
2386     UInt32 const noOfColumns = [columns count];
 
2389     for (UInt32 i=0; i<noOfColumns; ++i)
 
2390         if ([[columns objectAtIndex:i] sortDescriptorPrototype] != nil)
 
2391             return GetColumn(i);
 
2395 void wxCocoaDataViewControl::Resort()
 
2397     [m_DataSource clearChildren];
 
2398     [m_OutlineView reloadData];
 
2401 void wxCocoaDataViewControl::StartEditor( const wxDataViewItem & item, unsigned int column )
 
2403     [m_OutlineView editColumn:column row:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]] withEvent:nil select:YES];
 
2407 // other methods (inherited from wxDataViewWidgetImpl)
 
2409 void wxCocoaDataViewControl::DoSetIndent(int indent)
 
2411     [m_OutlineView setIndentationPerLevel:static_cast<CGFloat>(indent)];
 
2414 void wxCocoaDataViewControl::HitTest(const wxPoint& point, wxDataViewItem& item, wxDataViewColumn*& columnPtr) const
 
2416     NSPoint const nativePoint = wxToNSPoint((NSScrollView*) GetWXWidget(),point);
 
2422     indexColumn = [m_OutlineView columnAtPoint:nativePoint];
 
2423     indexRow    = [m_OutlineView rowAtPoint:   nativePoint];
 
2424     if ((indexColumn >= 0) && (indexRow >= 0))
 
2426         columnPtr = GetColumn(indexColumn);
 
2427         item      = wxDataViewItem([[m_OutlineView itemAtRow:indexRow] pointer]);
 
2432         item      = wxDataViewItem();
 
2436 void wxCocoaDataViewControl::SetRowHeight(const wxDataViewItem& WXUNUSED(item), unsigned int WXUNUSED(height))
 
2437     // Not supported by the native control
 
2441 void wxCocoaDataViewControl::OnSize()
 
2443     if ([m_OutlineView numberOfColumns] == 1)
 
2444         [m_OutlineView sizeLastColumnToFit];
 
2448 // drag & drop helper methods
 
2450 wxDataFormat wxCocoaDataViewControl::GetDnDDataFormat(wxDataObjectComposite* dataObjects)
 
2452     wxDataFormat resultFormat;
 
2454         return resultFormat;
 
2456     bool compatible(true);
 
2458     size_t const noOfFormats = dataObjects->GetFormatCount();
 
2461     wxDataFormat* formats;
 
2463     // get all formats and check afterwards if the formats are compatible; if
 
2464     // they are compatible the preferred format is returned otherwise
 
2465     // wxDF_INVALID is returned;
 
2466     // currently compatible types (ordered by priority are):
 
2467     //  - wxDF_UNICODETEXT - wxDF_TEXT
 
2468     formats = new wxDataFormat[noOfFormats];
 
2469     dataObjects->GetAllFormats(formats);
 
2471     while ((indexFormat < noOfFormats) && compatible)
 
2473         switch (resultFormat.GetType())
 
2476                 resultFormat.SetType(formats[indexFormat].GetType()); // first format (should only be reached if indexFormat == 0)
 
2479                 if (formats[indexFormat].GetType() == wxDF_UNICODETEXT)
 
2480                     resultFormat.SetType(wxDF_UNICODETEXT);
 
2481                 else // incompatible
 
2483                     resultFormat.SetType(wxDF_INVALID);
 
2487             case wxDF_UNICODETEXT:
 
2488                 if (formats[indexFormat].GetType() != wxDF_TEXT)
 
2490                     resultFormat.SetType(wxDF_INVALID);
 
2495                 resultFormat.SetType(wxDF_INVALID); // not (yet) supported format
 
2503     return resultFormat;
 
2506 wxDataObjectComposite* wxCocoaDataViewControl::GetDnDDataObjects(NSData* dataObject) const
 
2508     wxDataFormatId dataFormatID;
 
2511     [dataObject getBytes:&dataFormatID length:sizeof(wxDataFormatId)];
 
2512     switch (dataFormatID)
 
2515         case wxDF_UNICODETEXT:
 
2517                 wxTextDataObject* textDataObject(new wxTextDataObject());
 
2519                 if (textDataObject->SetData(wxDataFormat(dataFormatID),[dataObject length]-sizeof(wxDataFormatId),static_cast<char const*>([dataObject bytes])+sizeof(wxDataFormatId)))
 
2521                     wxDataObjectComposite* dataObjectComposite(new wxDataObjectComposite());
 
2523                     dataObjectComposite->Add(textDataObject);
 
2524                     return dataObjectComposite;
 
2528                     delete textDataObject;
 
2538 id wxCocoaDataViewControl::GetItemAtRow(int row) const
 
2540     return [m_OutlineView itemAtRow:row];
 
2543 // ----------------------------------------------------------------------------
 
2544 // wxDataViewRendererNativeData
 
2545 // ----------------------------------------------------------------------------
 
2547 void wxDataViewRendererNativeData::Init()
 
2550     m_origTextColour = NULL;
 
2551     m_ellipsizeMode = wxELLIPSIZE_MIDDLE;
 
2554         ApplyLineBreakMode(m_ColumnCell);
 
2557 void wxDataViewRendererNativeData::ApplyLineBreakMode(NSCell *cell)
 
2559     NSLineBreakMode nsMode = NSLineBreakByWordWrapping;
 
2560     switch ( m_ellipsizeMode )
 
2562         case wxELLIPSIZE_NONE:
 
2563             nsMode = NSLineBreakByClipping;
 
2566         case wxELLIPSIZE_START:
 
2567             nsMode = NSLineBreakByTruncatingHead;
 
2570         case wxELLIPSIZE_MIDDLE:
 
2571             nsMode = NSLineBreakByTruncatingMiddle;
 
2574         case wxELLIPSIZE_END:
 
2575             nsMode = NSLineBreakByTruncatingTail;
 
2579     wxASSERT_MSG( nsMode != NSLineBreakByWordWrapping, "unknown wxEllipsizeMode" );
 
2581     [cell setLineBreakMode: nsMode];
 
2584 // ---------------------------------------------------------
 
2585 // wxDataViewRenderer
 
2586 // ---------------------------------------------------------
 
2588 wxDataViewRenderer::wxDataViewRenderer(const wxString& varianttype,
 
2589                                        wxDataViewCellMode mode,
 
2591     : wxDataViewRendererBase(varianttype, mode, align),
 
2594       m_NativeDataPtr(NULL)
 
2598 wxDataViewRenderer::~wxDataViewRenderer()
 
2600     delete m_NativeDataPtr;
 
2603 void wxDataViewRenderer::SetAlignment(int align)
 
2605     m_alignment = align;
 
2606     [GetNativeData()->GetColumnCell() setAlignment:ConvertToNativeHorizontalTextAlignment(align)];
 
2609 void wxDataViewRenderer::SetMode(wxDataViewCellMode mode)
 
2613         [GetOwner()->GetNativeData()->GetNativeColumnPtr() setEditable:(mode == wxDATAVIEW_CELL_EDITABLE)];
 
2616 void wxDataViewRenderer::SetNativeData(wxDataViewRendererNativeData* newNativeDataPtr)
 
2618     delete m_NativeDataPtr;
 
2619     m_NativeDataPtr = newNativeDataPtr;
 
2622 void wxDataViewRenderer::EnableEllipsize(wxEllipsizeMode mode)
 
2624     // we need to store this value to apply it to the columns headerCell in
 
2625     // CreateNativeColumn()
 
2626     GetNativeData()->SetEllipsizeMode(mode);
 
2628     // but we may already apply it to the column cell which will be used for
 
2630     GetNativeData()->ApplyLineBreakMode(GetNativeData()->GetColumnCell());
 
2633 wxEllipsizeMode wxDataViewRenderer::GetEllipsizeMode() const
 
2635     return GetNativeData()->GetEllipsizeMode();
 
2639 wxDataViewRenderer::OSXOnCellChanged(NSObject *object,
 
2640                                      const wxDataViewItem& item,
 
2643     // TODO: This code should really be removed and this function be made pure
 
2644     //       virtual. We just need to decide what to do with custom renderers
 
2645     //       (i.e. wxDataViewCustomRenderer), currently OS X "in place" editing
 
2646     //       which doesn't really create an editor control is not compatible
 
2647     //       with the in place editing under other platforms.
 
2650     if ( [object isKindOfClass:[NSString class]] )
 
2651         value = ObjectToString(object);
 
2652     else if ( [object isKindOfClass:[NSNumber class]] )
 
2653         value = ObjectToLong(object);
 
2654     else if ( [object isKindOfClass:[NSDate class]] )
 
2655         value = ObjectToDate(object);
 
2658         wxFAIL_MSG( wxString::Format
 
2660                      "unknown value type %s",
 
2661                      wxCFStringRef::AsString([object className])
 
2666     wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
 
2667     model->ChangeValue(value, item, col);
 
2670 void wxDataViewRenderer::OSXApplyAttr(const wxDataViewItemAttr& attr)
 
2672     wxDataViewRendererNativeData * const data = GetNativeData();
 
2673     NSCell * const cell = data->GetItemCell();
 
2675     // set the font and text colour to use: we need to do it if we had ever
 
2676     // changed them before, even if this item itself doesn't have any special
 
2677     // attributes as otherwise it would reuse the attributes from the previous
 
2678     // cell rendered using the same renderer
 
2679     NSFont *font = NULL;
 
2680     NSColor *colText = NULL;
 
2682     if ( attr.HasFont() )
 
2684         font = data->GetOriginalFont();
 
2687             // this is the first time we're setting the font, remember the
 
2688             // original one before changing it
 
2690             data->SaveOriginalFont(font);
 
2695             // FIXME: using wxFont methods here doesn't work for some reason
 
2696             NSFontManager * const fm = [NSFontManager sharedFontManager];
 
2697             if ( attr.GetBold() )
 
2698                 font = [fm convertFont:font toHaveTrait:NSBoldFontMask];
 
2699             if ( attr.GetItalic() )
 
2700                 font = [fm convertFont:font toHaveTrait:NSItalicFontMask];
 
2702         //else: can't change font if the cell doesn't have any
 
2705     if ( attr.HasColour() )
 
2707         // we can set font for any cell but only NSTextFieldCell provides
 
2708         // a method for setting text colour so check that this method is
 
2709         // available before using it
 
2710         if ( [cell respondsToSelector:@selector(setTextColor:)] &&
 
2711                 [cell respondsToSelector:@selector(textColor)] )
 
2713             if ( !data->GetOriginalTextColour() )
 
2715                 // the cast to (untyped) id is safe because of the check above
 
2716                 data->SaveOriginalTextColour([(id)cell textColor]);
 
2719             const wxColour& c = attr.GetColour();
 
2720             colText = [NSColor colorWithCalibratedRed:c.Red() / 255.
 
2721                 green:c.Green() / 255.
 
2722                 blue:c.Blue() / 255.
 
2723                 alpha:c.Alpha() / 255.];
 
2728         font = data->GetOriginalFont();
 
2730         colText = data->GetOriginalTextColour();
 
2733         [cell setFont:font];
 
2736         [(id)cell setTextColor:colText];
 
2739 void wxDataViewRenderer::OSXApplyEnabled(bool enabled)
 
2741     [GetNativeData()->GetItemCell() setEnabled:enabled];
 
2744 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer,wxDataViewRendererBase)
 
2746 // ---------------------------------------------------------
 
2747 // wxDataViewCustomRenderer
 
2748 // ---------------------------------------------------------
 
2749 wxDataViewCustomRenderer::wxDataViewCustomRenderer(const wxString& varianttype,
 
2750                                                    wxDataViewCellMode mode,
 
2752     : wxDataViewCustomRendererBase(varianttype, mode, align),
 
2753       m_editorCtrlPtr(NULL),
 
2756     SetNativeData(new wxDataViewRendererNativeData([[wxCustomCell alloc] init]));
 
2759 bool wxDataViewCustomRenderer::MacRender()
 
2761     [GetNativeData()->GetItemCell() setObjectValue:[[[wxCustomRendererObject alloc] initWithRenderer:this] autorelease]];
 
2765 void wxDataViewCustomRenderer::OSXApplyAttr(const wxDataViewItemAttr& attr)
 
2767     // simply save the attribute so that it could be reused from our Render()
 
2770     // it's not necessary to call the base class version which sets the cell
 
2771     // properties to correspond to this attribute because we currently don't
 
2772     // use any NSCell methods in custom renderers anyhow but if we ever start
 
2773     // doing this (e.g. override RenderText() here to use NSTextFieldCell
 
2774     // methods), then we should pass it on to wxDataViewRenderer here
 
2777 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
 
2779 // ---------------------------------------------------------
 
2780 // wxDataViewTextRenderer
 
2781 // ---------------------------------------------------------
 
2782 wxDataViewTextRenderer::wxDataViewTextRenderer(const wxString& varianttype,
 
2783                                                wxDataViewCellMode mode,
 
2785     : wxDataViewRenderer(varianttype,mode,align)
 
2787     NSTextFieldCell* cell;
 
2790     cell = [[NSTextFieldCell alloc] init];
 
2791     [cell setAlignment:ConvertToNativeHorizontalTextAlignment(align)];
 
2792     SetNativeData(new wxDataViewRendererNativeData(cell));
 
2796 bool wxDataViewTextRenderer::MacRender()
 
2798     if (GetValue().GetType() == GetVariantType())
 
2800         [GetNativeData()->GetItemCell() setObjectValue:wxCFStringRef(GetValue().GetString()).AsNSString()];
 
2805         wxFAIL_MSG(wxString("Text renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType());
 
2811 wxDataViewTextRenderer::OSXOnCellChanged(NSObject *value,
 
2812                                          const wxDataViewItem& item,
 
2815     wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
 
2816     model->ChangeValue(ObjectToString(value), item, col);
 
2819 IMPLEMENT_CLASS(wxDataViewTextRenderer,wxDataViewRenderer)
 
2821 // ---------------------------------------------------------
 
2822 // wxDataViewBitmapRenderer
 
2823 // ---------------------------------------------------------
 
2824 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer(const wxString& varianttype,
 
2825                                                    wxDataViewCellMode mode,
 
2827     : wxDataViewRenderer(varianttype,mode,align)
 
2832     cell = [[NSImageCell alloc] init];
 
2833     SetNativeData(new wxDataViewRendererNativeData(cell));
 
2837 // This method returns 'true' if
 
2838 //  - the passed bitmap is valid and it could be assigned to the native data
 
2840 //  - the passed bitmap is invalid (or is not initialized); this case
 
2841 //  simulates a non-existing bitmap.
 
2842 // In all other cases the method returns 'false'.
 
2843 bool wxDataViewBitmapRenderer::MacRender()
 
2845     wxCHECK_MSG(GetValue().GetType() == GetVariantType(),false,wxString("Bitmap renderer cannot render value; value type: ") << GetValue().GetType());
 
2849     bitmap << GetValue();
 
2851         [GetNativeData()->GetItemCell() setObjectValue:[[bitmap.GetNSImage() retain] autorelease]];
 
2855 IMPLEMENT_CLASS(wxDataViewBitmapRenderer,wxDataViewRenderer)
 
2857 // -------------------------------------
 
2858 // wxDataViewChoiceRenderer
 
2859 // -------------------------------------
 
2860 wxDataViewChoiceRenderer::wxDataViewChoiceRenderer(const wxArrayString& choices,
 
2861                                                    wxDataViewCellMode mode,
 
2863     : wxDataViewRenderer(wxT("string"), mode, alignment),
 
2866     NSPopUpButtonCell* cell;
 
2869     cell = [[NSPopUpButtonCell alloc] init];
 
2870     [cell setControlSize:NSMiniControlSize];
 
2871     [cell setFont:[NSFont fontWithName:[[cell font] fontName] size:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]];
 
2872     for (size_t i=0; i<choices.GetCount(); ++i)
 
2873         [cell addItemWithTitle:wxCFStringRef(choices[i]).AsNSString()];
 
2874     SetNativeData(new wxDataViewRendererNativeData(cell));
 
2879 wxDataViewChoiceRenderer::OSXOnCellChanged(NSObject *value,
 
2880                                            const wxDataViewItem& item,
 
2883     // At least under OS X 10.7 we get the index of the item selected and not
 
2885     wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
 
2886     model->ChangeValue(GetChoice(ObjectToLong(value)), item, col);
 
2889 bool wxDataViewChoiceRenderer::MacRender()
 
2891     if (GetValue().GetType() == GetVariantType())
 
2893         [((NSPopUpButtonCell*) GetNativeData()->GetItemCell()) selectItemWithTitle:[[wxCFStringRef(GetValue().GetString()).AsNSString() retain] autorelease]];
 
2898         wxFAIL_MSG(wxString("Choice renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType());
 
2903 IMPLEMENT_CLASS(wxDataViewChoiceRenderer,wxDataViewRenderer)
 
2905 // ---------------------------------------------------------
 
2906 // wxDataViewDateRenderer
 
2907 // ---------------------------------------------------------
 
2909 wxDataViewDateRenderer::wxDataViewDateRenderer(const wxString& varianttype,
 
2910                                                wxDataViewCellMode mode,
 
2912     : wxDataViewRenderer(varianttype,mode,align)
 
2914     NSTextFieldCell* cell;
 
2916     NSDateFormatter* dateFormatter;
 
2919     dateFormatter = [[NSDateFormatter alloc] init];
 
2920     [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
 
2921     [dateFormatter setDateStyle:NSDateFormatterShortStyle];
 
2922     cell = [[NSTextFieldCell alloc] init];
 
2923     [cell setFormatter:dateFormatter];
 
2924     SetNativeData(new wxDataViewRendererNativeData(cell,[NSDate dateWithString:@"2000-12-30 20:00:00 +0000"]));
 
2926     [dateFormatter release];
 
2929 bool wxDataViewDateRenderer::MacRender()
 
2931     if (GetValue().GetType() == GetVariantType())
 
2933         if (GetValue().GetDateTime().IsValid())
 
2935             // -- find best fitting style to show the date --
 
2936             // as the style should be identical for all cells a reference date
 
2937             // instead of the actual cell's date value is used for all cells;
 
2938             // this reference date is stored in the renderer's native data
 
2939             // section for speed purposes; otherwise, the reference date's
 
2940             // string has to be recalculated for each item that may become
 
2941             // timewise long if a lot of rows using dates exist; the algorithm
 
2942             // has the preference to display as much information as possible
 
2943             // in the first instance; but as this is often impossible due to
 
2944             // space restrictions the style is shortened per loop; finally, if
 
2945             // the shortest time and date format does not fit into the cell
 
2946             // the time part is dropped; remark: the time part itself is not
 
2947             // modified per iteration loop and only uses the short style,
 
2948             // means that only the hours and minutes are being shown
 
2950             // GetObject() returns a date for testing the size of a date object
 
2951             [GetNativeData()->GetItemCell() setObjectValue:GetNativeData()->GetObject()];
 
2952             [[GetNativeData()->GetItemCell() formatter] setTimeStyle:NSDateFormatterShortStyle];
 
2953             for (int dateFormatterStyle=4; dateFormatterStyle>0; --dateFormatterStyle)
 
2955                 [[GetNativeData()->GetItemCell() formatter] setDateStyle:(NSDateFormatterStyle)dateFormatterStyle];
 
2956                 if (dateFormatterStyle == 1)
 
2958                     // if the shortest style for displaying the date and time
 
2959                     // is too long to be fully visible remove the time part of
 
2961                     if ([GetNativeData()->GetItemCell() cellSize].width > [GetNativeData()->GetColumnPtr() width])
 
2962                         [[GetNativeData()->GetItemCell() formatter] setTimeStyle:NSDateFormatterNoStyle];
 
2964                         // basically not necessary as the loop would end anyway
 
2965                         // but let's save the last comparison
 
2969                 else if ([GetNativeData()->GetItemCell() cellSize].width <= [GetNativeData()->GetColumnPtr() width])
 
2972             // set data (the style is set by the previous loop); on OSX the
 
2973             // date has to be specified with respect to UTC; in wxWidgets the
 
2974             // date is always entered in the local timezone; so, we have to do
 
2975             // a conversion from the local to UTC timezone when adding the
 
2976             // seconds to 1970-01-01 UTC:
 
2977             [GetNativeData()->GetItemCell() setObjectValue:[NSDate dateWithTimeIntervalSince1970:GetValue().GetDateTime().ToUTC().Subtract(wxDateTime(1,wxDateTime::Jan,1970)).GetSeconds().ToDouble()]];
 
2983         wxFAIL_MSG(wxString("Date renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType());
 
2989 wxDataViewDateRenderer::OSXOnCellChanged(NSObject *value,
 
2990                                          const wxDataViewItem& item,
 
2993     wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
 
2994     model->ChangeValue(ObjectToDate(value), item, col);
 
2997 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer,wxDataViewRenderer)
 
2999 // ---------------------------------------------------------
 
3000 // wxDataViewIconTextRenderer
 
3001 // ---------------------------------------------------------
 
3002 wxDataViewIconTextRenderer::wxDataViewIconTextRenderer(const wxString& varianttype,
 
3003                                                        wxDataViewCellMode mode,
 
3005      : wxDataViewRenderer(varianttype,mode)
 
3007     wxImageTextCell* cell;
 
3010     cell = [[wxImageTextCell alloc] init];
 
3011     [cell setAlignment:ConvertToNativeHorizontalTextAlignment(align)];
 
3012     SetNativeData(new wxDataViewRendererNativeData(cell));
 
3016 bool wxDataViewIconTextRenderer::MacRender()
 
3018     if (GetValue().GetType() == GetVariantType())
 
3020         wxDataViewIconText iconText;
 
3022         wxImageTextCell* cell;
 
3024         cell = (wxImageTextCell*) GetNativeData()->GetItemCell();
 
3025         iconText << GetValue();
 
3026         if (iconText.GetIcon().IsOk())
 
3027             [cell setImage:[[wxBitmap(iconText.GetIcon()).GetNSImage() retain] autorelease]];
 
3029             [cell setImage:nil];
 
3030         [cell setStringValue:[[wxCFStringRef(iconText.GetText()).AsNSString() retain] autorelease]];
 
3035         wxFAIL_MSG(wxString("Icon & text renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType());
 
3041 wxDataViewIconTextRenderer::OSXOnCellChanged(NSObject *value,
 
3042                                              const wxDataViewItem& item,
 
3045     wxVariant valueIconText;
 
3046     valueIconText << wxDataViewIconText(ObjectToString(value));
 
3048     wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
 
3049     model->ChangeValue(valueIconText, item, col);
 
3052 IMPLEMENT_ABSTRACT_CLASS(wxDataViewIconTextRenderer,wxDataViewRenderer)
 
3054 // ---------------------------------------------------------
 
3055 // wxDataViewToggleRenderer
 
3056 // ---------------------------------------------------------
 
3057 wxDataViewToggleRenderer::wxDataViewToggleRenderer(const wxString& varianttype,
 
3058                                                    wxDataViewCellMode mode,
 
3060     : wxDataViewRenderer(varianttype,mode)
 
3065     cell = [[NSButtonCell alloc] init];
 
3066     [cell setAlignment:ConvertToNativeHorizontalTextAlignment(align)];
 
3067     [cell setButtonType:NSSwitchButton];
 
3068     [cell setImagePosition:NSImageOnly];
 
3069     SetNativeData(new wxDataViewRendererNativeData(cell));
 
3073 bool wxDataViewToggleRenderer::MacRender()
 
3075     if (GetValue().GetType() == GetVariantType())
 
3077         [GetNativeData()->GetItemCell() setIntValue:GetValue().GetLong()];
 
3082         wxFAIL_MSG(wxString("Toggle renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType());
 
3088 wxDataViewToggleRenderer::OSXOnCellChanged(NSObject *value,
 
3089                                            const wxDataViewItem& item,
 
3092     wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
 
3093     model->ChangeValue(ObjectToBool(value), item, col);
 
3096 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer,wxDataViewRenderer)
 
3098 // ---------------------------------------------------------
 
3099 // wxDataViewProgressRenderer
 
3100 // ---------------------------------------------------------
 
3101 wxDataViewProgressRenderer::wxDataViewProgressRenderer(const wxString& label,
 
3102                                                        const wxString& varianttype,
 
3103                                                        wxDataViewCellMode mode,
 
3105     : wxDataViewRenderer(varianttype,mode,align)
 
3109     NSLevelIndicatorCell* cell;
 
3111     cell = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle];
 
3112     [cell setMinValue:0];
 
3113     [cell setMaxValue:100];
 
3114     SetNativeData(new wxDataViewRendererNativeData(cell));
 
3118 bool wxDataViewProgressRenderer::MacRender()
 
3120     if (GetValue().GetType() == GetVariantType())
 
3122         [GetNativeData()->GetItemCell() setIntValue:GetValue().GetLong()];
 
3127         wxFAIL_MSG(wxString("Progress renderer cannot render value because of wrong value type; value type: ") << GetValue().GetType());
 
3133 wxDataViewProgressRenderer::OSXOnCellChanged(NSObject *value,
 
3134                                              const wxDataViewItem& item,
 
3137     wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
 
3138     model->ChangeValue(ObjectToLong(value), item, col);
 
3141 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer,wxDataViewRenderer)
 
3143 // ---------------------------------------------------------
 
3145 // ---------------------------------------------------------
 
3147 wxDataViewColumn::wxDataViewColumn(const wxString& title,
 
3148                                    wxDataViewRenderer* renderer,
 
3149                                    unsigned int model_column,
 
3153      : wxDataViewColumnBase(renderer, model_column),
 
3154        m_NativeDataPtr(new wxDataViewColumnNativeData()),
 
3157     InitCommon(width, align, flags);
 
3158     if (renderer && !renderer->IsCustomRenderer() &&
 
3159         (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT))
 
3160         renderer->SetAlignment(align);
 
3163 wxDataViewColumn::wxDataViewColumn(const wxBitmap& bitmap,
 
3164                                    wxDataViewRenderer* renderer,
 
3165                                    unsigned int model_column,
 
3169     : wxDataViewColumnBase(bitmap, renderer, model_column),
 
3170       m_NativeDataPtr(new wxDataViewColumnNativeData())
 
3172     InitCommon(width, align, flags);
 
3173     if (renderer && !renderer->IsCustomRenderer() &&
 
3174         (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT))
 
3175         renderer->SetAlignment(align);
 
3178 wxDataViewColumn::~wxDataViewColumn()
 
3180     delete m_NativeDataPtr;
 
3183 int wxDataViewColumn::GetWidth() const
 
3185     return [m_NativeDataPtr->GetNativeColumnPtr() width];
 
3188 bool wxDataViewColumn::IsSortKey() const
 
3190     NSTableColumn *nsCol = GetNativeData()->GetNativeColumnPtr();
 
3191     return nsCol && ([nsCol sortDescriptorPrototype] != nil);
 
3194 void wxDataViewColumn::SetAlignment(wxAlignment align)
 
3196     m_alignment = align;
 
3197     [[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setAlignment:ConvertToNativeHorizontalTextAlignment(align)];
 
3198     if (m_renderer && !m_renderer->IsCustomRenderer() &&
 
3199         (m_renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT))
 
3200         m_renderer->SetAlignment(align);
 
3203 void wxDataViewColumn::SetBitmap(const wxBitmap& bitmap)
 
3205     // bitmaps and titles cannot exist at the same time - if the bitmap is set
 
3206     // the title is removed:
 
3207     m_title = wxEmptyString;
 
3208     wxDataViewColumnBase::SetBitmap(bitmap);
 
3209     [[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setImage:[[bitmap.GetNSImage() retain] autorelease]];
 
3212 void wxDataViewColumn::SetMaxWidth(int maxWidth)
 
3214     m_maxWidth = maxWidth;
 
3215     [m_NativeDataPtr->GetNativeColumnPtr() setMaxWidth:maxWidth];
 
3218 void wxDataViewColumn::SetMinWidth(int minWidth)
 
3220     m_minWidth = minWidth;
 
3221     [m_NativeDataPtr->GetNativeColumnPtr() setMinWidth:minWidth];
 
3224 void wxDataViewColumn::SetReorderable(bool reorderable)
 
3226     wxUnusedVar(reorderable);
 
3229 void wxDataViewColumn::SetHidden(bool hidden)
 
3231     // How to set flag here?
 
3233     [m_NativeDataPtr->GetNativeColumnPtr() setHidden:hidden];
 
3236 bool wxDataViewColumn::IsHidden() const
 
3238     return [m_NativeDataPtr->GetNativeColumnPtr() isHidden];
 
3241 void wxDataViewColumn::SetResizeable(bool resizable)
 
3243     wxDataViewColumnBase::SetResizeable(resizable);
 
3245         [m_NativeDataPtr->GetNativeColumnPtr() setResizingMask:NSTableColumnUserResizingMask];
 
3247         [m_NativeDataPtr->GetNativeColumnPtr() setResizingMask:NSTableColumnNoResizing];
 
3250 void wxDataViewColumn::SetSortable(bool sortable)
 
3252     // wxDataViewColumnBase::SetSortable(sortable);
 
3253     // Avoid endless recursion and just set the flag here
 
3255         m_flags |= wxDATAVIEW_COL_SORTABLE;
 
3257         m_flags &= ~wxDATAVIEW_COL_SORTABLE;
 
3260 void wxDataViewColumn::SetSortOrder(bool ascending)
 
3262     if (m_ascending != ascending)
 
3264         m_ascending = ascending;
 
3267             // change sorting order:
 
3268             NSArray*          sortDescriptors;
 
3269             NSSortDescriptor* sortDescriptor;
 
3270             NSTableColumn*    tableColumn;
 
3272             tableColumn     = m_NativeDataPtr->GetNativeColumnPtr();
 
3273             sortDescriptor  = [[NSSortDescriptor alloc] initWithKey:[[tableColumn sortDescriptorPrototype] key] ascending:m_ascending];
 
3274             sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
 
3275             [tableColumn setSortDescriptorPrototype:sortDescriptor];
 
3276             [[tableColumn tableView] setSortDescriptors:sortDescriptors];
 
3277             [sortDescriptor release];
 
3282 void wxDataViewColumn::SetTitle(const wxString& title)
 
3284     // bitmaps and titles cannot exist at the same time - if the title is set
 
3285     // the bitmap is removed:
 
3286     wxDataViewColumnBase::SetBitmap(wxBitmap());
 
3288     [[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setStringValue:[[wxCFStringRef(title).AsNSString() retain] autorelease]];
 
3291 void wxDataViewColumn::SetWidth(int width)
 
3297         case wxCOL_WIDTH_AUTOSIZE:
 
3298 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
 
3301                 wxCocoaDataViewControl *peer = static_cast<wxCocoaDataViewControl*>(GetOwner()->GetPeer());
 
3302                 peer->FitColumnWidthToContent(GetOwner()->GetColumnPosition(this));
 
3306             // fall through if unsupported (OSX < 10.5) or not yet settable
 
3308         case wxCOL_WIDTH_DEFAULT:
 
3309             width = wxDVC_DEFAULT_WIDTH;
 
3313             [m_NativeDataPtr->GetNativeColumnPtr() setWidth:width];
 
3318 void wxDataViewColumn::SetNativeData(wxDataViewColumnNativeData* newNativeDataPtr)
 
3320     delete m_NativeDataPtr;
 
3321     m_NativeDataPtr = newNativeDataPtr;
 
3324 #endif // (wxUSE_DATAVIEWCTRL == 1) && !defined(wxUSE_GENERICDATAVIEWCTRL)