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