/ExtLibs/wxWidgets/src/osx/cocoa/dataview.mm
Objective C++ | 1548 lines | 1081 code | 252 blank | 215 comment | 112 complexity | 8896804a4d79e8bdaaf12b9cb3c0ed74 MD5 | raw file
- ///////////////////////////////////////////////////////////////////////////////
- // Name: src/osx/cocoa/dataview.mm
- // Purpose: wxDataView
- // Author:
- // Modified by:
- // Created: 2009-01-31
- // RCS-ID: $Id: dataview.mm$
- // Copyright:
- // Licence: wxWindows licence
- ///////////////////////////////////////////////////////////////////////////////
- #include "wx/wxprec.h"
- #if (wxUSE_DATAVIEWCTRL == 1) && !defined(wxUSE_GENERICDATAVIEWCTRL)
- #ifndef WX_PRECOMP
- #include "wx/app.h"
- #include "wx/toplevel.h"
- #include "wx/font.h"
- #include "wx/settings.h"
- #include "wx/utils.h"
- #endif
- #include "wx/osx/private.h"
- #include "wx/osx/cocoa/dataview.h"
- #include "wx/renderer.h"
- #include "wx/stopwatch.h"
- #include "wx/dcgraph.h"
- // ============================================================================
- // Constants used locally
- // ============================================================================
- #define DataViewPboardType @"OutlineViewItem"
- // ============================================================================
- // Classes used locally in dataview.mm
- // ============================================================================
- // ============================================================================
- // wxPointerObject
- // ============================================================================
- @implementation wxPointerObject
- -(id) init
- {
- self = [super init];
- if (self != nil)
- self->pointer = NULL;
- return self;
- }
- -(id) initWithPointer:(void*) initPointer
- {
- self = [super init];
- if (self != nil)
- self->pointer = initPointer;
- return self;
- }
- //
- // inherited methods from NSObject
- //
- -(BOOL) isEqual:(id)object
- {
- return (object != nil) &&
- ([object isKindOfClass:[wxPointerObject class]]) &&
- (pointer == [((wxPointerObject*) object) pointer]);
- }
- -(NSUInteger) hash
- {
- return (NSUInteger) pointer;
- }
- -(void*) pointer
- {
- return pointer;
- }
- -(void) setPointer:(void*) newPointer
- {
- pointer = newPointer;
- }
- @end
- namespace
- {
- inline wxDataViewItem wxDataViewItemFromItem(id item)
- {
- return wxDataViewItem([static_cast<wxPointerObject *>(item) pointer]);
- }
- inline wxDataViewItem wxDataViewItemFromMaybeNilItem(id item)
- {
- return item == nil ? wxDataViewItem() : wxDataViewItemFromItem(item);
- }
- } // anonymous namespace
- // ----------------------------------------------------------------------------
- // wxCustomRendererObject
- // ----------------------------------------------------------------------------
- @interface wxCustomRendererObject : NSObject <NSCopying>
- {
- @public
- wxDataViewCustomRenderer* customRenderer; // not owned by the class
- }
- -(id) init;
- -(id) initWithRenderer:(wxDataViewCustomRenderer*)renderer;
- @end
- @implementation wxCustomRendererObject
- -(id) init
- {
- self = [super init];
- if (self != nil)
- {
- customRenderer = NULL;
- }
- return self;
- }
- -(id) initWithRenderer:(wxDataViewCustomRenderer*)renderer
- {
- self = [super init];
- if (self != nil)
- {
- customRenderer = renderer;
- }
- return self;
- }
- -(id) copyWithZone:(NSZone*)zone
- {
- wxCustomRendererObject* copy;
- copy = [[[self class] allocWithZone:zone] init];
- copy->customRenderer = customRenderer;
- return copy;
- }
- @end
- // ----------------------------------------------------------------------------
- // wxDVCNSTableColumn: exists only to override NSTableColumn:dataCellForRow:
- // ----------------------------------------------------------------------------
- @interface wxDVCNSTableColumn : NSTableColumn
- {
- }
- -(id) dataCellForRow:(NSInteger)row;
- @end
- @implementation wxDVCNSTableColumn
- -(id) dataCellForRow:(NSInteger)row
- {
- // what we want to do here is to simply return nil for the cells which
- // shouldn't show anything as otherwise we would show e.g. empty combo box
- // or progress cells in the columns using the corresponding types even for
- // the container rows which is wrong
- // half of the problem is just finding the objects we need from the column
- // pointer which is itself stashed inside wxPointerObject which we use as
- // our identifier
- const wxDataViewColumn * const
- dvCol = static_cast<wxDataViewColumn *>(
- [(wxPointerObject *)[self identifier] pointer]
- );
- const wxDataViewCtrl * const dvc = dvCol->GetOwner();
- const wxCocoaDataViewControl * const
- peer = static_cast<wxCocoaDataViewControl *>(dvc->GetPeer());
- // once we do have everything, simply ask NSOutlineView for the item...
- const id item = peer->GetItemAtRow(row);
- if ( item )
- {
- // ... and if it succeeded, ask the model whether it has any value
- wxDataViewItem dvItem(wxDataViewItemFromItem(item));
- if ( !dvc->GetModel()->HasValue(dvItem, dvCol->GetModelColumn()) )
- return nil;
- }
- return [super dataCellForRow:row];
- }
- @end
- // ============================================================================
- // local helpers
- // ============================================================================
- namespace
- {
- // convert from NSObject to different C++ types: all these functions check
- // that the conversion really makes sense and assert if it doesn't
- wxString ObjectToString(NSObject *object)
- {
- wxCHECK_MSG( [object isKindOfClass:[NSString class]], "",
- wxString::Format
- (
- "string expected but got %s",
- wxCFStringRef::AsString([object className])
- ));
- return wxCFStringRef([((NSString*) object) retain]).AsString();
- }
- bool ObjectToBool(NSObject *object)
- {
- // actually the value must be of NSCFBoolean class but it's private so we
- // can't check for it directly
- wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], false,
- wxString::Format
- (
- "number expected but got %s",
- wxCFStringRef::AsString([object className])
- ));
- return [(NSNumber *)object boolValue];
- }
- long ObjectToLong(NSObject *object)
- {
- wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], -1,
- wxString::Format
- (
- "number expected but got %s",
- wxCFStringRef::AsString([object className])
- ));
- return [(NSNumber *)object longValue];
- }
- wxDateTime ObjectToDate(NSObject *object)
- {
- wxCHECK_MSG( [object isKindOfClass:[NSDate class]], wxInvalidDateTime,
- wxString::Format
- (
- "date expected but got %s",
- wxCFStringRef::AsString([object className])
- ));
- // get the number of seconds since 1970-01-01 UTC and this is the only
- // way to convert a double to a wxLongLong
- const wxLongLong seconds = [((NSDate*) object) timeIntervalSince1970];
- wxDateTime dt(1, wxDateTime::Jan, 1970);
- dt.Add(wxTimeSpan(0,0,seconds));
- // the user has entered a date in the local timezone but seconds
- // contains the number of seconds from date in the local timezone
- // since 1970-01-01 UTC; therefore, the timezone information has to be
- // transferred to wxWidgets, too:
- dt.MakeFromTimezone(wxDateTime::UTC);
- return dt;
- }
- NSInteger CompareItems(id item1, id item2, void* context)
- {
- NSArray* const sortDescriptors = (NSArray*) context;
- NSUInteger const count = [sortDescriptors count];
- NSInteger result = NSOrderedSame;
- for ( NSUInteger i = 0; i < count && result == NSOrderedSame; ++i )
- {
- wxSortDescriptorObject* const
- sortDescriptor = (wxSortDescriptorObject*)
- [sortDescriptors objectAtIndex:i];
- int rc = [sortDescriptor modelPtr]->Compare
- (
- wxDataViewItemFromItem(item1),
- wxDataViewItemFromItem(item2),
- [sortDescriptor columnPtr]->GetModelColumn(),
- [sortDescriptor ascending] == YES
- );
- if ( rc < 0 )
- result = NSOrderedAscending;
- else if ( rc > 0 )
- result = NSOrderedDescending;
- }
- return result;
- }
- NSTextAlignment ConvertToNativeHorizontalTextAlignment(int alignment)
- {
- if (alignment & wxALIGN_CENTER_HORIZONTAL)
- return NSCenterTextAlignment;
- else if (alignment & wxALIGN_RIGHT)
- return NSRightTextAlignment;
- else
- return NSLeftTextAlignment;
- }
- NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column)
- {
- wxDataViewRenderer * const renderer = column->GetRenderer();
- wxCHECK_MSG( renderer, NULL, "column should have a renderer" );
- wxDVCNSTableColumn * const nativeColumn(
- [[wxDVCNSTableColumn alloc] initWithIdentifier:
- [[[wxPointerObject alloc] initWithPointer:
- const_cast<wxDataViewColumn*>(column)]
- autorelease]]
- );
- // setting the size related parameters:
- int resizingMask;
- if (column->IsResizeable())
- {
- resizingMask = NSTableColumnUserResizingMask;
- [nativeColumn setMinWidth:column->GetMinWidth()];
- [nativeColumn setMaxWidth:column->GetMaxWidth()];
- }
- else // column is not resizable [by user]
- {
- // if the control doesn't show a header, make the columns resize
- // automatically, this is particularly important for the single column
- // controls (such as wxDataViewTreeCtrl) as their unique column should
- // always take up all the available splace
- resizingMask = column->GetOwner()->HasFlag(wxDV_NO_HEADER)
- ? NSTableColumnAutoresizingMask
- : NSTableColumnNoResizing;
- }
- [nativeColumn setResizingMask:resizingMask];
- #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
- // setting the visibility:
- [nativeColumn setHidden:static_cast<BOOL>(column->IsHidden())];
- #endif
- wxDataViewRendererNativeData * const renderData = renderer->GetNativeData();
- // setting the header:
- [[nativeColumn headerCell] setAlignment:
- ConvertToNativeHorizontalTextAlignment(column->GetAlignment())];
- [[nativeColumn headerCell] setStringValue:
- [[wxCFStringRef(column->GetTitle()).AsNSString() retain] autorelease]];
- renderData->ApplyLineBreakMode([nativeColumn headerCell]);
- // setting data cell's properties:
- [[nativeColumn dataCell] setWraps:NO];
- // setting the default data cell:
- [nativeColumn setDataCell:renderData->GetColumnCell()];
- // setting the editablility:
- const bool isEditable = renderer->GetMode() == wxDATAVIEW_CELL_EDITABLE;
- [nativeColumn setEditable:isEditable];
- [[nativeColumn dataCell] setEditable:isEditable];
- return nativeColumn;
- }
- } // anonymous namespace
- // ============================================================================
- // Public helper functions for dataview implementation on OSX
- // ============================================================================
- wxWidgetImplType* CreateDataView(wxWindowMac* wxpeer,
- wxWindowMac* WXUNUSED(parent),
- wxWindowID WXUNUSED(id),
- const wxPoint& pos,
- const wxSize& size,
- long style,
- long WXUNUSED(extraStyle))
- {
- return new wxCocoaDataViewControl(wxpeer,pos,size,style);
- }
- // ============================================================================
- // wxSortDescriptorObject
- // ============================================================================
- @implementation wxSortDescriptorObject
- -(id) init
- {
- self = [super init];
- if (self != nil)
- {
- columnPtr = NULL;
- modelPtr = NULL;
- }
- return self;
- }
- -(id)
- initWithModelPtr:(wxDataViewModel*)initModelPtr
- sortingColumnPtr:(wxDataViewColumn*)initColumnPtr
- ascending:(BOOL)sortAscending
- {
- self = [super initWithKey:@"dummy" ascending:sortAscending];
- if (self != nil)
- {
- columnPtr = initColumnPtr;
- modelPtr = initModelPtr;
- }
- return self;
- }
- -(id) copyWithZone:(NSZone*)zone
- {
- wxSortDescriptorObject* copy;
- copy = [super copyWithZone:zone];
- copy->columnPtr = columnPtr;
- copy->modelPtr = modelPtr;
- return copy;
- }
- //
- // access to model column's index
- //
- -(wxDataViewColumn*) columnPtr
- {
- return columnPtr;
- }
- -(wxDataViewModel*) modelPtr
- {
- return modelPtr;
- }
- -(void) setColumnPtr:(wxDataViewColumn*)newColumnPtr
- {
- columnPtr = newColumnPtr;
- }
- -(void) setModelPtr:(wxDataViewModel*)newModelPtr
- {
- modelPtr = newModelPtr;
- }
- @end
- // ============================================================================
- // wxCocoaOutlineDataSource
- // ============================================================================
- @implementation wxCocoaOutlineDataSource
- //
- // constructors / destructor
- //
- -(id) init
- {
- self = [super init];
- if (self != nil)
- {
- implementation = NULL;
- model = NULL;
- currentParentItem = nil;
- children = [[NSMutableArray alloc] init];
- items = [[NSMutableSet alloc] init];
- }
- return self;
- }
- -(void) dealloc
- {
- [currentParentItem release];
- [children release];
- [items release];
- [super dealloc];
- }
- //
- // methods of informal protocol:
- //
- -(BOOL)
- outlineView:(NSOutlineView*)outlineView
- acceptDrop:(id<NSDraggingInfo>)info
- item:(id)item childIndex:(NSInteger)index
- {
- wxUnusedVar(outlineView);
- wxUnusedVar(index);
-
- NSArray* supportedTypes(
- [NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]
- );
- NSPasteboard* pasteboard([info draggingPasteboard]);
- NSString* bestType([pasteboard availableTypeFromArray:supportedTypes]);
- if ( bestType == nil )
- return FALSE;
- wxDataViewCtrl * const dvc(implementation->GetDataViewCtrl());
- wxCHECK_MSG( dvc, false,
- "Pointer to data view control not set correctly." );
- wxCHECK_MSG( dvc->GetModel(), false,
- "Pointer to model not set correctly." );
- wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_DROP, dvc->GetId());
- event.SetEventObject(dvc);
- event.SetItem(wxDataViewItemFromItem(item));
- event.SetModel(dvc->GetModel());
- BOOL dragSuccessful = false;
- if ( [bestType compare:DataViewPboardType] == NSOrderedSame )
- {
- NSArray* dataArray((NSArray*)
- [pasteboard propertyListForType:DataViewPboardType]);
- NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]);
- indexDraggedItem = 0;
- while (indexDraggedItem < noOfDraggedItems)
- {
- wxDataObjectComposite* dataObjects(
- implementation->GetDnDDataObjects((NSData*)
- [dataArray objectAtIndex:indexDraggedItem]));
- if (dataObjects && (dataObjects->GetFormatCount() > 0))
- {
- wxMemoryBuffer buffer;
- // copy data into data object:
- event.SetDataObject(dataObjects);
- event.SetDataFormat(
- implementation->GetDnDDataFormat(dataObjects));
- // copy data into buffer:
- dataObjects->GetDataHere(
- event.GetDataFormat().GetType(),
- buffer.GetWriteBuf(event.GetDataSize()));
- buffer.UngetWriteBuf(event.GetDataSize());
- event.SetDataBuffer(buffer.GetData());
- // finally, send event:
- if (dvc->HandleWindowEvent(event) && event.IsAllowed())
- {
- dragSuccessful = true;
- ++indexDraggedItem;
- }
- else
- {
- dragSuccessful = true;
- indexDraggedItem = noOfDraggedItems; // stop loop
- }
- }
- else
- {
- dragSuccessful = false;
- indexDraggedItem = noOfDraggedItems; // stop loop
- }
- // clean-up:
- delete dataObjects;
- }
- }
- else
- {
- // needed to convert internally used UTF-16 representation to a UTF-8
- // representation
- CFDataRef osxData;
- wxDataObjectComposite* dataObjects (new wxDataObjectComposite());
- wxTextDataObject* textDataObject(new wxTextDataObject());
- osxData = ::CFStringCreateExternalRepresentation
- (
- kCFAllocatorDefault,
- (CFStringRef)[pasteboard stringForType:NSStringPboardType],
- kCFStringEncodingUTF8,
- 32
- );
- if (textDataObject->SetData(::CFDataGetLength(osxData),
- ::CFDataGetBytePtr(osxData)))
- dataObjects->Add(textDataObject);
- else
- delete textDataObject;
- // send event if data could be copied:
- if (dataObjects->GetFormatCount() > 0)
- {
- event.SetDataObject(dataObjects);
- event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects));
- if (dvc->HandleWindowEvent(event) && event.IsAllowed())
- dragSuccessful = true;
- else
- dragSuccessful = false;
- }
- else
- dragSuccessful = false;
- // clean up:
- ::CFRelease(osxData);
- delete dataObjects;
- }
- return dragSuccessful;
- }
- -(id) outlineView:(NSOutlineView*)outlineView
- child:(NSInteger)index
- ofItem:(id)item
- {
- wxUnusedVar(outlineView);
- if ((item == currentParentItem) &&
- (index < ((NSInteger) [self getChildCount])))
- return [self getChild:index];
- wxDataViewItemArray dataViewChildren;
- wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
- model->GetChildren(wxDataViewItemFromMaybeNilItem(item), dataViewChildren);
- [self bufferItem:item withChildren:&dataViewChildren];
- if ([sortDescriptors count] > 0)
- [children sortUsingFunction:CompareItems context:sortDescriptors];
- return [self getChild:index];
- }
- -(BOOL) outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item
- {
- wxUnusedVar(outlineView);
- wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
- return model->IsContainer(wxDataViewItemFromItem(item));
- }
- -(NSInteger) outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item
- {
- wxUnusedVar(outlineView);
- NSInteger noOfChildren;
- wxDataViewItemArray dataViewChildren;
- wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
- noOfChildren = model->GetChildren(wxDataViewItemFromMaybeNilItem(item),
- dataViewChildren);
- [self bufferItem:item withChildren:&dataViewChildren];
- if ([sortDescriptors count] > 0)
- [children sortUsingFunction:CompareItems context:sortDescriptors];
- return noOfChildren;
- }
- -(id)
- outlineView:(NSOutlineView*)outlineView
- objectValueForTableColumn:(NSTableColumn*)tableColumn
- byItem:(id)item
- {
- wxUnusedVar(outlineView);
- wxCHECK_MSG( model, nil, "Valid model in data source does not exist." );
- wxDataViewColumn* col(static_cast<wxDataViewColumn*>([[tableColumn identifier] pointer]));
- const unsigned colIdx = col->GetModelColumn();
- wxDataViewItem dataViewItem(wxDataViewItemFromItem(item));
- if ( model->HasValue(dataViewItem, colIdx) )
- {
- wxVariant value;
- model->GetValue(value,dataViewItem, colIdx);
- col->GetRenderer()->SetValue(value);
- }
- return nil;
- }
- -(void)
- outlineView:(NSOutlineView*)outlineView
- setObjectValue:(id)object
- forTableColumn:(NSTableColumn*)tableColumn
- byItem:(id)item
- {
- wxUnusedVar(outlineView);
- wxDataViewColumn* col(static_cast<wxDataViewColumn*>([[tableColumn identifier] pointer]));
- col->GetRenderer()->
- OSXOnCellChanged(object, wxDataViewItemFromItem(item), col->GetModelColumn());
- }
- -(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)oldDescriptors
- {
- wxUnusedVar(oldDescriptors);
-
- // Warning: the new sort descriptors are guaranteed to be only of type
- // NSSortDescriptor! Therefore, the sort descriptors for the data source
- // have to be converted.
- NSArray* newDescriptors;
- NSMutableArray* wxSortDescriptors;
- NSUInteger noOfDescriptors;
- wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
- // convert NSSortDescriptors to wxSortDescriptorObjects:
- newDescriptors = [outlineView sortDescriptors];
- noOfDescriptors = [newDescriptors count];
- wxSortDescriptors = [NSMutableArray arrayWithCapacity:noOfDescriptors];
- for (NSUInteger i=0; i<noOfDescriptors; ++i)
- {
- NSSortDescriptor* const newDescriptor = [newDescriptors objectAtIndex:i];
- [wxSortDescriptors addObject:[[[wxSortDescriptorObject alloc] initWithModelPtr:model
- sortingColumnPtr:dvc->GetColumn([[newDescriptor key] intValue])
- ascending:[newDescriptor ascending]] autorelease]];
- }
- [(wxCocoaOutlineDataSource*)[outlineView dataSource] setSortDescriptors:wxSortDescriptors];
- // send first the event to wxWidgets that the sorting has changed so that
- // the program can do special actions before the sorting actually starts:
- wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED,dvc->GetId()); // variable defintion
- event.SetEventObject(dvc);
- if (noOfDescriptors > 0)
- {
- wxDataViewColumn* const col = [[wxSortDescriptors objectAtIndex:0] columnPtr];
- event.SetColumn(dvc->GetColumnPosition(col));
- event.SetDataViewColumn(col);
- }
- dvc->GetEventHandler()->ProcessEvent(event);
- // start re-ordering the data;
- // children's buffer must be cleared first because it contains the old order:
- [self clearChildren];
- // sorting is done while reloading the data:
- [outlineView reloadData];
- }
- -(NSDragOperation) outlineView:(NSOutlineView*)outlineView validateDrop:(id<NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
- {
- wxUnusedVar(outlineView);
- wxUnusedVar(index);
- NSArray* supportedTypes([NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]);
- NSPasteboard* pasteboard([info draggingPasteboard]);
- NSString* bestType([pasteboard availableTypeFromArray:supportedTypes]);
- if (bestType == nil)
- return NSDragOperationNone;
- NSDragOperation dragOperation = NSDragOperationNone;
- wxDataViewCtrl* const dvc(implementation->GetDataViewCtrl());
- wxCHECK_MSG(dvc, false, "Pointer to data view control not set correctly.");
- wxCHECK_MSG(dvc->GetModel(), false, "Pointer to model not set correctly.");
- wxDataViewEvent
- event(wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE,dvc->GetId());
- event.SetEventObject(dvc);
- event.SetItem(wxDataViewItemFromItem(item));
- event.SetModel(dvc->GetModel());
- if ([bestType compare:DataViewPboardType] == NSOrderedSame)
- {
- NSArray* dataArray((NSArray*)[pasteboard propertyListForType:DataViewPboardType]);
- NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]);
- indexDraggedItem = 0;
- while (indexDraggedItem < noOfDraggedItems)
- {
- wxDataObjectComposite* dataObjects(implementation->GetDnDDataObjects((NSData*)[dataArray objectAtIndex:indexDraggedItem]));
- if (dataObjects && (dataObjects->GetFormatCount() > 0))
- {
- wxMemoryBuffer buffer;
- // copy data into data object:
- event.SetDataObject(dataObjects);
- event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects));
- // copy data into buffer:
- dataObjects->GetDataHere(event.GetDataFormat().GetType(),buffer.GetWriteBuf(event.GetDataSize()));
- buffer.UngetWriteBuf(event.GetDataSize());
- event.SetDataBuffer(buffer.GetData());
- // finally, send event:
- if (dvc->HandleWindowEvent(event) && event.IsAllowed())
- {
- dragOperation = NSDragOperationEvery;
- ++indexDraggedItem;
- }
- else
- {
- dragOperation = NSDragOperationNone;
- indexDraggedItem = noOfDraggedItems; // stop loop
- }
- }
- else
- {
- dragOperation = NSDragOperationNone;
- indexDraggedItem = noOfDraggedItems; // stop loop
- }
- // clean-up:
- delete dataObjects;
- }
- }
- else
- {
- // needed to convert internally used UTF-16 representation to a UTF-8
- // representation
- CFDataRef osxData;
- wxDataObjectComposite* dataObjects (new wxDataObjectComposite());
- wxTextDataObject* textDataObject(new wxTextDataObject());
- osxData = ::CFStringCreateExternalRepresentation(kCFAllocatorDefault,(CFStringRef)[pasteboard stringForType:NSStringPboardType],kCFStringEncodingUTF8,32);
- if (textDataObject->SetData(::CFDataGetLength(osxData),::CFDataGetBytePtr(osxData)))
- dataObjects->Add(textDataObject);
- else
- delete textDataObject;
- // send event if data could be copied:
- if (dataObjects->GetFormatCount() > 0)
- {
- event.SetDataObject(dataObjects);
- event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects));
- if (dvc->HandleWindowEvent(event) && event.IsAllowed())
- dragOperation = NSDragOperationEvery;
- else
- dragOperation = NSDragOperationNone;
- }
- else
- dragOperation = NSDragOperationNone;
- // clean up:
- ::CFRelease(osxData);
- delete dataObjects;
- }
- return dragOperation;
- }
- -(BOOL) outlineView:(NSOutlineView*)outlineView writeItems:(NSArray*)writeItems toPasteboard:(NSPasteboard*)pasteboard
- {
- wxUnusedVar(outlineView);
- // the pasteboard will be filled up with an array containing the data as
- // returned by the events (including the data type) and a concatenation of
- // text (string) data; the text data will only be put onto the pasteboard
- // if for all items a string representation exists
- wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
- wxDataViewItemArray dataViewItems;
- wxCHECK_MSG(dvc, false,"Pointer to data view control not set correctly.");
- wxCHECK_MSG(dvc->GetModel(),false,"Pointer to model not set correctly.");
- if ([writeItems count] > 0)
- {
- bool dataStringAvailable(true); // a flag indicating if for all items a data string is available
- NSMutableArray* dataArray = [NSMutableArray arrayWithCapacity:[writeItems count]]; // data of all items
- wxString dataString; // contains the string data of all items
- // send a begin drag event for all selected items and proceed with
- // dragging unless the event is vetoed:
- wxDataViewEvent
- event(wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG,dvc->GetId());
- event.SetEventObject(dvc);
- event.SetModel(dvc->GetModel());
- for (size_t itemCounter=0; itemCounter<[writeItems count]; ++itemCounter)
- {
- bool itemStringAvailable(false); // a flag indicating if for the current item a string is available
- wxDataObjectComposite* itemObject(new wxDataObjectComposite()); // data object for current item
- wxString itemString; // contains the TAB concatenated data of an item
- event.SetItem(
- wxDataViewItemFromItem([writeItems objectAtIndex:itemCounter]));
- itemString = ::ConcatenateDataViewItemValues(dvc,event.GetItem());
- itemObject->Add(new wxTextDataObject(itemString));
- event.SetDataObject(itemObject);
- // check if event has not been vetoed:
- if (dvc->HandleWindowEvent(event) && event.IsAllowed() && (event.GetDataObject()->GetFormatCount() > 0))
- {
- size_t const noOfFormats = event.GetDataObject()->GetFormatCount();
- wxDataFormat* dataFormats(new wxDataFormat[noOfFormats]);
- event.GetDataObject()->GetAllFormats(dataFormats,wxDataObject::Get);
- for (size_t formatCounter=0; formatCounter<noOfFormats; ++formatCounter)
- {
- // constant definitions for abbreviational purposes:
- wxDataFormatId const idDataFormat = dataFormats[formatCounter].GetType();
- size_t const dataSize = event.GetDataObject()->GetDataSize(idDataFormat);
- size_t const dataBufferSize = sizeof(wxDataFormatId)+dataSize;
- // variable definitions (used in all case statements):
- wxMemoryBuffer dataBuffer(dataBufferSize);
- dataBuffer.AppendData(&idDataFormat,sizeof(wxDataFormatId));
- switch (idDataFormat)
- {
- case wxDF_TEXT:
- // otherwise wxDF_UNICODETEXT already filled up
- // the string; and the UNICODE representation has
- // priority
- if (!itemStringAvailable)
- {
- event.GetDataObject()->GetDataHere(wxDF_TEXT,dataBuffer.GetAppendBuf(dataSize));
- dataBuffer.UngetAppendBuf(dataSize);
- [dataArray addObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]];
- itemString = wxString(static_cast<char const*>(dataBuffer.GetData())+sizeof(wxDataFormatId),wxConvLocal);
- itemStringAvailable = true;
- }
- break;
- case wxDF_UNICODETEXT:
- {
- event.GetDataObject()->GetDataHere(wxDF_UNICODETEXT,dataBuffer.GetAppendBuf(dataSize));
- dataBuffer.UngetAppendBuf(dataSize);
- if (itemStringAvailable) // does an object already exist as an ASCII text (see wxDF_TEXT case statement)?
- [dataArray replaceObjectAtIndex:itemCounter withObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]];
- else
- [dataArray addObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]];
- itemString = wxString::FromUTF8(static_cast<char const*>(dataBuffer.GetData())+sizeof(wxDataFormatId),dataSize);
- itemStringAvailable = true;
- } /* block */
- break;
- default:
- wxFAIL_MSG("Data object has invalid or unsupported data format");
- [dataArray release];
- return NO;
- }
- }
- delete[] dataFormats;
- delete itemObject;
- if (dataStringAvailable)
- if (itemStringAvailable)
- {
- if (itemCounter > 0)
- dataString << wxT('\n');
- dataString << itemString;
- }
- else
- dataStringAvailable = false;
- }
- else
- {
- [dataArray release];
- delete itemObject;
- return NO; // dragging was vetoed or no data available
- }
- }
- if (dataStringAvailable)
- {
- wxCFStringRef osxString(dataString);
- [pasteboard declareTypes:[NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil] owner:nil];
- [pasteboard setPropertyList:dataArray forType:DataViewPboardType];
- [pasteboard setString:osxString.AsNSString() forType:NSStringPboardType];
- }
- else
- {
- [pasteboard declareTypes:[NSArray arrayWithObject:DataViewPboardType] owner:nil];
- [pasteboard setPropertyList:dataArray forType:DataViewPboardType];
- }
- return YES;
- }
- else
- return NO; // no items to drag (should never occur)
- }
- //
- // buffer handling
- //
- -(void) addToBuffer:(wxPointerObject*)item
- {
- [items addObject:item];
- }
- -(void) clearBuffer
- {
- [items removeAllObjects];
- }
- -(wxPointerObject*) getDataViewItemFromBuffer:(const wxDataViewItem&)item
- {
- return [items member:[[[wxPointerObject alloc] initWithPointer:item.GetID()] autorelease]];
- }
- -(wxPointerObject*) getItemFromBuffer:(wxPointerObject*)item
- {
- return [items member:item];
- }
- -(BOOL) isInBuffer:(wxPointerObject*)item
- {
- return [items containsObject:item];
- }
- -(void) removeFromBuffer:(wxPointerObject*)item
- {
- [items removeObject:item];
- }
- //
- // children handling
- //
- -(void) appendChild:(wxPointerObject*)item
- {
- [children addObject:item];
- }
- -(void) clearChildren
- {
- [children removeAllObjects];
- }
- -(wxPointerObject*) getChild:(NSUInteger)index
- {
- return [children objectAtIndex:index];
- }
- -(NSUInteger) getChildCount
- {
- return [children count];
- }
- -(void) removeChild:(NSUInteger)index
- {
- [children removeObjectAtIndex:index];
- }
- //
- // buffer handling
- //
- -(void) clearBuffers
- {
- [self clearBuffer];
- [self clearChildren];
- [self setCurrentParentItem:nil];
- }
- //
- // sorting
- //
- -(NSArray*) sortDescriptors
- {
- return sortDescriptors;
- }
- -(void) setSortDescriptors:(NSArray*)newSortDescriptors
- {
- [newSortDescriptors retain];
- [sortDescriptors release];
- sortDescriptors = newSortDescriptors;
- }
- //
- // access to wxWidget's implementation
- //
- -(wxPointerObject*) currentParentItem
- {
- return currentParentItem;
- }
- -(wxCocoaDataViewControl*) implementation
- {
- return implementation;
- }
- -(wxDataViewModel*) model
- {
- return model;
- }
- -(void) setCurrentParentItem:(wxPointerObject*)newCurrentParentItem
- {
- [newCurrentParentItem retain];
- [currentParentItem release];
- currentParentItem = newCurrentParentItem;
- }
- -(void) setImplementation:(wxCocoaDataViewControl*) newImplementation
- {
- implementation = newImplementation;
- }
- -(void) setModel:(wxDataViewModel*) newModel
- {
- model = newModel;
- }
- //
- // other methods
- //
- -(void) bufferItem:(wxPointerObject*)parentItem withChildren:(wxDataViewItemArray*)dataViewChildrenPtr
- {
- NSInteger const noOfChildren = (*dataViewChildrenPtr).GetCount();
- [self setCurrentParentItem:parentItem];
- [self clearChildren];
- for (NSInteger indexChild=0; indexChild<noOfChildren; ++indexChild)
- {
- wxPointerObject* bufferedPointerObject;
- wxPointerObject* newPointerObject([[wxPointerObject alloc] initWithPointer:(*dataViewChildrenPtr)[indexChild].GetID()]);
- // The next statement and test looks strange but there is
- // unfortunately no workaround: due to the fact that two pointer
- // objects are identical if their pointers are identical - because the
- // method isEqual has been overloaded - the set operation will only
- // add a new pointer object if there is not already one in the set
- // having the same pointer. On the other side the children's array
- // would always add the new pointer object. This means that different
- // pointer objects are stored in the set and array. This will finally
- // lead to a crash as objects diverge. To solve this issue it is first
- // tested if the child already exists in the set and if it is the case
- // the sets object is going to be appended to the array, otheriwse the
- // new pointer object is added to the set and array:
- bufferedPointerObject = [self getItemFromBuffer:newPointerObject];
- if (bufferedPointerObject == nil)
- {
- [items addObject:newPointerObject];
- [children addObject:newPointerObject];
- }
- else
- [children addObject:bufferedPointerObject];
- [newPointerObject release];
- }
- }
- @end
- // ============================================================================
- // wxCustomCell
- // ============================================================================
- @implementation wxCustomCell
- -(NSSize) cellSize
- {
- wxCustomRendererObject * const
- obj = static_cast<wxCustomRendererObject *>([self objectValue]);
- const wxSize size = obj->customRenderer->GetSize();
- return NSMakeSize(size.x, size.y);
- }
- //
- // implementations
- //
- -(void) drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
- {
- wxCustomRendererObject * const
- obj = static_cast<wxCustomRendererObject *>([self objectValue]);
- if ( !obj )
- {
- // this may happen for the custom cells in container rows: they don't
- // have any values
- return;
- }
- wxDataViewCustomRenderer * const renderer = obj->customRenderer;
- // if this method is called everything is already setup correctly,
- CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
- CGContextSaveGState( context );
-
- if ( ![controlView isFlipped] )
- {
- CGContextTranslateCTM( context, 0, [controlView bounds].size.height );
- CGContextScaleCTM( context, 1, -1 );
- }
-
- wxGCDC dc;
- wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(context);
- dc.SetGraphicsContext(gc);
- int state = 0;
- if ( [self isHighlighted] )
- state |= wxDATAVIEW_CELL_SELECTED;
- renderer->WXCallRender(wxFromNSRect(controlView, cellFrame), &dc, state);
- CGContextRestoreGState( context );
- }
- -(NSRect) imageRectForBounds:(NSRect)cellFrame
- {
- return cellFrame;
- }
- -(NSRect) titleRectForBounds:(NSRect)cellFrame
- {
- return cellFrame;
- }
- @end
- // ============================================================================
- // wxImageTextCell
- // ============================================================================
- @implementation wxImageTextCell
- //
- // initialization
- //
- -(id) init
- {
- self = [super init];
- if (self != nil)
- {
- // initializing the text part:
- [self setSelectable:YES];
- // initializing the image part:
- image = nil;
- imageSize = NSMakeSize(16,16);
- spaceImageText = 5.0;
- xImageShift = 5.0;
- }
- return self;
- }
- -(id) copyWithZone:(NSZone*)zone
- {
- wxImageTextCell* cell;
- cell = (wxImageTextCell*) [super copyWithZone:zone];
- cell->image = [image retain];
- cell->imageSize = imageSize;
- cell->spaceImageText = spaceImageText;
- cell->xImageShift = xImageShift;
- return cell;
- }
- -(void) dealloc
- {
- [image release];
- [super dealloc];
- }
- //
- // alignment
- //
- -(NSTextAlignment) alignment
- {
- return cellAlignment;
- }
- -(void) setAlignment:(NSTextAlignment)newAlignment
- {
- cellAlignment = newAlignment;
- switch (newAlignment)
- {
- case NSCenterTextAlignment:
- case NSLeftTextAlignment:
- case NSJustifiedTextAlignment:
- case NSNaturalTextAlignment:
- [super setAlignment:NSLeftTextAlignment];
- break;
- case NSRightTextAlignment:
- [super setAlignment:NSRightTextAlignment];
- break;
- default:
- wxFAIL_MSG("Unknown alignment type.");
- }
- }
- //
- // image access
- //
- -(NSImage*) image
- {
- return image;
- }
- -(void) setImage:(NSImage*)newImage
- {
- [newImage retain];
- [image release];
- image = newImage;
- }
- -(NSSize) imageSize
- {
- return imageSize;
- }
- -(void) setImageSize:(NSSize) newImageSize
- {
- imageSize = newImageSize;
- }
- //
- // other methods
- //
- -(NSSize) cellImageSize
- {
- return NSMakeSize(imageSize.width+xImageShift+spaceImageText,imageSize.height);
- }
- -(NSSize) cellSize
- {
- NSSize cellSize([super cellSize]);
- if (imageSize.height > cellSize.height)
- cellSize.height = imageSize.height;
- cellSize.width += imageSize.width+xImageShift+spaceImageText;
- return cellSize;
- }
- -(NSSize) cellTextSize
- {
- return [super cellSize];
- }
- //
- // implementations
- //
- -(void) determineCellParts:(NSRect)cellFrame imagePart:(NSRect*)imageFrame textPart:(NSRect*)textFrame
- {
- switch (cellAlignment)
- {
- case NSCenterTextAlignment:
- {
- CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width;
- // if the cell's frame is smaller than its contents (at least
- // in x-direction) make sure that the image is visible:
- if (cellSpace <= 0)
- NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge);
- else // otherwise center the image and text in the cell's frame
- NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText+0.5*cellSpace,NSMinXEdge);
- }
- break;
- case NSJustifiedTextAlignment:
- case NSLeftTextAlignment:
- case NSNaturalTextAlignment: // how to determine the natural writing direction? TODO
- NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge);
- break;
- case NSRightTextAlignment:
- {
- CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width;
- // if the cell's frame is smaller than its contents (at least
- // in x-direction) make sure that the image is visible:
- if (cellSpace <= 0)
- NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge);
- else // otherwise right align the image and text in the cell's frame
- NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText+cellSpace,NSMinXEdge);
- }
- break;
- default:
- *imageFrame = NSZeroRect;
- *textFrame = NSZeroRect;
- wxFAIL_MSG("Unhandled alignment type.");
- }
- }
- -(void) drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
- {
- NSRect textFrame, imageFrame;
- [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
- // draw the image part by ourselves;
- // check if the cell has to draw its own background (checking is done by
- // the parameter of the textfield's cell):
- if ([self drawsBackground])
- {
- [[self backgroundColor] set];
- NSRectFill(imageFrame);
- }
- if (image != nil)
- {
- // the image is slightly shifted (xImageShift) and has a fixed size
- // but the image's frame might be larger and starts currently on the
- // left side of the cell's frame; therefore, the origin and the
- // image's frame size have to be adjusted:
- if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText)
- {
- imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText;
- imageFrame.size.width = imageSize.width;
- }
- else
- {
- imageFrame.origin.x += xImageShift;
- imageFrame.size.width -= xImageShift+spaceImageText;
- }
- // ...and the image has to be centered in the y-direction:
- if (imageFrame.size.height > imageSize.height)
- imageFrame.size.height = imageSize.height;
- imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height));
- // according to the documentation the coordinate system should be
- // flipped for NSTableViews (y-coordinate goes from top to bottom); to
- // draw an image correctly the coordinate system has to be transformed
- // to a bottom-top coordinate system, otherwise the image's
- // content is flipped:
- NSAffineTransform* coordinateTransform([NSAffineTransform transform]);
- if ([controlView isFlipped])
- {
- [coordinateTransform scaleXBy: 1.0 yBy:-1.0]; // first the coordinate system is brought back to bottom-top orientation
- [coordinateTransform translateXBy:0.0 yBy:(-2.0)*imageFrame.origin.y-imageFrame.size.height]; // the coordinate system has to be moved to compensate for the
- [coordinateTransform concat]; // other orientation and the position of the image's frame
- }
- [image drawInRect:imageFrame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; // suggested method to draw the image
- // instead of compositeToPoint:operation:
- // take back previous transformation (if the view is not flipped the
- // coordinate transformation matrix contains the identity matrix and
- // the next two operations do not change the content's transformation
- // matrix):
- [coordinateTransform invert];
- [coordinateTransform concat];
- }
- // let the textfield cell draw the text part:
- if (textFrame.size.width > [self cellTextSize].width)
- {
- // for unknown reasons the alignment of the text cell is ignored;
- // therefore change the size so that alignment does not influence the
- // visualization anymore
- textFrame.size.width = [self cellTextSize].width;
- }
- [super drawWithFrame:textFrame inView:controlView];
- }
- -(void) editWithFrame:(NSRect)aRect inView:(NSView*)controlView editor:(NSText*)textObj delegate:(id)anObject event:(NSEvent*)theEvent
- {
- NSRect textFrame, imageFrame;
- [self determineCellParts:aRect imagePart:&imageFrame textPart:&textFrame];
- [super editWithFrame:textFrame inView:controlView editor:textObj delegate:anObject event:theEvent];
- }
- #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
- -(NSUInteger) hitTestForEvent:(NSEvent*)event inRect:(NSRect)cellFrame ofView:(NSView*)controlView
- {
- NSPoint point = [controlView convertPoint:[event locationInWindow] fromView:nil];
- NSRect imageFrame, textFrame;
- [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
- if (image != nil)
- {
- // the image is shifted...
- if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText)
- {
- imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText;
- imageFrame.size.width = imageSize.width;
- }
- else
- {
- imageFrame.origin.x += xImageShift;
- imageFrame.size.width -= xImageShift+spaceImageText;
- }
- // ...and centered:
- if (imageFrame.size.height > imageSize.height)
- imageFrame.size.height = imageSize.height;
- imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height));
- // If the point is in the image rect, then it is a content hit (see
- // documentation for hitTestForEvent:inRect:ofView):
- if (NSMouseInRect(point, imageFrame, [controlView isFlipped]))
- return NSCellHitContentArea;
- }
- // if the image was not hit let's try the text part:
- if (textFrame.size.width > [self cellTextSize].width)
- {
- // for unknown reasons the alignment of the text cell is ignored;
- // therefore change the size so that alignment does not influence the
- // visualization anymore
- textFrame.size.width = [self cellTextSize].width;
- }
- return [super hitTestForEvent:event inRect:textFrame ofView:controlView];
- }
- #endif
- -(NSRect) imageRectForBounds:(NSRect)cellFrame
- {
- NSRect textFrame, imageFrame;
- [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
- if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText)
- {
- imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText;
- imageFrame.size.width = imageSize.width;
- }
- else
- {
- imageFrame.origin.x += xImageShift;
- imageFrame.size.width -= xImageShift+spaceImageText;
- }
- // ...and centered:
- if (imageFrame.size.height > imageSize.height)
- imageFrame.size.height = imageSize.height;
- imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height));
- return imageFrame;
- }
- -(void) selectWithFrame:(NSRect)aRect inView:(NSView*)controlView editor:(NSText*)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength
- {
- NSRect textFrame, imageFrame;
- [self determineCellParts:aRect imagePart:&imageFrame textPart:&textFrame];
- [super selectWithFrame:textFrame inView:controlView editor:textObj delegate:anObject start:selStart length:selLength];
- }
- -(NSRect) titleRectForBounds:(NSRect)cellFrame
- {
- NSRect textFrame, imageFrame;
- [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
- return textFrame;
- }
- @end
- // ============================================================================
- // wxCocoaOutlineView
- // ============================================================================
- @implementation wxCocoaOutlineView
- //
- // initializers / destructor
- //
- -(id) init
- {
- self = [super init];
- if (self != nil)
- {
- currentlyEditedColumn =
- currentlyEditedRow = -1;
- [self registe