]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/dataview.cpp
Add wxRichMessageDialog class.
[wxWidgets.git] / src / gtk / dataview.cpp
index bc3f27ad3571b9b8816b767e655b7bb8e4fe3655..e3ae840c43582b22c9c9503ff47a01f90e8f4ccc 100644 (file)
@@ -47,6 +47,146 @@ extern "C" {
 typedef struct _GtkWxTreeModel       GtkWxTreeModel;
 }
 
+// ----------------------------------------------------------------------------
+// wxGtkTreePath: self-destroying GtkTreePath
+// ----------------------------------------------------------------------------
+
+// Usually this object is initialized with the associated GtkTreePath
+// immediately when it's constructed but it can also be changed later either by
+// using Assign() or by getting the pointer to the internally stored pointer
+// value using ByRef(). The latter should be avoided but is very convenient
+// when using GTK functions with GtkTreePath output parameters.
+class wxGtkTreePath
+{
+public:
+    // Ctor takes ownership of the given path and will free it if non-NULL.
+    wxGtkTreePath(GtkTreePath *path = NULL) : m_path(path) { }
+
+    // Creates a tree path for the given string path.
+    wxGtkTreePath(const gchar *strpath)
+        : m_path(gtk_tree_path_new_from_string(strpath))
+    {
+    }
+
+    // Set the stored pointer if not done by ctor.
+    void Assign(GtkTreePath *path)
+    {
+        wxASSERT_MSG( !m_path, "shouldn't be already initialized" );
+
+        m_path = path;
+    }
+
+    // Return the pointer to the internally stored pointer. This should only be
+    // used to initialize the object by passing it to some GTK function.
+    GtkTreePath **ByRef()
+    {
+        wxASSERT_MSG( !m_path, "shouldn't be already initialized" );
+
+        return &m_path;
+    }
+
+
+    operator GtkTreePath *() const { return m_path; }
+
+    ~wxGtkTreePath() { if ( m_path ) gtk_tree_path_free(m_path); }
+
+private:
+    GtkTreePath *m_path;
+
+    wxDECLARE_NO_COPY_CLASS(wxGtkTreePath);
+};
+
+// ----------------------------------------------------------------------------
+// wxGtkTreeSelectionLock: prevent selection from changing during the
+//                                 lifetime of this object
+// ----------------------------------------------------------------------------
+
+// Implementation note: it could be expected that setting the selection
+// function in this class ctor and resetting it back to the old value in its
+// dtor would work. However currently gtk_tree_selection_get_select_function()
+// can't be passed NULL (see https://bugzilla.gnome.org/show_bug.cgi?id=626276)
+// so we can't do this. Instead, we always use the selection function (which
+// imposes extra overhead, albeit minimal one, on all selection operations) and
+// just set/reset the flag telling it whether it should allow or forbid the
+// selection.
+//
+// Also notice that currently only a single object of this class may exist at
+// any given moment. It's just simpler like this and we don't need anything
+// more for now.
+
+extern "C"
+gboolean wxdataview_selection_func(GtkTreeSelection * WXUNUSED(selection),
+                                   GtkTreeModel * WXUNUSED(model),
+                                   GtkTreePath * WXUNUSED(path),
+                                   gboolean WXUNUSED(path_currently_selected),
+                                   gpointer data)
+{
+    return data == NULL;
+}
+
+class wxGtkTreeSelectionLock
+{
+public:
+    wxGtkTreeSelectionLock(GtkTreeSelection *selection)
+        : m_selection(selection)
+    {
+        wxASSERT_MSG( !ms_instance, "this class is not reentrant currently" );
+
+        ms_instance = this;
+
+        CheckCurrentSelectionFunc(NULL);
+
+        // Pass some non-NULL pointer as "data" for the callback, it doesn't
+        // matter what it is as long as it's non-NULL.
+        gtk_tree_selection_set_select_function(selection,
+                                               wxdataview_selection_func,
+                                               this,
+                                               NULL);
+    }
+
+    ~wxGtkTreeSelectionLock()
+    {
+        CheckCurrentSelectionFunc(wxdataview_selection_func);
+
+        gtk_tree_selection_set_select_function(m_selection,
+                                               wxdataview_selection_func,
+                                               NULL,
+                                               NULL);
+
+        ms_instance = NULL;
+    }
+
+private:
+    void CheckCurrentSelectionFunc(GtkTreeSelectionFunc func)
+    {
+        // We can only use gtk_tree_selection_get_select_function() with 2.14+
+        // so check for its availability both during compile- and run-time.
+#if GTK_CHECK_VERSION(2, 14, 0)
+        if ( gtk_check_version(2, 14, 0) != NULL )
+            return;
+
+        // If this assert is triggered, it means the code elsewhere has called
+        // gtk_tree_selection_set_select_function() but currently doing this
+        // breaks this class so the code here needs to be changed.
+        wxASSERT_MSG
+        (
+            gtk_tree_selection_get_select_function(m_selection) == func,
+            "selection function has changed unexpectedly, review this code!"
+        );
+#endif // GTK+ 2.14+
+
+        wxUnusedVar(func);
+    }
+
+    static wxGtkTreeSelectionLock *ms_instance;
+
+    GtkTreeSelection * const m_selection;
+
+    wxDECLARE_NO_COPY_CLASS(wxGtkTreeSelectionLock);
+};
+
+wxGtkTreeSelectionLock *wxGtkTreeSelectionLock::ms_instance = NULL;
+
 //-----------------------------------------------------------------------------
 // wxDataViewCtrlInternal
 //-----------------------------------------------------------------------------
