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