From 9461dd8c71836ef5140b8c27ac5b7c1202bb1452 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 23 Oct 2009 23:49:26 +0000 Subject: [PATCH] Use virtual functions to convert NSObject to the correct type in wxDVC. Instead of trying to determine the type of the value which should be extracted from the NSObject we receive from NSOutlineView, just pass it to a virtual method in the renderer which knows which type does it need. This fixes the problem with editing boolean/checkbox columns and makes the code more elegant. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@62490 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/osx/dataview.h | 33 ++++++-- src/osx/cocoa/dataview.mm | 167 ++++++++++++++++++++++++++++++-------- 2 files changed, 160 insertions(+), 40 deletions(-) diff --git a/include/wx/osx/dataview.h b/include/wx/osx/dataview.h index 42df0c23c6..3ed73d5965 100644 --- a/include/wx/osx/dataview.h +++ b/include/wx/osx/dataview.h @@ -85,7 +85,7 @@ public: #if wxOSX_USE_COCOA // called when a value was edited by user - virtual void OSXOnCellChanged(const wxVariant& value, + virtual void OSXOnCellChanged(NSObject *value, const wxDataViewItem& item, unsigned col); #endif // Cocoa @@ -197,7 +197,12 @@ public: // virtual bool MacRender(); -protected: +#if wxOSX_USE_COCOA + virtual void OSXOnCellChanged(NSObject *value, + const wxDataViewItem& item, + unsigned col); +#endif // Cocoa + private: DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewTextRenderer) }; @@ -281,8 +286,7 @@ public: virtual bool MacRender(); #if wxOSX_USE_COCOA - // called when a value was edited by user - virtual void OSXOnCellChanged(const wxVariant& value, + virtual void OSXOnCellChanged(NSObject *value, const wxDataViewItem& item, unsigned col); #endif // Cocoa @@ -306,7 +310,12 @@ public: // virtual bool MacRender(); -protected: +#if wxOSX_USE_COCOA + virtual void OSXOnCellChanged(NSObject *value, + const wxDataViewItem& item, + unsigned col); +#endif // Cocoa + private: DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewToggleRenderer) }; @@ -326,7 +335,12 @@ public: // virtual bool MacRender(); -protected: +#if wxOSX_USE_COCOA + virtual void OSXOnCellChanged(NSObject *value, + const wxDataViewItem& item, + unsigned col); +#endif // Cocoa + private: DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewProgressRenderer) }; @@ -345,7 +359,12 @@ public: // virtual bool MacRender(); -protected: +#if wxOSX_USE_COCOA + virtual void OSXOnCellChanged(NSObject *value, + const wxDataViewItem& item, + unsigned col); +#endif // Cocoa + private: DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewDateRenderer) }; diff --git a/src/osx/cocoa/dataview.mm b/src/osx/cocoa/dataview.mm index 1d63bdf4fa..8140fd6101 100644 --- a/src/osx/cocoa/dataview.mm +++ b/src/osx/cocoa/dataview.mm @@ -84,6 +84,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; @@ -516,31 +581,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 @@ -2231,10 +2273,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 +2360,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 +2510,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 +2561,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 +2607,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 +2650,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) // --------------------------------------------------------- -- 2.45.2