X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/795dac4c866d89804e625fa1d04cb70aba320183..dbc7ceb9250bcc2f856f87555f8f4273da31c148:/src/osx/cocoa/dataview.mm?ds=sidebyside diff --git a/src/osx/cocoa/dataview.mm b/src/osx/cocoa/dataview.mm index 1d63bdf4fa..de70d511ca 100644 --- a/src/osx/cocoa/dataview.mm +++ b/src/osx/cocoa/dataview.mm @@ -34,6 +34,11 @@ // ============================================================================ // Classes used locally in dataview.mm // ============================================================================ + +// ---------------------------------------------------------------------------- +// wxCustomRendererObject +// ---------------------------------------------------------------------------- + @interface wxCustomRendererObject : NSObject { @public @@ -77,6 +82,55 @@ } @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( + [(wxPointerObject *)[self identifier] pointer] + ); + + 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([((wxPointerObject*) item) pointer]); + + if ( !dvc->GetModel()->HasValue(dvItem, dvCol->GetModelColumn()) ) + return nil; + } + + return [super dataCellForRow:row]; +} + +@end + // ============================================================================ // local helpers // ============================================================================ @@ -84,6 +138,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; @@ -131,8 +250,8 @@ NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column) wxCHECK_MSG( renderer, NULL, "column should have a renderer" ); - NSTableColumn * const nativeColumn( - [[NSTableColumn alloc] initWithIdentifier: + wxDVCNSTableColumn * const nativeColumn( + [[wxDVCNSTableColumn alloc] initWithIdentifier: [[[wxPointerObject alloc] initWithPointer: const_cast(column)] autorelease]] @@ -493,16 +612,20 @@ outlineView:(NSOutlineView*)outlineView objectValueForTableColumn:(NSTableColumn*)tableColumn byItem:(id)item { + wxCHECK_MSG( model, nil, "Valid model in data source does not exist." ); + wxDataViewColumn* col(static_cast([[tableColumn identifier] pointer])); + const unsigned colIdx = col->GetModelColumn(); wxDataViewItem dataViewItem([((wxPointerObject*) item) pointer]); - wxVariant value; - + 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; } @@ -516,31 +639,8 @@ outlineView:(NSOutlineView*)outlineView 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)); - - // 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; - } - col->GetRenderer()-> - OSXOnCellChanged(value, dataViewItem, col->GetModelColumn()); + OSXOnCellChanged(object, dataViewItem, col->GetModelColumn()); } -(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)oldDescriptors @@ -994,6 +1094,13 @@ 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: @@ -1500,12 +1607,16 @@ item:(id)item [[tableColumn identifier] pointer] ) ); + const unsigned colIdx = dvCol->GetModelColumn(); + + wxDataViewItem dvItem([static_cast(item) pointer]); + + if ( !model->HasValue(dvItem, colIdx) ) + return; 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 @@ -1514,7 +1625,7 @@ item:(id)item NSColor *colText = NULL; wxDataViewItemAttr attr; - if ( model && model->GetAttr(dvItem, dvCol->GetModelColumn(), attr) ) + if ( model && model->GetAttr(dvItem, colIdx, attr) ) { if ( attr.HasFont() ) { @@ -2136,6 +2247,11 @@ wxDataObjectComposite* wxCocoaDataViewControl::GetDnDDataObjects(NSData* dataObj } } +id wxCocoaDataViewControl::GetItemAtRow(int row) const +{ + return [m_OutlineView itemAtRow:row]; +} + // ---------------------------------------------------------------------------- // wxDataViewRendererNativeData // ---------------------------------------------------------------------------- @@ -2231,10 +2347,35 @@ 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: we probably should get rid of this code entirely and make this + // function pure virtual, but currently we still have some native + // renderers (wxDataViewChoiceRenderer) which don't override it and + // there is also wxDataViewCustomRenderer for which it's not obvious + // how it should be implemented so keep this "auto-deduction" of + // variant type from NSObject for now + + 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); } @@ -2293,6 +2434,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) // --------------------------------------------------------- @@ -2434,6 +2584,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) // --------------------------------------------------------- @@ -2476,17 +2635,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 +2681,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) // --------------------------------------------------------- @@ -2558,6 +2724,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) // ---------------------------------------------------------