///////////////////////////////////////////////////////////////////////////// // Name: src/gtk/dataview.cpp // Purpose: wxDataViewCtrl GTK+2 implementation // Author: Robert Roebling // Id: $Id$ // Copyright: (c) 1998 Robert Roebling // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #if wxUSE_DATAVIEWCTRL #include "wx/dataview.h" #ifndef wxUSE_GENERICDATAVIEWCTRL #ifndef WX_PRECOMP #include "wx/log.h" #include "wx/dcclient.h" #include "wx/sizer.h" #include "wx/settings.h" #include "wx/crt.h" #endif #include "wx/stockitem.h" #include "wx/calctrl.h" #include "wx/popupwin.h" #include "wx/listimpl.cpp" #include "wx/gtk/private.h" #include "wx/gtk/dc.h" #include "wx/gtk/dcclient.h" //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- static wxDataViewCtrlInternal *gs_internal = NULL; class wxGtkTreeModelNode; extern "C" { typedef struct _GtkWxTreeModel GtkWxTreeModel; } //----------------------------------------------------------------------------- // wxDataViewCtrlInternal //----------------------------------------------------------------------------- WX_DECLARE_LIST(wxDataViewItem, ItemList); WX_DEFINE_LIST(ItemList) class WXDLLIMPEXP_ADV wxDataViewCtrlInternal { public: wxDataViewCtrlInternal( wxDataViewCtrl *owner, wxDataViewModel *wx_model, GtkWxTreeModel *gtk_model ); ~wxDataViewCtrlInternal(); // model iface GtkTreeModelFlags get_flags(); gboolean get_iter( GtkTreeIter *iter, GtkTreePath *path ); GtkTreePath *get_path( GtkTreeIter *iter); gboolean iter_next( GtkTreeIter *iter ); gboolean iter_children( GtkTreeIter *iter, GtkTreeIter *parent); gboolean iter_has_child( GtkTreeIter *iter ); gint iter_n_children( GtkTreeIter *iter ); gboolean iter_nth_child( GtkTreeIter *iter, GtkTreeIter *parent, gint n ); gboolean iter_parent( GtkTreeIter *iter, GtkTreeIter *child ); // dnd iface bool EnableDragSource( const wxDataFormat &format ); bool EnableDropTarget( const wxDataFormat &format ); gboolean row_draggable( GtkTreeDragSource *drag_source, GtkTreePath *path ); gboolean drag_data_delete( GtkTreeDragSource *drag_source, GtkTreePath* path ); gboolean drag_data_get( GtkTreeDragSource *drag_source, GtkTreePath *path, GtkSelectionData *selection_data ); gboolean drag_data_received( GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data ); gboolean row_drop_possible( GtkTreeDragDest *drag_dest, GtkTreePath *dest_path, GtkSelectionData *selection_data ); // notifactions from wxDataViewModel bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item ); bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item ); bool ItemChanged( const wxDataViewItem &item ); bool ValueChanged( const wxDataViewItem &item, unsigned int col ); bool Cleared(); void Resort(); // sorting interface void SetSortOrder( GtkSortType sort_order ) { m_sort_order = sort_order; } GtkSortType GetSortOrder() const { return m_sort_order; } void SetSortColumn( int column ) { m_sort_column = column; } int GetSortColumn() const { return m_sort_column; } void SetDataViewSortColumn( wxDataViewColumn *column ) { m_dataview_sort_column = column; } wxDataViewColumn *GetDataViewSortColumn() { return m_dataview_sort_column; } bool IsSorted() { return (m_sort_column >= 0); } // accessors wxDataViewModel* GetDataViewModel() { return m_wx_model; } const wxDataViewModel* GetDataViewModel() const { return m_wx_model; } wxDataViewCtrl* GetOwner() { return m_owner; } GtkWxTreeModel* GetGtkModel() { return m_gtk_model; } protected: void InitTree(); wxGtkTreeModelNode *FindNode( const wxDataViewItem &item ); wxGtkTreeModelNode *FindNode( GtkTreeIter *iter ); wxGtkTreeModelNode *FindParentNode( const wxDataViewItem &item ); wxGtkTreeModelNode *FindParentNode( GtkTreeIter *iter ); void BuildBranch( wxGtkTreeModelNode *branch ); private: wxGtkTreeModelNode *m_root; wxDataViewModel *m_wx_model; GtkWxTreeModel *m_gtk_model; wxDataViewCtrl *m_owner; GtkSortType m_sort_order; wxDataViewColumn *m_dataview_sort_column; int m_sort_column; GtkTargetEntry m_dragSourceTargetEntry; wxCharBuffer m_dragSourceTargetEntryTarget; wxDataObject *m_dragDataObject; GtkTargetEntry m_dropTargetTargetEntry; wxCharBuffer m_dropTargetTargetEntryTarget; wxDataObject *m_dropDataObject; }; //----------------------------------------------------------------------------- // wxGtkTreeModelNode //----------------------------------------------------------------------------- static int LINKAGEMODE wxGtkTreeModelChildCmp( void** id1, void** id2 ) { int ret = gs_internal->GetDataViewModel()->Compare( *id1, *id2, gs_internal->GetSortColumn(), (gs_internal->GetSortOrder() == GTK_SORT_ASCENDING) ); return ret; } WX_DEFINE_ARRAY_PTR( wxGtkTreeModelNode*, wxGtkTreeModelNodes ); WX_DEFINE_ARRAY_PTR( void*, wxGtkTreeModelChildren ); class wxGtkTreeModelNode { public: wxGtkTreeModelNode( wxGtkTreeModelNode* parent, const wxDataViewItem &item, wxDataViewCtrlInternal *internal ) { m_parent = parent; m_item = item; m_internal = internal; } ~wxGtkTreeModelNode() { size_t count = m_nodes.GetCount(); size_t i; for (i = 0; i < count; i++) { wxGtkTreeModelNode *child = m_nodes.Item( i ); delete child; } } unsigned int AddNode( wxGtkTreeModelNode* child ) { m_nodes.Add( child ); void *id = child->GetItem().GetID(); m_children.Add( id ); if (m_internal->IsSorted() || m_internal->GetDataViewModel()->HasDefaultCompare()) { gs_internal = m_internal; m_children.Sort( &wxGtkTreeModelChildCmp ); return m_children.Index( id ); } return m_children.GetCount()-1; } unsigned int AddLeave( void* id ) { m_children.Add( id ); if (m_internal->IsSorted() || m_internal->GetDataViewModel()->HasDefaultCompare()) { gs_internal = m_internal; m_children.Sort( &wxGtkTreeModelChildCmp ); return m_children.Index( id ); } return m_children.GetCount()-1; } void DeleteChild( void* id ) { m_children.Remove( id ); unsigned int count = m_nodes.GetCount(); unsigned int pos; for (pos = 0; pos < count; pos++) { wxGtkTreeModelNode *node = m_nodes.Item( pos ); if (node->GetItem().GetID() == id) { m_nodes.RemoveAt( pos ); delete node; break; } } } wxGtkTreeModelNode* GetParent() { return m_parent; } wxGtkTreeModelNodes &GetNodes() { return m_nodes; } wxGtkTreeModelChildren &GetChildren() { return m_children; } unsigned int GetChildCount() const { return m_children.GetCount(); } unsigned int GetNodesCount() const { return m_nodes.GetCount(); } wxDataViewItem &GetItem() { return m_item; } wxDataViewCtrlInternal *GetInternal() { return m_internal; } void Resort(); private: wxGtkTreeModelNode *m_parent; wxGtkTreeModelNodes m_nodes; wxGtkTreeModelChildren m_children; wxDataViewItem m_item; wxDataViewCtrlInternal *m_internal; }; //----------------------------------------------------------------------------- // data //----------------------------------------------------------------------------- extern bool g_blockEventsOnDrag; //----------------------------------------------------------------------------- // define new GTK+ class wxGtkTreeModel //----------------------------------------------------------------------------- extern "C" { #define GTK_TYPE_WX_TREE_MODEL (gtk_wx_tree_model_get_type ()) #define GTK_WX_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WX_TREE_MODEL, GtkWxTreeModel)) #define GTK_WX_TREE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_WX_TREE_MODEL, GtkWxTreeModelClass)) #define GTK_IS_WX_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WX_TREE_MODEL)) #define GTK_IS_WX_TREE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WX_TREE_MODEL)) #define GTK_WX_TREE_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_WX_TREE_MODEL, GtkWxTreeModelClass)) GType gtk_wx_tree_model_get_type (void); typedef struct _GtkWxTreeModelClass GtkWxTreeModelClass; struct _GtkWxTreeModel { GObject parent; /*< private >*/ gint stamp; wxDataViewCtrlInternal *internal; }; struct _GtkWxTreeModelClass { GObjectClass list_parent_class; }; static GtkWxTreeModel *wxgtk_tree_model_new (void); static void wxgtk_tree_model_init (GtkWxTreeModel *tree_model); static void wxgtk_tree_model_class_init (GtkWxTreeModelClass *klass); static void wxgtk_tree_model_tree_model_init (GtkTreeModelIface *iface); static void wxgtk_tree_model_sortable_init (GtkTreeSortableIface *iface); static void wxgtk_tree_model_drag_source_init(GtkTreeDragSourceIface *iface); static void wxgtk_tree_model_drag_dest_init (GtkTreeDragDestIface *iface); static void wxgtk_tree_model_finalize (GObject *object); static GtkTreeModelFlags wxgtk_tree_model_get_flags (GtkTreeModel *tree_model); static gint wxgtk_tree_model_get_n_columns (GtkTreeModel *tree_model); static GType wxgtk_tree_model_get_column_type (GtkTreeModel *tree_model, gint index); static gboolean wxgtk_tree_model_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path); static GtkTreePath *wxgtk_tree_model_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter); static void wxgtk_tree_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value); static gboolean wxgtk_tree_model_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter); static gboolean wxgtk_tree_model_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent); static gboolean wxgtk_tree_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter); static gint wxgtk_tree_model_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter); static gboolean wxgtk_tree_model_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n); static gboolean wxgtk_tree_model_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child); /* sortable */ static gboolean wxgtk_tree_model_get_sort_column_id (GtkTreeSortable *sortable, gint *sort_column_id, GtkSortType *order); static void wxgtk_tree_model_set_sort_column_id (GtkTreeSortable *sortable, gint sort_column_id, GtkSortType order); static void wxgtk_tree_model_set_sort_func (GtkTreeSortable *sortable, gint sort_column_id, GtkTreeIterCompareFunc func, gpointer data, GtkDestroyNotify destroy); static void wxgtk_tree_model_set_default_sort_func (GtkTreeSortable *sortable, GtkTreeIterCompareFunc func, gpointer data, GtkDestroyNotify destroy); static gboolean wxgtk_tree_model_has_default_sort_func (GtkTreeSortable *sortable); /* drag'n'drop */ static gboolean wxgtk_tree_model_row_draggable (GtkTreeDragSource *drag_source, GtkTreePath *path); static gboolean wxgtk_tree_model_drag_data_delete (GtkTreeDragSource *drag_source, GtkTreePath *path); static gboolean wxgtk_tree_model_drag_data_get (GtkTreeDragSource *drag_source, GtkTreePath *path, GtkSelectionData *selection_data); static gboolean wxgtk_tree_model_drag_data_received (GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data); static gboolean wxgtk_tree_model_row_drop_possible (GtkTreeDragDest *drag_dest, GtkTreePath *dest_path, GtkSelectionData *selection_data); static GObjectClass *list_parent_class = NULL; GType gtk_wx_tree_model_get_type (void) { static GType tree_model_type = 0; if (!tree_model_type) { const GTypeInfo tree_model_info = { sizeof (GtkWxTreeModelClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) wxgtk_tree_model_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GtkWxTreeModel), 0, (GInstanceInitFunc) wxgtk_tree_model_init, }; static const GInterfaceInfo tree_model_iface_info = { (GInterfaceInitFunc) wxgtk_tree_model_tree_model_init, NULL, NULL }; static const GInterfaceInfo sortable_iface_info = { (GInterfaceInitFunc) wxgtk_tree_model_sortable_init, NULL, NULL }; static const GInterfaceInfo drag_source_iface_info = { (GInterfaceInitFunc) wxgtk_tree_model_drag_source_init, NULL, NULL }; static const GInterfaceInfo drag_dest_iface_info = { (GInterfaceInitFunc) wxgtk_tree_model_drag_dest_init, NULL, NULL }; tree_model_type = g_type_register_static (G_TYPE_OBJECT, "GtkWxTreeModel", &tree_model_info, (GTypeFlags)0 ); g_type_add_interface_static (tree_model_type, GTK_TYPE_TREE_MODEL, &tree_model_iface_info); g_type_add_interface_static (tree_model_type, GTK_TYPE_TREE_SORTABLE, &sortable_iface_info); g_type_add_interface_static (tree_model_type, GTK_TYPE_TREE_DRAG_DEST, &drag_dest_iface_info); g_type_add_interface_static (tree_model_type, GTK_TYPE_TREE_DRAG_SOURCE, &drag_source_iface_info); } return tree_model_type; } static GtkWxTreeModel * wxgtk_tree_model_new(void) { GtkWxTreeModel *retval = (GtkWxTreeModel *) g_object_new (GTK_TYPE_WX_TREE_MODEL, NULL); return retval; } static void wxgtk_tree_model_class_init (GtkWxTreeModelClass *klass) { list_parent_class = (GObjectClass*) g_type_class_peek_parent (klass); GObjectClass *object_class = (GObjectClass*) klass; object_class->finalize = wxgtk_tree_model_finalize; } static void wxgtk_tree_model_tree_model_init (GtkTreeModelIface *iface) { iface->get_flags = wxgtk_tree_model_get_flags; iface->get_n_columns = wxgtk_tree_model_get_n_columns; iface->get_column_type = wxgtk_tree_model_get_column_type; iface->get_iter = wxgtk_tree_model_get_iter; iface->get_path = wxgtk_tree_model_get_path; iface->get_value = wxgtk_tree_model_get_value; iface->iter_next = wxgtk_tree_model_iter_next; iface->iter_children = wxgtk_tree_model_iter_children; iface->iter_has_child = wxgtk_tree_model_iter_has_child; iface->iter_n_children = wxgtk_tree_model_iter_n_children; iface->iter_nth_child = wxgtk_tree_model_iter_nth_child; iface->iter_parent = wxgtk_tree_model_iter_parent; } static void wxgtk_tree_model_sortable_init (GtkTreeSortableIface *iface) { iface->get_sort_column_id = wxgtk_tree_model_get_sort_column_id; iface->set_sort_column_id = wxgtk_tree_model_set_sort_column_id; iface->set_sort_func = wxgtk_tree_model_set_sort_func; iface->set_default_sort_func = wxgtk_tree_model_set_default_sort_func; iface->has_default_sort_func = wxgtk_tree_model_has_default_sort_func; } static void wxgtk_tree_model_drag_source_init(GtkTreeDragSourceIface *iface) { iface->row_draggable = wxgtk_tree_model_row_draggable; iface->drag_data_delete = wxgtk_tree_model_drag_data_delete; iface->drag_data_get = wxgtk_tree_model_drag_data_get; } static void wxgtk_tree_model_drag_dest_init (GtkTreeDragDestIface *iface) { iface->drag_data_received = wxgtk_tree_model_drag_data_received; iface->row_drop_possible = wxgtk_tree_model_row_drop_possible; } static void wxgtk_tree_model_init (GtkWxTreeModel *tree_model) { tree_model->internal = NULL; tree_model->stamp = g_random_int(); } static void wxgtk_tree_model_finalize (GObject *object) { /* must chain up */ (* list_parent_class->finalize) (object); } } // extern "C" //----------------------------------------------------------------------------- // implement callbacks from wxGtkTreeModel class by letting // them call the methods of wxWidgets' wxDataViewModel //----------------------------------------------------------------------------- static GtkTreeModelFlags wxgtk_tree_model_get_flags (GtkTreeModel *tree_model) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), (GtkTreeModelFlags)0 ); return wxtree_model->internal->get_flags(); } static gint wxgtk_tree_model_get_n_columns (GtkTreeModel *tree_model) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), 0); return wxtree_model->internal->GetDataViewModel()->GetColumnCount(); } static GType wxgtk_tree_model_get_column_type (GtkTreeModel *tree_model, gint index) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), G_TYPE_INVALID); GType gtype = G_TYPE_INVALID; wxString wxtype = wxtree_model->internal->GetDataViewModel()->GetColumnType( (unsigned int) index ); if (wxtype == wxT("string")) gtype = G_TYPE_STRING; else { gtype = G_TYPE_STRING; // wxFAIL_MSG( wxT("non-string columns not supported yet") ); } return gtype; } static gboolean wxgtk_tree_model_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE); return wxtree_model->internal->get_iter( iter, path ); } static GtkTreePath * wxgtk_tree_model_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), NULL); g_return_val_if_fail (iter->stamp == GTK_WX_TREE_MODEL (wxtree_model)->stamp, NULL); return wxtree_model->internal->get_path( iter ); } static void wxgtk_tree_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; g_return_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model) ); wxDataViewModel *model = wxtree_model->internal->GetDataViewModel(); wxString mtype = model->GetColumnType( (unsigned int) column ); if (mtype == wxT("string")) { wxVariant variant; g_value_init( value, G_TYPE_STRING ); wxDataViewItem item( (void*) iter->user_data ); model->GetValue( variant, item, (unsigned int) column ); g_value_set_string( value, variant.GetString().utf8_str() ); } else { wxFAIL_MSG( wxT("non-string columns not supported yet") ); } } static gboolean wxgtk_tree_model_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; // This happens when clearing the view by calling .._set_model( NULL ); if (iter->stamp == 0) return FALSE; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); g_return_val_if_fail (wxtree_model->stamp == iter->stamp, FALSE); return wxtree_model->internal->iter_next( iter ); } static gboolean wxgtk_tree_model_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); g_return_val_if_fail (wxtree_model->stamp == parent->stamp, FALSE); return wxtree_model->internal->iter_children( iter, parent ); } static gboolean wxgtk_tree_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); g_return_val_if_fail (wxtree_model->stamp == iter->stamp, FALSE); return wxtree_model->internal->iter_has_child( iter ); } static gint wxgtk_tree_model_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), 0); g_return_val_if_fail ( !iter || wxtree_model->stamp == iter->stamp, 0); return wxtree_model->internal->iter_n_children( iter ); } static gboolean wxgtk_tree_model_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); return wxtree_model->internal->iter_nth_child( iter, parent, n ); } static gboolean wxgtk_tree_model_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); g_return_val_if_fail (wxtree_model->stamp == child->stamp, FALSE); return wxtree_model->internal->iter_parent( iter, child ); } /* drag'n'drop iface */ static gboolean wxgtk_tree_model_row_draggable (GtkTreeDragSource *drag_source, GtkTreePath *path) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_source; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); return wxtree_model->internal->row_draggable( drag_source, path ); } static gboolean wxgtk_tree_model_drag_data_delete (GtkTreeDragSource *drag_source, GtkTreePath *path) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_source; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); return wxtree_model->internal->drag_data_delete( drag_source, path ); } static gboolean wxgtk_tree_model_drag_data_get (GtkTreeDragSource *drag_source, GtkTreePath *path, GtkSelectionData *selection_data) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_source; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); #if 0 wxPrintf( "drag_get_data\n"); wxGtkString atom_selection(gdk_atom_name(selection_data->selection)); wxPrintf( "selection %s\n", wxString::FromAscii(atom_selection) ); wxGtkString atom_target(gdk_atom_name(selection_data->target)); wxPrintf( "target %s\n", wxString::FromAscii(atom_target) ); wxGtkString atom_type(gdk_atom_name(selection_data->type)); wxPrintf( "type %s\n", wxString::FromAscii(atom_type) ); wxPrintf( "format %d\n", selection_data->format ); #endif return wxtree_model->internal->drag_data_get( drag_source, path, selection_data ); } static gboolean wxgtk_tree_model_drag_data_received (GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_dest; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); return wxtree_model->internal->drag_data_received( drag_dest, dest, selection_data ); } static gboolean wxgtk_tree_model_row_drop_possible (GtkTreeDragDest *drag_dest, GtkTreePath *dest_path, GtkSelectionData *selection_data) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_dest; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); return wxtree_model->internal->row_drop_possible( drag_dest, dest_path, selection_data ); } /* sortable iface */ static gboolean wxgtk_tree_model_get_sort_column_id (GtkTreeSortable *sortable, gint *sort_column_id, GtkSortType *order) { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) sortable; g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (sortable), FALSE); if (!wxtree_model->internal->IsSorted()) { if (sort_column_id) *sort_column_id = -1; return TRUE; } if (sort_column_id) *sort_column_id = wxtree_model->internal->GetSortColumn(); if (order) *order = wxtree_model->internal->GetSortOrder(); return TRUE; } wxDataViewColumn *gs_lastLeftClickHeader = NULL; static void wxgtk_tree_model_set_sort_column_id (GtkTreeSortable *sortable, gint sort_column_id, GtkSortType order) { GtkWxTreeModel *tree_model = (GtkWxTreeModel *) sortable; g_return_if_fail (GTK_IS_WX_TREE_MODEL (sortable) ); tree_model->internal->SetDataViewSortColumn( gs_lastLeftClickHeader ); if ((sort_column_id != (gint) tree_model->internal->GetSortColumn()) || (order != tree_model->internal->GetSortOrder())) { tree_model->internal->SetSortColumn( sort_column_id ); tree_model->internal->SetSortOrder( order ); gtk_tree_sortable_sort_column_changed (sortable); tree_model->internal->GetDataViewModel()->Resort(); } if (gs_lastLeftClickHeader) { wxDataViewCtrl *dv = tree_model->internal->GetOwner(); wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED, dv->GetId() ); event.SetDataViewColumn( gs_lastLeftClickHeader ); event.SetModel( dv->GetModel() ); dv->HandleWindowEvent( event ); } gs_lastLeftClickHeader = NULL; } static void wxgtk_tree_model_set_sort_func (GtkTreeSortable *sortable, gint WXUNUSED(sort_column_id), GtkTreeIterCompareFunc func, gpointer WXUNUSED(data), GtkDestroyNotify WXUNUSED(destroy)) { g_return_if_fail (GTK_IS_WX_TREE_MODEL (sortable) ); g_return_if_fail (func != NULL); } static void wxgtk_tree_model_set_default_sort_func (GtkTreeSortable *sortable, GtkTreeIterCompareFunc func, gpointer WXUNUSED(data), GtkDestroyNotify WXUNUSED(destroy)) { g_return_if_fail (GTK_IS_WX_TREE_MODEL (sortable) ); g_return_if_fail (func != NULL); //wxPrintf( "wxgtk_tree_model_set_default_sort_func\n" ); // TODO: remove this code } static gboolean wxgtk_tree_model_has_default_sort_func (GtkTreeSortable *sortable) { g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (sortable), FALSE ); return FALSE; } //----------------------------------------------------------------------------- // define new GTK+ class GtkWxRendererText //----------------------------------------------------------------------------- extern "C" { #define GTK_TYPE_WX_CELL_RENDERER_TEXT (gtk_wx_cell_renderer_text_get_type ()) #define GTK_WX_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WX_CELL_RENDERER_TEXT, GtkWxCellRendererText)) #define GTK_WX_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_WX_CELL_RENDERER_TEXT, GtkWxCellRendererTextClass)) #define GTK_IS_WX_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WX_CELL_RENDERER_TEXT)) #define GTK_IS_WX_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WX_CELL_RENDERER_TEXT)) #define GTK_WX_CELL_RENDERER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_WX_CELL_RENDERER_TEXT, GtkWxCellRendererTextClass)) GType gtk_wx_cell_renderer_text_get_type (void); typedef struct _GtkWxCellRendererText GtkWxCellRendererText; typedef struct _GtkWxCellRendererTextClass GtkWxCellRendererTextClass; struct _GtkWxCellRendererText { GtkCellRendererText parent; wxDataViewRenderer *wx_renderer; }; struct _GtkWxCellRendererTextClass { GtkCellRendererTextClass cell_parent_class; }; static GtkWxCellRendererText *gtk_wx_cell_renderer_text_new (void); static void gtk_wx_cell_renderer_text_init ( GtkWxCellRendererText *cell ); static void gtk_wx_cell_renderer_text_class_init( GtkWxCellRendererTextClass *klass ); static void gtk_wx_cell_renderer_text_finalize ( GObject *object ); static GtkCellEditable *gtk_wx_cell_renderer_text_start_editing( GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, GdkRectangle *background_area, GdkRectangle *cell_area, GtkCellRendererState flags ); static GObjectClass *text_cell_parent_class = NULL; } // extern "C" GType gtk_wx_cell_renderer_text_get_type (void) { static GType cell_wx_type = 0; if (!cell_wx_type) { const GTypeInfo cell_wx_info = { sizeof (GtkWxCellRendererTextClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) gtk_wx_cell_renderer_text_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GtkWxCellRendererText), 0, /* n_preallocs */ (GInstanceInitFunc) gtk_wx_cell_renderer_text_init, }; cell_wx_type = g_type_register_static( GTK_TYPE_CELL_RENDERER_TEXT, "GtkWxCellRendererText", &cell_wx_info, (GTypeFlags)0 ); } return cell_wx_type; } static void gtk_wx_cell_renderer_text_init (GtkWxCellRendererText *cell) { cell->wx_renderer = NULL; } static void gtk_wx_cell_renderer_text_class_init (GtkWxCellRendererTextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); text_cell_parent_class = (GObjectClass*) g_type_class_peek_parent (klass); object_class->finalize = gtk_wx_cell_renderer_text_finalize; cell_class->start_editing = gtk_wx_cell_renderer_text_start_editing; } static void gtk_wx_cell_renderer_text_finalize (GObject *object) { /* must chain up */ (* G_OBJECT_CLASS (text_cell_parent_class)->finalize) (object); } GtkWxCellRendererText* gtk_wx_cell_renderer_text_new (void) { return (GtkWxCellRendererText*) g_object_new (GTK_TYPE_WX_CELL_RENDERER_TEXT, NULL); } static GtkCellEditable *gtk_wx_cell_renderer_text_start_editing( GtkCellRenderer *gtk_renderer, GdkEvent *gdk_event, GtkWidget *widget, const gchar *path, GdkRectangle *background_area, GdkRectangle *cell_area, GtkCellRendererState flags ) { GtkWxCellRendererText *wxgtk_renderer = (GtkWxCellRendererText *) gtk_renderer; wxDataViewRenderer *wx_renderer = wxgtk_renderer->wx_renderer; 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 ); 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.SetItem( item ); dv->HandleWindowEvent( event ); if (event.IsAllowed()) return GTK_CELL_RENDERER_CLASS(text_cell_parent_class)-> start_editing( gtk_renderer, gdk_event, widget, path, background_area, cell_area, flags ); else return NULL; } //----------------------------------------------------------------------------- // define new GTK+ class GtkWxCellRenderer //----------------------------------------------------------------------------- extern "C" { #define GTK_TYPE_WX_CELL_RENDERER (gtk_wx_cell_renderer_get_type ()) #define GTK_WX_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WX_CELL_RENDERER, GtkWxCellRenderer)) #define GTK_WX_CELL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_WX_CELL_RENDERER, GtkWxCellRendererClass)) #define GTK_IS_WX_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WX_CELL_RENDERER)) #define GTK_IS_WX_CELL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WX_CELL_RENDERER)) #define GTK_WX_CELL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_WX_CELL_RENDERER, GtkWxCellRendererClass)) GType gtk_wx_cell_renderer_get_type (void); typedef struct _GtkWxCellRenderer GtkWxCellRenderer; typedef struct _GtkWxCellRendererClass GtkWxCellRendererClass; struct _GtkWxCellRenderer { GtkCellRenderer parent; /*< private >*/ wxDataViewCustomRenderer *cell; guint32 last_click; }; struct _GtkWxCellRendererClass { GtkCellRendererClass cell_parent_class; }; static GtkCellRenderer *gtk_wx_cell_renderer_new (void); static void gtk_wx_cell_renderer_init ( GtkWxCellRenderer *cell ); static void gtk_wx_cell_renderer_class_init( GtkWxCellRendererClass *klass ); static void gtk_wx_cell_renderer_finalize ( GObject *object ); static void gtk_wx_cell_renderer_get_size ( GtkCellRenderer *cell, GtkWidget *widget, GdkRectangle *rectangle, gint *x_offset, gint *y_offset, gint *width, gint *height ); static void gtk_wx_cell_renderer_render ( GtkCellRenderer *cell, GdkWindow *window, GtkWidget *widget, GdkRectangle *background_area, GdkRectangle *cell_area, GdkRectangle *expose_area, GtkCellRendererState flags ); static gboolean gtk_wx_cell_renderer_activate( GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, GdkRectangle *background_area, GdkRectangle *cell_area, GtkCellRendererState flags ); static GtkCellEditable *gtk_wx_cell_renderer_start_editing( GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, GdkRectangle *background_area, GdkRectangle *cell_area, GtkCellRendererState flags ); static GObjectClass *cell_parent_class = NULL; } // extern "C" GType gtk_wx_cell_renderer_get_type (void) { static GType cell_wx_type = 0; if (!cell_wx_type) { const GTypeInfo cell_wx_info = { sizeof (GtkWxCellRendererClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) gtk_wx_cell_renderer_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GtkWxCellRenderer), 0, /* n_preallocs */ (GInstanceInitFunc) gtk_wx_cell_renderer_init, }; cell_wx_type = g_type_register_static( GTK_TYPE_CELL_RENDERER, "GtkWxCellRenderer", &cell_wx_info, (GTypeFlags)0 ); } return cell_wx_type; } static void gtk_wx_cell_renderer_init (GtkWxCellRenderer *cell) { cell->cell = NULL; cell->last_click = 0; } static void gtk_wx_cell_renderer_class_init (GtkWxCellRendererClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); cell_parent_class = (GObjectClass*) g_type_class_peek_parent (klass); object_class->finalize = gtk_wx_cell_renderer_finalize; cell_class->get_size = gtk_wx_cell_renderer_get_size; cell_class->render = gtk_wx_cell_renderer_render; cell_class->activate = gtk_wx_cell_renderer_activate; cell_class->start_editing = gtk_wx_cell_renderer_start_editing; } static void gtk_wx_cell_renderer_finalize (GObject *object) { /* must chain up */ (* G_OBJECT_CLASS (cell_parent_class)->finalize) (object); } GtkCellRenderer* gtk_wx_cell_renderer_new (void) { return (GtkCellRenderer*) g_object_new (GTK_TYPE_WX_CELL_RENDERER, NULL); } static GtkCellEditable *gtk_wx_cell_renderer_start_editing( GtkCellRenderer *renderer, GdkEvent *WXUNUSED(event), GtkWidget *widget, const gchar *path, GdkRectangle *WXUNUSED(background_area), GdkRectangle *cell_area, GtkCellRendererState WXUNUSED(flags) ) { GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer; wxDataViewCustomRenderer *cell = wxrenderer->cell; // Renderer doesn't support in-place editing if (!cell->HasEditorCtrl()) return NULL; // An in-place editing control is still around if (cell->GetEditorCtrl()) return NULL; GdkRectangle rect; gtk_wx_cell_renderer_get_size (renderer, widget, cell_area, &rect.x, &rect.y, &rect.width, &rect.height); rect.x += cell_area->x; rect.y += cell_area->y; // rect.width -= renderer->xpad * 2; // rect.height -= renderer->ypad * 2; // wxRect renderrect( rect.x, rect.y, rect.width, rect.height ); wxRect renderrect( cell_area->x, cell_area->y, cell_area->width, cell_area->height ); 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 ); cell->StartEditing( item, renderrect ); return NULL; } static void gtk_wx_cell_renderer_get_size (GtkCellRenderer *renderer, GtkWidget *WXUNUSED(widget), GdkRectangle *cell_area, gint *x_offset, gint *y_offset, gint *width, gint *height) { GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer; wxDataViewCustomRenderer *cell = wxrenderer->cell; wxSize size = cell->GetSize(); gint calc_width = (gint) renderer->xpad * 2 + size.x; gint calc_height = (gint) renderer->ypad * 2 + size.y; if (x_offset) *x_offset = 0; if (y_offset) *y_offset = 0; if (cell_area && size.x > 0 && size.y > 0) { if (x_offset) { *x_offset = (gint)((renderer->xalign * (cell_area->width - calc_width - 2 * renderer->xpad))); *x_offset = MAX (*x_offset, 0) + renderer->xpad; } if (y_offset) { *y_offset = (gint)((renderer->yalign * (cell_area->height - calc_height - 2 * renderer->ypad))); *y_offset = MAX (*y_offset, 0) + renderer->ypad; } } if (width) *width = calc_width; if (height) *height = calc_height; } static void gtk_wx_cell_renderer_render (GtkCellRenderer *renderer, GdkWindow *window, GtkWidget *widget, GdkRectangle *background_area, GdkRectangle *cell_area, GdkRectangle *expose_area, GtkCellRendererState flags) { GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer; wxDataViewCustomRenderer *cell = wxrenderer->cell; cell->window = window; cell->widget = widget; cell->background_area = (void*) background_area; cell->cell_area = (void*) cell_area; cell->expose_area = (void*) expose_area; cell->flags = (int) flags; GdkRectangle rect; gtk_wx_cell_renderer_get_size (renderer, widget, cell_area, &rect.x, &rect.y, &rect.width, &rect.height); rect.x += cell_area->x; rect.y += cell_area->y; rect.width -= renderer->xpad * 2; rect.height -= renderer->ypad * 2; GdkRectangle dummy; if (gdk_rectangle_intersect (expose_area, &rect, &dummy)) { wxRect renderrect( rect.x, rect.y, rect.width, rect.height ); wxWindowDC* dc = (wxWindowDC*) cell->GetDC(); wxWindowDCImpl *impl = (wxWindowDCImpl *) dc->GetImpl(); // Reinitialize wxWindowDC's GDK window if drawing occurs into a different // window such as a DnD drop window. if (window != impl->m_gdkwindow) { impl->Destroy(); impl->m_gdkwindow = window; impl->SetUpDC(); } int state = 0; if (flags & GTK_CELL_RENDERER_SELECTED) state |= wxDATAVIEW_CELL_SELECTED; if (flags & GTK_CELL_RENDERER_PRELIT) state |= wxDATAVIEW_CELL_PRELIT; if (flags & GTK_CELL_RENDERER_INSENSITIVE) state |= wxDATAVIEW_CELL_INSENSITIVE; if (flags & GTK_CELL_RENDERER_INSENSITIVE) state |= wxDATAVIEW_CELL_INSENSITIVE; if (flags & GTK_CELL_RENDERER_FOCUSED) state |= wxDATAVIEW_CELL_FOCUSED; cell->WXCallRender( renderrect, dc, state ); } } static gboolean gtk_wx_cell_renderer_activate( GtkCellRenderer *renderer, GdkEvent *event, GtkWidget *widget, const gchar *path, GdkRectangle *WXUNUSED(background_area), GdkRectangle *cell_area, GtkCellRendererState WXUNUSED(flags) ) { GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer; wxDataViewCustomRenderer *cell = wxrenderer->cell; GdkRectangle rect; gtk_wx_cell_renderer_get_size (renderer, widget, cell_area, &rect.x, &rect.y, &rect.width, &rect.height); rect.x += cell_area->x; rect.y += cell_area->y; rect.width -= renderer->xpad * 2; rect.height -= renderer->ypad * 2; wxRect renderrect( rect.x, rect.y, rect.width, rect.height ); wxDataViewModel *model = cell->GetOwner()->GetOwner()->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 ); unsigned int model_col = cell->GetOwner()->GetModelColumn(); if (!event) { bool ret = false; // activated by if (cell->Activate( renderrect, model, item, model_col )) ret = true; return ret; } else if (event->type == GDK_BUTTON_PRESS) { GdkEventButton *button_event = (GdkEventButton*) event; wxPoint pt( ((int) button_event->x) - renderrect.x, ((int) button_event->y) - renderrect.y ); bool ret = false; if (button_event->button == 1) { if (cell->LeftClick( pt, renderrect, model, item, model_col )) ret = true; // TODO: query system double-click time if (button_event->time - wxrenderer->last_click < 400) if (cell->Activate( renderrect, model, item, model_col )) ret = true; } wxrenderer->last_click = button_event->time; return ret; } return false; } // --------------------------------------------------------- // wxGtkDataViewModelNotifier // --------------------------------------------------------- class wxGtkDataViewModelNotifier: public wxDataViewModelNotifier { public: wxGtkDataViewModelNotifier( GtkWxTreeModel *wxgtk_model, wxDataViewModel *wx_model, wxDataViewCtrl *ctrl ); ~wxGtkDataViewModelNotifier(); virtual bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item ); virtual bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item ); virtual bool ItemChanged( const wxDataViewItem &item ); virtual bool ValueChanged( const wxDataViewItem &item, unsigned int col ); virtual bool Cleared(); virtual void Resort(); void SetGtkModel( GtkWxTreeModel *model ) { m_wxgtk_model = model; } private: GtkWxTreeModel *m_wxgtk_model; wxDataViewModel *m_wx_model; wxDataViewCtrl *m_owner; }; // --------------------------------------------------------- // wxGtkDataViewListModelNotifier // --------------------------------------------------------- wxGtkDataViewModelNotifier::wxGtkDataViewModelNotifier( GtkWxTreeModel* wxgtk_model, wxDataViewModel *wx_model, wxDataViewCtrl *ctrl ) { m_wxgtk_model = wxgtk_model; m_wx_model = wx_model; m_owner = ctrl; } wxGtkDataViewModelNotifier::~wxGtkDataViewModelNotifier() { m_wx_model = NULL; m_wxgtk_model = NULL; } bool wxGtkDataViewModelNotifier::ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item ) { m_owner->GtkGetInternal()->ItemAdded( parent, item ); 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 ); gtk_tree_model_row_inserted( GTK_TREE_MODEL(m_wxgtk_model), path, &iter); gtk_tree_path_free (path); return true; } bool wxGtkDataViewModelNotifier::ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item ) { 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 ); gtk_tree_model_row_deleted( GTK_TREE_MODEL(m_wxgtk_model), path ); gtk_tree_path_free (path); m_owner->GtkGetInternal()->ItemDeleted( parent, item ); return true; } void wxGtkDataViewModelNotifier::Resort() { m_owner->GtkGetInternal()->Resort(); } bool wxGtkDataViewModelNotifier::ItemChanged( const wxDataViewItem &item ) { 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 ); gtk_tree_model_row_changed( GTK_TREE_MODEL(m_wxgtk_model), path, &iter ); gtk_tree_path_free (path); m_owner->GtkGetInternal()->ItemChanged( item ); return true; } bool wxGtkDataViewModelNotifier::ValueChanged( const wxDataViewItem &item, unsigned int model_col ) { // This adds GTK+'s missing MVC logic for ValueChanged unsigned int index; for (index = 0; index < m_owner->GetColumnCount(); index++) { wxDataViewColumn *column = m_owner->GetColumn( index ); if (column->GetModelColumn() == model_col) { GtkTreeView *widget = GTK_TREE_VIEW(m_owner->m_treeview); GtkTreeViewColumn *gcolumn = GTK_TREE_VIEW_COLUMN(column->GetGtkHandle()); // Get cell area 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 ); 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 ); int xdiff = (int) d; int ydiff = gcolumn->button->allocation.height; // Redraw gtk_widget_queue_draw_area( GTK_WIDGET(widget), cell_area.x - xdiff, ydiff + cell_area.y, cell_area.width, cell_area.height ); m_owner->GtkGetInternal()->ValueChanged( item, model_col ); return true; } } return false; } bool wxGtkDataViewModelNotifier::Cleared() { m_owner->GtkGetInternal()->Cleared(); return true; } // --------------------------------------------------------- // wxDataViewRenderer // --------------------------------------------------------- static gpointer s_user_data = NULL; static void wxgtk_cell_editable_editing_done( GtkCellEditable *WXUNUSED(editable), wxDataViewRenderer *wxrenderer ) { wxDataViewColumn *column = wxrenderer->GetOwner(); wxDataViewCtrl *dv = column->GetOwner(); wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE, dv->GetId() ); event.SetDataViewColumn( column ); event.SetModel( dv->GetModel() ); wxDataViewItem item( s_user_data ); event.SetItem( item ); dv->HandleWindowEvent( event ); } static void wxgtk_renderer_editing_started( GtkCellRenderer *WXUNUSED(cell), GtkCellEditable *editable, gchar *path, wxDataViewRenderer *wxrenderer ) { if (!editable) return; wxDataViewColumn *column = wxrenderer->GetOwner(); wxDataViewCtrl *dv = column->GetOwner(); 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 ); event.SetItem( item ); dv->HandleWindowEvent( event ); if (GTK_IS_CELL_EDITABLE(editable)) { s_user_data = iter.user_data; g_signal_connect (GTK_CELL_EDITABLE (editable), "editing_done", G_CALLBACK (wxgtk_cell_editable_editing_done), (gpointer) wxrenderer ); } } IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase) wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype, wxDataViewCellMode mode, int align ) : wxDataViewRendererBase( varianttype, mode, align ) { m_renderer = NULL; // we haven't changed them yet m_usingDefaultAttrs = true; // NOTE: SetMode() and SetAlignment() needs to be called in the renderer's ctor, // after the m_renderer pointer has been initialized } void wxDataViewRenderer::GtkPackIntoColumn(GtkTreeViewColumn *column) { gtk_tree_view_column_pack_end( column, m_renderer, TRUE /* expand */); } void wxDataViewRenderer::GtkInitHandlers() { if (!gtk_check_version(2,6,0)) { g_signal_connect (GTK_CELL_RENDERER(m_renderer), "editing_started", G_CALLBACK (wxgtk_renderer_editing_started), this); } } void wxDataViewRenderer::SetMode( wxDataViewCellMode mode ) { GtkCellRendererMode gtkMode; switch (mode) { case wxDATAVIEW_CELL_INERT: gtkMode = GTK_CELL_RENDERER_MODE_INERT; break; case wxDATAVIEW_CELL_ACTIVATABLE: gtkMode = GTK_CELL_RENDERER_MODE_ACTIVATABLE; break; case wxDATAVIEW_CELL_EDITABLE: gtkMode = GTK_CELL_RENDERER_MODE_EDITABLE; break; default: wxFAIL_MSG( "unknown wxDataViewCellMode value" ); return; } // This value is most often ignored in GtkTreeView GValue gvalue = { 0, }; g_value_init( &gvalue, gtk_cell_renderer_mode_get_type() ); g_value_set_enum( &gvalue, gtkMode ); g_object_set_property( G_OBJECT(m_renderer), "mode", &gvalue ); g_value_unset( &gvalue ); } wxDataViewCellMode wxDataViewRenderer::GetMode() const { wxDataViewCellMode ret; GValue gvalue; g_object_get( G_OBJECT(m_renderer), "mode", &gvalue, NULL); switch (g_value_get_enum(&gvalue)) { default: wxFAIL_MSG( "unknown GtkCellRendererMode value" ); // fall through (we have to return something) case GTK_CELL_RENDERER_MODE_INERT: ret = wxDATAVIEW_CELL_INERT; break; case GTK_CELL_RENDERER_MODE_ACTIVATABLE: ret = wxDATAVIEW_CELL_ACTIVATABLE; break; case GTK_CELL_RENDERER_MODE_EDITABLE: ret = wxDATAVIEW_CELL_EDITABLE; break; } g_value_unset( &gvalue ); return ret; } void wxDataViewRenderer::GtkUpdateAlignment() { int align = m_alignment; // query alignment from column ? if (align == -1) { // None there yet if (GetOwner() == NULL) return; align = GetOwner()->GetAlignment(); align |= wxALIGN_CENTRE_VERTICAL; } // horizontal alignment: gfloat xalign = 0.0; if (align & wxALIGN_RIGHT) xalign = 1.0; else if (align & wxALIGN_CENTER_HORIZONTAL) xalign = 0.5; GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_FLOAT ); g_value_set_float( &gvalue, xalign ); g_object_set_property( G_OBJECT(m_renderer), "xalign", &gvalue ); g_value_unset( &gvalue ); // vertical alignment: gfloat yalign = 0.0; if (align & wxALIGN_BOTTOM) yalign = 1.0; else if (align & wxALIGN_CENTER_VERTICAL) yalign = 0.5; GValue gvalue2 = { 0, }; g_value_init( &gvalue2, G_TYPE_FLOAT ); g_value_set_float( &gvalue2, yalign ); g_object_set_property( G_OBJECT(m_renderer), "yalign", &gvalue2 ); g_value_unset( &gvalue2 ); } void wxDataViewRenderer::SetAlignment( int align ) { m_alignment = align; GtkUpdateAlignment(); } int wxDataViewRenderer::GetAlignment() const { return m_alignment; } void wxDataViewRenderer::EnableEllipsize(wxEllipsizeMode mode) { if ( gtk_check_version(2, 6, 0) != NULL ) return; // we use the same values in wxEllipsizeMode as PangoEllipsizeMode so we // can just cast between them GValue gvalue = { 0, }; g_value_init( &gvalue, PANGO_TYPE_ELLIPSIZE_MODE ); g_value_set_enum( &gvalue, static_cast(mode) ); g_object_set_property( G_OBJECT(m_renderer), "ellipsize", &gvalue ); g_value_unset( &gvalue ); } wxEllipsizeMode wxDataViewRenderer::GetEllipsizeMode() const { if ( gtk_check_version(2, 6, 0) != NULL ) return wxELLIPSIZE_NONE; GValue gvalue = { 0, }; g_value_init( &gvalue, PANGO_TYPE_ELLIPSIZE_MODE ); g_object_get_property( G_OBJECT(m_renderer), "ellipsize", &gvalue ); wxEllipsizeMode mode = static_cast(g_value_get_enum( &gvalue )); g_value_unset( &gvalue ); return mode; } void wxDataViewRenderer::GtkOnTextEdited(const gchar *itempath, const wxString& str) { wxVariant value(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 ); GtkOnCellChanged(value, item, GetOwner()->GetModelColumn()); } void wxDataViewRenderer::GtkOnCellChanged(const wxVariant& value, const wxDataViewItem& item, unsigned col) { wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); model->ChangeValue( value, item, col ); } // --------------------------------------------------------- // wxDataViewTextRenderer // --------------------------------------------------------- extern "C" { static void wxGtkTextRendererEditedCallback( GtkCellRendererText *WXUNUSED(renderer), gchar *arg1, gchar *arg2, gpointer user_data ) { wxDataViewRenderer *cell = (wxDataViewRenderer*) user_data; cell->GtkOnTextEdited(arg1, wxGTK_CONV_BACK_FONT( arg2, cell->GetOwner()->GetOwner()->GetFont())); } } IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewRenderer) wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype, wxDataViewCellMode mode, int align ) : wxDataViewRenderer( varianttype, mode, align ) { GtkWxCellRendererText *text_renderer = gtk_wx_cell_renderer_text_new(); text_renderer->wx_renderer = this; m_renderer = (GtkCellRenderer*) text_renderer; if (mode & wxDATAVIEW_CELL_EDITABLE) { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_BOOLEAN ); g_value_set_boolean( &gvalue, true ); g_object_set_property( G_OBJECT(m_renderer), "editable", &gvalue ); g_value_unset( &gvalue ); g_signal_connect_after( m_renderer, "edited", G_CALLBACK(wxGtkTextRendererEditedCallback), this ); GtkInitHandlers(); } SetMode(mode); SetAlignment(align); } bool wxDataViewTextRenderer::SetTextValue(const wxString& str) { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_STRING ); g_value_set_string( &gvalue, wxGTK_CONV_FONT( str, GetOwner()->GetOwner()->GetFont() ) ); g_object_set_property( G_OBJECT(m_renderer), "text", &gvalue ); g_value_unset( &gvalue ); return true; } bool wxDataViewTextRenderer::GetTextValue(wxString& str) const { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_STRING ); g_object_get_property( G_OBJECT(m_renderer), "text", &gvalue ); str = wxGTK_CONV_BACK_FONT( g_value_get_string( &gvalue ), const_cast(this)->GetOwner()->GetOwner()->GetFont() ); g_value_unset( &gvalue ); return true; } void wxDataViewTextRenderer::SetAlignment( int align ) { wxDataViewRenderer::SetAlignment(align); if (gtk_check_version(2,10,0)) return; // horizontal alignment: PangoAlignment pangoAlign = PANGO_ALIGN_LEFT; if (align & wxALIGN_RIGHT) pangoAlign = PANGO_ALIGN_RIGHT; else if (align & wxALIGN_CENTER_HORIZONTAL) pangoAlign = PANGO_ALIGN_CENTER; GValue gvalue = { 0, }; g_value_init( &gvalue, gtk_cell_renderer_mode_get_type() ); g_value_set_enum( &gvalue, pangoAlign ); g_object_set_property( G_OBJECT(m_renderer), "alignment", &gvalue ); g_value_unset( &gvalue ); } // --------------------------------------------------------- // wxDataViewBitmapRenderer // --------------------------------------------------------- namespace { // set "pixbuf" property on the given renderer void SetPixbufProp(GtkCellRenderer *renderer, GdkPixbuf *pixbuf) { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_OBJECT ); g_value_set_object( &gvalue, pixbuf ); g_object_set_property( G_OBJECT(renderer), "pixbuf", &gvalue ); g_value_unset( &gvalue ); } } // anonymous namespace IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewRenderer) wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype, wxDataViewCellMode mode, int align ) : wxDataViewRenderer( varianttype, mode, align ) { m_renderer = gtk_cell_renderer_pixbuf_new(); SetMode(mode); SetAlignment(align); } bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value ) { if (value.GetType() == wxT("wxBitmap")) { wxBitmap bitmap; bitmap << value; // GetPixbuf() may create a Pixbuf representation in the wxBitmap // object (and it will stay there and remain owned by wxBitmap) SetPixbufProp(m_renderer, bitmap.GetPixbuf()); } else if (value.GetType() == wxT("wxIcon")) { wxIcon icon; icon << value; SetPixbufProp(m_renderer, icon.GetPixbuf()); } else { return false; } return true; } bool wxDataViewBitmapRenderer::GetValue( wxVariant &WXUNUSED(value) ) const { return false; } // --------------------------------------------------------- // wxDataViewToggleRenderer // --------------------------------------------------------- extern "C" { static void wxGtkToggleRendererToggledCallback( GtkCellRendererToggle *renderer, gchar *path, gpointer user_data ); } static void wxGtkToggleRendererToggledCallback( GtkCellRendererToggle *renderer, gchar *path, gpointer user_data ) { wxDataViewToggleRenderer *cell = (wxDataViewToggleRenderer*) user_data; // get old value GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_BOOLEAN ); g_object_get_property( G_OBJECT(renderer), "active", &gvalue ); bool tmp = g_value_get_boolean( &gvalue ); g_value_unset( &gvalue ); // invert it tmp = !tmp; wxVariant value = tmp; if (!cell->Validate( value )) return; wxDataViewModel *model = cell->GetOwner()->GetOwner()->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 ); unsigned int model_col = cell->GetOwner()->GetModelColumn(); model->ChangeValue( value, item, model_col ); } IMPLEMENT_CLASS(wxDataViewToggleRenderer, wxDataViewRenderer) wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype, wxDataViewCellMode mode, int align ) : wxDataViewRenderer( varianttype, mode, align ) { m_renderer = (GtkCellRenderer*) gtk_cell_renderer_toggle_new(); if (mode & wxDATAVIEW_CELL_ACTIVATABLE) { g_signal_connect_after( m_renderer, "toggled", G_CALLBACK(wxGtkToggleRendererToggledCallback), this ); } else { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_BOOLEAN ); g_value_set_boolean( &gvalue, false ); g_object_set_property( G_OBJECT(m_renderer), "activatable", &gvalue ); g_value_unset( &gvalue ); } SetMode(mode); SetAlignment(align); } bool wxDataViewToggleRenderer::SetValue( const wxVariant &value ) { bool tmp = value; GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_BOOLEAN ); g_value_set_boolean( &gvalue, tmp ); g_object_set_property( G_OBJECT(m_renderer), "active", &gvalue ); g_value_unset( &gvalue ); return true; } bool wxDataViewToggleRenderer::GetValue( wxVariant &value ) const { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_BOOLEAN ); g_object_get_property( G_OBJECT(m_renderer), "active", &gvalue ); bool tmp = g_value_get_boolean( &gvalue ); g_value_unset( &gvalue ); value = tmp; return true; } // --------------------------------------------------------- // wxDataViewCustomRenderer // --------------------------------------------------------- class wxDataViewCtrlDCImpl: public wxWindowDCImpl { public: wxDataViewCtrlDCImpl( wxDC *owner, wxDataViewCtrl *window ) : wxWindowDCImpl( owner ) { GtkWidget *widget = window->m_treeview; // Set later m_gdkwindow = NULL; m_window = window; m_context = window->GTKGetPangoDefaultContext(); m_layout = pango_layout_new( m_context ); m_fontdesc = pango_font_description_copy( widget->style->font_desc ); m_cmap = gtk_widget_get_colormap( widget ? widget : window->m_widget ); // Set m_gdkwindow later // SetUpDC(); } }; class wxDataViewCtrlDC: public wxWindowDC { public: wxDataViewCtrlDC( wxDataViewCtrl *window ) : wxWindowDC( new wxDataViewCtrlDCImpl( this, window ) ) { } }; // --------------------------------------------------------- // wxDataViewCustomRenderer // --------------------------------------------------------- IMPLEMENT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer) wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype, wxDataViewCellMode mode, int align, bool no_init ) : wxDataViewCustomRendererBase( varianttype, mode, align ) { m_dc = NULL; m_text_renderer = NULL; if (no_init) m_renderer = NULL; else Init(mode, align); } void wxDataViewCustomRenderer::RenderText( const wxString &text, int xoffset, wxRect WXUNUSED(cell), wxDC *WXUNUSED(dc), int WXUNUSED(state) ) { #if 0 wxDataViewCtrl *view = GetOwner()->GetOwner(); wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ? wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) : view->GetForegroundColour(); dc->SetTextForeground(col); dc->DrawText( text, cell.x + xoffset, cell.y + ((cell.height - dc->GetCharHeight()) / 2)); #else if (!m_text_renderer) m_text_renderer = gtk_cell_renderer_text_new(); GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_STRING ); g_value_set_string( &gvalue, wxGTK_CONV_FONT( text, GetOwner()->GetOwner()->GetFont() ) ); g_object_set_property( G_OBJECT(m_text_renderer), "text", &gvalue ); g_value_unset( &gvalue ); ((GdkRectangle*) cell_area)->x += xoffset; ((GdkRectangle*) cell_area)->width -= xoffset; gtk_cell_renderer_render( m_text_renderer, window, widget, (GdkRectangle*) background_area, (GdkRectangle*) cell_area, (GdkRectangle*) expose_area, (GtkCellRendererState) flags ); ((GdkRectangle*) cell_area)->x -= xoffset; ((GdkRectangle*) cell_area)->width += xoffset; #endif } bool wxDataViewCustomRenderer::Init(wxDataViewCellMode mode, int align) { GtkWxCellRenderer *renderer = (GtkWxCellRenderer *) gtk_wx_cell_renderer_new(); renderer->cell = this; m_renderer = (GtkCellRenderer*) renderer; SetMode(mode); SetAlignment(align); GtkInitHandlers(); return true; } wxDataViewCustomRenderer::~wxDataViewCustomRenderer() { if (m_dc) delete m_dc; if (m_text_renderer) gtk_object_sink( GTK_OBJECT(m_text_renderer) ); } wxDC *wxDataViewCustomRenderer::GetDC() { if (m_dc == NULL) { if (GetOwner() == NULL) return NULL; if (GetOwner()->GetOwner() == NULL) return NULL; m_dc = new wxDataViewCtrlDC( GetOwner()->GetOwner() ); } return m_dc; } // --------------------------------------------------------- // wxDataViewProgressRenderer // --------------------------------------------------------- IMPLEMENT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer) wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label, const wxString &varianttype, wxDataViewCellMode mode, int align ) : wxDataViewCustomRenderer( varianttype, mode, align, true ) { m_label = label; m_value = 0; #ifdef __WXGTK26__ if (!gtk_check_version(2,6,0)) { m_renderer = (GtkCellRenderer*) gtk_cell_renderer_progress_new(); GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_STRING ); g_value_set_string( &gvalue, wxGTK_CONV_FONT( m_label, GetOwner()->GetOwner()->GetFont() ) ); g_object_set_property( G_OBJECT(m_renderer), "text", &gvalue ); g_value_unset( &gvalue ); SetMode(mode); SetAlignment(align); } else #endif { // Use custom cell code wxDataViewCustomRenderer::Init(mode, align); } } wxDataViewProgressRenderer::~wxDataViewProgressRenderer() { } bool wxDataViewProgressRenderer::SetValue( const wxVariant &value ) { #ifdef __WXGTK26__ if (!gtk_check_version(2,6,0)) { gint tmp = (long) value; GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_INT ); g_value_set_int( &gvalue, tmp ); g_object_set_property( G_OBJECT(m_renderer), "value", &gvalue ); g_value_unset( &gvalue ); } else #endif { m_value = (long) value; if (m_value < 0) m_value = 0; if (m_value > 100) m_value = 100; } return true; } bool wxDataViewProgressRenderer::GetValue( wxVariant &WXUNUSED(value) ) const { return false; } bool wxDataViewProgressRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) ) { double pct = (double)m_value / 100.0; wxRect bar = cell; bar.width = (int)(cell.width * pct); dc->SetPen( *wxTRANSPARENT_PEN ); dc->SetBrush( *wxBLUE_BRUSH ); dc->DrawRectangle( bar ); dc->SetBrush( *wxTRANSPARENT_BRUSH ); dc->SetPen( *wxBLACK_PEN ); dc->DrawRectangle( cell ); return true; } wxSize wxDataViewProgressRenderer::GetSize() const { return wxSize(40,12); } // ------------------------------------- // wxDataViewChoiceRenderer // ------------------------------------- wxDataViewChoiceRenderer::wxDataViewChoiceRenderer( const wxArrayString &choices, wxDataViewCellMode mode, int alignment ) : wxDataViewCustomRenderer( "string", mode, alignment, true ) { m_choices = choices; #ifdef __WXGTK26__ if (!gtk_check_version(2,6,0)) { m_renderer = (GtkCellRenderer*) gtk_cell_renderer_combo_new(); GtkListStore *store = gtk_list_store_new( 1, G_TYPE_STRING ); for (size_t n = 0; n < m_choices.GetCount(); n++) { gtk_list_store_insert_with_values( store, NULL, n, 0, static_cast(m_choices[n].utf8_str()), -1 ); } g_object_set (m_renderer, "model", store, "text-column", 0, "has-entry", FALSE, NULL); bool editable = (mode & wxDATAVIEW_CELL_EDITABLE); g_object_set (m_renderer, "editable", editable, NULL); SetAlignment(alignment); g_signal_connect_after( m_renderer, "edited", G_CALLBACK(wxGtkTextRendererEditedCallback), this ); GtkInitHandlers(); } else #endif { // Use custom cell code wxDataViewCustomRenderer::Init(mode, alignment); } } bool wxDataViewChoiceRenderer::Render( wxRect rect, wxDC *dc, int state ) { RenderText( m_data, 0, rect, dc, state ); return true; } wxSize wxDataViewChoiceRenderer::GetSize() const { return wxSize(70,20); } bool wxDataViewChoiceRenderer::SetValue( const wxVariant &value ) { #ifdef __WXGTK26__ if (!gtk_check_version(2,6,0)) { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_STRING ); g_value_set_string( &gvalue, wxGTK_CONV_FONT( value.GetString(), GetOwner()->GetOwner()->GetFont() ) ); g_object_set_property( G_OBJECT(m_renderer), "text", &gvalue ); g_value_unset( &gvalue ); } else #endif m_data = value.GetString(); return true; } bool wxDataViewChoiceRenderer::GetValue( wxVariant &value ) const { #ifdef __WXGTK26__ if (!gtk_check_version(2,6,0)) { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_STRING ); g_object_get_property( G_OBJECT(m_renderer), "text", &gvalue ); wxString temp = wxGTK_CONV_BACK_FONT( g_value_get_string( &gvalue ), const_cast(this)->GetOwner()->GetOwner()->GetFont() ); g_value_unset( &gvalue ); value = temp; //wxPrintf( "temp %s\n", temp ); // TODO: remove this code } else #endif value = m_data; return true; } void wxDataViewChoiceRenderer::SetAlignment( int align ) { wxDataViewCustomRenderer::SetAlignment(align); if (gtk_check_version(2,10,0)) return; // horizontal alignment: PangoAlignment pangoAlign = PANGO_ALIGN_LEFT; if (align & wxALIGN_RIGHT) pangoAlign = PANGO_ALIGN_RIGHT; else if (align & wxALIGN_CENTER_HORIZONTAL) pangoAlign = PANGO_ALIGN_CENTER; GValue gvalue = { 0, }; g_value_init( &gvalue, gtk_cell_renderer_mode_get_type() ); g_value_set_enum( &gvalue, pangoAlign ); g_object_set_property( G_OBJECT(m_renderer), "alignment", &gvalue ); g_value_unset( &gvalue ); } // --------------------------------------------------------- // wxDataViewDateRenderer // --------------------------------------------------------- class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow { public: wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value, wxDataViewModel *model, const wxDataViewItem &item, unsigned int col ) : wxPopupTransientWindow( parent, wxBORDER_SIMPLE ) { m_model = model; m_item = item; m_col = col; m_cal = new wxCalendarCtrl( this, -1, *value ); wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL ); sizer->Add( m_cal, 1, wxGROW ); SetSizer( sizer ); sizer->Fit( this ); } virtual void OnDismiss() { } void OnCalendar( wxCalendarEvent &event ); wxCalendarCtrl *m_cal; wxDataViewModel *m_model; wxDataViewItem m_item; unsigned int m_col; private: DECLARE_EVENT_TABLE() }; BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow) EVT_CALENDAR( -1, wxDataViewDateRendererPopupTransient::OnCalendar ) END_EVENT_TABLE() void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event ) { m_model->ChangeValue( event.GetDate(), m_item, m_col ); DismissAndNotify(); } IMPLEMENT_CLASS(wxDataViewDateRenderer, wxDataViewCustomRenderer) wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype, wxDataViewCellMode mode, int align ) : wxDataViewCustomRenderer( varianttype, mode, align ) { SetMode(mode); SetAlignment(align); } bool wxDataViewDateRenderer::SetValue( const wxVariant &value ) { m_date = value.GetDateTime(); return true; } bool wxDataViewDateRenderer::GetValue( wxVariant &WXUNUSED(value) ) const { return false; } bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int state ) { dc->SetFont( GetOwner()->GetOwner()->GetFont() ); wxString tmp = m_date.FormatDate(); RenderText( tmp, 0, cell, dc, state ); return true; } wxSize wxDataViewDateRenderer::GetSize() const { wxString tmp = m_date.FormatDate(); wxCoord x,y,d; GetView()->GetTextExtent( tmp, &x, &y, &d ); return wxSize(x,y+d); } bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewModel *model, const wxDataViewItem &item, unsigned int col ) { wxVariant variant; model->GetValue( variant, item, col ); wxDateTime value = variant.GetDateTime(); wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient( GetOwner()->GetOwner()->GetParent(), &value, model, item, col ); wxPoint pos = wxGetMousePosition(); popup->Move( pos ); popup->Layout(); popup->Popup( popup->m_cal ); return true; } // --------------------------------------------------------- // wxDataViewIconTextRenderer // --------------------------------------------------------- IMPLEMENT_CLASS(wxDataViewIconTextRenderer, wxDataViewCustomRenderer) wxDataViewIconTextRenderer::wxDataViewIconTextRenderer ( const wxString &varianttype, wxDataViewCellMode mode, int align ) : wxDataViewTextRenderer(varianttype, mode, align) { m_rendererIcon = gtk_cell_renderer_pixbuf_new(); } wxDataViewIconTextRenderer::~wxDataViewIconTextRenderer() { } void wxDataViewIconTextRenderer::GtkPackIntoColumn(GtkTreeViewColumn *column) { // add the icon renderer first gtk_tree_view_column_pack_start(column, m_rendererIcon, FALSE /* !expand */); // add the text renderer too wxDataViewRenderer::GtkPackIntoColumn(column); } bool wxDataViewIconTextRenderer::SetValue( const wxVariant &value ) { m_value << value; SetTextValue(m_value.GetText()); SetPixbufProp(m_rendererIcon, m_value.GetIcon().GetPixbuf()); return true; } bool wxDataViewIconTextRenderer::GetValue(wxVariant& value) const { wxString str; if ( !GetTextValue(str) ) return false; // user doesn't have any way to edit the icon so leave it unchanged value << wxDataViewIconText(str, m_value.GetIcon()); return true; } void wxDataViewIconTextRenderer::GtkOnCellChanged(const wxVariant& value, const wxDataViewItem& item, unsigned col) { // we receive just the text part of our value as it's the only one which // can be edited, but we need the full wxDataViewIconText value for the // model wxVariant valueIconText; valueIconText << wxDataViewIconText(value.GetString(), m_value.GetIcon()); wxDataViewTextRenderer::GtkOnCellChanged(valueIconText, item, col); } // --------------------------------------------------------- // wxDataViewColumn // --------------------------------------------------------- static gboolean gtk_dataview_header_button_press_callback( GtkWidget *WXUNUSED(widget), GdkEventButton *gdk_event, wxDataViewColumn *column ) { if (gdk_event->type != GDK_BUTTON_PRESS) return FALSE; if (gdk_event->button == 1) { gs_lastLeftClickHeader = column; wxDataViewCtrl *dv = column->GetOwner(); wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK, dv->GetId() ); event.SetDataViewColumn( column ); event.SetModel( dv->GetModel() ); if (dv->HandleWindowEvent( event )) return FALSE; } if (gdk_event->button == 3) { wxDataViewCtrl *dv = column->GetOwner(); wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK, dv->GetId() ); event.SetDataViewColumn( column ); event.SetModel( dv->GetModel() ); if (dv->HandleWindowEvent( event )) return FALSE; } return FALSE; } extern "C" { static void wxGtkTreeCellDataFunc( GtkTreeViewColumn *WXUNUSED(column), GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer data ) { g_return_if_fail (GTK_IS_WX_TREE_MODEL (model)); GtkWxTreeModel *tree_model = (GtkWxTreeModel *) model; wxDataViewRenderer *cell = (wxDataViewRenderer*) data; wxDataViewItem item( (void*) iter->user_data ); wxDataViewModel *wx_model = tree_model->internal->GetDataViewModel(); if (!wx_model->IsVirtualListModel()) { gboolean visible; if (wx_model->IsContainer( item )) { visible = wx_model->HasContainerColumns( item ) || (cell->GetOwner()->GetModelColumn() == 0); } else { visible = true; } GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_BOOLEAN ); g_value_set_boolean( &gvalue, visible ); g_object_set_property( G_OBJECT(renderer), "visible", &gvalue ); g_value_unset( &gvalue ); if ( !visible ) return; } wxVariant value; wx_model->GetValue( value, item, cell->GetOwner()->GetModelColumn() ); if (value.GetType() != cell->GetVariantType()) { wxLogError( wxT("Wrong type, required: %s but: %s"), value.GetType().c_str(), cell->GetVariantType().c_str() ); } cell->SetValue( value ); // deal with attributes wxDataViewItemAttr attr; if ( !wx_model->GetAttr( item, cell->GetOwner()->GetModelColumn(), attr ) && cell->GtkIsUsingDefaultAttrs() ) { // no custom attributes specified and we're already using the default // ones -- nothing to do return; } bool usingDefaultAttrs = true; if (attr.HasColour()) { const GdkColor * const gcol = attr.GetColour().GetColor(); GValue gvalue = { 0, }; g_value_init( &gvalue, GDK_TYPE_COLOR ); g_value_set_boxed( &gvalue, gcol ); g_object_set_property( G_OBJECT(renderer), "foreground_gdk", &gvalue ); g_value_unset( &gvalue ); usingDefaultAttrs = false; } else { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_BOOLEAN ); g_value_set_boolean( &gvalue, FALSE ); g_object_set_property( G_OBJECT(renderer), "foreground-set", &gvalue ); g_value_unset( &gvalue ); } if (attr.GetItalic()) { GValue gvalue = { 0, }; g_value_init( &gvalue, PANGO_TYPE_STYLE ); g_value_set_enum( &gvalue, PANGO_STYLE_ITALIC ); g_object_set_property( G_OBJECT(renderer), "style", &gvalue ); g_value_unset( &gvalue ); usingDefaultAttrs = false; } else { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_BOOLEAN ); g_value_set_boolean( &gvalue, FALSE ); g_object_set_property( G_OBJECT(renderer), "style-set", &gvalue ); g_value_unset( &gvalue ); } if (attr.GetBold()) { GValue gvalue = { 0, }; g_value_init( &gvalue, PANGO_TYPE_WEIGHT ); g_value_set_enum( &gvalue, PANGO_WEIGHT_BOLD ); g_object_set_property( G_OBJECT(renderer), "weight", &gvalue ); g_value_unset( &gvalue ); usingDefaultAttrs = false; } else { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_BOOLEAN ); g_value_set_boolean( &gvalue, FALSE ); g_object_set_property( G_OBJECT(renderer), "weight-set", &gvalue ); g_value_unset( &gvalue ); } #if 0 if (attr.HasBackgroundColour()) { wxColour colour = attr.GetBackgroundColour(); const GdkColor * const gcol = colour.GetColor(); GValue gvalue = { 0, }; g_value_init( &gvalue, GDK_TYPE_COLOR ); g_value_set_boxed( &gvalue, gcol ); g_object_set_property( G_OBJECT(renderer), "cell-background_gdk", &gvalue ); g_value_unset( &gvalue ); } else { GValue gvalue = { 0, }; g_value_init( &gvalue, G_TYPE_BOOLEAN ); g_value_set_boolean( &gvalue, FALSE ); g_object_set_property( G_OBJECT(renderer), "cell-background-set", &gvalue ); g_value_unset( &gvalue ); } #endif cell->GtkSetUsingDefaultAttrs(usingDefaultAttrs); } } // extern "C" #include WX_DEFINE_LIST(wxDataViewColumnList) wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell, unsigned int model_column, int width, wxAlignment align, int flags ) : wxDataViewColumnBase( cell, model_column ) { Init( align, flags, width ); SetTitle( title ); } wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell, unsigned int model_column, int width, wxAlignment align, int flags ) : wxDataViewColumnBase( bitmap, cell, model_column ) { Init( align, flags, width ); SetBitmap( bitmap ); } void wxDataViewColumn::Init(wxAlignment align, int flags, int width) { m_isConnected = false; GtkTreeViewColumn *column = gtk_tree_view_column_new(); m_column = (GtkWidget*) column; SetFlags( flags ); SetAlignment( align ); SetWidth( width ); // Create container for icon and label GtkWidget *box = gtk_hbox_new( FALSE, 1 ); gtk_widget_show( box ); // gtk_container_set_border_width((GtkContainer*)box, 2); m_image = gtk_image_new(); gtk_box_pack_start(GTK_BOX(box), m_image, FALSE, FALSE, 1); m_label = gtk_label_new(""); gtk_box_pack_end( GTK_BOX(box), GTK_WIDGET(m_label), FALSE, FALSE, 1 ); gtk_tree_view_column_set_widget( column, box ); wxDataViewRenderer * const colRenderer = GetRenderer(); GtkCellRenderer * const cellRenderer = colRenderer->GetGtkHandle(); colRenderer->GtkPackIntoColumn(column); gtk_tree_view_column_set_cell_data_func( column, cellRenderer, wxGtkTreeCellDataFunc, (gpointer) colRenderer, NULL ); } void wxDataViewColumn::OnInternalIdle() { if (m_isConnected) return; if (GTK_WIDGET_REALIZED(GetOwner()->m_treeview)) { GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column); if (column->button) { g_signal_connect(column->button, "button_press_event", G_CALLBACK (gtk_dataview_header_button_press_callback), this); m_isConnected = true; } } } void wxDataViewColumn::SetOwner( wxDataViewCtrl *owner ) { wxDataViewColumnBase::SetOwner( owner ); GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column); gtk_tree_view_column_set_title( column, wxGTK_CONV_FONT(GetTitle(), GetOwner()->GetFont() ) ); } void wxDataViewColumn::SetTitle( const wxString &title ) { wxDataViewCtrl *ctrl = GetOwner(); gtk_label_set_text( GTK_LABEL(m_label), ctrl ? wxGTK_CONV_FONT(title, ctrl->GetFont()) : wxGTK_CONV_SYS(title) ); if (title.empty()) gtk_widget_hide( m_label ); else gtk_widget_show( m_label ); } wxString wxDataViewColumn::GetTitle() const { return wxGTK_CONV_BACK_FONT( gtk_label_get_text( GTK_LABEL(m_label) ), GetOwner()->GetFont() ); } void wxDataViewColumn::SetBitmap( const wxBitmap &bitmap ) { wxDataViewColumnBase::SetBitmap( bitmap ); if (bitmap.Ok()) { GtkImage *gtk_image = GTK_IMAGE(m_image); GdkBitmap *mask = NULL; if (bitmap.GetMask()) mask = bitmap.GetMask()->GetBitmap(); if (bitmap.HasPixbuf()) { gtk_image_set_from_pixbuf(GTK_IMAGE(gtk_image), bitmap.GetPixbuf()); } else { gtk_image_set_from_pixmap(GTK_IMAGE(gtk_image), bitmap.GetPixmap(), mask); } gtk_widget_show( m_image ); } else { gtk_widget_hide( m_image ); } } void wxDataViewColumn::SetHidden( bool hidden ) { gtk_tree_view_column_set_visible( GTK_TREE_VIEW_COLUMN(m_column), !hidden ); } void wxDataViewColumn::SetResizeable( bool resizeable ) { gtk_tree_view_column_set_resizable( GTK_TREE_VIEW_COLUMN(m_column), resizeable ); } void wxDataViewColumn::SetAlignment( wxAlignment align ) { GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column); gfloat xalign = 0.0; if (align == wxALIGN_RIGHT) xalign = 1.0; if (align == wxALIGN_CENTER_HORIZONTAL || align == wxALIGN_CENTER) xalign = 0.5; gtk_tree_view_column_set_alignment( column, xalign ); if (m_renderer && m_renderer->GetAlignment() == -1) m_renderer->GtkUpdateAlignment(); } wxAlignment wxDataViewColumn::GetAlignment() const { gfloat xalign = gtk_tree_view_column_get_alignment( GTK_TREE_VIEW_COLUMN(m_column) ); if (xalign == 1.0) return wxALIGN_RIGHT; if (xalign == 0.5) return wxALIGN_CENTER_HORIZONTAL; return wxALIGN_LEFT; } void wxDataViewColumn::SetSortable( bool sortable ) { GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column); if ( sortable ) { gtk_tree_view_column_set_sort_column_id( column, GetModelColumn() ); } else { gtk_tree_view_column_set_sort_column_id( column, -1 ); gtk_tree_view_column_set_sort_indicator( column, FALSE ); gtk_tree_view_column_set_clickable( column, FALSE ); } } bool wxDataViewColumn::IsSortable() const { GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column); return gtk_tree_view_column_get_clickable( column ); } void wxDataViewColumn::SetAsSortKey( bool WXUNUSED(sort) ) { // it might not make sense to have this function in wxHeaderColumn at // all in fact, changing of the sort order should only be done using the // associated control API wxFAIL_MSG( "not implemented" ); } bool wxDataViewColumn::IsSortKey() const { GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column); return gtk_tree_view_column_get_sort_indicator( column ); } bool wxDataViewColumn::IsResizeable() const { GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column); return gtk_tree_view_column_get_resizable( column ); } bool wxDataViewColumn::IsHidden() const { GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column); return !gtk_tree_view_column_get_visible( column ); } void wxDataViewColumn::SetSortOrder( bool ascending ) { GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column); if (ascending) gtk_tree_view_column_set_sort_order( column, GTK_SORT_ASCENDING ); else gtk_tree_view_column_set_sort_order( column, GTK_SORT_DESCENDING ); gtk_tree_view_column_set_sort_indicator( column, TRUE ); } bool wxDataViewColumn::IsSortOrderAscending() const { GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column); return (gtk_tree_view_column_get_sort_order( column ) != GTK_SORT_DESCENDING); } void wxDataViewColumn::SetMinWidth( int width ) { gtk_tree_view_column_set_min_width( GTK_TREE_VIEW_COLUMN(m_column), width ); } int wxDataViewColumn::GetMinWidth() const { return gtk_tree_view_column_get_min_width( GTK_TREE_VIEW_COLUMN(m_column) ); } int wxDataViewColumn::GetWidth() const { return gtk_tree_view_column_get_width( GTK_TREE_VIEW_COLUMN(m_column) ); } void wxDataViewColumn::SetWidth( int width ) { if (width < 0) { #if 1 gtk_tree_view_column_set_sizing( GTK_TREE_VIEW_COLUMN(m_column), GTK_TREE_VIEW_COLUMN_FIXED ); // TODO find a better calculation gtk_tree_view_column_set_fixed_width( GTK_TREE_VIEW_COLUMN(m_column), wxDVC_DEFAULT_WIDTH ); #else // this is unpractical for large numbers of items and disables // user resizing, which is totally unexpected gtk_tree_view_column_set_sizing( GTK_TREE_VIEW_COLUMN(m_column), GTK_TREE_VIEW_COLUMN_AUTOSIZE ); #endif } else { gtk_tree_view_column_set_sizing( GTK_TREE_VIEW_COLUMN(m_column), GTK_TREE_VIEW_COLUMN_FIXED ); gtk_tree_view_column_set_fixed_width( GTK_TREE_VIEW_COLUMN(m_column), width ); } } void wxDataViewColumn::SetReorderable( bool reorderable ) { gtk_tree_view_column_set_reorderable( GTK_TREE_VIEW_COLUMN(m_column), reorderable ); } bool wxDataViewColumn::IsReorderable() const { return gtk_tree_view_column_get_reorderable( GTK_TREE_VIEW_COLUMN(m_column) ); } //----------------------------------------------------------------------------- // wxGtkTreeModelNode //----------------------------------------------------------------------------- #if 0 class wxGtkTreeModelChildWithPos { public: unsigned int pos; void *id; }; static int wxGtkTreeModelChildWithPosCmp( const void* data1, const void* data2, const void* user_data ) { const wxGtkTreeModelChildWithPos* child1 = (const wxGtkTreeModelChildWithPos*) data1; const wxGtkTreeModelChildWithPos* child2 = (const wxGtkTreeModelChildWithPos*) data2; const wxDataViewCtrlInternal *internal = (const wxDataViewCtrlInternal *) user_data; int ret = internal->GetDataViewModel()->Compare( child1->id, child2->id, internal->GetSortColumn(), (internal->GetSortOrder() == GTK_SORT_DESCENDING) ); return ret; } #else static int LINKAGEMODE wxGtkTreeModelChildPtrCmp( void*** data1, void*** data2 ) { return gs_internal->GetDataViewModel()->Compare( **data1, **data2, gs_internal->GetSortColumn(), (gs_internal->GetSortOrder() == GTK_SORT_ASCENDING) ); } WX_DEFINE_ARRAY_PTR( void**, wxGtkTreeModelChildrenPtr ); #endif void wxGtkTreeModelNode::Resort() { size_t child_count = GetChildCount(); if (child_count == 0) return; size_t node_count = GetNodesCount(); if (child_count == 1) { if (node_count == 1) { wxGtkTreeModelNode *node = m_nodes.Item( 0 ); node->Resort(); } return; } gint *new_order = new gint[child_count]; #if 1 // m_children has the original *void // ptrs points to these wxGtkTreeModelChildrenPtr ptrs; size_t i; for (i = 0; i < child_count; i++) ptrs.Add( &(m_children[i]) ); // Sort the ptrs gs_internal = m_internal; ptrs.Sort( &wxGtkTreeModelChildPtrCmp ); wxGtkTreeModelChildren temp; void** base_ptr = &(m_children[0]); // Transfer positions to new_order array and // IDs to temp for (i = 0; i < child_count; i++) { new_order[i] = ptrs[i] - base_ptr; temp.Add( *ptrs[i] ); } // Transfer IDs back to m_children m_children.Clear(); WX_APPEND_ARRAY( temp, m_children ); #endif #if 0 // Too slow // Build up array with IDs and original positions wxGtkTreeModelChildWithPos* temp = new wxGtkTreeModelChildWithPos[child_count]; size_t i; for (i = 0; i < child_count; i++) { temp[i].pos = i; temp[i].id = m_children[i]; } // Sort array keeping original positions wxQsort( temp, child_count, sizeof(wxGtkTreeModelChildWithPos), &wxGtkTreeModelChildWithPosCmp, m_internal ); // Transfer positions to new_order array and // IDs to m_children m_children.Clear(); for (i = 0; i < child_count; i++) { new_order[i] = temp[i].pos; m_children.Add( temp[i].id ); } // Delete array delete [] temp; #endif #if 0 // Too slow wxGtkTreeModelChildren temp; WX_APPEND_ARRAY( temp, m_children ); gs_internal = m_internal; m_children.Sort( &wxGtkTreeModelChildCmp ); unsigned int pos; for (pos = 0; pos < child_count; pos++) { void *id = m_children.Item( pos ); int old_pos = temp.Index( id ); new_order[pos] = old_pos; } #endif GtkTreeModel *gtk_tree_model = GTK_TREE_MODEL( m_internal->GetGtkModel() ); GtkTreeIter iter; 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); delete [] new_order; unsigned int pos; for (pos = 0; pos < node_count; pos++) { wxGtkTreeModelNode *node = m_nodes.Item( pos ); node->Resort(); } } //----------------------------------------------------------------------------- // wxDataViewCtrlInternal //----------------------------------------------------------------------------- wxDataViewCtrlInternal::wxDataViewCtrlInternal( wxDataViewCtrl *owner, wxDataViewModel *wx_model, GtkWxTreeModel *gtk_model ) { m_owner = owner; m_wx_model = wx_model; m_gtk_model = gtk_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; if (!m_wx_model->IsVirtualListModel()) InitTree(); } wxDataViewCtrlInternal::~wxDataViewCtrlInternal() { g_object_unref( m_gtk_model ); delete m_dragDataObject; delete m_dropDataObject; } 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->AddLeave( 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(-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(-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; GtkTreeIter iter; if (!get_iter( &iter, path )) return FALSE; wxDataViewItem item( (void*) iter.user_data ); wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, m_owner->GetId() ); event.SetEventObject( m_owner ); event.SetItem( item ); event.SetModel( m_wx_model ); 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 ) { GtkTreeIter iter; if (!get_iter( &iter, path )) return FALSE; wxDataViewItem item( (void*) iter.user_data ); if (!m_dragDataObject->IsSupported( selection_data->target )) return FALSE; size_t size = m_dragDataObject->GetDataSize( selection_data->target ); if (size == 0) return FALSE; void *buf = malloc( size ); gboolean res = FALSE; if (m_dragDataObject->GetDataHere( selection_data->target, buf )) { res = TRUE; gtk_selection_data_set( selection_data, 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) { GtkTreeIter iter; if (!get_iter( &iter, path )) return FALSE; wxDataViewItem item( (void*) iter.user_data ); wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP, m_owner->GetId() ); event.SetEventObject( m_owner ); event.SetItem( item ); event.SetModel( m_wx_model ); event.SetDataFormat( selection_data->target ); event.SetDataSize( selection_data->length ); event.SetDataBuffer( selection_data->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) { GtkTreeIter iter; if (!get_iter( &iter, path )) return FALSE; wxDataViewItem item( (void*) iter.user_data ); wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() ); event.SetEventObject( m_owner ); event.SetItem( item ); event.SetModel( m_wx_model ); event.SetDataFormat( selection_data->target ); if (!m_owner->HandleWindowEvent( event )) return FALSE; if (!event.IsAllowed()) return FALSE; return TRUE; } // notifications from wxDataViewModel bool wxDataViewCtrlInternal::Cleared() { GtkWidget* tree_widget = GetOwner()->GtkGetTreeView(); gtk_tree_view_set_model( GTK_TREE_VIEW(tree_widget), NULL ); gtk_tree_view_set_model( GTK_TREE_VIEW(tree_widget), GTK_TREE_MODEL(m_gtk_model) ); if (m_root) { delete m_root; InitTree(); } return true; } void wxDataViewCtrlInternal::Resort() { if (!m_wx_model->IsVirtualListModel()) m_root->Resort(); } bool wxDataViewCtrlInternal::ItemAdded( 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"); if (m_wx_model->IsContainer( item )) parent_node->AddNode( new wxGtkTreeModelNode( parent_node, item, this ) ); else parent_node->AddLeave( item.GetID() ); } 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() ); } return true; } bool wxDataViewCtrlInternal::ItemChanged( const wxDataViewItem &item ) { wxDataViewEvent event( wxEVT_COMMAND_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 col ) { wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, m_owner->GetId() ); event.SetEventObject( m_owner ); event.SetModel( m_owner->GetModel() ); event.SetColumn( col ); event.SetDataViewColumn( GetOwner()->GetColumn(col) ); event.SetItem( item ); m_owner->HandleWindowEvent( event ); return true; } // GTK+ model iface GtkTreeModelFlags wxDataViewCtrlInternal::get_flags() { if (m_wx_model->IsVirtualListModel()) return GTK_TREE_MODEL_LIST_ONLY; else return GTK_TREE_MODEL_ITERS_PERSIST; } 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 = (gpointer) (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 ) { GtkTreePath *retval = gtk_tree_path_new (); if (m_wx_model->IsVirtualListModel()) { // 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 = (gpointer) (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( (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 = (gpointer) (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; } } 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_REALIZED(dv->m_widget)) return; wxDataViewEvent event( wxEVT_COMMAND_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_COMMAND_DATAVIEW_ITEM_ACTIVATED, dv->GetId() ); GtkTreeIter iter; dv->GtkGetInternal()->get_iter( &iter, path ); wxDataViewItem item( (void*) iter.user_data );; 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_COMMAND_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_COMMAND_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_COMMAND_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_COMMAND_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_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_size_request( child->m_widget, &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 ) { if (gdk_event->is_hint) { int x = 0; int y = 0; GdkModifierType state; gdk_window_get_pointer(gdk_event->window, &x, &y, &state); gdk_event->x = x; gdk_event->y = y; } GtkTreePath *path = NULL; 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, &column, &cell_x, &cell_y)) { if (path) { GtkTreeIter iter; dv->GtkGetInternal()->get_iter( &iter, path ); gtk_tree_path_free( 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)) { GtkTreePath *path = NULL; 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, &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.SetModel( dv->GetModel() ); bool ret = dv->HandleWindowEvent( event ); gtk_tree_path_free( path ); return ret; } } } return FALSE; } IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase) wxDataViewCtrl::~wxDataViewCtrl() { if (m_notifier) GetModel()->RemoveNotifier( m_notifier ); m_cols.Clear(); // remove the model from the GtkTreeView before it gets destroyed by the // wxDataViewCtrlBase's dtor gtk_tree_view_set_model( GTK_TREE_VIEW(m_treeview), NULL ); delete m_internal; } void wxDataViewCtrl::Init() { m_notifier = NULL; m_internal = NULL; m_cols.DeleteContents( true ); } bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator ) { if (!PreCreation( parent, pos, size ) || !CreateBase( parent, id, pos, size, style, validator )) { 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); g_signal_connect (m_treeview, "size_allocate", G_CALLBACK (gtk_dataviewctrl_size_callback), this); #ifdef __WXGTK26__ if (!gtk_check_version(2,6,0)) { bool fixed = (style & wxDV_VARIABLE_LINE_HEIGHT) == 0; gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), fixed ); } #endif 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 ); #ifdef __WXGTK210__ if (!gtk_check_version(2,10,0)) { GtkTreeViewGridLines grid = GTK_TREE_VIEW_GRID_LINES_NONE; if ((style & wxDV_HORIZ_RULES) != 0 && (style & wxDV_VERT_RULES) != 0) grid = GTK_TREE_VIEW_GRID_LINES_BOTH; else if (style & wxDV_VERT_RULES) grid = GTK_TREE_VIEW_GRID_LINES_VERTICAL; else if (style & wxDV_HORIZ_RULES) grid = GTK_TREE_VIEW_GRID_LINES_HORIZONTAL; if (grid != GTK_TREE_VIEW_GRID_LINES_NONE) gtk_tree_view_set_grid_lines( GTK_TREE_VIEW(m_treeview), grid ); } #endif gtk_tree_view_set_rules_hint( GTK_TREE_VIEW(m_treeview), (style & wxDV_ROW_LINES) != 0 ); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (m_widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_widget_show (m_treeview); m_parent->DoAddChild( this ); PostCreation(size); GtkEnableSelectionEvents(); g_signal_connect_after (m_treeview, "row-activated", G_CALLBACK (wxdataview_row_activated_callback), this); g_signal_connect (m_treeview, "test-collapse-row", G_CALLBACK (wxdataview_test_collapse_row_callback), this); g_signal_connect_after (m_treeview, "row-collapsed", G_CALLBACK (wxdataview_row_collapsed_callback), this); g_signal_connect (m_treeview, "test-expand-row", G_CALLBACK (wxdataview_test_expand_row_callback), this); g_signal_connect_after (m_treeview, "row-expanded", G_CALLBACK (wxdataview_row_expanded_callback), this); g_signal_connect (m_treeview, "motion_notify_event", G_CALLBACK (gtk_dataview_motion_notify_callback), this); g_signal_connect (m_treeview, "button_press_event", G_CALLBACK (gtk_dataview_button_press_callback), this); return true; } void wxDataViewCtrl::OnInternalIdle() { wxWindow::OnInternalIdle(); unsigned int cols = GetColumnCount(); unsigned int i; for (i = 0; i < cols; i++) { wxDataViewColumn *col = GetColumn( i ); col->OnInternalIdle(); } } bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model ) { if ( m_internal ) { delete m_internal; m_internal = NULL; } if (!wxDataViewCtrlBase::AssociateModel( model )) return false; #ifdef __WXGTK26__ if (!gtk_check_version(2,6,0)) { bool fixed = (((GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT) == 0) || (model->IsVirtualListModel())); gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), fixed ); } #endif GtkWxTreeModel *gtk_model = wxgtk_tree_model_new(); m_internal = new wxDataViewCtrlInternal( this, model, gtk_model ); gtk_model->internal = m_internal; m_notifier = new wxGtkDataViewModelNotifier( gtk_model, model, this ); model->AddNotifier( m_notifier ); gtk_tree_view_set_model( GTK_TREE_VIEW(m_treeview), GTK_TREE_MODEL(gtk_model) ); // unref in wxDataViewCtrlInternal // g_object_unref( gtk_model ); return true; } bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format ) { return m_internal->EnableDragSource( format ); } bool wxDataViewCtrl::EnableDropTarget( const wxDataFormat &format ) { return m_internal->EnableDropTarget( format ); } bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col ) { if (!wxDataViewCtrlBase::AppendColumn(col)) return false; m_cols.Append( col ); #ifdef __WXGTK26__ if (!gtk_check_version(2,6,0)) { if (gtk_tree_view_column_get_sizing( GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) ) != GTK_TREE_VIEW_COLUMN_FIXED) gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), FALSE ); } #endif gtk_tree_view_append_column( GTK_TREE_VIEW(m_treeview), GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) ); return true; } bool wxDataViewCtrl::PrependColumn( wxDataViewColumn *col ) { if (!wxDataViewCtrlBase::PrependColumn(col)) return false; m_cols.Insert( col ); #ifdef __WXGTK26__ if (!gtk_check_version(2,6,0)) { if (gtk_tree_view_column_get_sizing( GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) ) != GTK_TREE_VIEW_COLUMN_FIXED) gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), FALSE ); } #endif gtk_tree_view_insert_column( GTK_TREE_VIEW(m_treeview), GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()), 0 ); return true; } bool wxDataViewCtrl::InsertColumn( unsigned int pos, wxDataViewColumn *col ) { if (!wxDataViewCtrlBase::InsertColumn(pos,col)) return false; m_cols.Insert( pos, col ); #ifdef __WXGTK26__ if (!gtk_check_version(2,6,0)) { if (gtk_tree_view_column_get_sizing( GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) ) != GTK_TREE_VIEW_COLUMN_FIXED) gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), FALSE ); } #endif gtk_tree_view_insert_column( GTK_TREE_VIEW(m_treeview), GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()), pos ); return true; } unsigned int wxDataViewCtrl::GetColumnCount() const { return m_cols.GetCount(); } wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int pos ) const { GtkTreeViewColumn *gtk_col = gtk_tree_view_get_column( GTK_TREE_VIEW(m_treeview), pos ); if (!gtk_col) return NULL; wxDataViewColumnList::const_iterator iter; for (iter = m_cols.begin(); iter != m_cols.end(); ++iter) { wxDataViewColumn *col = *iter; if (GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) == gtk_col) { return col; } } return NULL; } bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column ) { gtk_tree_view_remove_column( GTK_TREE_VIEW(m_treeview), GTK_TREE_VIEW_COLUMN(column->GetGtkHandle()) ); m_cols.DeleteObject( column ); return true; } bool wxDataViewCtrl::ClearColumns() { wxDataViewColumnList::iterator iter; for (iter = m_cols.begin(); iter != m_cols.end(); ++iter) { wxDataViewColumn *col = *iter; gtk_tree_view_remove_column( GTK_TREE_VIEW(m_treeview), GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) ); } m_cols.Clear(); return true; } int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const { GtkTreeViewColumn *gtk_column = GTK_TREE_VIEW_COLUMN(column->GetConstGtkHandle()); GList *list = gtk_tree_view_get_columns( GTK_TREE_VIEW(m_treeview) ); gint pos = g_list_index( list, (gconstpointer) gtk_column ); g_list_free( list ); return pos; } wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const { return m_internal->GetDataViewSortColumn(); } void wxDataViewCtrl::Expand( const wxDataViewItem & item ) { GtkTreeIter iter; iter.user_data = item.GetID(); GtkTreePath *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 ); 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 ); return res; } wxDataViewItem wxDataViewCtrl::GetSelection() const { GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) ); if (m_windowStyle & wxDV_MULTIPLE) { // Report the first one GtkTreeModel *model; GList *list = gtk_tree_selection_get_selected_rows( selection, &model ); if (list) { GtkTreePath *path = (GtkTreePath*) list->data; GtkTreeIter iter; m_internal->get_iter( &iter, path ); // delete list g_list_foreach( list, (GFunc) gtk_tree_path_free, NULL ); g_list_free( list ); return wxDataViewItem( (void*) iter.user_data ); } } else { GtkTreeIter iter; if (gtk_tree_selection_get_selected( selection, NULL, &iter )) { wxDataViewItem item( (void*) iter.user_data ); return item; } } return wxDataViewItem(0); } int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const { sel.Clear(); GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) ); if (HasFlag(wxDV_MULTIPLE)) { GtkTreeModel *model; GList *list = gtk_tree_selection_get_selected_rows( selection, &model ); int count = 0; while (list) { GtkTreePath *path = (GtkTreePath*) list->data; GtkTreeIter iter; m_internal->get_iter( &iter, path ); sel.Add( wxDataViewItem( (void*) iter.user_data ) ); list = g_list_next( list ); count++; } // delete list g_list_foreach( list, (GFunc) gtk_tree_path_free, NULL ); g_list_free( list ); return count; } else { GtkTreeModel *model; GtkTreeIter iter; gboolean has_selection = gtk_tree_selection_get_selected( selection, &model, &iter ); if (has_selection) { sel.Add( wxDataViewItem( (void*) iter.user_data) ); return 1; } } return 0; } void wxDataViewCtrl::SetSelections( const wxDataViewItemArray & sel ) { GtkDisableSelectionEvents(); GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) ); gtk_tree_selection_unselect_all( selection ); wxDataViewItem last_parent; size_t i; for (i = 0; i < sel.GetCount(); i++) { wxDataViewItem item = sel[i]; wxDataViewItem parent = GetModel()->GetParent( item ); if (parent) { if (parent != last_parent) ExpandAncestors(item); } last_parent = parent; GtkTreeIter iter; iter.stamp = m_internal->GetGtkModel()->stamp; iter.user_data = (gpointer) item.GetID(); gtk_tree_selection_select_iter( selection, &iter ); } GtkEnableSelectionEvents(); } void wxDataViewCtrl::Select( const wxDataViewItem & item ) { ExpandAncestors(item); GtkDisableSelectionEvents(); GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) ); GtkTreeIter iter; iter.stamp = m_internal->GetGtkModel()->stamp; iter.user_data = (gpointer) item.GetID(); gtk_tree_selection_select_iter( selection, &iter ); GtkEnableSelectionEvents(); } void wxDataViewCtrl::Unselect( const wxDataViewItem & item ) { GtkDisableSelectionEvents(); GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) ); GtkTreeIter iter; iter.stamp = m_internal->GetGtkModel()->stamp; iter.user_data = (gpointer) item.GetID(); gtk_tree_selection_unselect_iter( selection, &iter ); GtkEnableSelectionEvents(); } bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const { GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) ); GtkTreeIter iter; iter.stamp = m_internal->GetGtkModel()->stamp; iter.user_data = (gpointer) item.GetID(); return gtk_tree_selection_iter_is_selected( selection, &iter ); } void wxDataViewCtrl::SelectAll() { GtkDisableSelectionEvents(); GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) ); gtk_tree_selection_select_all( selection ); GtkEnableSelectionEvents(); } void wxDataViewCtrl::UnselectAll() { GtkDisableSelectionEvents(); GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) ); gtk_tree_selection_unselect_all( selection ); GtkEnableSelectionEvents(); } void wxDataViewCtrl::EnsureVisible(const wxDataViewItem& item, const wxDataViewColumn *WXUNUSED(column)) { ExpandAncestors(item); GtkTreeIter iter; iter.user_data = (gpointer) item.GetID(); GtkTreePath *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), wxDataViewItem& item, wxDataViewColumn *& column) const { item = wxDataViewItem(0); column = NULL; } wxRect wxDataViewCtrl::GetItemRect(const wxDataViewItem& WXUNUSED(item), const wxDataViewColumn *WXUNUSED(column)) const { return wxRect(); } void wxDataViewCtrl::DoSetExpanderColumn() { gtk_tree_view_set_expander_column( GTK_TREE_VIEW(m_treeview), GTK_TREE_VIEW_COLUMN( GetExpanderColumn()->GetGtkHandle() ) ); } void wxDataViewCtrl::DoSetIndent() { } void wxDataViewCtrl::GtkDisableSelectionEvents() { GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) ); g_signal_handlers_disconnect_by_func( selection, (gpointer) (wxdataview_selection_changed_callback), this); } void wxDataViewCtrl::GtkEnableSelectionEvents() { GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) ); g_signal_connect_after (selection, "changed", G_CALLBACK (wxdataview_selection_changed_callback), this); } // ---------------------------------------------------------------------------- // visual attributes stuff // ---------------------------------------------------------------------------- // static wxVisualAttributes wxDataViewCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) { return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new); } void wxDataViewCtrl::DoApplyWidgetStyle(GtkRcStyle *style) { wxDataViewCtrlBase::DoApplyWidgetStyle(style); gtk_widget_modify_style(m_treeview, style); } #endif // !wxUSE_GENERICDATAVIEWCTRL #endif // wxUSE_DATAVIEWCTRL