+//-----------------------------------------------------------------------------
+// wxDataViewCtrlInternal
+//-----------------------------------------------------------------------------
+
+wxDataViewCtrlInternal::wxDataViewCtrlInternal( wxDataViewCtrl *owner, wxDataViewModel *wx_model )
+{
+ m_owner = owner;
+ m_wx_model = wx_model;
+
+ m_root = NULL;
+ m_sort_order = GTK_SORT_ASCENDING;
+ m_sort_column = -1;
+ m_dataview_sort_column = NULL;
+
+ m_dragDataObject = NULL;
+ m_dropDataObject = NULL;
+
+ m_dirty = false;
+
+ m_gtk_model = wxgtk_tree_model_new();
+ m_gtk_model->internal = this;
+
+ m_notifier = new wxGtkDataViewModelNotifier( wx_model, this );
+
+ wx_model->AddNotifier( m_notifier );
+
+ // g_object_unref( gtk_model ); ???
+
+ if (!m_wx_model->IsVirtualListModel())
+ InitTree();
+
+ gtk_tree_view_set_model( GTK_TREE_VIEW(m_owner->GtkGetTreeView()), GTK_TREE_MODEL(m_gtk_model) );
+}
+
+wxDataViewCtrlInternal::~wxDataViewCtrlInternal()
+{
+ m_wx_model->RemoveNotifier( m_notifier );
+
+ // remove the model from the GtkTreeView before it gets destroyed
+ gtk_tree_view_set_model( GTK_TREE_VIEW( m_owner->GtkGetTreeView() ), NULL );
+
+ g_object_unref( m_gtk_model );
+
+ delete m_dragDataObject;
+ delete m_dropDataObject;
+}
+
+void wxDataViewCtrlInternal::ScheduleRefresh()
+{
+ m_dirty = true;
+}
+
+void wxDataViewCtrlInternal::OnInternalIdle()
+{
+ if (m_dirty)
+ {
+ GtkWidget *widget = m_owner->GtkGetTreeView();
+ gtk_widget_queue_draw( widget );
+ m_dirty = false;
+ }
+}
+
+void wxDataViewCtrlInternal::InitTree()
+{
+ wxDataViewItem item;
+ m_root = new wxGtkTreeModelNode( NULL, item, this );
+
+ BuildBranch( m_root );
+}
+
+void wxDataViewCtrlInternal::BuildBranch( wxGtkTreeModelNode *node )
+{
+ if (node->GetChildCount() == 0)
+ {
+ wxDataViewItemArray children;
+ unsigned int count = m_wx_model->GetChildren( node->GetItem(), children );
+
+ unsigned int pos;
+ for (pos = 0; pos < count; pos++)
+ {
+ wxDataViewItem child = children[pos];
+
+ if (m_wx_model->IsContainer( child ))
+ node->AddNode( new wxGtkTreeModelNode( node, child, this ) );
+ else
+ node->AddLeaf( child.GetID() );
+
+ // Don't send any events here
+ }
+ }
+}
+
+// GTK+ dnd iface
+
+bool wxDataViewCtrlInternal::EnableDragSource( const wxDataFormat &format )
+{
+ wxGtkString atom_str( gdk_atom_name( format ) );
+ m_dragSourceTargetEntryTarget = wxCharBuffer( atom_str );
+
+ m_dragSourceTargetEntry.target = m_dragSourceTargetEntryTarget.data();
+ m_dragSourceTargetEntry.flags = 0;
+ m_dragSourceTargetEntry.info = static_cast<guint>(-1);
+
+ gtk_tree_view_enable_model_drag_source( GTK_TREE_VIEW(m_owner->GtkGetTreeView() ),
+ GDK_BUTTON1_MASK, &m_dragSourceTargetEntry, 1, (GdkDragAction) GDK_ACTION_COPY );
+
+ return true;
+}
+
+bool wxDataViewCtrlInternal::EnableDropTarget( const wxDataFormat &format )
+{
+ wxGtkString atom_str( gdk_atom_name( format ) );
+ m_dropTargetTargetEntryTarget = wxCharBuffer( atom_str );
+
+ m_dropTargetTargetEntry.target = m_dropTargetTargetEntryTarget.data();
+ m_dropTargetTargetEntry.flags = 0;
+ m_dropTargetTargetEntry.info = static_cast<guint>(-1);
+
+ gtk_tree_view_enable_model_drag_dest( GTK_TREE_VIEW(m_owner->GtkGetTreeView() ),
+ &m_dropTargetTargetEntry, 1, (GdkDragAction) GDK_ACTION_COPY );
+
+ return true;
+}
+
+gboolean wxDataViewCtrlInternal::row_draggable( GtkTreeDragSource *WXUNUSED(drag_source),
+ GtkTreePath *path )
+{
+ delete m_dragDataObject;
+ m_dragDataObject = NULL;
+
+ wxDataViewItem item(GetOwner()->GTKPathToItem(path));
+ if ( !item )
+ return FALSE;
+
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_BEGIN_DRAG, m_owner->GetId() );
+ event.SetEventObject( m_owner );
+ event.SetItem( item );
+ event.SetModel( m_wx_model );
+ gint x, y;
+ gtk_widget_get_pointer(m_owner->GtkGetTreeView(), &x, &y);
+ event.SetPosition(x, y);
+ if (!m_owner->HandleWindowEvent( event ))
+ return FALSE;
+
+ if (!event.IsAllowed())
+ return FALSE;
+
+ wxDataObject *obj = event.GetDataObject();
+ if (!obj)
+ return FALSE;
+
+ m_dragDataObject = obj;
+
+ return TRUE;
+}
+
+gboolean
+wxDataViewCtrlInternal::drag_data_delete(GtkTreeDragSource *WXUNUSED(drag_source),
+ GtkTreePath *WXUNUSED(path))
+{
+ return FALSE;
+}
+
+gboolean wxDataViewCtrlInternal::drag_data_get( GtkTreeDragSource *WXUNUSED(drag_source),
+ GtkTreePath *path, GtkSelectionData *selection_data )
+{
+ wxDataViewItem item(GetOwner()->GTKPathToItem(path));
+ if ( !item )
+ return FALSE;
+
+ GdkAtom target = gtk_selection_data_get_target(selection_data);
+ if (!m_dragDataObject->IsSupported(target))
+ return FALSE;
+
+ size_t size = m_dragDataObject->GetDataSize(target);
+ if (size == 0)
+ return FALSE;
+
+ void *buf = malloc( size );
+
+ gboolean res = FALSE;
+ if (m_dragDataObject->GetDataHere(target, buf))
+ {
+ res = TRUE;
+
+ gtk_selection_data_set(selection_data, target,
+ 8, (const guchar*) buf, size );
+ }
+
+ free( buf );
+
+ return res;
+}
+
+gboolean
+wxDataViewCtrlInternal::drag_data_received(GtkTreeDragDest *WXUNUSED(drag_dest),
+ GtkTreePath *path,
+ GtkSelectionData *selection_data)
+{
+ wxDataViewItem item(GetOwner()->GTKPathToItem(path));
+ if ( !item )
+ return FALSE;
+
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_DROP, m_owner->GetId() );
+ event.SetEventObject( m_owner );
+ event.SetItem( item );
+ event.SetModel( m_wx_model );
+ event.SetDataFormat(gtk_selection_data_get_target(selection_data));
+ event.SetDataSize(gtk_selection_data_get_length(selection_data));
+ event.SetDataBuffer(const_cast<guchar*>(gtk_selection_data_get_data(selection_data)));
+ if (!m_owner->HandleWindowEvent( event ))
+ return FALSE;
+
+ if (!event.IsAllowed())
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+wxDataViewCtrlInternal::row_drop_possible(GtkTreeDragDest *WXUNUSED(drag_dest),
+ GtkTreePath *path,
+ GtkSelectionData *selection_data)
+{
+ wxDataViewItem item(GetOwner()->GTKPathToItem(path));
+ if ( !item )
+ return FALSE;
+
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
+ event.SetEventObject( m_owner );
+ event.SetItem( item );
+ event.SetModel( m_wx_model );
+ event.SetDataFormat(gtk_selection_data_get_target(selection_data));
+ event.SetDataSize(gtk_selection_data_get_length(selection_data));
+ if (!m_owner->HandleWindowEvent( event ))
+ return FALSE;
+
+ if (!event.IsAllowed())
+ return FALSE;
+
+ return TRUE;
+}
+
+// notifications from wxDataViewModel
+
+bool wxDataViewCtrlInternal::Cleared()
+{
+ if (m_root)
+ {
+ delete m_root;
+ m_root = NULL;
+ }
+
+ InitTree();
+
+ ScheduleRefresh();
+
+ return true;
+}
+
+void wxDataViewCtrlInternal::Resort()
+{
+ if (!m_wx_model->IsVirtualListModel())
+ m_root->Resort();
+
+ ScheduleRefresh();
+}
+
+bool wxDataViewCtrlInternal::ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item )
+{
+ if (!m_wx_model->IsVirtualListModel())
+ {
+ wxGtkTreeModelNode *parent_node = FindNode( parent );
+ wxCHECK_MSG(parent_node, false,
+ "Did you forget a call to ItemAdded()? The parent node is unknown to the wxGtkTreeModel");
+
+ wxDataViewItemArray modelSiblings;
+ m_wx_model->GetChildren(parent, modelSiblings);
+ const int modelSiblingsSize = modelSiblings.size();
+
+ int posInModel = modelSiblings.Index(item, /*fromEnd=*/true);
+ wxCHECK_MSG( posInModel != wxNOT_FOUND, false, "adding non-existent item?" );
+
+ const wxGtkTreeModelChildren& nodeSiblings = parent_node->GetChildren();
+ const int nodeSiblingsSize = nodeSiblings.size();
+
+ int nodePos = 0;
+
+ if ( posInModel == modelSiblingsSize - 1 )
+ {
+ nodePos = nodeSiblingsSize;
+ }
+ else if ( modelSiblingsSize == nodeSiblingsSize + 1 )
+ {
+ // This is the simple case when our node tree already matches the
+ // model and only this one item is missing.
+ nodePos = posInModel;
+ }
+ else
+ {
+ // It's possible that a larger discrepancy between the model and
+ // our realization exists. This can happen e.g. when adding a bunch
+ // of items to the model and then calling ItemsAdded() just once
+ // afterwards. In this case, we must find the right position by
+ // looking at sibling items.
+
+ // append to the end if we won't find a better position:
+ nodePos = nodeSiblingsSize;
+
+ for ( int nextItemPos = posInModel + 1;
+ nextItemPos < modelSiblingsSize;
+ nextItemPos++ )
+ {
+ int nextNodePos = parent_node->FindChildByItem(modelSiblings[nextItemPos]);
+ if ( nextNodePos != wxNOT_FOUND )
+ {
+ nodePos = nextNodePos;
+ break;
+ }
+ }
+ }
+
+ if (m_wx_model->IsContainer( item ))
+ parent_node->InsertNode( new wxGtkTreeModelNode( parent_node, item, this ), nodePos );
+ else
+ parent_node->InsertLeaf( item.GetID(), nodePos );
+ }
+
+ ScheduleRefresh();
+
+ return true;
+}
+
+bool wxDataViewCtrlInternal::ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item )
+{
+ if (!m_wx_model->IsVirtualListModel())
+ {
+ wxGtkTreeModelNode *parent_node = FindNode( parent );
+ wxASSERT_MSG(parent_node,
+ "Did you forget a call to ItemAdded()? The parent node is unknown to the wxGtkTreeModel");
+
+ parent_node->DeleteChild( item.GetID() );
+ }
+
+ ScheduleRefresh();
+
+ return true;
+}
+
+bool wxDataViewCtrlInternal::ItemChanged( const wxDataViewItem &item )
+{
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, m_owner->GetId() );
+ event.SetEventObject( m_owner );
+ event.SetModel( m_owner->GetModel() );
+ event.SetItem( item );
+ m_owner->HandleWindowEvent( event );
+
+ return true;
+}
+
+bool wxDataViewCtrlInternal::ValueChanged( const wxDataViewItem &item, unsigned int view_column )
+{
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, m_owner->GetId() );
+ event.SetEventObject( m_owner );
+ event.SetModel( m_owner->GetModel() );
+ event.SetColumn( view_column );
+ event.SetDataViewColumn( GetOwner()->GetColumn(view_column) );
+ event.SetItem( item );
+ m_owner->HandleWindowEvent( event );
+
+ return true;
+}
+
+// GTK+ model iface
+
+GtkTreeModelFlags wxDataViewCtrlInternal::get_flags()
+{
+ 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 )
+{
+
+ if (m_wx_model->IsVirtualListModel())
+ {
+ wxDataViewVirtualListModel *wx_model = (wxDataViewVirtualListModel*) m_wx_model;
+
+ unsigned int i = (unsigned int)gtk_tree_path_get_indices (path)[0];
+
+ if (i >= wx_model->GetCount())
+ return FALSE;
+
+ iter->stamp = m_gtk_model->stamp;
+ // user_data is just the index +1
+ iter->user_data = wxUIntToPtr(i+1);
+
+ return TRUE;
+ }
+ else
+ {
+ int depth = gtk_tree_path_get_depth( path );
+
+ wxGtkTreeModelNode *node = m_root;
+
+ int i;
+ for (i = 0; i < depth; i++)
+ {
+ BuildBranch( node );
+
+ gint pos = gtk_tree_path_get_indices (path)[i];
+ if (pos < 0) return FALSE;
+ if ((size_t)pos >= node->GetChildCount()) return FALSE;
+
+ void* id = node->GetChildren().Item( (size_t) pos );
+
+ if (i == depth-1)
+ {
+ iter->stamp = m_gtk_model->stamp;
+ iter->user_data = id;
+ return TRUE;
+ }
+
+ size_t count = node->GetNodes().GetCount();
+ size_t pos2;
+ for (pos2 = 0; pos2 < count; pos2++)
+ {
+ wxGtkTreeModelNode *child_node = node->GetNodes().Item( pos2 );
+ if (child_node->GetItem().GetID() == id)
+ {
+ node = child_node;
+ break;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+GtkTreePath *wxDataViewCtrlInternal::get_path( GtkTreeIter *iter )
+{
+ // When this is called from ItemDeleted(), the item is already
+ // deleted in the model.
+
+ GtkTreePath *retval = gtk_tree_path_new ();
+
+ if (m_wx_model->IsVirtualListModel())
+ {
+ // iter is root, add nothing
+ if (!iter->user_data)
+ return retval;
+
+ // user_data is just the index +1
+ int i = ( (wxUIntPtr) iter->user_data ) -1;
+ gtk_tree_path_append_index (retval, i);
+ }
+ else
+ {
+ void *id = iter->user_data;
+
+ wxGtkTreeModelNode *node = FindParentNode( iter );
+ while (node)
+ {
+ int pos = node->GetChildren().Index( id );
+
+ gtk_tree_path_prepend_index( retval, pos );
+
+ id = node->GetItem().GetID();
+ node = node->GetParent();
+ }
+ }
+
+ return retval;
+}
+
+gboolean wxDataViewCtrlInternal::iter_next( GtkTreeIter *iter )
+{
+ if (m_wx_model->IsVirtualListModel())
+ {
+ wxDataViewVirtualListModel *wx_model = (wxDataViewVirtualListModel*) m_wx_model;
+
+ // user_data is just the index +1
+ int n = ( (wxUIntPtr) iter->user_data ) -1;
+
+ if (n == -1)
+ {
+ iter->user_data = NULL;
+ return FALSE;
+ }
+
+ if (n >= (int) wx_model->GetCount()-1)
+ {
+ iter->user_data = NULL;
+ return FALSE;
+ }
+
+ // user_data is just the index +1 (+2 because we need the next)
+ iter->user_data = wxUIntToPtr(n+2);
+ }
+ else
+ {
+ wxGtkTreeModelNode *parent = FindParentNode( iter );
+ if( parent == NULL )
+ {
+ iter->user_data = NULL;
+ return FALSE;
+ }
+
+ int pos = parent->GetChildren().Index( iter->user_data );
+
+ if (pos == (int) parent->GetChildCount()-1)
+ {
+ iter->user_data = NULL;
+ return FALSE;
+ }
+
+ iter->user_data = parent->GetChildren().Item( pos+1 );
+ }
+
+ return TRUE;
+}
+
+gboolean wxDataViewCtrlInternal::iter_children( GtkTreeIter *iter, GtkTreeIter *parent )
+{
+ if (m_wx_model->IsVirtualListModel())
+ {
+ // this is a list, nodes have no children
+ if (parent)
+ return FALSE;
+
+ iter->stamp = m_gtk_model->stamp;
+ iter->user_data = (gpointer) 1;
+
+ return TRUE;
+ }
+ else
+ {
+ if (iter == NULL)
+ {
+ if (m_root->GetChildCount() == 0) return FALSE;
+ iter->stamp = m_gtk_model->stamp;
+ iter->user_data = (gpointer) m_root->GetChildren().Item( 0 );
+ return TRUE;
+ }
+
+
+ wxDataViewItem item;
+ if (parent)
+ item = wxDataViewItem( (void*) parent->user_data );
+
+ if (!m_wx_model->IsContainer( item ))
+ return FALSE;
+
+ wxGtkTreeModelNode *parent_node = FindNode( parent );
+ wxASSERT_MSG(parent_node,
+ "Did you forget a call to ItemAdded()? The parent node is unknown to the wxGtkTreeModel");
+
+ BuildBranch( parent_node );
+
+ if (parent_node->GetChildCount() == 0)
+ return FALSE;
+
+ iter->stamp = m_gtk_model->stamp;
+ iter->user_data = (gpointer) parent_node->GetChildren().Item( 0 );
+ }
+
+ return TRUE;
+}
+
+gboolean wxDataViewCtrlInternal::iter_has_child( GtkTreeIter *iter )
+{
+ if (m_wx_model->IsVirtualListModel())
+ {
+ wxDataViewVirtualListModel *wx_model = (wxDataViewVirtualListModel*) m_wx_model;
+
+ if (iter == NULL)
+ return (wx_model->GetCount() > 0);
+
+ // this is a list, nodes have no children
+ return FALSE;
+ }
+ else
+ {
+ if (iter == NULL)
+ return (m_root->GetChildCount() > 0);
+
+ wxDataViewItem item( (void*) iter->user_data );
+
+ bool is_container = m_wx_model->IsContainer( item );
+
+ if (!is_container)
+ return FALSE;
+
+ wxGtkTreeModelNode *node = FindNode( iter );
+ wxASSERT_MSG(node,
+ "Did you forget a call to ItemAdded()? The iterator is unknown to the wxGtkTreeModel");
+
+ BuildBranch( node );
+
+ return (node->GetChildCount() > 0);
+ }
+}
+
+gint wxDataViewCtrlInternal::iter_n_children( GtkTreeIter *iter )
+{
+ if (m_wx_model->IsVirtualListModel())
+ {
+ wxDataViewVirtualListModel *wx_model = (wxDataViewVirtualListModel*) m_wx_model;
+
+ if (iter == NULL)
+ return (gint) wx_model->GetCount();
+
+ return 0;
+ }
+ else
+ {
+ if (iter == NULL)
+ return m_root->GetChildCount();
+
+ wxDataViewItem item( (void*) iter->user_data );
+
+ if (!m_wx_model->IsContainer( item ))
+ return 0;
+
+ wxGtkTreeModelNode *parent_node = FindNode( iter );
+ wxASSERT_MSG(parent_node,
+ "Did you forget a call to ItemAdded()? The parent node is unknown to the wxGtkTreeModel");
+
+ BuildBranch( parent_node );
+
+ return parent_node->GetChildCount();
+ }
+}
+
+gboolean wxDataViewCtrlInternal::iter_nth_child( GtkTreeIter *iter, GtkTreeIter *parent, gint n )
+{
+ if (m_wx_model->IsVirtualListModel())
+ {
+ wxDataViewVirtualListModel *wx_model = (wxDataViewVirtualListModel*) m_wx_model;
+
+ if (parent)
+ return FALSE;
+
+ if (n < 0)
+ return FALSE;
+
+ if (n >= (gint) wx_model->GetCount())
+ return FALSE;
+
+ iter->stamp = m_gtk_model->stamp;
+ // user_data is just the index +1
+ iter->user_data = wxUIntToPtr(n+1);
+
+ return TRUE;
+ }
+ else
+ {
+ void* id = NULL;
+ if (parent) id = (void*) parent->user_data;
+ wxDataViewItem item( id );
+
+ if (!m_wx_model->IsContainer( item ))
+ return FALSE;
+
+ wxGtkTreeModelNode *parent_node = FindNode( parent );
+ wxASSERT_MSG(parent_node,
+ "Did you forget a call to ItemAdded()? The parent node is unknown to the wxGtkTreeModel");
+
+ BuildBranch( parent_node );
+
+ iter->stamp = m_gtk_model->stamp;
+ iter->user_data = parent_node->GetChildren().Item( n );
+
+ return TRUE;
+ }
+}
+
+gboolean wxDataViewCtrlInternal::iter_parent( GtkTreeIter *iter, GtkTreeIter *child )
+{
+ if (m_wx_model->IsVirtualListModel())
+ {
+ return FALSE;
+ }
+ else
+ {
+ wxGtkTreeModelNode *node = FindParentNode( child );
+ if (!node)
+ return FALSE;
+
+ iter->stamp = m_gtk_model->stamp;
+ iter->user_data = (gpointer) node->GetItem().GetID();
+
+ return TRUE;
+ }
+}
+
+// item can be deleted already in the model
+int wxDataViewCtrlInternal::GetIndexOf( const wxDataViewItem &parent, const wxDataViewItem &item )
+{
+ if (m_wx_model->IsVirtualListModel())
+ {
+ return wxPtrToUInt(item.GetID()) - 1;
+ }
+ else
+ {
+ wxGtkTreeModelNode *parent_node = FindNode( parent );
+ wxGtkTreeModelChildren &children = parent_node->GetChildren();
+ size_t j;
+ for (j = 0; j < children.GetCount(); j++)
+ {
+ if (children[j] == item.GetID())
+ return j;
+ }
+ }
+ return -1;
+}
+
+
+static wxGtkTreeModelNode*
+wxDataViewCtrlInternal_FindNode( wxDataViewModel * model, wxGtkTreeModelNode *treeNode, const wxDataViewItem &item )
+{
+ if( model == NULL )
+ return NULL;
+
+ ItemList list;
+ list.DeleteContents( true );
+ wxDataViewItem it( item );
+
+ while( it.IsOk() )
+ {
+ wxDataViewItem * pItem = new wxDataViewItem( it );
+ list.Insert( pItem );
+ it = model->GetParent( it );
+ }
+
+ wxGtkTreeModelNode * node = treeNode;
+ for( ItemList::compatibility_iterator n = list.GetFirst(); n; n = n->GetNext() )
+ {
+ if( node && node->GetNodes().GetCount() != 0 )
+ {
+ int len = node->GetNodes().GetCount();
+ wxGtkTreeModelNodes &nodes = node->GetNodes();
+ int j = 0;
+ for( ; j < len; j ++)
+ {
+ if( nodes[j]->GetItem() == *(n->GetData()))
+ {
+ node = nodes[j];
+ break;
+ }
+ }
+
+ if( j == len )
+ {
+ return NULL;
+ }
+ }
+ else
+ return NULL;
+ }
+ return node;
+
+}
+
+wxGtkTreeModelNode *wxDataViewCtrlInternal::FindNode( GtkTreeIter *iter )
+{
+ if (!iter)
+ return m_root;
+
+ wxDataViewItem item( (void*) iter->user_data );
+ if (!item.IsOk())
+ return m_root;
+
+ wxGtkTreeModelNode *result = wxDataViewCtrlInternal_FindNode( m_wx_model, m_root, item );
+
+/*
+ if (!result)
+ {
+ wxLogDebug( "Not found %p", iter->user_data );
+ char *crash = NULL;
+ *crash = 0;
+ }
+ // TODO: remove this code
+*/
+
+ return result;
+}
+
+wxGtkTreeModelNode *wxDataViewCtrlInternal::FindNode( const wxDataViewItem &item )
+{
+ if (!item.IsOk())
+ return m_root;
+
+ wxGtkTreeModelNode *result = wxDataViewCtrlInternal_FindNode( m_wx_model, m_root, item );
+
+/*
+ if (!result)
+ {
+ wxLogDebug( "Not found %p", item.GetID() );
+ char *crash = NULL;
+ *crash = 0;
+ }
+ // TODO: remove this code
+*/
+
+ return result;
+}
+
+static wxGtkTreeModelNode*
+wxDataViewCtrlInternal_FindParentNode( wxDataViewModel * model, wxGtkTreeModelNode *treeNode, const wxDataViewItem &item )
+{
+ if( model == NULL )
+ return NULL;
+
+ ItemList list;
+ list.DeleteContents( true );
+ if( !item.IsOk() )
+ return NULL;
+
+ wxDataViewItem it( model->GetParent( item ) );
+ while( it.IsOk() )
+ {
+ wxDataViewItem * pItem = new wxDataViewItem( it );
+ list.Insert( pItem );
+ it = model->GetParent( it );
+ }
+
+ wxGtkTreeModelNode * node = treeNode;
+ for( ItemList::compatibility_iterator n = list.GetFirst(); n; n = n->GetNext() )
+ {
+ if( node && node->GetNodes().GetCount() != 0 )
+ {
+ int len = node->GetNodes().GetCount();
+ wxGtkTreeModelNodes nodes = node->GetNodes();
+ int j = 0;
+ for( ; j < len; j ++)
+ {
+ if( nodes[j]->GetItem() == *(n->GetData()))
+ {
+ node = nodes[j];
+ break;
+ }
+ }
+
+ if( j == len )
+ {
+ return NULL;
+ }
+ }
+ else
+ return NULL;
+ }
+ //Examine whether the node is item's parent node
+ int len = node->GetChildCount();
+ for( int i = 0; i < len ; i ++ )
+ {
+ if( node->GetChildren().Item( i ) == item.GetID() )
+ return node;
+ }
+ return NULL;
+}
+
+wxGtkTreeModelNode *wxDataViewCtrlInternal::FindParentNode( GtkTreeIter *iter )
+{
+ if (!iter)
+ return NULL;
+
+ wxDataViewItem item( (void*) iter->user_data );
+ if (!item.IsOk())
+ return NULL;
+
+ return wxDataViewCtrlInternal_FindParentNode( m_wx_model, m_root, item );
+}
+
+wxGtkTreeModelNode *wxDataViewCtrlInternal::FindParentNode( const wxDataViewItem &item )
+{
+ if (!item.IsOk())
+ return NULL;
+
+ return wxDataViewCtrlInternal_FindParentNode( m_wx_model, m_root, item );
+}
+
+//-----------------------------------------------------------------------------
+// wxDataViewCtrl signal callbacks
+//-----------------------------------------------------------------------------
+
+static void
+wxdataview_selection_changed_callback( GtkTreeSelection* WXUNUSED(selection), wxDataViewCtrl *dv )
+{
+ if (!gtk_widget_get_realized(dv->m_widget))
+ return;
+
+ wxDataViewEvent event( wxEVT_DATAVIEW_SELECTION_CHANGED, dv->GetId() );
+ event.SetItem( dv->GetSelection() );
+ event.SetModel( dv->GetModel() );
+ dv->HandleWindowEvent( event );
+}
+
+static void
+wxdataview_row_activated_callback( GtkTreeView* WXUNUSED(treeview), GtkTreePath *path,
+ GtkTreeViewColumn *WXUNUSED(column), wxDataViewCtrl *dv )
+{
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_ACTIVATED, dv->GetId() );
+
+ wxDataViewItem item(dv->GTKPathToItem(path));
+ event.SetItem( item );
+ event.SetModel( dv->GetModel() );
+ dv->HandleWindowEvent( event );
+}
+
+static gboolean
+wxdataview_test_expand_row_callback( GtkTreeView* WXUNUSED(treeview), GtkTreeIter* iter,
+ GtkTreePath *WXUNUSED(path), wxDataViewCtrl *dv )
+{
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_EXPANDING, dv->GetId() );
+
+ wxDataViewItem item( (void*) iter->user_data );;
+ event.SetItem( item );
+ event.SetModel( dv->GetModel() );
+ dv->HandleWindowEvent( event );
+
+ return !event.IsAllowed();
+}
+
+static void
+wxdataview_row_expanded_callback( GtkTreeView* WXUNUSED(treeview), GtkTreeIter* iter,
+ GtkTreePath *WXUNUSED(path), wxDataViewCtrl *dv )
+{
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_EXPANDED, dv->GetId() );
+
+ wxDataViewItem item( (void*) iter->user_data );;
+ event.SetItem( item );
+ event.SetModel( dv->GetModel() );
+ dv->HandleWindowEvent( event );
+}
+
+static gboolean
+wxdataview_test_collapse_row_callback( GtkTreeView* WXUNUSED(treeview), GtkTreeIter* iter,
+ GtkTreePath *WXUNUSED(path), wxDataViewCtrl *dv )
+{
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_COLLAPSING, dv->GetId() );
+
+ wxDataViewItem item( (void*) iter->user_data );;
+ event.SetItem( item );
+ event.SetModel( dv->GetModel() );
+ dv->HandleWindowEvent( event );
+
+ return !event.IsAllowed();
+}
+
+static void
+wxdataview_row_collapsed_callback( GtkTreeView* WXUNUSED(treeview), GtkTreeIter* iter,
+ GtkTreePath *WXUNUSED(path), wxDataViewCtrl *dv )
+{
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_COLLAPSED, dv->GetId() );
+
+ wxDataViewItem item( (void*) iter->user_data );;
+ event.SetItem( item );
+ event.SetModel( dv->GetModel() );
+ dv->HandleWindowEvent( event );
+}
+
+//-----------------------------------------------------------------------------
+ // wxDataViewCtrl
+//-----------------------------------------------------------------------------
+
+void wxDataViewCtrl::AddChildGTK(wxWindowGTK* child)
+{
+ GtkWidget* treeview = GtkGetTreeView();
+
+ // Insert widget in GtkTreeView
+ if (gtk_widget_get_realized(treeview))
+ gtk_widget_set_parent_window( child->m_widget,
+ gtk_tree_view_get_bin_window( GTK_TREE_VIEW(treeview) ) );
+ gtk_widget_set_parent( child->m_widget, treeview );
+}
+
+static
+void gtk_dataviewctrl_size_callback( GtkWidget *WXUNUSED(widget),
+ GtkAllocation *WXUNUSED(gtk_alloc),
+ wxDataViewCtrl *win )
+{
+ wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
+ while (node)
+ {
+ wxWindow *child = node->GetData();
+
+ GtkRequisition req;
+ gtk_widget_get_preferred_size(child->m_widget, NULL, &req);
+
+ GtkAllocation alloc;
+ alloc.x = child->m_x;
+ alloc.y = child->m_y;
+ alloc.width = child->m_width;
+ alloc.height = child->m_height;
+ gtk_widget_size_allocate( child->m_widget, &alloc );
+
+ node = node->GetNext();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// "motion_notify_event"
+//-----------------------------------------------------------------------------
+
+static gboolean
+gtk_dataview_motion_notify_callback( GtkWidget *WXUNUSED(widget),
+ GdkEventMotion *gdk_event,
+ wxDataViewCtrl *dv )
+{
+ int x = gdk_event->x;
+ int y = gdk_event->y;
+ if (gdk_event->is_hint)
+ {
+#ifdef __WXGTK3__
+ gdk_window_get_device_position(gdk_event->window, gdk_event->device, &x, &y, NULL);
+#else
+ gdk_window_get_pointer(gdk_event->window, &x, &y, NULL);
+#endif
+ }
+
+ 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()),
+ x, y,
+ path.ByRef(),
+ &column,
+ &cell_x,
+ &cell_y))
+ {
+ if (path)
+ {
+ GtkTreeIter iter;
+ dv->GtkGetInternal()->get_iter( &iter, path );
+ }
+ }
+
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// "button_press_event"
+//-----------------------------------------------------------------------------
+
+static gboolean
+gtk_dataview_button_press_callback( GtkWidget *WXUNUSED(widget),
+ GdkEventButton *gdk_event,
+ wxDataViewCtrl *dv )
+{
+ if ((gdk_event->button == 3) && (gdk_event->type == GDK_BUTTON_PRESS))
+ {
+ wxGtkTreePath path;
+ GtkTreeViewColumn *column = NULL;
+ gint cell_x = 0;
+ gint cell_y = 0;
+ gtk_tree_view_get_path_at_pos
+ (
+ GTK_TREE_VIEW(dv->GtkGetTreeView()),
+ (int) gdk_event->x, (int) gdk_event->y,
+ path.ByRef(),
+ &column,
+ &cell_x,
+ &cell_y
+ );
+
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, dv->GetId() );
+ if (path)
+ event.SetItem(dv->GTKPathToItem(path));
+ event.SetModel( dv->GetModel() );
+ return dv->HandleWindowEvent( event );
+ }
+
+ return FALSE;
+}
+
+IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
+
+wxDataViewCtrl::~wxDataViewCtrl()
+{
+ // Stop editing before destroying the control to remove any event handlers
+ // which are added when editing started: if we didn't do this, the base
+ // class dtor would assert as it checks for any leftover handlers.
+ if ( m_treeview )
+ {
+ GtkTreeViewColumn *col;
+ gtk_tree_view_get_cursor(GTK_TREE_VIEW(m_treeview), NULL, &col);
+
+ wxDataViewColumn * const wxcol = FromGTKColumn(col);
+ if ( wxcol )
+ {
+ // This won't do anything if we're not editing it
+ wxcol->GetRenderer()->CancelEditing();
+ }
+
+ GTKDisconnect(m_treeview);
+ GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_treeview));
+ if (selection)
+ GTKDisconnect(selection);
+ }
+
+ m_cols.Clear();
+
+ delete m_internal;
+}
+
+void wxDataViewCtrl::Init()
+{
+ m_treeview = NULL;
+ m_internal = NULL;
+
+ m_cols.DeleteContents( true );
+
+ m_uniformRowHeight = -1;
+}
+
+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, name ))
+ {
+ wxFAIL_MSG( wxT("wxDataViewCtrl creation failed") );
+ return false;
+ }
+
+ m_widget = gtk_scrolled_window_new (NULL, NULL);
+ g_object_ref(m_widget);
+
+ GTKScrolledWindowSetBorder(m_widget, style);
+
+ m_treeview = gtk_tree_view_new();
+ gtk_container_add (GTK_CONTAINER (m_widget), m_treeview);
+
+ m_focusWidget = GTK_WIDGET(m_treeview);
+
+ g_signal_connect (m_treeview, "size_allocate",
+ G_CALLBACK (gtk_dataviewctrl_size_callback), this);
+
+ bool fixed = (style & wxDV_VARIABLE_LINE_HEIGHT) == 0;
+ gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), fixed );
+
+ if (style & wxDV_MULTIPLE)
+ {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
+ gtk_tree_selection_set_mode( selection, GTK_SELECTION_MULTIPLE );
+ }
+
+ gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(m_treeview), (style & wxDV_NO_HEADER) == 0 );