@@ -54,7 +194,7 @@ typedef struct _GtkWxTreeModel       GtkWxTreeModel;
 WX_DECLARE_LIST(wxDataViewItem, ItemList);
 WX_DEFINE_LIST(ItemList)
 
-class WXDLLIMPEXP_ADV wxDataViewCtrlInternal
+class wxDataViewCtrlInternal
 {
 public:
     wxDataViewCtrlInternal( wxDataViewCtrl *owner, wxDataViewModel *wx_model, GtkWxTreeModel *gtk_model );
@@ -963,19 +1103,16 @@ static GtkCellEditable *gtk_wx_cell_renderer_text_start_editing(
 {
     GtkWxCellRendererText *wxgtk_renderer = (GtkWxCellRendererText *) gtk_renderer;
     wxDataViewRenderer *wx_renderer = wxgtk_renderer->wx_renderer;
+    wxDataViewColumn *column = wx_renderer->GetOwner();
 
-    GtkTreePath *treepath = gtk_tree_path_new_from_string( path );
-    GtkTreeIter iter;
-    wx_renderer->GetOwner()->GetOwner()->GtkGetInternal()->get_iter( &iter, treepath );
-    wxDataViewItem item( (void*) iter.user_data );;
-    gtk_tree_path_free( treepath );
+    wxDataViewItem
+        item(column->GetOwner()->GTKPathToItem(wxGtkTreePath(path)));
 
-    wxDataViewColumn *column = wx_renderer->GetOwner();
     wxDataViewCtrl *dv = column->GetOwner();
     wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_START_EDITING, dv->GetId() );
     event.SetDataViewColumn( column );
     event.SetModel( dv->GetModel() );
-    event.SetColumn( wx_renderer->GetOwner()->GetModelColumn() );
+    event.SetColumn( column->GetModelColumn() );
     event.SetItem( item );
     dv->HandleWindowEvent( event );
 
@@ -1162,11 +1299,8 @@ static GtkCellEditable *gtk_wx_cell_renderer_start_editing(
 //    wxRect renderrect(wxRectFromGDKRect(&rect));
     wxRect renderrect(wxRectFromGDKRect(cell_area));
 
-    GtkTreePath *treepath = gtk_tree_path_new_from_string( path );
-    GtkTreeIter iter;
-    cell->GetOwner()->GetOwner()->GtkGetInternal()->get_iter( &iter, treepath );
-    wxDataViewItem item( (void*) iter.user_data );
-    gtk_tree_path_free( treepath );
+    wxDataViewItem
+        item(cell->GetOwner()->GetOwner()->GTKPathToItem(wxGtkTreePath(path)));
 
     cell->StartEditing( item, renderrect );
 
@@ -1290,14 +1424,10 @@ gtk_wx_cell_renderer_activate(
 
     wxRect renderrect(wxRectFromGDKRect(&rect));
 
-    wxDataViewModel *model = cell->GetOwner()->GetOwner()->GetModel();
+    wxDataViewCtrl * const ctrl = cell->GetOwner()->GetOwner();
+    wxDataViewModel *model = ctrl->GetModel();
 
-    GtkTreePath *treepath = gtk_tree_path_new_from_string( path );
-
-    GtkTreeIter iter;
-    cell->GetOwner()->GetOwner()->GtkGetInternal()->get_iter( &iter, treepath );
-    wxDataViewItem item( iter.user_data );
-    gtk_tree_path_free( treepath );
+    wxDataViewItem item(ctrl->GTKPathToItem(wxGtkTreePath(path)));
 
     unsigned int model_col = cell->GetOwner()->GetModelColumn();
 
@@ -1387,13 +1517,12 @@ bool wxGtkDataViewModelNotifier::ItemAdded( const wxDataViewItem &parent, const
 
     GtkTreeIter iter;
     iter.stamp = m_wxgtk_model->stamp;
-    iter.user_data = (gpointer) item.GetID();
+    iter.user_data = item.GetID();
 
-    GtkTreePath *path = wxgtk_tree_model_get_path(
-        GTK_TREE_MODEL(m_wxgtk_model), &iter );
+    wxGtkTreePath path(wxgtk_tree_model_get_path(
+        GTK_TREE_MODEL(m_wxgtk_model), &iter ));
     gtk_tree_model_row_inserted(
         GTK_TREE_MODEL(m_wxgtk_model), path, &iter);
-    gtk_tree_path_free (path);
 
     return true;
 }
@@ -1406,15 +1535,15 @@ bool wxGtkDataViewModelNotifier::ItemDeleted( const wxDataViewItem &parent, cons
     GtkTreeIter iter;
     iter.stamp = m_wxgtk_model->stamp;
     iter.user_data = (gpointer) item.GetID();
-    GtkTreePath *path = wxgtk_tree_model_get_path(
-        GTK_TREE_MODEL(m_wxgtk_model), &iter );
+    wxGtkTreePath path(wxgtk_tree_model_get_path(
+        GTK_TREE_MODEL(m_wxgtk_model), &iter ));
 #else
     // so get the path from the parent
     GtkTreeIter iter;
     iter.stamp = m_wxgtk_model->stamp;
     iter.user_data = (gpointer) parent.GetID();
-    GtkTreePath *path = wxgtk_tree_model_get_path(
-        GTK_TREE_MODEL(m_wxgtk_model), &iter );
+    wxGtkTreePath path(wxgtk_tree_model_get_path(
+        GTK_TREE_MODEL(m_wxgtk_model), &iter ));
     // and add the final index ourselves
     int index = m_owner->GtkGetInternal()->GetIndexOf( parent, item );
     gtk_tree_path_append_index( path, index );
@@ -1422,7 +1551,6 @@ bool wxGtkDataViewModelNotifier::ItemDeleted( const wxDataViewItem &parent, cons
 
     gtk_tree_model_row_deleted(
         GTK_TREE_MODEL(m_wxgtk_model), path );
-    gtk_tree_path_free (path);
 
     m_owner->GtkGetInternal()->ItemDeleted( parent, item );
 
@@ -1440,11 +1568,10 @@ bool wxGtkDataViewModelNotifier::ItemChanged( const wxDataViewItem &item )
     iter.stamp = m_wxgtk_model->stamp;
     iter.user_data = (gpointer) item.GetID();
 
-    GtkTreePath *path = wxgtk_tree_model_get_path(
-        GTK_TREE_MODEL(m_wxgtk_model), &iter );
+    wxGtkTreePath path(wxgtk_tree_model_get_path(
+        GTK_TREE_MODEL(m_wxgtk_model), &iter ));
     gtk_tree_model_row_changed(
         GTK_TREE_MODEL(m_wxgtk_model), path, &iter );
-    gtk_tree_path_free (path);
 
     m_owner->GtkGetInternal()->ItemChanged( item );
 
@@ -1467,11 +1594,10 @@ bool wxGtkDataViewModelNotifier::ValueChanged( const wxDataViewItem &item, unsig
             GtkTreeIter iter;
             iter.stamp = m_wxgtk_model->stamp;
             iter.user_data = (gpointer) item.GetID();
-            GtkTreePath *path = wxgtk_tree_model_get_path(
-                GTK_TREE_MODEL(m_wxgtk_model), &iter );
+            wxGtkTreePath path(wxgtk_tree_model_get_path(
+                GTK_TREE_MODEL(m_wxgtk_model), &iter ));
             GdkRectangle cell_area;
             gtk_tree_view_get_cell_area( widget, path, gcolumn, &cell_area );
-            gtk_tree_path_free( path );
 
             GtkAdjustment* hadjust = gtk_tree_view_get_hadjustment( widget );
             double d = gtk_adjustment_get_value( hadjust );
@@ -1530,17 +1656,13 @@ wxgtk_renderer_editing_started( GtkCellRenderer *WXUNUSED(cell), GtkCellEditable
     wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_STARTED, dv->GetId() );
     event.SetDataViewColumn( column );
     event.SetModel( dv->GetModel() );
-    GtkTreePath *tree_path = gtk_tree_path_new_from_string( path );
-    GtkTreeIter iter;
-    dv->GtkGetInternal()->get_iter( &iter, tree_path );
-    gtk_tree_path_free( tree_path );
-    wxDataViewItem item( iter.user_data );
+    wxDataViewItem item(dv->GTKPathToItem(wxGtkTreePath(path)));
     event.SetItem( item );
     dv->HandleWindowEvent( event );
 
     if (GTK_IS_CELL_EDITABLE(editable))
     {
-        s_user_data = iter.user_data;
+        s_user_data = item.GetID();
 
         g_signal_connect (GTK_CELL_EDITABLE (editable), "editing_done",
             G_CALLBACK (wxgtk_cell_editable_editing_done),
@@ -1748,11 +1870,8 @@ wxDataViewRenderer::GtkOnTextEdited(const gchar *itempath, const wxString& str)
     if (!Validate( value ))
         return;
 
-    GtkTreePath *path = gtk_tree_path_new_from_string( itempath );
-    GtkTreeIter iter;
-    GetOwner()->GetOwner()->GtkGetInternal()->get_iter( &iter, path );
-    wxDataViewItem item( (void*) iter.user_data );;
-    gtk_tree_path_free( path );
+    wxDataViewItem
+        item(GetOwner()->GetOwner()->GTKPathToItem(wxGtkTreePath(itempath)));
 
     GtkOnCellChanged(value, item, GetOwner()->GetModelColumn());
 }
@@ -2049,13 +2168,10 @@ static void wxGtkToggleRendererToggledCallback( GtkCellRendererToggle *renderer,
     if (!cell->Validate( value ))
         return;
 
-    wxDataViewModel *model = cell->GetOwner()->GetOwner()->GetModel();
+    wxDataViewCtrl * const ctrl = cell->GetOwner()->GetOwner();
+    wxDataViewModel *model = ctrl->GetModel();
 
-    GtkTreePath *gtk_path = gtk_tree_path_new_from_string( path );
-    GtkTreeIter iter;
-    cell->GetOwner()->GetOwner()->GtkGetInternal()->get_iter( &iter, gtk_path );
-    wxDataViewItem item( (void*) iter.user_data );;
-    gtk_tree_path_free( gtk_path );
+    wxDataViewItem item(ctrl->GTKPathToItem(wxGtkTreePath(path)));
 
     unsigned int model_col = cell->GetOwner()->GetModelColumn();
 
@@ -2481,11 +2597,8 @@ void wxDataViewChoiceByIndexRenderer::GtkOnTextEdited(const gchar *itempath, con
     if (!Validate( value ))
         return;
 
-    GtkTreePath *path = gtk_tree_path_new_from_string( itempath );
-    GtkTreeIter iter;
-    GetOwner()->GetOwner()->GtkGetInternal()->get_iter( &iter, path );
-    wxDataViewItem item( (void*) iter.user_data );;
-    gtk_tree_path_free( path );
+    wxDataViewItem
+        item(GetOwner()->GetOwner()->GTKPathToItem(wxGtkTreePath(itempath)));
 
     GtkOnCellChanged(value, item, GetOwner()->GetModelColumn());
 }
@@ -3200,11 +3313,8 @@ void wxGtkTreeModelNode::Resort()
     iter.user_data = GetItem().GetID();
     iter.stamp = m_internal->GetGtkModel()->stamp;
 
-    GtkTreePath *path = m_internal->get_path( &iter );
-
-    gtk_tree_model_rows_reordered( gtk_tree_model, path, &iter, new_order );
-
-    gtk_tree_path_free (path);
+    gtk_tree_model_rows_reordered( gtk_tree_model,
+            wxGtkTreePath(m_internal->get_path(&iter)), &iter, new_order );
 
     delete [] new_order;
 
@@ -3312,9 +3422,9 @@ gboolean wxDataViewCtrlInternal::row_draggable( GtkTreeDragSource *WXUNUSED(drag
 {
     delete m_dragDataObject;
 
-    GtkTreeIter iter;
-    if (!get_iter( &iter, path )) return FALSE;
-    wxDataViewItem item( (void*) iter.user_data );
+    wxDataViewItem item(GetOwner()->GTKPathToItem(path));
+    if ( !item )
+        return FALSE;
 
     wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, m_owner->GetId() );
     event.SetEventObject( m_owner );
@@ -3345,9 +3455,9 @@ wxDataViewCtrlInternal::drag_data_delete(GtkTreeDragSource *WXUNUSED(drag_source
 gboolean wxDataViewCtrlInternal::drag_data_get( GtkTreeDragSource *WXUNUSED(drag_source),
     GtkTreePath *path, GtkSelectionData *selection_data )
 {
-    GtkTreeIter iter;
-    if (!get_iter( &iter, path )) return FALSE;
-    wxDataViewItem item( (void*) iter.user_data );
+    wxDataViewItem item(GetOwner()->GTKPathToItem(path));
+    if ( !item )
+        return FALSE;
 
     if (!m_dragDataObject->IsSupported( selection_data->target ))
         return FALSE;
@@ -3377,9 +3487,9 @@ wxDataViewCtrlInternal::drag_data_received(GtkTreeDragDest *WXUNUSED(drag_dest),
                                            GtkTreePath *path,
                                            GtkSelectionData *selection_data)
 {
-    GtkTreeIter iter;
-    if (!get_iter( &iter, path )) return FALSE;
-    wxDataViewItem item( (void*) iter.user_data );
+    wxDataViewItem item(GetOwner()->GTKPathToItem(path));
+    if ( !item )
+        return FALSE;
 
     wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP, m_owner->GetId() );
     event.SetEventObject( m_owner );
@@ -3402,9 +3512,9 @@ wxDataViewCtrlInternal::row_drop_possible(GtkTreeDragDest *WXUNUSED(drag_dest),
                                           GtkTreePath *path,
                                           GtkSelectionData *selection_data)
 {
-    GtkTreeIter iter;
-    if (!get_iter( &iter, path )) return FALSE;
-    wxDataViewItem item( (void*) iter.user_data );
+    wxDataViewItem item(GetOwner()->GTKPathToItem(path));
+    if ( !item )
+        return FALSE;
 
     wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
     event.SetEventObject( m_owner );
@@ -3502,10 +3612,15 @@ bool wxDataViewCtrlInternal::ValueChanged( const wxDataViewItem &item, unsigned
 
 GtkTreeModelFlags wxDataViewCtrlInternal::get_flags()
 {
-    if (m_wx_model->IsVirtualListModel())
-        return GTK_TREE_MODEL_LIST_ONLY;
-    else
-        return GTK_TREE_MODEL_ITERS_PERSIST;
+    int flags = 0;
+
+    if ( m_wx_model->IsListModel() )
+        flags |= GTK_TREE_MODEL_LIST_ONLY;
+
+    if ( !m_wx_model->IsVirtualListModel() )
+        flags |= GTK_TREE_MODEL_ITERS_PERSIST;
+
+    return GtkTreeModelFlags(flags);
 }
 
 gboolean wxDataViewCtrlInternal::get_iter( GtkTreeIter *iter, GtkTreePath *path )
@@ -4032,9 +4147,7 @@ wxdataview_row_activated_callback( GtkTreeView* WXUNUSED(treeview), GtkTreePath
 {
     wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, dv->GetId() );
 
-    GtkTreeIter iter;
-    dv->GtkGetInternal()->get_iter( &iter, path );
-    wxDataViewItem item( (void*) iter.user_data );;
+    wxDataViewItem item(dv->GTKPathToItem(path));
     event.SetItem( item );
     event.SetModel( dv->GetModel() );
     dv->HandleWindowEvent( event );
@@ -4151,14 +4264,14 @@ gtk_dataview_motion_notify_callback( GtkWidget *WXUNUSED(widget),
         gdk_event->y = y;
     }
 
-    GtkTreePath *path = NULL;
+    wxGtkTreePath path;
     GtkTreeViewColumn *column = NULL;
     gint cell_x = 0;
     gint cell_y = 0;
     if (gtk_tree_view_get_path_at_pos(
         GTK_TREE_VIEW(dv->GtkGetTreeView()),
         (int) gdk_event->x, (int) gdk_event->y,
-        &path,
+        path.ByRef(),
         &column,
         &cell_x,
         &cell_y))
@@ -4167,8 +4280,6 @@ gtk_dataview_motion_notify_callback( GtkWidget *WXUNUSED(widget),
         {
             GtkTreeIter iter;
             dv->GtkGetInternal()->get_iter( &iter, path );
-
-            gtk_tree_path_free( path );
         }
     }
 
@@ -4187,30 +4298,24 @@ gtk_dataview_button_press_callback( GtkWidget *WXUNUSED(widget),
 {
     if ((gdk_event->button == 3) && (gdk_event->type == GDK_BUTTON_PRESS))
     {
-        GtkTreePath *path = NULL;
+        wxGtkTreePath path;
         GtkTreeViewColumn *column = NULL;
         gint cell_x = 0;
         gint cell_y = 0;
         if (gtk_tree_view_get_path_at_pos(
             GTK_TREE_VIEW(dv->GtkGetTreeView()),
             (int) gdk_event->x, (int) gdk_event->y,
-            &path,
+            path.ByRef(),
             &column,
             &cell_x,
             &cell_y))
         {
             if (path)
             {
-                GtkTreeIter iter;
-                dv->GtkGetInternal()->get_iter( &iter, path );
-
                 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, dv->GetId() );
-                wxDataViewItem item( (void*) iter.user_data );;
-                event.SetItem( item );
+                event.SetItem(dv->GTKPathToItem(path));
                 event.SetModel( dv->GetModel() );
-                bool ret = dv->HandleWindowEvent( event );
-                gtk_tree_path_free( path );
-                return ret;
+                return dv->HandleWindowEvent( event );
              }
         }
     }
@@ -4242,12 +4347,16 @@ void wxDataViewCtrl::Init()
     m_cols.DeleteContents( true );
 }
 
-bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
-           const wxPoint& pos, const wxSize& size,
-           long style, const wxValidator& validator )
+bool wxDataViewCtrl::Create(wxWindow *parent,
+                            wxWindowID id,
+                            const wxPoint& pos,
+                            const wxSize& size,
+                            long style,
+                            const wxValidator& validator,
+                            const wxString& name)
 {
     if (!PreCreation( parent, pos, size ) ||
-        !CreateBase( parent, id, pos, size, style, validator ))
+        !CreateBase( parent, id, pos, size, style, validator, name ))
     {
         wxFAIL_MSG( wxT("wxDataViewCtrl creation failed") );
         return false;
@@ -4334,6 +4443,14 @@ bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
     return true;
 }
 
+wxDataViewItem wxDataViewCtrl::GTKPathToItem(GtkTreePath *path) const
+{
+    GtkTreeIter iter;
+    return wxDataViewItem(path && m_internal->get_iter(&iter, path)
+                            ? iter.user_data
+                            : NULL);
+}
+
 void wxDataViewCtrl::OnInternalIdle()
 {
     wxWindow::OnInternalIdle();
@@ -4349,11 +4466,7 @@ void wxDataViewCtrl::OnInternalIdle()
 
 bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
 {
-    if ( m_internal )
-    {
-        delete m_internal;
-        m_internal = NULL;
-    }
+    wxDELETE(m_internal);
 
     if (!wxDataViewCtrlBase::AssociateModel( model ))
         return false;
@@ -4529,29 +4642,61 @@ void wxDataViewCtrl::Expand( const wxDataViewItem & item )
 {
     GtkTreeIter iter;
     iter.user_data = item.GetID();
-    GtkTreePath *path = m_internal->get_path( &iter );
+    wxGtkTreePath path(m_internal->get_path( &iter ));
     gtk_tree_view_expand_row( GTK_TREE_VIEW(m_treeview), path, false );
-    gtk_tree_path_free( path );
 }
 
 void wxDataViewCtrl::Collapse( const wxDataViewItem & item )
 {
     GtkTreeIter iter;
     iter.user_data = item.GetID();
-    GtkTreePath *path = m_internal->get_path( &iter );
+    wxGtkTreePath path(m_internal->get_path( &iter ));
     gtk_tree_view_collapse_row( GTK_TREE_VIEW(m_treeview), path );
-    gtk_tree_path_free( path );
 }
 
 bool wxDataViewCtrl::IsExpanded( const wxDataViewItem & item ) const
 {
     GtkTreeIter iter;
     iter.user_data = item.GetID();
-    GtkTreePath *path = m_internal->get_path( &iter );
-    bool res = gtk_tree_view_row_expanded( GTK_TREE_VIEW(m_treeview), path );
-    gtk_tree_path_free( path );
+    wxGtkTreePath path(m_internal->get_path( &iter ));
+    return gtk_tree_view_row_expanded( GTK_TREE_VIEW(m_treeview), path );
+}
 
-    return res;
+wxDataViewItem wxDataViewCtrl::DoGetCurrentItem() const
+{
+    // The tree doesn't have any current item if it hadn't been created yet but
+    // it's arguably not an error to call this function in this case so just
+    // return an invalid item without asserting.
+    if ( !m_treeview )
+        return wxDataViewItem();
+
+    wxGtkTreePath path;
+    gtk_tree_view_get_cursor(GTK_TREE_VIEW(m_treeview), path.ByRef(), NULL);
+
+    return GTKPathToItem(path);
+}
+
+void wxDataViewCtrl::DoSetCurrentItem(const wxDataViewItem& item)
+{
+    wxCHECK_RET( m_treeview,
+                 "Current item can't be set before creating the control." );
+
+    // We need to make sure the model knows about this item or the path would
+    // be invalid and gtk_tree_view_set_cursor() would silently do nothing.
+    ExpandAncestors(item);
+
+    // We also need to preserve the existing selection from changing.
+    // Unfortunately the only way to do it seems to use our own selection
+    // function and forbid any selection changes during set cursor call.
+    wxGtkTreeSelectionLock
+        lock(gtk_tree_view_get_selection(GTK_TREE_VIEW(m_treeview)));
+
+    // Do move the cursor now.
+    GtkTreeIter iter;
+    iter.user_data = item.GetID();
+    wxGtkTreePath path(m_internal->get_path( &iter ));
+
+    gtk_tree_view_set_cursor(GTK_TREE_VIEW(m_treeview), path, NULL, FALSE);
 }
 
 wxDataViewItem wxDataViewCtrl::GetSelection() const
@@ -4567,14 +4712,13 @@ wxDataViewItem wxDataViewCtrl::GetSelection() const
         if (list)
         {
             GtkTreePath *path = (GtkTreePath*) list->data;
-            GtkTreeIter iter;
-            m_internal->get_iter( &iter, path );
+            wxDataViewItem item(GTKPathToItem(path));
 
             // delete list
             g_list_foreach( list, (GFunc) gtk_tree_path_free, NULL );
             g_list_free( list );
 
-            return wxDataViewItem( (void*) iter.user_data );
+            return item;
         }
     }
     else
@@ -4582,7 +4726,7 @@ wxDataViewItem wxDataViewCtrl::GetSelection() const
         GtkTreeIter iter;
         if (gtk_tree_selection_get_selected( selection, NULL, &iter ))
         {
-            wxDataViewItem item( (void*) iter.user_data );
+            wxDataViewItem item( iter.user_data );
             return item;
         }
     }
@@ -4605,10 +4749,7 @@ int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const
         {
             GtkTreePath *path = (GtkTreePath*) list->data;
 
-            GtkTreeIter iter;
-            m_internal->get_iter( &iter, path );
-
-            sel.Add( wxDataViewItem( (void*) iter.user_data ) );
+            sel.Add(GTKPathToItem(path));
 
             list = g_list_next( list );
             count++;
@@ -4736,9 +4877,8 @@ void wxDataViewCtrl::EnsureVisible(const wxDataViewItem& item,
 
     GtkTreeIter iter;
     iter.user_data = (gpointer) item.GetID();
-    GtkTreePath *path = m_internal->get_path( &iter );
+    wxGtkTreePath path(m_internal->get_path( &iter ));
     gtk_tree_view_scroll_to_cell( GTK_TREE_VIEW(m_treeview), path, NULL, false, 0.0, 0.0 );
-    gtk_tree_path_free( path );
 }
 
 void wxDataViewCtrl::HitTest(const wxPoint& WXUNUSED(point),