X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/795dac4c866d89804e625fa1d04cb70aba320183..0afeb753e0a6a3fdba290bf3612bb2f012d44d95:/src/osx/cocoa/dataview.mm?ds=sidebyside diff --git a/src/osx/cocoa/dataview.mm b/src/osx/cocoa/dataview.mm index 1d63bdf4fa..465305fbbc 100644 --- a/src/osx/cocoa/dataview.mm +++ b/src/osx/cocoa/dataview.mm @@ -21,9 +21,11 @@ #include "wx/utils.h" #endif -#include "wx/osx/cocoa/dataview.h" #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 @@ -34,6 +36,75 @@ // ============================================================================ // 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(item) pointer]); +} + +inline wxDataViewItem wxDataViewItemFromMaybeNilItem(id item) +{ + return item == nil ? wxDataViewItem() : wxDataViewItemFromItem(item); +} + +} // anonymous namespace + +// ---------------------------------------------------------------------------- +// wxCustomRendererObject +// ---------------------------------------------------------------------------- + @interface wxCustomRendererObject : NSObject { @public @@ -77,6 +148,84 @@ } @end +// ---------------------------------------------------------------------------- +// wxDVCNSTableColumn: exists only to override NSTableColumn:dataCellForRow: +// ---------------------------------------------------------------------------- + +@interface wxDVCNSTableColumn : NSTableColumn +{ +} + + // Get the identifier we use for the specified column. This should be used + // for finding columns from identifier only, to initialize the identifier + // of a new column use initWithColumnPointer below instead. + +(NSString*) identifierForColumnPointer:(const wxDataViewColumn*)column; + + // Initialize the column with the given pointer to the associated + // wxDataViewColumn. This pointer can later be retrieved using + // getColumnPointer. + -(id) initWithColumnPointer:(const wxDataViewColumn*)column; + + // Retrieve the associated column. + -(wxDataViewColumn*) getColumnPointer; + + -(id) dataCellForRow:(NSInteger)row; +@end + +@implementation wxDVCNSTableColumn + ++(NSString*) identifierForColumnPointer:(const wxDataViewColumn*)column +{ + // Starting from OS X 10.7 the column identifier must be an NSString and + // not just some arbitrary object, so we serialize the pointer into the + // string. Notice the use of NSInteger which is big enough to store a + // pointer in both 32 and 64 bit builds. + return [NSString stringWithFormat:@"%lu", reinterpret_cast(column)]; +} + +-(id) initWithColumnPointer:(const wxDataViewColumn*)column +{ + [self initWithIdentifier: [wxDVCNSTableColumn identifierForColumnPointer:column]]; + return self; +} + +-(wxDataViewColumn*) getColumnPointer +{ + // The case to NSString is needed for OS X < 10.7. + return reinterpret_cast( + [static_cast([self identifier]) integerValue]); +} + +-(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 + + const wxDataViewColumn * const dvCol = [self getColumnPointer]; + + const wxDataViewCtrl * const dvc = dvCol->GetOwner(); + const wxCocoaDataViewControl * const + peer = static_cast(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 // ============================================================================ @@ -84,6 +233,71 @@ 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; @@ -93,15 +307,14 @@ NSInteger CompareItems(id item1, id item2, void* context) NSInteger result = NSOrderedSame; for ( NSUInteger i = 0; i < count && result == NSOrderedSame; ++i ) { - // constant definition for abbreviational purposes: wxSortDescriptorObject* const sortDescriptor = (wxSortDescriptorObject*) [sortDescriptors objectAtIndex:i]; int rc = [sortDescriptor modelPtr]->Compare ( - wxDataViewItem([((wxPointerObject*) item1) pointer]), - wxDataViewItem([((wxPointerObject*) item2) pointer]), + wxDataViewItemFromItem(item1), + wxDataViewItemFromItem(item2), [sortDescriptor columnPtr]->GetModelColumn(), [sortDescriptor ascending] == YES ); @@ -131,27 +344,29 @@ NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column) wxCHECK_MSG( renderer, NULL, "column should have a renderer" ); - NSTableColumn * const nativeColumn( - [[NSTableColumn alloc] initWithIdentifier: - [[[wxPointerObject alloc] initWithPointer: - const_cast(column)] - autorelease]] + wxDVCNSTableColumn * const nativeColumn( + [[wxDVCNSTableColumn alloc] initWithColumnPointer: column] ); // setting the size related parameters: + int resizingMask; if (column->IsResizeable()) { - [nativeColumn setResizingMask:NSTableColumnUserResizingMask]; + resizingMask = NSTableColumnUserResizingMask; [nativeColumn setMinWidth:column->GetMinWidth()]; [nativeColumn setMaxWidth:column->GetMaxWidth()]; } - else + else // column is not resizable [by user] { - [nativeColumn setResizingMask:NSTableColumnNoResizing]; - [nativeColumn setMinWidth:column->GetWidth()]; - [nativeColumn setMaxWidth:column->GetWidth()]; + // 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 setWidth:column->GetWidth()]; + [nativeColumn setResizingMask:resizingMask]; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 // setting the visibility: @@ -197,55 +412,6 @@ wxWidgetImplType* CreateDataView(wxWindowMac* wxpeer, return new wxCocoaDataViewControl(wxpeer,pos,size,style); } -// ============================================================================ -// 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 - // ============================================================================ // wxSortDescriptorObject // ============================================================================ @@ -355,6 +521,9 @@ outlineView:(NSOutlineView*)outlineView acceptDrop:(id)info item:(id)item childIndex:(NSInteger)index { + wxUnusedVar(outlineView); + wxUnusedVar(index); + NSArray* supportedTypes( [NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil] ); @@ -373,21 +542,24 @@ outlineView:(NSOutlineView*)outlineView wxCHECK_MSG( dvc->GetModel(), false, "Pointer to model not set correctly." ); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_DROP, dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_DROP, dvc->GetId()); event.SetEventObject(dvc); - event.SetItem(wxDataViewItem([((wxPointerObject*) item) pointer])); + event.SetItem(wxDataViewItemFromItem(item)); event.SetModel(dvc->GetModel()); - BOOL dragSuccessful; + BOOL dragSuccessful = false; if ( [bestType compare:DataViewPboardType] == NSOrderedSame ) { - NSArray* dataArray((NSArray*)[pasteboard propertyListForType:DataViewPboardType]); + NSArray* dataArray((NSArray*) + [pasteboard propertyListForType:DataViewPboardType]); NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]); indexDraggedItem = 0; while (indexDraggedItem < noOfDraggedItems) { - wxDataObjectComposite* dataObjects(implementation->GetDnDDataObjects((NSData*)[dataArray objectAtIndex:indexDraggedItem])); + wxDataObjectComposite* dataObjects( + implementation->GetDnDDataObjects((NSData*) + [dataArray objectAtIndex:indexDraggedItem])); if (dataObjects && (dataObjects->GetFormatCount() > 0)) { @@ -395,9 +567,12 @@ outlineView:(NSOutlineView*)outlineView // copy data into data object: event.SetDataObject(dataObjects); - event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects)); + event.SetDataFormat( + implementation->GetDnDDataFormat(dataObjects)); // copy data into buffer: - dataObjects->GetDataHere(event.GetDataFormat().GetType(),buffer.GetWriteBuf(event.GetDataSize())); + dataObjects->GetDataHere( + event.GetDataFormat().GetType(), + buffer.GetWriteBuf(event.GetDataSize())); buffer.UngetWriteBuf(event.GetDataSize()); event.SetDataBuffer(buffer.GetData()); // finally, send event: @@ -423,12 +598,21 @@ outlineView:(NSOutlineView*)outlineView } else { - CFDataRef osxData; // needed to convert internally used UTF-16 representation to a UTF-8 representation + // 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))) + osxData = ::CFStringCreateExternalRepresentation + ( + kCFAllocatorDefault, + (CFStringRef)[pasteboard stringForType:NSStringPboardType], + kCFStringEncodingUTF8, + 32 + ); + if (textDataObject->SetData(::CFDataGetLength(osxData), + ::CFDataGetBytePtr(osxData))) dataObjects->Add(textDataObject); else delete textDataObject; @@ -448,40 +632,49 @@ outlineView:(NSOutlineView*)outlineView ::CFRelease(osxData); delete dataObjects; } + return dragSuccessful; } --(id) outlineView:(NSOutlineView*)outlineView child:(NSInteger)index ofItem:(id)item +-(id) outlineView:(NSOutlineView*)outlineView + child:(NSInteger)index + ofItem:(id)item { - if ((item == currentParentItem) && (index < ((NSInteger) [self getChildCount]))) - return [self getChild:index]; - else - { - wxDataViewItemArray dataViewChildren; + wxUnusedVar(outlineView); - wxCHECK_MSG( model, 0, "Valid model in data source does not exist." ); - (void) model->GetChildren((item == nil) ? wxDataViewItem() : wxDataViewItem([((wxPointerObject*) item) pointer]),dataViewChildren); - [self bufferItem:item withChildren:&dataViewChildren]; - if ([sortDescriptors count] > 0) - [children sortUsingFunction:CompareItems context:sortDescriptors]; + 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(wxDataViewItem([((wxPointerObject*) item) pointer])); + 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((item == nil) ? wxDataViewItem() : wxDataViewItem([((wxPointerObject*) item) pointer]),dataViewChildren); + noOfChildren = model->GetChildren(wxDataViewItemFromMaybeNilItem(item), + dataViewChildren); [self bufferItem:item withChildren:&dataViewChildren]; if ([sortDescriptors count] > 0) [children sortUsingFunction:CompareItems context:sortDescriptors]; @@ -493,16 +686,23 @@ outlineView:(NSOutlineView*)outlineView objectValueForTableColumn:(NSTableColumn*)tableColumn byItem:(id)item { - wxDataViewColumn* col(static_cast([[tableColumn identifier] pointer])); + wxUnusedVar(outlineView); - wxDataViewItem dataViewItem([((wxPointerObject*) item) pointer]); + wxCHECK_MSG( model, nil, "Valid model in data source does not exist." ); - wxVariant value; + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); + 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); + } - wxCHECK_MSG( model, 0, "Valid model in data source does not exist." ); - model->GetValue(value,dataViewItem,col->GetModelColumn()); - col->GetRenderer()->SetValue(value); return nil; } @@ -512,41 +712,22 @@ outlineView:(NSOutlineView*)outlineView forTableColumn:(NSTableColumn*)tableColumn byItem:(id)item { - wxDataViewColumn* col(static_cast([[tableColumn identifier] pointer])); - - wxDataViewItem dataViewItem([((wxPointerObject*) item) pointer]); - - wxVariant value; - if ( [object isKindOfClass:[NSString class]] ) - value = wxCFStringRef([((NSString*) object) retain]).AsString(); - else if ( [object isKindOfClass:[NSNumber class]] ) - value = (long)[((NSNumber *)object) intValue]; - else if ( [object isKindOfClass:[NSDate class]] ) - { - // 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)); + wxUnusedVar(outlineView); - // 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); - - value = dt; - } + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); col->GetRenderer()-> - OSXOnCellChanged(value, dataViewItem, col->GetModelColumn()); + OSXOnCellChanged(object, wxDataViewItemFromItem(item), col->GetModelColumn()); } -(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)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. { + 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; @@ -562,23 +743,21 @@ outlineView:(NSOutlineView*)outlineView wxSortDescriptors = [NSMutableArray arrayWithCapacity:noOfDescriptors]; for (NSUInteger i=0; iGetColumn([[newDescriptor key] intValue]) ascending:[newDescriptor ascending]] autorelease]]; } - [[outlineView dataSource] setSortDescriptors:wxSortDescriptors]; + [(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 + // 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_DATAVIEW_COLUMN_SORTED,dvc->GetId()); // variable definition event.SetEventObject(dvc); if (noOfDescriptors > 0) { - // constant definition for abbreviational purposes: wxDataViewColumn* const col = [[wxSortDescriptors objectAtIndex:0] columnPtr]; event.SetColumn(dvc->GetColumnPosition(col)); @@ -595,6 +774,9 @@ outlineView:(NSOutlineView*)outlineView -(NSDragOperation) outlineView:(NSOutlineView*)outlineView validateDrop:(id)info proposedItem:(id)item proposedChildIndex:(NSInteger)index { + wxUnusedVar(outlineView); + wxUnusedVar(index); + NSArray* supportedTypes([NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]); NSPasteboard* pasteboard([info draggingPasteboard]); @@ -603,17 +785,17 @@ outlineView:(NSOutlineView*)outlineView if (bestType == nil) return NSDragOperationNone; - NSDragOperation dragOperation; + 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(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE,dvc->GetId()); event.SetEventObject(dvc); - event.SetItem(wxDataViewItem([((wxPointerObject*) item) pointer])); + event.SetItem(wxDataViewItemFromItem(item)); event.SetModel(dvc->GetModel()); if ([bestType compare:DataViewPboardType] == NSOrderedSame) { @@ -659,7 +841,9 @@ outlineView:(NSOutlineView*)outlineView } else { - CFDataRef osxData; // needed to convert internally used UTF-16 representation to a UTF-8 representation + // needed to convert internally used UTF-16 representation to a UTF-8 + // representation + CFDataRef osxData; wxDataObjectComposite* dataObjects (new wxDataObjectComposite()); wxTextDataObject* textDataObject(new wxTextDataObject()); @@ -689,10 +873,13 @@ outlineView:(NSOutlineView*)outlineView } -(BOOL) outlineView:(NSOutlineView*)outlineView writeItems:(NSArray*)writeItems toPasteboard:(NSPasteboard*)pasteboard -// 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 { + 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; @@ -704,12 +891,13 @@ outlineView:(NSOutlineView*)outlineView 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]] retain]; // data of all items + 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: + // 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(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG,dvc->GetId()); event.SetEventObject(dvc); event.SetModel(dvc->GetModel()); @@ -719,16 +907,15 @@ outlineView:(NSOutlineView*)outlineView wxDataObjectComposite* itemObject(new wxDataObjectComposite()); // data object for current item wxString itemString; // contains the TAB concatenated data of an item - event.SetItem(wxDataViewItem([((wxPointerObject*) [writeItems objectAtIndex:itemCounter]) pointer])); + 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)) { - // constant definition for abbreviational purposes: size_t const noOfFormats = event.GetDataObject()->GetFormatCount(); - // variable definition and initialization: wxDataFormat* dataFormats(new wxDataFormat[noOfFormats]); event.GetDataObject()->GetAllFormats(dataFormats,wxDataObject::Get); @@ -739,13 +926,17 @@ outlineView:(NSOutlineView*)outlineView 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); + // give additional headroom for trailing NULL + wxMemoryBuffer dataBuffer(dataBufferSize+4); dataBuffer.AppendData(&idDataFormat,sizeof(wxDataFormatId)); switch (idDataFormat) { case wxDF_TEXT: - if (!itemStringAvailable) // otherwise wxDF_UNICODETEXT already filled up the string; and the UNICODE representation has priority + // 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); @@ -768,7 +959,6 @@ outlineView:(NSOutlineView*)outlineView break; default: wxFAIL_MSG("Data object has invalid or unsupported data format"); - [dataArray release]; return NO; } } @@ -786,7 +976,6 @@ outlineView:(NSOutlineView*)outlineView } else { - [dataArray release]; delete itemObject; return NO; // dragging was vetoed or no data available } @@ -977,6 +1166,23 @@ outlineView:(NSOutlineView*)outlineView @implementation wxCustomCell +#if 0 // starting implementation for custom cell clicks + +- (id)init +{ + self = [super init]; + [self setAction:@selector(clickedAction)]; + [self setTarget:self]; + return self; +} + +- (void) clickedAction: (id) sender +{ + wxUnusedVar(sender); +} + +#endif + -(NSSize) cellSize { wxCustomRendererObject * const @@ -994,15 +1200,36 @@ outlineView:(NSOutlineView*)outlineView { wxCustomRendererObject * const obj = static_cast([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; - // draw its own background: - [[self backgroundColor] set]; - NSRectFill(cellFrame); + // 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); - // TODO: attributes support - renderer->Render(wxFromNSRect(controlView, cellFrame), renderer->GetDC(), 0); - renderer->SetDC(NULL); + CGContextRestoreGState( context ); } -(NSRect) imageRectForBounds:(NSRect)cellFrame @@ -1149,7 +1376,9 @@ outlineView:(NSOutlineView*)outlineView { CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width; - 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: + // 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); @@ -1164,7 +1393,9 @@ outlineView:(NSOutlineView*)outlineView { CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width; - 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: + // 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); @@ -1184,7 +1415,8 @@ outlineView:(NSOutlineView*)outlineView [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): + // 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]; @@ -1192,8 +1424,10 @@ outlineView:(NSOutlineView*)outlineView } 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: + // 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; @@ -1209,8 +1443,10 @@ outlineView:(NSOutlineView*)outlineView 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 + // 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]); @@ -1222,14 +1458,21 @@ outlineView:(NSOutlineView*)outlineView } [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): + // 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 - textFrame.size.width = [self cellTextSize].width; // alignment does not influence the visualization anymore + 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]; } @@ -1268,13 +1511,20 @@ outlineView:(NSOutlineView*)outlineView 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 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 - textFrame.size.width = [self cellTextSize].width; // alignment does not influence the visualization anymore + 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 @@ -1366,16 +1616,19 @@ outlineView:(NSOutlineView*)outlineView // actions // -(void) actionDoubleClick:(id)sender -// actually the documentation (NSTableView 2007-10-31) for doubleAction: and setDoubleAction: seems to be wrong as this action message is always sent -// whether the cell is editable or not { + wxUnusedVar(sender); + + // actually the documentation (NSTableView 2007-10-31) for doubleAction: + // and setDoubleAction: seems to be wrong as this action message is always + // sent whether the cell is editable or not wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED,dvc->GetId()); // variable definition + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_ACTIVATED,dvc->GetId()); event.SetEventObject(dvc); - event.SetItem(wxDataViewItem([((wxPointerObject*) [self itemAtRow:[self clickedRow]]) pointer])); + event.SetItem(wxDataViewItemFromItem([self itemAtRow:[self clickedRow]])); dvc->GetEventHandler()->ProcessEvent(event); } @@ -1384,12 +1637,15 @@ outlineView:(NSOutlineView*)outlineView // contextual menus // -(NSMenu*) menuForEvent:(NSEvent*)theEvent -// this method does not do any special menu event handling but only sends an event message; therefore, the user -// has full control if a context menu should be shown or not { + wxUnusedVar(theEvent); + + // this method does not do any special menu event handling but only sends + // an event message; therefore, the user has full control if a context + // menu should be shown or not wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU,dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU,dvc->GetId()); wxDataViewItemArray selectedItems; @@ -1397,8 +1653,9 @@ outlineView:(NSOutlineView*)outlineView event.SetEventObject(dvc); event.SetModel(dvc->GetModel()); // get the item information; - // theoretically more than one ID can be returned but the event can only handle one item, therefore only the first - // item of the array is returned: + // theoretically more than one ID can be returned but the event can only + // handle one item, therefore only the first item of the array is + // returned: if (dvc->GetSelections(selectedItems) > 0) event.SetItem(selectedItems[0]); dvc->GetEventHandler()->ProcessEvent(event); @@ -1411,12 +1668,13 @@ outlineView:(NSOutlineView*)outlineView // -(void) outlineView:(NSOutlineView*)outlineView mouseDownInHeaderOfTableColumn:(NSTableColumn*)tableColumn { - wxDataViewColumn* const col(static_cast([[tableColumn identifier] pointer])); + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); wxDataViewEvent - event(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK,dvc->GetId()); + event(wxEVT_DATAVIEW_COLUMN_HEADER_CLICK,dvc->GetId()); // first, send an event that the user clicked into a column's header: @@ -1426,13 +1684,15 @@ outlineView:(NSOutlineView*)outlineView dvc->HandleWindowEvent(event); // now, check if the click may have had an influence on sorting, too; - // the sorting setup has to be done only if the clicked table column is sortable and has not been used for - // sorting before the click; if the column is already responsible for sorting the native control changes - // the sorting direction automatically and informs the data source via outlineView:sortDescriptorsDidChange: + // the sorting setup has to be done only if the clicked table column is + // sortable and has not been used for sorting before the click; if the + // column is already responsible for sorting the native control changes + // the sorting direction automatically and informs the data source via + // outlineView:sortDescriptorsDidChange: if (col->IsSortable() && ([tableColumn sortDescriptorPrototype] == nil)) { - // remove the sort order from the previously sorted column table (it can also be that - // no sorted column table exists): + // remove the sort order from the previously sorted column table (it + // can also be that no sorted column table exists): UInt32 const noOfColumns = [outlineView numberOfColumns]; for (UInt32 i=0; iGetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,dvc->GetId()); // variable definition + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_COLLAPSING,dvc->GetId()); event.SetEventObject(dvc); - event.SetItem (wxDataViewItem([((wxPointerObject*) item) pointer])); + event.SetItem (wxDataViewItemFromItem(item)); event.SetModel (dvc->GetModel()); // finally send the equivalent wxWidget event: dvc->GetEventHandler()->ProcessEvent(event); @@ -1468,13 +1730,15 @@ outlineView:(NSOutlineView*)outlineView -(BOOL) outlineView:(NSOutlineView*)outlineView shouldExpandItem:(id)item { + wxUnusedVar(outlineView); + wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING,dvc->GetId()); // variable definition + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_EXPANDING,dvc->GetId()); event.SetEventObject(dvc); - event.SetItem (wxDataViewItem([((wxPointerObject*) item) pointer])); + event.SetItem (wxDataViewItemFromItem(item)); event.SetModel (dvc->GetModel()); // finally send the equivalent wxWidget event: dvc->GetEventHandler()->ProcessEvent(event); @@ -1484,99 +1748,50 @@ outlineView:(NSOutlineView*)outlineView -(BOOL) outlineView:(NSOutlineView*)outlineView shouldSelectTableColumn:(NSTableColumn*)tableColumn { + wxUnusedVar(tableColumn); + wxUnusedVar(outlineView); + return NO; } -(void) outlineView:(wxCocoaOutlineView*)outlineView -willDisplayCell:(id)cell -forTableColumn:(NSTableColumn*)tableColumn -item:(id)item + willDisplayCell:(id)cell + forTableColumn:(NSTableColumn*)tableColumn + item:(id)item { + wxUnusedVar(outlineView); + wxDataViewCtrl * const dvc = implementation->GetDataViewCtrl(); wxDataViewModel * const model = dvc->GetModel(); - wxDataViewColumn * const - dvCol(static_cast( - [[tableColumn identifier] pointer] - ) - ); - - wxDataViewRenderer * const renderer = dvCol->GetRenderer(); - wxDataViewRendererNativeData * const data = renderer->GetNativeData(); - - wxDataViewItem dvItem([static_cast(item) pointer]); - - // set the font and text colour to use: we need to do it if we had ever - // changed them before, even if this item itself doesn't have any special - // attributes as otherwise it would reuse the attributes from the previous - // cell rendered using the same renderer - NSFont *font = NULL; - NSColor *colText = NULL; - - wxDataViewItemAttr attr; - if ( model && model->GetAttr(dvItem, dvCol->GetModelColumn(), attr) ) - { - if ( attr.HasFont() ) - { - font = data->GetOriginalFont(); - if ( !font ) - { - // this is the first time we're setting the font, remember the - // original one before changing it - font = [cell font]; - data->SaveOriginalFont(font); - } + wxDataViewColumn* const + dvCol([static_cast(tableColumn) getColumnPointer]); + const unsigned colIdx = dvCol->GetModelColumn(); - if ( font ) - { - // FIXME: using wxFont methods here doesn't work for some reason - NSFontManager * const fm = [NSFontManager sharedFontManager]; - if ( attr.GetBold() ) - font = [fm convertFont:font toHaveTrait:NSBoldFontMask]; - if ( attr.GetItalic() ) - font = [fm convertFont:font toHaveTrait:NSItalicFontMask]; - } - //else: can't change font if the cell doesn't have any - } + wxDataViewItem dvItem(wxDataViewItemFromItem(item)); - if ( attr.HasColour() ) - { - // we can set font for any cell but only NSTextFieldCell provides - // a method for setting text colour so check that this method is - // available before using it - if ( [cell respondsToSelector:@selector(setTextColor:)] && - [cell respondsToSelector:@selector(textColor)] ) - { - if ( !data->GetOriginalTextColour() ) - { - data->SaveOriginalTextColour([cell textColor]); - } - - const wxColour& c = attr.GetColour(); - colText = [NSColor colorWithDeviceRed:c.Red() / 255. - green:c.Green() / 255. - blue:c.Blue() / 255. - alpha:c.Alpha() / 255.]; - } - } - } - - if ( !font ) - font = data->GetOriginalFont(); - if ( !colText ) - colText = data->GetOriginalTextColour(); - - if ( font ) - [cell setFont:font]; - - if ( colText ) - [cell setTextColor:colText]; + if ( !model->HasValue(dvItem, colIdx) ) + return; + wxDataViewRenderer * const renderer = dvCol->GetRenderer(); + wxDataViewRendererNativeData * const data = renderer->GetNativeData(); + // let the renderer know about what it's going to render next data->SetColumnPtr(tableColumn); data->SetItem(item); data->SetItemCell(cell); + // use the attributes: notice that we need to do this whether we have them + // or not as even if this cell doesn't have any attributes, the previous + // one might have had some and then we need to reset them back to default + wxDataViewItemAttr attr; + model->GetAttr(dvItem, colIdx, attr); + renderer->OSXApplyAttr(attr); + + // set the state (enabled/disabled) of the item + renderer->OSXApplyEnabled(model->IsEnabled(dvItem, colIdx)); + + // and finally do draw it renderer->MacRender(); } @@ -1587,11 +1802,14 @@ item:(id)item { int const newColumnPosition = [[[notification userInfo] objectForKey:@"NSNewColumn"] intValue]; - wxDataViewColumn* const col(static_cast([[[[self tableColumns] objectAtIndex:newColumnPosition] identifier] pointer])); + NSTableColumn* + tableColumn = [[self tableColumns] objectAtIndex:newColumnPosition]; + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_COLUMN_REORDERED,dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_COLUMN_REORDERED,dvc->GetId()); event.SetEventObject(dvc); @@ -1604,11 +1822,12 @@ item:(id)item { wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_COLLAPSED,dvc->GetId()); event.SetEventObject(dvc); - event.SetItem(wxDataViewItem([((wxPointerObject*) [[notification userInfo] objectForKey:@"NSObject"]) pointer])); + event.SetItem(wxDataViewItemFromItem( + [[notification userInfo] objectForKey:@"NSObject"])); dvc->GetEventHandler()->ProcessEvent(event); } @@ -1616,41 +1835,47 @@ item:(id)item { wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_EXPANDED,dvc->GetId()); event.SetEventObject(dvc); - event.SetItem(wxDataViewItem([((wxPointerObject*) [[notification userInfo] objectForKey:@"NSObject"]) pointer])); + event.SetItem(wxDataViewItemFromItem( + [[notification userInfo] objectForKey:@"NSObject"])); dvc->GetEventHandler()->ProcessEvent(event); } -(void) outlineViewSelectionDidChange:(NSNotification*)notification { - wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); + wxUnusedVar(notification); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED,dvc->GetId()); // variable definition + wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); + wxDataViewEvent event(wxEVT_DATAVIEW_SELECTION_CHANGED,dvc->GetId()); event.SetEventObject(dvc); - event.SetModel (dvc->GetModel()); + event.SetModel(dvc->GetModel()); + event.SetItem(dvc->GetSelection()); dvc->GetEventHandler()->ProcessEvent(event); } -(void) textDidBeginEditing:(NSNotification*)notification -// this notification is only sent if the user started modifying the cell (not when the user clicked into the cell -// and the cell's editor is called!) { - // call method of superclass (otherwise editing does not work correctly - the outline data source class is not - // informed about a change of data): + // this notification is only sent if the user started modifying the cell + // (not when the user clicked into the cell and the cell's editor is + // called!) + + // call method of superclass (otherwise editing does not work correctly - + // the outline data source class is not informed about a change of data): [super textDidBeginEditing:notification]; // remember the column being edited, it will be used in textDidEndEditing: currentlyEditedColumn = [self editedColumn]; currentlyEditedRow = [self editedRow]; - wxDataViewColumn* const col = - static_cast( - [[[[self tableColumns] objectAtIndex:currentlyEditedColumn] identifier] pointer]); + NSTableColumn* + tableColumn = [[self tableColumns] objectAtIndex:currentlyEditedColumn]; + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); @@ -1660,11 +1885,11 @@ item:(id)item // now, send the event: wxDataViewEvent - event(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_STARTED,dvc->GetId()); // variable definition + event(wxEVT_DATAVIEW_ITEM_EDITING_STARTED,dvc->GetId()); event.SetEventObject(dvc); event.SetItem( - wxDataViewItem([((wxPointerObject*) [self itemAtRow:currentlyEditedRow]) pointer])); + wxDataViewItemFromItem([self itemAtRow:currentlyEditedRow])); event.SetColumn(dvc->GetColumnPosition(col)); event.SetDataViewColumn(col); dvc->GetEventHandler()->ProcessEvent(event); @@ -1672,30 +1897,34 @@ item:(id)item -(void) textDidEndEditing:(NSNotification*)notification { - // call method of superclass (otherwise editing does not work correctly - the outline data source class is not - // informed about a change of data): + // call method of superclass (otherwise editing does not work correctly - + // the outline data source class is not informed about a change of data): [super textDidEndEditing:notification]; - // under OSX an event indicating the end of an editing session can be sent even if no event indicating a start of an - // editing session has been sent (see Documentation for NSControl controlTextDidEndEditing:); this is not expected by a user - // of the wxWidgets library and therefore an wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE event is only sent if a corresponding - // wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_STARTED has been sent before; to check if a wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_STARTED - // has been sent the last edited column/row are valid: + // under OSX an event indicating the end of an editing session can be sent + // even if no event indicating a start of an editing session has been sent + // (see Documentation for NSControl controlTextDidEndEditing:); this is + // not expected by a user of the wxWidgets library and therefore an + // wxEVT_DATAVIEW_ITEM_EDITING_DONE event is only sent if a + // corresponding wxEVT_DATAVIEW_ITEM_EDITING_STARTED has been sent + // before; to check if a wxEVT_DATAVIEW_ITEM_EDITING_STARTED has + // been sent the last edited column/row are valid: if ( currentlyEditedColumn != -1 && currentlyEditedRow != -1 ) { - wxDataViewColumn* const col = - static_cast( - [[[[self tableColumns] objectAtIndex:currentlyEditedColumn] identifier] pointer]); + NSTableColumn* + tableColumn = [[self tableColumns] objectAtIndex:currentlyEditedColumn]; + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); // send event to wxWidgets: wxDataViewEvent - event(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE,dvc->GetId()); // variable definition + event(wxEVT_DATAVIEW_ITEM_EDITING_DONE,dvc->GetId()); event.SetEventObject(dvc); event.SetItem( - wxDataViewItem([((wxPointerObject*) [self itemAtRow:currentlyEditedRow]) pointer])); + wxDataViewItemFromItem([self itemAtRow:currentlyEditedRow])); event.SetColumn(dvc->GetColumnPosition(col)); event.SetDataViewColumn(col); dvc->GetEventHandler()->ProcessEvent(event); @@ -1708,19 +1937,25 @@ item:(id)item } @end + // ============================================================================ // wxCocoaDataViewControl // ============================================================================ -// -// constructors / destructor -// - wxCocoaDataViewControl::wxCocoaDataViewControl(wxWindow* peer, const wxPoint& pos, const wxSize& size, long style) -:wxWidgetCocoaImpl(peer,[[NSScrollView alloc] initWithFrame:wxOSXGetFrameForControl(peer,pos,size)]), - m_DataSource(NULL), m_OutlineView([[wxCocoaOutlineView alloc] init]) + +wxCocoaDataViewControl::wxCocoaDataViewControl(wxWindow* peer, + const wxPoint& pos, + const wxSize& size, + long style) + : wxWidgetCocoaImpl + ( + peer, + [[NSScrollView alloc] initWithFrame:wxOSXGetFrameForControl(peer,pos,size)] + ), + m_DataSource(NULL), + m_OutlineView([[wxCocoaOutlineView alloc] init]) { // initialize scrollview (the outline view is part of a scrollview): - NSScrollView* scrollview = (NSScrollView*) GetWXWidget(); // definition for abbreviational purposes - + NSScrollView* scrollview = (NSScrollView*) GetWXWidget(); [scrollview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; [scrollview setBorderType:NSNoBorder]; @@ -1729,12 +1964,19 @@ item:(id)item [scrollview setAutohidesScrollers:YES]; [scrollview setDocumentView:m_OutlineView]; - // setting up the native control itself - NSUInteger maskGridStyle(NSTableViewGridNone); + // we cannot call InstallHandler(m_OutlineView) here, because we are handling + // our action:s ourselves, only associate the view with this impl + Associate(m_OutlineView,this); + // initialize the native control itself too + InitOutlineView(style); +} +void wxCocoaDataViewControl::InitOutlineView(long style) +{ [m_OutlineView setImplementation:this]; [m_OutlineView setColumnAutoresizingStyle:NSTableViewSequentialColumnAutoresizingStyle]; [m_OutlineView setIndentationPerLevel:GetDataViewCtrl()->GetIndent()]; + NSUInteger maskGridStyle(NSTableViewGridNone); if (style & wxDV_HORIZ_RULES) maskGridStyle |= NSTableViewSolidHorizontalGridLineMask; if (style & wxDV_VERT_RULES) @@ -1742,6 +1984,9 @@ item:(id)item [m_OutlineView setGridStyleMask:maskGridStyle]; [m_OutlineView setAllowsMultipleSelection: (style & wxDV_MULTIPLE) != 0]; [m_OutlineView setUsesAlternatingRowBackgroundColors:(style & wxDV_ROW_LINES) != 0]; + + if ( style & wxDV_NO_HEADER ) + [m_OutlineView setHeaderView:nil]; } wxCocoaDataViewControl::~wxCocoaDataViewControl() @@ -1755,23 +2000,16 @@ wxCocoaDataViewControl::~wxCocoaDataViewControl() // bool wxCocoaDataViewControl::ClearColumns() { - bool const bufAllowsMultipleSelection = [m_OutlineView allowsMultipleSelection]; - - - // 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; + // 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; // therefore, the whole view is deleted and newly constructed: [m_OutlineView release]; m_OutlineView = [[wxCocoaOutlineView alloc] init]; [((NSScrollView*) GetWXWidget()) setDocumentView:m_OutlineView]; - - // setting up the native control itself - [m_OutlineView setImplementation:this]; - [m_OutlineView setColumnAutoresizingStyle:NSTableViewSequentialColumnAutoresizingStyle]; - [m_OutlineView setIndentationPerLevel:GetDataViewCtrl()->GetIndent()]; - if (bufAllowsMultipleSelection) - [m_OutlineView setAllowsMultipleSelection:YES]; [m_OutlineView setDataSource:m_DataSource]; - // done: + + InitOutlineView(GetDataViewCtrl()->GetWindowStyle()); + return true; } @@ -1781,7 +2019,7 @@ bool wxCocoaDataViewControl::DeleteColumn(wxDataViewColumn* columnPtr) [m_OutlineView setOutlineTableColumn:nil]; // due to a bug this does not work [m_OutlineView removeTableColumn:columnPtr->GetNativeData()->GetNativeColumnPtr()]; // due to a confirmed bug #6555162 the deletion does not work for // outline table columns (... and there is no workaround) - return (([m_OutlineView columnWithIdentifier:[[[wxPointerObject alloc] initWithPointer:columnPtr] autorelease]]) == -1); + return (([m_OutlineView columnWithIdentifier:[wxDVCNSTableColumn identifierForColumnPointer:columnPtr]]) == -1); } void wxCocoaDataViewControl::DoSetExpanderColumn(const wxDataViewColumn *columnPtr) @@ -1791,12 +2029,13 @@ void wxCocoaDataViewControl::DoSetExpanderColumn(const wxDataViewColumn *columnP wxDataViewColumn* wxCocoaDataViewControl::GetColumn(unsigned int pos) const { - return static_cast([[[[m_OutlineView tableColumns] objectAtIndex:pos] identifier] pointer]); + NSTableColumn* tableColumn = [[m_OutlineView tableColumns] objectAtIndex:pos]; + return [static_cast(tableColumn) getColumnPointer]; } int wxCocoaDataViewControl::GetColumnPosition(const wxDataViewColumn *columnPtr) const { - return [m_OutlineView columnWithIdentifier:[[[wxPointerObject alloc] initWithPointer:const_cast(columnPtr)] autorelease]]; + return [m_OutlineView columnWithIdentifier:[wxDVCNSTableColumn identifierForColumnPointer:columnPtr]]; } bool wxCocoaDataViewControl::InsertColumn(unsigned int pos, wxDataViewColumn* columnPtr) @@ -1804,15 +2043,138 @@ bool wxCocoaDataViewControl::InsertColumn(unsigned int pos, wxDataViewColumn* co // create column and set the native data of the dataview column: NSTableColumn *nativeColumn = ::CreateNativeColumn(columnPtr); columnPtr->GetNativeData()->SetNativeColumnPtr(nativeColumn); - // as the native control does not allow the insertion of a column at a specified position the column is first appended and - // - if necessary - moved to its final position: + // as the native control does not allow the insertion of a column at a + // specified position the column is first appended and - if necessary - + // moved to its final position: [m_OutlineView addTableColumn:nativeColumn]; if (pos != static_cast([m_OutlineView numberOfColumns]-1)) [m_OutlineView moveColumn:[m_OutlineView numberOfColumns]-1 toColumn:pos]; + + // set columns width now that it can be computed even for autosized columns: + columnPtr->SetWidth(columnPtr->GetWidthVariable()); + // done: return true; } +void wxCocoaDataViewControl::FitColumnWidthToContent(unsigned int pos) +{ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + const int count = GetCount(); + NSTableColumn *column = GetColumn(pos)->GetNativeData()->GetNativeColumnPtr(); + + class MaxWidthCalculator + { + public: + MaxWidthCalculator(wxCocoaOutlineView *view, + NSTableColumn *column, unsigned columnIndex) + : m_width(0), + m_view(view), + m_column(columnIndex), + m_indent(0) + { + // account for indentation in the column with expander + if ( column == [m_view outlineTableColumn] ) + m_indent = [m_view indentationPerLevel]; + } + + void UpdateWithWidth(int width) + { + m_width = wxMax(m_width, width); + } + + void UpdateWithRow(int row) + { + NSCell *cell = [m_view preparedCellAtColumn:m_column row:row]; + unsigned cellWidth = [cell cellSize].width + 1/*round the float up*/; + + if ( m_indent ) + cellWidth += m_indent * ([m_view levelForRow:row] + 1); + + m_width = wxMax(m_width, cellWidth); + } + + int GetMaxWidth() const { return m_width; } + + private: + int m_width; + wxCocoaOutlineView *m_view; + unsigned m_column; + int m_indent; + }; + + MaxWidthCalculator calculator(m_OutlineView, column, pos); + + if ( [column headerCell] ) + { + calculator.UpdateWithWidth([[column headerCell] cellSize].width + 1/*round the float up*/); + } + + // The code below deserves some explanation. For very large controls, we + // simply can't afford to calculate sizes for all items, it takes too + // long. So the best we can do is to check the first and the last N/2 + // items in the control for some sufficiently large N and calculate best + // sizes from that. That can result in the calculated best width being too + // small for some outliers, but it's better to get slightly imperfect + // result than to wait several seconds after every update. To avoid highly + // visible miscalculations, we also include all currently visible items + // no matter what. Finally, the value of N is determined dynamically by + // measuring how much time we spent on the determining item widths so far. + +#if wxUSE_STOPWATCH + int top_part_end = count; + static const long CALC_TIMEOUT = 20/*ms*/; + // don't call wxStopWatch::Time() too often + static const unsigned CALC_CHECK_FREQ = 100; + wxStopWatch timer; +#else + // use some hard-coded limit, that's the best we can do without timer + int top_part_end = wxMin(500, count); +#endif // wxUSE_STOPWATCH/!wxUSE_STOPWATCH + + int row = 0; + + for ( row = 0; row < top_part_end; row++ ) + { +#if wxUSE_STOPWATCH + if ( row % CALC_CHECK_FREQ == CALC_CHECK_FREQ-1 && + timer.Time() > CALC_TIMEOUT ) + break; +#endif // wxUSE_STOPWATCH + calculator.UpdateWithRow(row); + } + + // row is the first unmeasured item now; that's our value of N/2 + + if ( row < count ) + { + top_part_end = row; + + // add bottom N/2 items now: + const int bottom_part_start = wxMax(row, count - row); + for ( row = bottom_part_start; row < count; row++ ) + calculator.UpdateWithRow(row); + + // finally, include currently visible items in the calculation: + const NSRange visible = [m_OutlineView rowsInRect:[m_OutlineView visibleRect]]; + const int first_visible = wxMax(visible.location, top_part_end); + const int last_visible = wxMin(first_visible + visible.length, bottom_part_start); + + for ( row = first_visible; row < last_visible; row++ ) + calculator.UpdateWithRow(row); + + wxLogTrace("dataview", + "determined best size from %d top, %d bottom plus %d more visible items out of %d total", + top_part_end, + count - bottom_part_start, + wxMax(0, last_visible - first_visible), + count); + } + + [column setWidth:calculator.GetMaxWidth()]; +#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 +} + // // item related methods (inherited from wxDataViewWidgetImpl) // @@ -1873,9 +2235,9 @@ bool wxCocoaDataViewControl::IsExpanded(const wxDataViewItem& item) const bool wxCocoaDataViewControl::Reload() { [m_DataSource clearBuffers]; + [m_OutlineView reloadData]; [m_OutlineView scrollColumnToVisible:0]; [m_OutlineView scrollRowToVisible:0]; - [m_OutlineView reloadData]; return true; } @@ -1899,6 +2261,8 @@ bool wxCocoaDataViewControl::Remove(const wxDataViewItem& parent, const wxDataVi bool wxCocoaDataViewControl::Update(const wxDataViewColumn *columnPtr) { + wxUnusedVar(columnPtr); + return false; } @@ -1936,6 +2300,35 @@ bool wxCocoaDataViewControl::AssociateModel(wxDataViewModel* model) // // selection related methods (inherited from wxDataViewWidgetImpl) // + +wxDataViewItem wxCocoaDataViewControl::GetCurrentItem() const +{ + return wxDataViewItem([[m_OutlineView itemAtRow:[m_OutlineView selectedRow]] pointer]); +} + +wxDataViewColumn *wxCocoaDataViewControl::GetCurrentColumn() const +{ + int col = [m_OutlineView selectedColumn]; + if ( col == -1 ) + return NULL; + return GetColumn(col); +} + +void wxCocoaDataViewControl::SetCurrentItem(const wxDataViewItem& item) +{ + // We can't have unselected current item in a NSTableView, as the + // documentation of its deselectRow method explains, the control will + // automatically change the current item to the closest still selected item + // if the current item is deselected. So we have no choice but to select + // the item in the same time as making it current. + Select(item); +} + +int wxCocoaDataViewControl::GetSelectedItemsCount() const +{ + return [m_OutlineView numberOfSelectedRows]; +} + int wxCocoaDataViewControl::GetSelections(wxDataViewItemArray& sel) const { NSIndexSet* selectedRowIndexes([m_OutlineView selectedRowIndexes]); @@ -1963,7 +2356,7 @@ void wxCocoaDataViewControl::Select(const wxDataViewItem& item) { if (item.IsOk()) [m_OutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]] - byExtendingSelection:NO]; + byExtendingSelection:GetDataViewCtrl()->HasFlag(wxDV_MULTIPLE) ? YES : NO]; } void wxCocoaDataViewControl::SelectAll() @@ -1994,7 +2387,7 @@ wxDataViewColumn* wxCocoaDataViewControl::GetSortingColumn() const for (UInt32 i=0; i([[[columns objectAtIndex:i] identifier] pointer]); + return GetColumn(i); return NULL; } @@ -2004,6 +2397,11 @@ void wxCocoaDataViewControl::Resort() [m_OutlineView reloadData]; } +void wxCocoaDataViewControl::StartEditor( const wxDataViewItem & item, unsigned int column ) +{ + [m_OutlineView editColumn:column row:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]] withEvent:nil select:YES]; +} + // // other methods (inherited from wxDataViewWidgetImpl) // @@ -2024,7 +2422,7 @@ void wxCocoaDataViewControl::HitTest(const wxPoint& point, wxDataViewItem& item, indexRow = [m_OutlineView rowAtPoint: nativePoint]; if ((indexColumn >= 0) && (indexRow >= 0)) { - columnPtr = static_cast([[[[m_OutlineView tableColumns] objectAtIndex:indexColumn] identifier] pointer]); + columnPtr = GetColumn(indexColumn); item = wxDataViewItem([[m_OutlineView itemAtRow:indexRow] pointer]); } else @@ -2136,6 +2534,11 @@ wxDataObjectComposite* wxCocoaDataViewControl::GetDnDDataObjects(NSData* dataObj } } +id wxCocoaDataViewControl::GetItemAtRow(int row) const +{ + return [m_OutlineView itemAtRow:row]; +} + // ---------------------------------------------------------------------------- // wxDataViewRendererNativeData // ---------------------------------------------------------------------------- @@ -2231,14 +2634,112 @@ wxEllipsizeMode wxDataViewRenderer::GetEllipsizeMode() const return GetNativeData()->GetEllipsizeMode(); } -void wxDataViewRenderer::OSXOnCellChanged(const wxVariant& value, - const wxDataViewItem& item, - unsigned col) +void +wxDataViewRenderer::OSXOnCellChanged(NSObject *object, + const wxDataViewItem& item, + unsigned col) { + // TODO: This code should really be removed and this function be made pure + // virtual. We just need to decide what to do with custom renderers + // (i.e. wxDataViewCustomRenderer), currently OS X "in place" editing + // which doesn't really create an editor control is not compatible + // with the in place editing under other platforms. + + wxVariant value; + if ( [object isKindOfClass:[NSString class]] ) + value = ObjectToString(object); + else if ( [object isKindOfClass:[NSNumber class]] ) + value = ObjectToLong(object); + else if ( [object isKindOfClass:[NSDate class]] ) + value = ObjectToDate(object); + else + { + wxFAIL_MSG( wxString::Format + ( + "unknown value type %s", + wxCFStringRef::AsString([object className]) + )); + return; + } + wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); model->ChangeValue(value, item, col); } +void wxDataViewRenderer::OSXApplyAttr(const wxDataViewItemAttr& attr) +{ + wxDataViewRendererNativeData * const data = GetNativeData(); + NSCell * const cell = data->GetItemCell(); + + // set the font and text colour to use: we need to do it if we had ever + // changed them before, even if this item itself doesn't have any special + // attributes as otherwise it would reuse the attributes from the previous + // cell rendered using the same renderer + NSFont *font = NULL; + NSColor *colText = NULL; + + if ( attr.HasFont() ) + { + font = data->GetOriginalFont(); + if ( !font ) + { + // this is the first time we're setting the font, remember the + // original one before changing it + font = [cell font]; + data->SaveOriginalFont(font); + } + + if ( font ) + { + // FIXME: using wxFont methods here doesn't work for some reason + NSFontManager * const fm = [NSFontManager sharedFontManager]; + if ( attr.GetBold() ) + font = [fm convertFont:font toHaveTrait:NSBoldFontMask]; + if ( attr.GetItalic() ) + font = [fm convertFont:font toHaveTrait:NSItalicFontMask]; + } + //else: can't change font if the cell doesn't have any + } + + if ( attr.HasColour() ) + { + // we can set font for any cell but only NSTextFieldCell provides + // a method for setting text colour so check that this method is + // available before using it + if ( [cell respondsToSelector:@selector(setTextColor:)] && + [cell respondsToSelector:@selector(textColor)] ) + { + if ( !data->GetOriginalTextColour() ) + { + // the cast to (untyped) id is safe because of the check above + data->SaveOriginalTextColour([(id)cell textColor]); + } + + const wxColour& c = attr.GetColour(); + colText = [NSColor colorWithCalibratedRed:c.Red() / 255. + green:c.Green() / 255. + blue:c.Blue() / 255. + alpha:c.Alpha() / 255.]; + } + } + + if ( !font ) + font = data->GetOriginalFont(); + if ( !colText ) + colText = data->GetOriginalTextColour(); + + if ( font ) + [cell setFont:font]; + + if ( colText ) + [(id)cell setTextColor:colText]; +} + +void wxDataViewRenderer::OSXApplyEnabled(bool enabled) +{ + [GetNativeData()->GetItemCell() setEnabled:enabled]; +} + IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer,wxDataViewRendererBase) // --------------------------------------------------------- @@ -2247,7 +2748,7 @@ IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer,wxDataViewRendererBase) wxDataViewCustomRenderer::wxDataViewCustomRenderer(const wxString& varianttype, wxDataViewCellMode mode, int align) - : wxDataViewRenderer(varianttype, mode, align), + : wxDataViewCustomRendererBase(varianttype, mode, align), m_editorCtrlPtr(NULL), m_DCPtr(NULL) { @@ -2260,6 +2761,18 @@ bool wxDataViewCustomRenderer::MacRender() return true; } +void wxDataViewCustomRenderer::OSXApplyAttr(const wxDataViewItemAttr& attr) +{ + // simply save the attribute so that it could be reused from our Render() + SetAttr(attr); + + // it's not necessary to call the base class version which sets the cell + // properties to correspond to this attribute because we currently don't + // use any NSCell methods in custom renderers anyhow but if we ever start + // doing this (e.g. override RenderText() here to use NSTextFieldCell + // methods), then we should pass it on to wxDataViewRenderer here +} + IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer) // --------------------------------------------------------- @@ -2293,6 +2806,15 @@ bool wxDataViewTextRenderer::MacRender() } } +void +wxDataViewTextRenderer::OSXOnCellChanged(NSObject *value, + const wxDataViewItem& item, + unsigned col) +{ + wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); + model->ChangeValue(ObjectToString(value), item, col); +} + IMPLEMENT_CLASS(wxDataViewTextRenderer,wxDataViewRenderer) // --------------------------------------------------------- @@ -2311,11 +2833,13 @@ wxDataViewBitmapRenderer::wxDataViewBitmapRenderer(const wxString& varianttype, [cell release]; } +// This method returns 'true' if +// - the passed bitmap is valid and it could be assigned to the native data +// browser; +// - the passed bitmap is invalid (or is not initialized); this case +// simulates a non-existing bitmap. +// In all other cases the method returns 'false'. bool wxDataViewBitmapRenderer::MacRender() - // This method returns 'true' if - // - the passed bitmap is valid and it could be assigned to the native data browser; - // - the passed bitmap is invalid (or is not initialized); this case simulates a non-existing bitmap. - // In all other cases the method returns 'false'. { wxCHECK_MSG(GetValue().GetType() == GetVariantType(),false,wxString("Bitmap renderer cannot render value; value type: ") << GetValue().GetType()); @@ -2335,20 +2859,32 @@ IMPLEMENT_CLASS(wxDataViewBitmapRenderer,wxDataViewRenderer) wxDataViewChoiceRenderer::wxDataViewChoiceRenderer(const wxArrayString& choices, wxDataViewCellMode mode, int alignment) - : wxDataViewRenderer(wxT("string"),mode,alignment), m_Choices(choices) + : wxDataViewRenderer(wxT("string"), mode, alignment), + m_choices(choices) { NSPopUpButtonCell* cell; cell = [[NSPopUpButtonCell alloc] init]; [cell setControlSize:NSMiniControlSize]; - [cell setFont:[[NSFont fontWithName:[[cell font] fontName] size:[NSFont systemFontSizeForControlSize:NSMiniControlSize]] autorelease]]; + [cell setFont:[NSFont fontWithName:[[cell font] fontName] size:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]]; for (size_t i=0; iGetOwner()->GetModel(); + model->ChangeValue(GetChoice(ObjectToLong(value)), item, col); +} + bool wxDataViewChoiceRenderer::MacRender() { if (GetValue().GetType() == GetVariantType()) @@ -2396,33 +2932,47 @@ bool wxDataViewDateRenderer::MacRender() if (GetValue().GetDateTime().IsValid()) { // -- find best fitting style to show the date -- - // as the style should be identical for all cells a reference date instead of the actual cell's date - // value is used for all cells; this reference date is stored in the renderer's native data section - // for speed purposes; otherwise, the reference date's string has to be recalculated for each item that - // may become timewise long if a lot of rows using dates exist; - // the algorithm has the preference to display as much information as possible in the first instance; - // but as this is often impossible due to space restrictions the style is shortened per loop; finally, - // if the shortest time and date format does not fit into the cell the time part is dropped; - // remark: the time part itself is not modified per iteration loop and only uses the short style, - // means that only the hours and minutes are being shown - [GetNativeData()->GetItemCell() setObjectValue:GetNativeData()->GetObject()]; // GetObject() returns a date for testing the size of a date object + // as the style should be identical for all cells a reference date + // instead of the actual cell's date value is used for all cells; + // this reference date is stored in the renderer's native data + // section for speed purposes; otherwise, the reference date's + // string has to be recalculated for each item that may become + // timewise long if a lot of rows using dates exist; the algorithm + // has the preference to display as much information as possible + // in the first instance; but as this is often impossible due to + // space restrictions the style is shortened per loop; finally, if + // the shortest time and date format does not fit into the cell + // the time part is dropped; remark: the time part itself is not + // modified per iteration loop and only uses the short style, + // means that only the hours and minutes are being shown + + // GetObject() returns a date for testing the size of a date object + [GetNativeData()->GetItemCell() setObjectValue:GetNativeData()->GetObject()]; [[GetNativeData()->GetItemCell() formatter] setTimeStyle:NSDateFormatterShortStyle]; for (int dateFormatterStyle=4; dateFormatterStyle>0; --dateFormatterStyle) { [[GetNativeData()->GetItemCell() formatter] setDateStyle:(NSDateFormatterStyle)dateFormatterStyle]; if (dateFormatterStyle == 1) { - // if the shortest style for displaying the date and time is too long to be fully visible remove the time part of the date: + // if the shortest style for displaying the date and time + // is too long to be fully visible remove the time part of + // the date: if ([GetNativeData()->GetItemCell() cellSize].width > [GetNativeData()->GetColumnPtr() width]) [[GetNativeData()->GetItemCell() formatter] setTimeStyle:NSDateFormatterNoStyle]; - break; // basically not necessary as the loop would end anyway but let's save the last comparison + { + // basically not necessary as the loop would end anyway + // but let's save the last comparison + break; + } } else if ([GetNativeData()->GetItemCell() cellSize].width <= [GetNativeData()->GetColumnPtr() width]) break; } - // set data (the style is set by the previous loop); - // 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 - // from the local to UTC timezone when adding the seconds to 1970-01-01 UTC: + // set data (the style is set by the previous loop); 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 from the local to UTC timezone when adding the + // seconds to 1970-01-01 UTC: [GetNativeData()->GetItemCell() setObjectValue:[NSDate dateWithTimeIntervalSince1970:GetValue().GetDateTime().ToUTC().Subtract(wxDateTime(1,wxDateTime::Jan,1970)).GetSeconds().ToDouble()]]; } return true; @@ -2434,6 +2984,15 @@ bool wxDataViewDateRenderer::MacRender() } } +void +wxDataViewDateRenderer::OSXOnCellChanged(NSObject *value, + const wxDataViewItem& item, + unsigned col) +{ + wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); + model->ChangeValue(ObjectToDate(value), item, col); +} + IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer,wxDataViewRenderer) // --------------------------------------------------------- @@ -2465,6 +3024,8 @@ bool wxDataViewIconTextRenderer::MacRender() iconText << GetValue(); if (iconText.GetIcon().IsOk()) [cell setImage:[[wxBitmap(iconText.GetIcon()).GetNSImage() retain] autorelease]]; + else + [cell setImage:nil]; [cell setStringValue:[[wxCFStringRef(iconText.GetText()).AsNSString() retain] autorelease]]; return true; } @@ -2476,17 +3037,15 @@ bool wxDataViewIconTextRenderer::MacRender() } void -wxDataViewIconTextRenderer::OSXOnCellChanged(const wxVariant& value, +wxDataViewIconTextRenderer::OSXOnCellChanged(NSObject *value, const wxDataViewItem& item, unsigned col) { - // we receive just the text (because it's the only component which can be - // edited by user) from the native control but we need wxDataViewIconText - // for the model, so construct it here wxVariant valueIconText; - valueIconText << wxDataViewIconText(value.GetString()); + valueIconText << wxDataViewIconText(ObjectToString(value)); - wxDataViewRenderer::OSXOnCellChanged(valueIconText, item, col); + wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); + model->ChangeValue(valueIconText, item, col); } IMPLEMENT_ABSTRACT_CLASS(wxDataViewIconTextRenderer,wxDataViewRenderer) @@ -2524,6 +3083,15 @@ bool wxDataViewToggleRenderer::MacRender() } } +void +wxDataViewToggleRenderer::OSXOnCellChanged(NSObject *value, + const wxDataViewItem& item, + unsigned col) +{ + wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); + model->ChangeValue(ObjectToBool(value), item, col); +} + IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer,wxDataViewRenderer) // --------------------------------------------------------- @@ -2535,6 +3103,8 @@ wxDataViewProgressRenderer::wxDataViewProgressRenderer(const wxString& label, int align) : wxDataViewRenderer(varianttype,mode,align) { + wxUnusedVar(label); + NSLevelIndicatorCell* cell; cell = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]; @@ -2558,6 +3128,15 @@ bool wxDataViewProgressRenderer::MacRender() } } +void +wxDataViewProgressRenderer::OSXOnCellChanged(NSObject *value, + const wxDataViewItem& item, + unsigned col) +{ + wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); + model->ChangeValue(ObjectToLong(value), item, col); +} + IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer,wxDataViewRenderer) // --------------------------------------------------------- @@ -2575,7 +3154,8 @@ wxDataViewColumn::wxDataViewColumn(const wxString& title, m_title(title) { InitCommon(width, align, flags); - if (renderer && (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT)) + if (renderer && !renderer->IsCustomRenderer() && + (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT)) renderer->SetAlignment(align); } @@ -2589,7 +3169,8 @@ wxDataViewColumn::wxDataViewColumn(const wxBitmap& bitmap, m_NativeDataPtr(new wxDataViewColumnNativeData()) { InitCommon(width, align, flags); - if (renderer && (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT)) + if (renderer && !renderer->IsCustomRenderer() && + (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT)) renderer->SetAlignment(align); } @@ -2598,6 +3179,11 @@ wxDataViewColumn::~wxDataViewColumn() delete m_NativeDataPtr; } +int wxDataViewColumn::GetWidth() const +{ + return [m_NativeDataPtr->GetNativeColumnPtr() width]; +} + bool wxDataViewColumn::IsSortKey() const { NSTableColumn *nsCol = GetNativeData()->GetNativeColumnPtr(); @@ -2608,13 +3194,15 @@ void wxDataViewColumn::SetAlignment(wxAlignment align) { m_alignment = align; [[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setAlignment:ConvertToNativeHorizontalTextAlignment(align)]; - if (m_renderer && (m_renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT)) + if (m_renderer && !m_renderer->IsCustomRenderer() && + (m_renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT)) m_renderer->SetAlignment(align); } void wxDataViewColumn::SetBitmap(const wxBitmap& bitmap) { - // bitmaps and titles cannot exist at the same time - if the bitmap is set the title is removed: + // bitmaps and titles cannot exist at the same time - if the bitmap is set + // the title is removed: m_title = wxEmptyString; wxDataViewColumnBase::SetBitmap(bitmap); [[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setImage:[[bitmap.GetNSImage() retain] autorelease]]; @@ -2634,12 +3222,25 @@ void wxDataViewColumn::SetMinWidth(int minWidth) void wxDataViewColumn::SetReorderable(bool reorderable) { + wxUnusedVar(reorderable); } -void wxDataViewColumn::SetResizeable(bool resizeable) +void wxDataViewColumn::SetHidden(bool hidden) { - wxDataViewColumnBase::SetResizeable(resizeable); - if (resizeable) + // How to set flag here? + + [m_NativeDataPtr->GetNativeColumnPtr() setHidden:hidden]; +} + +bool wxDataViewColumn::IsHidden() const +{ + return [m_NativeDataPtr->GetNativeColumnPtr() isHidden]; +} + +void wxDataViewColumn::SetResizeable(bool resizable) +{ + wxDataViewColumnBase::SetResizeable(resizable); + if (resizable) [m_NativeDataPtr->GetNativeColumnPtr() setResizingMask:NSTableColumnUserResizingMask]; else [m_NativeDataPtr->GetNativeColumnPtr() setResizingMask:NSTableColumnNoResizing]; @@ -2647,7 +3248,12 @@ void wxDataViewColumn::SetResizeable(bool resizeable) void wxDataViewColumn::SetSortable(bool sortable) { - wxDataViewColumnBase::SetSortable(sortable); + // wxDataViewColumnBase::SetSortable(sortable); + // Avoid endless recursion and just set the flag here + if (sortable) + m_flags |= wxDATAVIEW_COL_SORTABLE; + else + m_flags &= ~wxDATAVIEW_COL_SORTABLE; } void wxDataViewColumn::SetSortOrder(bool ascending) @@ -2674,7 +3280,8 @@ void wxDataViewColumn::SetSortOrder(bool ascending) void wxDataViewColumn::SetTitle(const wxString& title) { - // bitmaps and titles cannot exist at the same time - if the title is set the bitmap is removed: + // bitmaps and titles cannot exist at the same time - if the title is set + // the bitmap is removed: wxDataViewColumnBase::SetBitmap(wxBitmap()); m_title = title; [[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setStringValue:[[wxCFStringRef(title).AsNSString() retain] autorelease]]; @@ -2682,14 +3289,29 @@ void wxDataViewColumn::SetTitle(const wxString& title) void wxDataViewColumn::SetWidth(int width) { - [m_NativeDataPtr->GetNativeColumnPtr() setWidth:width]; m_width = width; -} -void wxDataViewColumn::SetAsSortKey(bool WXUNUSED(sort)) -{ - // see wxGTK native wxDataViewColumn implementation - wxFAIL_MSG("not implemented"); + switch ( width ) + { + case wxCOL_WIDTH_AUTOSIZE: +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + if ( GetOwner() ) + { + wxCocoaDataViewControl *peer = static_cast(GetOwner()->GetPeer()); + peer->FitColumnWidthToContent(GetOwner()->GetColumnPosition(this)); + break; + } +#endif + // fall through if unsupported (OSX < 10.5) or not yet settable + + case wxCOL_WIDTH_DEFAULT: + width = wxDVC_DEFAULT_WIDTH; + // fall through + + default: + [m_NativeDataPtr->GetNativeColumnPtr() setWidth:width]; + break; + } } void wxDataViewColumn::SetNativeData(wxDataViewColumnNativeData* newNativeDataPtr)