// ============================================================================
// Classes used locally in dataview.mm
// ============================================================================
+
+// ----------------------------------------------------------------------------
+// wxCustomRendererObject
+// ----------------------------------------------------------------------------
+
@interface wxCustomRendererObject : NSObject <NSCopying>
{
@public
}
@end
+// ----------------------------------------------------------------------------
+// wxDVCNSTableColumn: exists only to override NSTableColumn:dataCellForRow:
+// ----------------------------------------------------------------------------
+
+@interface wxDVCNSTableColumn : NSTableColumn
+{
+}
+
+ -(id) dataCellForRow:(NSInteger)row;
+@end
+
+@implementation wxDVCNSTableColumn
+
+-(id) dataCellForRow:(NSInteger)row
+{
+ // what we want to do here is to simply return nil for the cells which
+ // shouldn't show anything as otherwise we would show e.g. empty combo box
+ // or progress cells in the columns using the corresponding types even for
+ // the container rows which is wrong
+
+ // half of the problem is just finding the objects we need from the column
+ // pointer which is itself stashed inside wxPointerObject which we use as
+ // our identifier
+ const wxDataViewColumn * const
+ dvCol = static_cast<wxDataViewColumn *>(
+ [(wxPointerObject *)[self identifier] pointer]
+ );
+
+ const wxDataViewCtrl * const dvc = dvCol->GetOwner();
+ const wxCocoaDataViewControl * const
+ peer = static_cast<wxCocoaDataViewControl *>(dvc->GetPeer());
+
+
+ // once we do have everything, simply ask NSOutlineView for the item...
+ const id item = peer->GetItemAtRow(row);
+ if ( item )
+ {
+ // ... and if it succeeded, ask the model whether it has any value
+ wxDataViewItem dvItem([((wxPointerObject*) item) pointer]);
+
+ if ( !dvc->GetModel()->HasValue(dvItem, dvCol->GetModelColumn()) )
+ return nil;
+ }
+
+ return [super dataCellForRow:row];
+}
+
+@end
+
// ============================================================================
// local helpers
// ============================================================================
namespace
{
+// convert from NSObject to different C++ types: all these functions check
+// that the conversion really makes sense and assert if it doesn't
+wxString ObjectToString(NSObject *object)
+{
+ wxCHECK_MSG( [object isKindOfClass:[NSString class]], "",
+ wxString::Format
+ (
+ "string expected but got %s",
+ wxCFStringRef::AsString([object className])
+ ));
+
+ return wxCFStringRef([((NSString*) object) retain]).AsString();
+}
+
+bool ObjectToBool(NSObject *object)
+{
+ // actually the value must be of NSCFBoolean class but it's private so we
+ // can't check for it directly
+ wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], false,
+ wxString::Format
+ (
+ "number expected but got %s",
+ wxCFStringRef::AsString([object className])
+ ));
+
+ return [(NSNumber *)object boolValue];
+}
+
+long ObjectToLong(NSObject *object)
+{
+ wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], -1,
+ wxString::Format
+ (
+ "number expected but got %s",
+ wxCFStringRef::AsString([object className])
+ ));
+
+ return [(NSNumber *)object longValue];
+}
+
+wxDateTime ObjectToDate(NSObject *object)
+{
+ wxCHECK_MSG( [object isKindOfClass:[NSDate class]], wxInvalidDateTime,
+ wxString::Format
+ (
+ "date expected but got %s",
+ wxCFStringRef::AsString([object className])
+ ));
+
+ // get the number of seconds since 1970-01-01 UTC and this is the only
+ // way to convert a double to a wxLongLong
+ const wxLongLong seconds = [((NSDate*) object) timeIntervalSince1970];
+
+ wxDateTime dt(1, wxDateTime::Jan, 1970);
+ dt.Add(wxTimeSpan(0,0,seconds));
+
+ // the user has entered a date in the local timezone but seconds
+ // contains the number of seconds from date in the local timezone
+ // since 1970-01-01 UTC; therefore, the timezone information has to be
+ // transferred to wxWidgets, too:
+ dt.MakeFromTimezone(wxDateTime::UTC);
+
+ return dt;
+}
+
NSInteger CompareItems(id item1, id item2, void* context)
{
NSArray* const sortDescriptors = (NSArray*) context;
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<wxDataViewColumn*>(column)]
autorelease]]
objectValueForTableColumn:(NSTableColumn*)tableColumn
byItem:(id)item
{
+ wxCHECK_MSG( model, nil, "Valid model in data source does not exist." );
+
wxDataViewColumn* col(static_cast<wxDataViewColumn*>([[tableColumn identifier] pointer]));
+ const unsigned colIdx = col->GetModelColumn();
wxDataViewItem dataViewItem([((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;
}
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
{
wxCustomRendererObject * const
obj = static_cast<wxCustomRendererObject *>([self objectValue]);
+ if ( !obj )
+ {
+ // this may happen for the custom cells in container rows: they don't
+ // have any values
+ return;
+ }
+
wxDataViewCustomRenderer * const renderer = obj->customRenderer;
// draw its own background:
[[tableColumn identifier] pointer]
)
);
+ const unsigned colIdx = dvCol->GetModelColumn();
+
+ wxDataViewItem dvItem([static_cast<wxPointerObject *>(item) pointer]);
+
+ if ( !model->HasValue(dvItem, colIdx) )
+ return;
wxDataViewRenderer * const renderer = dvCol->GetRenderer();
wxDataViewRendererNativeData * const data = renderer->GetNativeData();
- wxDataViewItem dvItem([static_cast<wxPointerObject *>(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
NSColor *colText = NULL;
wxDataViewItemAttr attr;
- if ( model && model->GetAttr(dvItem, dvCol->GetModelColumn(), attr) )
+ if ( model && model->GetAttr(dvItem, colIdx, attr) )
{
if ( attr.HasFont() )
{
}
}
+id wxCocoaDataViewControl::GetItemAtRow(int row) const
+{
+ return [m_OutlineView itemAtRow:row];
+}
+
// ----------------------------------------------------------------------------
// wxDataViewRendererNativeData
// ----------------------------------------------------------------------------
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);
}
}
}
+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)
// ---------------------------------------------------------
}
}
+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)
// ---------------------------------------------------------
}
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)
}
}
+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)
// ---------------------------------------------------------
}
}
+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)
// ---------------------------------------------------------