]> git.saurik.com Git - wxWidgets.git/blobdiff - src/osx/cocoa/dataview.mm
Initial ShowWithoutActivating implementations for Mac and Windows, and attempt to...
[wxWidgets.git] / src / osx / cocoa / dataview.mm
index 1d63bdf4fa5a2602077295950d4f3eaccf83384e..de70d511ca13d0f466f6395006a698a4657552b7 100644 (file)
 // ============================================================================
 // Classes used locally in dataview.mm
 // ============================================================================
 // ============================================================================
 // Classes used locally in dataview.mm
 // ============================================================================
+
+// ----------------------------------------------------------------------------
+// wxCustomRendererObject
+// ----------------------------------------------------------------------------
+
 @interface wxCustomRendererObject : NSObject <NSCopying>
 {
 @public
 @interface wxCustomRendererObject : NSObject <NSCopying>
 {
 @public
 }
 @end
 
 }
 @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
 // ============================================================================
 // ============================================================================
 // local helpers
 // ============================================================================
 namespace
 {
 
 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;
 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" );
 
 
     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]]
             [[[wxPointerObject alloc] initWithPointer:
                 const_cast<wxDataViewColumn*>(column)]
              autorelease]]
@@ -493,16 +612,20 @@ outlineView:(NSOutlineView*)outlineView
     objectValueForTableColumn:(NSTableColumn*)tableColumn
     byItem:(id)item
 {
     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]));
     wxDataViewColumn* col(static_cast<wxDataViewColumn*>([[tableColumn identifier] pointer]));
+    const unsigned colIdx = col->GetModelColumn();
 
     wxDataViewItem dataViewItem([((wxPointerObject*) item) pointer]);
 
 
     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;
 }
 
     return nil;
 }
 
@@ -516,31 +639,8 @@ outlineView:(NSOutlineView*)outlineView
 
     wxDataViewItem dataViewItem([((wxPointerObject*) item) 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));
-
-        // 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()->
     col->GetRenderer()->
-        OSXOnCellChanged(value, dataViewItem, col->GetModelColumn());
+        OSXOnCellChanged(object, dataViewItem, col->GetModelColumn());
 }
 
 -(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)oldDescriptors
 }
 
 -(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)oldDescriptors
@@ -994,6 +1094,13 @@ outlineView:(NSOutlineView*)outlineView
 {
     wxCustomRendererObject * const
         obj = static_cast<wxCustomRendererObject *>([self objectValue]);
 {
     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:
     wxDataViewCustomRenderer * const renderer = obj->customRenderer;
 
     // draw its own background:
@@ -1500,12 +1607,16 @@ item:(id)item
                     [[tableColumn identifier] pointer]
                     )
              );
                     [[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();
 
 
     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
     // 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;
     NSColor *colText = NULL;
 
     wxDataViewItemAttr attr;
-    if ( model && model->GetAttr(dvItem, dvCol->GetModelColumn(), attr) )
+    if ( model && model->GetAttr(dvItem, colIdx, attr) )
     {
         if ( attr.HasFont() )
         {
     {
         if ( attr.HasFont() )
         {
@@ -2136,6 +2247,11 @@ wxDataObjectComposite* wxCocoaDataViewControl::GetDnDDataObjects(NSData* dataObj
     }
 }
 
     }
 }
 
+id wxCocoaDataViewControl::GetItemAtRow(int row) const
+{
+    return [m_OutlineView itemAtRow:row];
+}
+
 // ----------------------------------------------------------------------------
 // wxDataViewRendererNativeData
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // wxDataViewRendererNativeData
 // ----------------------------------------------------------------------------
@@ -2231,10 +2347,35 @@ wxEllipsizeMode wxDataViewRenderer::GetEllipsizeMode() const
     return GetNativeData()->GetEllipsizeMode();
 }
 
     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);
 }
     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)
 
 // ---------------------------------------------------------
 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)
 
 // ---------------------------------------------------------
 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer,wxDataViewRenderer)
 
 // ---------------------------------------------------------
@@ -2476,17 +2635,15 @@ bool wxDataViewIconTextRenderer::MacRender()
 }
 
 void
 }
 
 void
-wxDataViewIconTextRenderer::OSXOnCellChanged(const wxVariant& value,
+wxDataViewIconTextRenderer::OSXOnCellChanged(NSObject *value,
                                              const wxDataViewItem& item,
                                              unsigned col)
 {
                                              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;
     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)
 }
 
 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)
 
 // ---------------------------------------------------------
 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)
 
 // ---------------------------------------------------------
 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer,wxDataViewRenderer)
 
 // ---------------------------------------------------------