/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "treemodel.h" #include "debugging/debugging.h" #include #include #include #include "iscenegraph.h" #include "nameable.h" #include "generic/callback.h" #include "scenelib.h" #include "string/string.h" #include "generic/reference.h" inline Nameable* Node_getNameable( scene::Node& node ){ return NodeTypeCast::cast( node ); } #if 0 #include "gtkutil/gtktreestore.h" template inline void gtk_tree_model_get_pointer( GtkTreeModel* model, GtkTreeIter* iter, gint column, value_type** pointer ){ GValue value = GValue_default(); gtk_tree_model_get_value( model, iter, column, &value ); *pointer = (value_type*)g_value_get_pointer( &value ); } typedef GtkTreeStore GraphTreeModel; GtkTreeStore* graph_tree_model_new( graph_type* graph ){ return gtk_tree_store_new( 2, G_TYPE_POINTER, G_TYPE_POINTER ); } void graph_tree_model_delete( GraphTreeModel* model ){ g_object_unref( G_OBJECT( model ) ); } bool graph_tree_model_subtree_find_node( GraphTreeModel* model, GtkTreeIter* parent, const scene::Node& node, GtkTreeIter* iter ){ for ( gboolean success = gtk_tree_model_iter_children( GTK_TREE_MODEL( model ), iter, parent ); success != FALSE; success = gtk_tree_model_iter_next( GTK_TREE_MODEL( model ), iter ) ) { scene::Node* current; gtk_tree_model_get_pointer( GTK_TREE_MODEL( model ), iter, 0, ¤t ); if ( current == node ) { return true; } } return false; } typedef GtkTreeIter DoubleGtkTreeIter[2]; bool graph_tree_model_find_top( GraphTreeModel* model, const scene::Path& path, GtkTreeIter& iter ){ int swap = 0; GtkTreeIter* parent_pointer = NULL; GtkTreeIter parent; for ( scene::Path::const_iterator i = path.begin(); i != path.end(); ++i ) { if ( !graph_tree_model_subtree_find_node( model, parent_pointer, *i, &iter ) ) { return false; } parent = iter; parent_pointer = &parent; } return true; } bool graph_tree_model_find_parent( GraphTreeModel* model, const scene::Path& path, GtkTreeIter& iter ){ int swap = 0; GtkTreeIter* parent_pointer = NULL; ASSERT_MESSAGE( path.size() > 1, "path too short" ); for ( scene::Path::const_iterator i = path.begin(); i != path.end() - 1; ++i ) { GtkTreeIter child; if ( !graph_tree_model_subtree_find_node( model, parent_pointer, *i, &child ) ) { return false; } iter = child; parent_pointer = &iter; } return true; } void node_attach_name_changed_callback( scene::Node& node, const Callback& callback ){ if ( node != 0 ) { Nameable* nameable = Node_getNameable( node ); if ( nameable != 0 ) { nameable->attach( callback ); } } } void node_detach_name_changed_callback( scene::Node& node, const Callback& callback ){ if ( node != 0 ) { Nameable* nameable = Node_getNameable( node ); if ( nameable != 0 ) { nameable->detach( callback ); } } } GraphTreeModel* scene_graph_get_tree_model(); // temp hack void graph_tree_model_row_changed( const scene::Instance& instance ){ GraphTreeModel* model = scene_graph_get_tree_model(); GtkTreeIter child; ASSERT_MESSAGE( graph_tree_model_find_top( model, instance.path(), child ), "RUNTIME ERROR" ); gtk_tree_store_set( GTK_TREE_STORE( model ), &child, 0, instance.path().top(), -1 ); } void graph_tree_model_row_inserted( GraphTreeModel* model, const scene::Instance& instance ){ GtkTreeIter parent; GtkTreeIter* parent_pointer = NULL; if ( instance.path().size() != 1 ) { ASSERT_MESSAGE( graph_tree_model_find_parent( model, instance.path(), parent ), "RUNTIME ERROR" ); parent_pointer = &parent; } gpointer node = instance.path().top(); gconstpointer selectable = Instance_getSelectable( instance ); GtkTreeIter child; gtk_tree_store_append( GTK_TREE_STORE( model ), &child, parent_pointer ); gtk_tree_store_set( GTK_TREE_STORE( model ), &child, 0, node, 1, selectable, -1 ); node_attach_name_changed_callback( instance.path().top(), ConstReferenceCaller( instance ) ); } void graph_tree_model_row_deleted( GraphTreeModel* model, const scene::Instance& instance ){ GtkTreeIter child; ASSERT_MESSAGE( graph_tree_model_find_top( model, instance.path(), child ), "RUNTIME ERROR" ); node_detach_name_changed_callback( instance.path().top(), ConstReferenceCaller( instance ) ); gtk_tree_store_remove( GTK_TREE_STORE( model ), &child ); } #elif 0 const char* node_get_name( scene::Node& node ); typedef scene::Node* NodePointer; class NodeNameLess { public: bool operator()( const NodePointer& self, const NodePointer& other ) const { if ( self == 0 ) { return true; } if ( other == 0 ) { return false; } int result = string_compare( node_get_name( self ), node_get_name( other ) ); if ( result == 0 ) { return self < other; } return result < 0; } }; class PathNameLess { public: bool operator()( const PathConstReference& self, const PathConstReference& other ) const { return std::lexicographical_compare( self.get().begin(), self.get().end(), other.get().begin(), other.get().end(), NodeNameLess() ); } }; typedef std::map graph_type; struct GraphTreeModel { GObject parent; graph_type* graph; }; struct GraphTreeModelClass { GObjectClass parent_class; }; #define GRAPH_TREE_MODEL( p ) ( reinterpret_cast( p ) ) static GtkTreeModelFlags graph_tree_model_get_flags( GtkTreeModel* tree_model ){ return GTK_TREE_MODEL_ITERS_PERSIST; } static gint graph_tree_model_get_n_columns( GtkTreeModel* tree_model ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); GraphTreeModel* graph_tree_model = (GraphTreeModel*) tree_model; return 2; } static const gint c_stamp = 0xabcdef; inline graph_type::iterator graph_iterator_read_tree_iter( GtkTreeIter* iter ){ ASSERT_MESSAGE( iter != 0, "tree model error" ); ASSERT_MESSAGE( iter->user_data != 0, "tree model error" ); ASSERT_MESSAGE( iter->stamp == c_stamp, "tree model error" ); return *reinterpret_cast( &iter->user_data ); } inline void graph_iterator_write_tree_iter( graph_type::iterator i, GtkTreeIter* iter ){ ASSERT_MESSAGE( iter != 0, "tree model error" ); iter->stamp = c_stamp; *reinterpret_cast( &iter->user_data ) = i; ASSERT_MESSAGE( iter->user_data != 0, "tree model error" ); } static GType graph_tree_model_get_column_type( GtkTreeModel *tree_model, gint index ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); GraphTreeModel *graph_tree_model = (GraphTreeModel *) tree_model; return G_TYPE_POINTER; } static gboolean graph_tree_model_get_iter( GtkTreeModel* tree_model, GtkTreeIter* iter, GtkTreePath* path ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); gint* indices = gtk_tree_path_get_indices( path ); gint depth = gtk_tree_path_get_depth( path ); g_return_val_if_fail( depth > 0, FALSE ); graph_type& graph = *GRAPH_TREE_MODEL( tree_model )->graph; if ( graph.empty() ) { return FALSE; } GtkTreeIter tmp; GtkTreeIter* parent = 0; for ( gint i = 0; i < depth; i++ ) { if ( !gtk_tree_model_iter_nth_child( tree_model, iter, parent, indices[i] ) ) { return FALSE; } tmp = *iter; parent = &tmp; } return TRUE; } static GtkTreePath* graph_tree_model_get_path( GtkTreeModel* tree_model, GtkTreeIter* iter ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); graph_type& graph = *GRAPH_TREE_MODEL( tree_model )->graph; graph_type::iterator i = graph_iterator_read_tree_iter( iter ); GtkTreePath* path = ui::TreePath(); for ( std::size_t depth = ( *i ).first.get().size(); depth != 0; --depth ) { std::size_t index = 0; while ( i != graph.begin() && ( *i ).first.get().size() >= depth ) { --i; if ( ( *i ).first.get().size() == depth ) { ++index; } } gtk_tree_path_prepend_index( path, index ); } return path; } static void graph_tree_model_get_value( GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); ASSERT_MESSAGE( column == 0 || column == 1, "tree model error" ); graph_type::iterator i = graph_iterator_read_tree_iter( iter ); g_value_init( value, G_TYPE_POINTER ); if ( column == 0 ) { g_value_set_pointer( value, reinterpret_cast( ( *i ).first.get().top() ) ); } else{ g_value_set_pointer( value, reinterpret_cast( Instance_getSelectable( *( *i ).second ) ) ); } } static gboolean graph_tree_model_iter_next( GtkTreeModel *tree_model, GtkTreeIter *iter ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); graph_type& graph = *GRAPH_TREE_MODEL( tree_model )->graph; graph_type::iterator i = graph_iterator_read_tree_iter( iter ); std::size_t depth = ( *i ).first.get().size(); ++i; while ( i != graph.end() && ( *i ).first.get().size() > depth ) { ++i; } if ( i == graph.end() || ( *i ).first.get().size() != depth ) { return FALSE; } graph_iterator_write_tree_iter( i, iter ); return TRUE; } static gboolean graph_tree_model_iter_children( GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); graph_type& graph = *GRAPH_TREE_MODEL( tree_model )->graph; graph_type::iterator i = ( parent == 0 ) ? graph.begin() : graph_iterator_read_tree_iter( parent ); std::size_t depth = ( parent == 0 ) ? 1 : ( *i ).first.get().size() + 1; if ( parent != 0 ) { ++i; } if ( i != graph.end() && ( *i ).first.get().size() == depth ) { graph_iterator_write_tree_iter( i, iter ); return TRUE; } return FALSE; } static gboolean graph_tree_model_iter_has_child( GtkTreeModel *tree_model, GtkTreeIter *iter ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); graph_type& graph = *GRAPH_TREE_MODEL( tree_model )->graph; graph_type::iterator i = graph_iterator_read_tree_iter( iter ); std::size_t depth = ( *i ).first.get().size() + 1; return ++i != graph.end() && ( *i ).first.get().size() == depth; } static gint graph_tree_model_iter_n_children( GtkTreeModel *tree_model, GtkTreeIter *parent ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); graph_type& graph = *GRAPH_TREE_MODEL( tree_model )->graph; graph_type::iterator i = ( parent == 0 ) ? graph.begin() : graph_iterator_read_tree_iter( parent ); std::size_t depth = ( parent == 0 ) ? 1 : ( *i ).first.get().size() + 1; if ( parent != 0 ) { ++i; } gint count = 0; while ( i != graph.end() && ( *i ).first.get().size() >= depth ) { ++count; ++i; } return count; } static gboolean graph_tree_model_iter_nth_child( GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); graph_type& graph = *GRAPH_TREE_MODEL( tree_model )->graph; graph_type::iterator i = ( parent == 0 ) ? graph.begin() : graph_iterator_read_tree_iter( parent ); std::size_t depth = ( parent == 0 ) ? 1 : ( *i ).first.get().size() + 1; if ( parent != 0 ) { ++i; } while ( i != graph.end() && ( *i ).first.get().size() >= depth ) { if ( ( *i ).first.get().size() == depth && n-- == 0 ) { graph_iterator_write_tree_iter( i, iter ); return TRUE; } ++i; } return FALSE; } static gboolean graph_tree_model_iter_parent( GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); graph_type& graph = *GRAPH_TREE_MODEL( tree_model )->graph; graph_type::iterator i = graph_iterator_read_tree_iter( child ); std::size_t depth = ( *i ).first.get().size(); if ( depth == 1 ) { return FALSE; } else { do { --i; } while ( ( *i ).first.get().size() >= depth ); graph_iterator_write_tree_iter( i, iter ); return TRUE; } } static GObjectClass *g_parent_class = 0; static void graph_tree_model_init( GraphTreeModel *graph_tree_model ){ graph_tree_model->graph = 0; } static void graph_tree_model_finalize( GObject* object ){ GraphTreeModel* graph_tree_model = GRAPH_TREE_MODEL( object ); /* must chain up */ ( *g_parent_class->finalize )( object ); } static void graph_tree_model_class_init( GraphTreeModelClass *class_ ){ GObjectClass *object_class; g_parent_class = (GObjectClass*)g_type_class_peek_parent( class_ ); object_class = (GObjectClass *) class_; object_class->finalize = graph_tree_model_finalize; } static void graph_tree_model_tree_model_init( GtkTreeModelIface *iface ){ iface->get_flags = graph_tree_model_get_flags; iface->get_n_columns = graph_tree_model_get_n_columns; iface->get_column_type = graph_tree_model_get_column_type; iface->get_iter = graph_tree_model_get_iter; iface->get_path = graph_tree_model_get_path; iface->get_value = graph_tree_model_get_value; iface->iter_next = graph_tree_model_iter_next; iface->iter_children = graph_tree_model_iter_children; iface->iter_has_child = graph_tree_model_iter_has_child; iface->iter_n_children = graph_tree_model_iter_n_children; iface->iter_nth_child = graph_tree_model_iter_nth_child; iface->iter_parent = graph_tree_model_iter_parent; } static gboolean graph_tree_model_row_draggable( GtkTreeDragSource *drag_source, GtkTreePath *path ){ #ifdef _DEBUG gint depth = gtk_tree_path_get_depth( path ); #endif return gtk_tree_path_get_depth( path ) > 1; } static gboolean graph_tree_model_drag_data_delete( GtkTreeDragSource *drag_source, GtkTreePath *path ){ GtkTreeIter iter; if ( gtk_tree_model_get_iter( GTK_TREE_MODEL( drag_source ), &iter, path ) ) { graph_type::iterator i = graph_iterator_read_tree_iter( &iter ); Path_deleteTop( ( *i ).first ); return TRUE; } else { return FALSE; } } static gboolean graph_tree_model_drag_data_get( GtkTreeDragSource *drag_source, GtkTreePath *path, GtkSelectionData *selection_data ){ if ( gtk_tree_set_row_drag_data( selection_data, GTK_TREE_MODEL( drag_source ), path ) ) { return TRUE; } else { /* FIXME handle text targets at least. */ } return FALSE; } static void graph_tree_model_drag_source_init( GtkTreeDragSourceIface *iface ){ iface->row_draggable = graph_tree_model_row_draggable; iface->drag_data_delete = graph_tree_model_drag_data_delete; iface->drag_data_get = graph_tree_model_drag_data_get; } static gboolean graph_tree_model_drag_data_received( GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data ){ GtkTreeModel *tree_model = GTK_TREE_MODEL( drag_dest ); GtkTreeModel *src_model = 0; GtkTreePath *src_path = 0; if ( gtk_tree_get_row_drag_data( selection_data, &src_model, &src_path ) && src_model == tree_model ) { /* Copy the given row to a new position */ GtkTreeIter iter; if ( gtk_tree_model_get_iter( src_model, &iter, src_path ) ) { int bleh = 0; } } else { /* FIXME maybe add some data targets eventually, or handle text * targets in the simple case. */ } return FALSE; } static gboolean graph_tree_model_row_drop_possible( GtkTreeDragDest *drag_dest, GtkTreePath *dest_path, GtkSelectionData *selection_data ){ gboolean retval = FALSE; GtkTreeModel *src_model = 0; GtkTreePath *src_path = 0; if ( gtk_tree_get_row_drag_data( selection_data, &src_model, &src_path ) != FALSE ) { /* can only drag to ourselves */ if ( src_model == GTK_TREE_MODEL( drag_dest ) ) { /* Can't drop into ourself. */ if ( !gtk_tree_path_is_ancestor( src_path, dest_path ) ) { /* Can't drop if dest_path's parent doesn't exist */ if ( gtk_tree_path_get_depth( dest_path ) > 1 ) { GtkTreePath* tmp = gtk_tree_path_copy( dest_path ); gtk_tree_path_up( tmp ); GtkTreeIter iter; retval = gtk_tree_model_get_iter( GTK_TREE_MODEL( drag_dest ), &iter, tmp ); gtk_tree_path_free( tmp ); } } } gtk_tree_path_free( src_path ); } return retval; } static void graph_tree_model_drag_dest_init( GtkTreeDragDestIface *iface ){ iface->drag_data_received = graph_tree_model_drag_data_received; iface->row_drop_possible = graph_tree_model_row_drop_possible; } GType graph_tree_model_get_type( void ){ static GType graph_tree_model_type = 0; if ( !graph_tree_model_type ) { static const GTypeInfo graph_tree_model_info = { sizeof( GraphTreeModelClass ), 0, /* base_init */ 0, /* base_finalize */ (GClassInitFunc) graph_tree_model_class_init, 0, /* class_finalize */ 0, /* class_data */ sizeof( GraphTreeModel ), 0, /* n_preallocs */ (GInstanceInitFunc) graph_tree_model_init }; static const GInterfaceInfo tree_model_info = { (GInterfaceInitFunc) graph_tree_model_tree_model_init, 0, 0 }; static const GInterfaceInfo drag_source_info = { (GInterfaceInitFunc) graph_tree_model_drag_source_init, 0, 0 }; static const GInterfaceInfo drag_dest_info = { (GInterfaceInitFunc) graph_tree_model_drag_dest_init, 0, 0 }; graph_tree_model_type = g_type_register_static( G_TYPE_OBJECT, "GraphTreeModel", &graph_tree_model_info, (GTypeFlags)0 ); g_type_add_interface_static( graph_tree_model_type, GTK_TYPE_TREE_MODEL, &tree_model_info ); g_type_add_interface_static( graph_tree_model_type, GTK_TYPE_TREE_DRAG_SOURCE, &drag_source_info ); g_type_add_interface_static( graph_tree_model_type, GTK_TYPE_TREE_DRAG_DEST, &drag_dest_info ); } return graph_tree_model_type; } GraphTreeModel* graph_tree_model_new(){ GraphTreeModel* graph_tree_model = GRAPH_TREE_MODEL( g_object_new( graph_tree_model_get_type(), 0 ) ); graph_tree_model->graph = new graph_type; return graph_tree_model; } void graph_tree_model_delete( GraphTreeModel* model ){ delete model->graph; g_object_unref( G_OBJECT( model ) ); } class TempNameable : public Nameable { const char* m_name; public: TempNameable( const char* name ) : m_name( name ){ } const char* name() const { return m_name; } void attach( const NameCallback& callback ){ } void detach( const NameCallback& callback ){ } }; void node_attach_name_changed_callback( scene::Node& node, const NameCallback& callback ){ // Reference cannot be bound to dereferenced null pointer in well-defined // C++ code, and Clang will assume that comparison below always evaluates // to true, resulting in a segmentation fault. Use a dirty hack to force // Clang to check those "bad" references for null nonetheless. volatile intptr_t n = (intptr_t)&node; if ( n != 0 ) { Nameable* nameable = Node_getNameable( node ); if ( nameable != 0 ) { nameable->attach( callback ); } } } void node_detach_name_changed_callback( scene::Node& node, const NameCallback& callback ){ volatile intptr_t n = (intptr_t)&node; // see the comment on line 650 if ( n != 0 ) { Nameable* nameable = Node_getNameable( node ); if ( nameable != 0 ) { nameable->detach( callback ); } } } GraphTreeModel* scene_graph_get_tree_model(); // temp hack void graph_tree_model_row_inserted( GraphTreeModel* model, graph_type::iterator i ){ GtkTreeIter iter; graph_iterator_write_tree_iter( i, &iter ); GtkTreePath* tree_path = graph_tree_model_get_path( GTK_TREE_MODEL( model ), &iter ); gint depth = gtk_tree_path_get_depth( tree_path ); gint* indices = gtk_tree_path_get_indices( tree_path ); gtk_tree_model_row_inserted( GTK_TREE_MODEL( model ), tree_path, &iter ); gtk_tree_path_free( tree_path ); } void graph_tree_model_row_deleted( GraphTreeModel* model, graph_type::iterator i ){ GtkTreeIter iter; graph_iterator_write_tree_iter( i, &iter ); GtkTreePath* tree_path = graph_tree_model_get_path( GTK_TREE_MODEL( model ), &iter ); gtk_tree_model_row_deleted( GTK_TREE_MODEL( model ), tree_path ); gtk_tree_path_free( tree_path ); } #include "generic/referencecounted.h" void graph_tree_model_set_name( const scene::Instance& instance, const char* name ){ GraphTreeModel* model = scene_graph_get_tree_model(); if ( string_empty( name ) ) { // hack! graph_type::iterator i = model->graph->find( PathConstReference( instance.path() ) ); ASSERT_MESSAGE( i != model->graph->end(), "ERROR" ); graph_tree_model_row_deleted( model, i ); model->graph->erase( i ); } else { graph_type::iterator i = model->graph->insert( graph_type::value_type( PathConstReference( instance.path() ), &const_cast( instance ) ) ).first; graph_tree_model_row_inserted( model, i ); } } void graph_tree_model_insert( GraphTreeModel* model, const scene::Instance& instance ){ graph_type::iterator i = model->graph->insert( graph_type::value_type( PathConstReference( instance.path() ), &const_cast( instance ) ) ).first; graph_tree_model_row_inserted( model, i ); node_attach_name_changed_callback( instance.path().top(), ConstReferenceCaller1( instance ) ); } void graph_tree_model_erase( GraphTreeModel* model, const scene::Instance& instance ){ node_detach_name_changed_callback( instance.path().top(), ConstReferenceCaller1( instance ) ); graph_type::iterator i = model->graph->find( PathConstReference( instance.path() ) ); ASSERT_MESSAGE( i != model->graph->end(), "ERROR" ); graph_tree_model_row_deleted( model, i ); model->graph->erase( i ); } #elif 1 class GraphTreeNode; void graph_tree_model_row_changed( GraphTreeNode& node ); class GraphTreeNode { typedef std::map, GraphTreeNode*> ChildNodes; ChildNodes m_childnodes; public: Reference m_instance; GraphTreeNode* m_parent; typedef ChildNodes::iterator iterator; typedef ChildNodes::key_type key_type; typedef ChildNodes::value_type value_type; typedef ChildNodes::size_type size_type; GraphTreeNode( scene::Instance& instance ) : m_instance( instance ), m_parent( 0 ){ m_instance.get().setChildSelectedChangedCallback( RowChangedCaller( *this ) ); } ~GraphTreeNode(){ m_instance.get().setChildSelectedChangedCallback( Callback() ); ASSERT_MESSAGE( empty(), "GraphTreeNode::~GraphTreeNode: memory leak" ); } iterator begin(){ return m_childnodes.begin(); } iterator end(){ return m_childnodes.end(); } size_type size() const { return m_childnodes.size(); } bool empty() const { return m_childnodes.empty(); } iterator insert( const value_type& value ){ iterator i = m_childnodes.insert( value ).first; ( *i ).second->m_parent = this; return i; } void erase( iterator i ){ m_childnodes.erase( i ); } iterator find( const key_type& key ){ return m_childnodes.find( key ); } void swap( GraphTreeNode& other ){ std::swap( m_parent, other.m_parent ); std::swap( m_childnodes, other.m_childnodes ); std::swap( m_instance, other.m_instance ); } void rowChanged(){ graph_tree_model_row_changed( *this ); } typedef MemberCaller RowChangedCaller; }; struct GraphTreeModel { GObject parent; GraphTreeNode* m_graph; }; struct GraphTreeModelClass { GObjectClass parent_class; }; #define GRAPH_TREE_MODEL( p ) ( reinterpret_cast( p ) ) static GtkTreeModelFlags graph_tree_model_get_flags( GtkTreeModel* tree_model ){ return GTK_TREE_MODEL_ITERS_PERSIST; } static gint graph_tree_model_get_n_columns( GtkTreeModel* tree_model ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); //GraphTreeModel* graph_tree_model = (GraphTreeModel*) tree_model; return 2; } static const gint c_stamp = 0xabcdef; inline GraphTreeNode::iterator graph_iterator_read_tree_iter( GtkTreeIter* iter ){ ASSERT_MESSAGE( iter != 0, "tree model error" ); ASSERT_MESSAGE( iter->user_data != 0, "tree model error" ); ASSERT_MESSAGE( iter->stamp == c_stamp, "tree model error" ); return *reinterpret_cast( &iter->user_data ); } inline void graph_iterator_write_tree_iter( GraphTreeNode::iterator i, GtkTreeIter* iter ){ ASSERT_MESSAGE( iter != 0, "tree model error" ); iter->stamp = c_stamp; *reinterpret_cast( &iter->user_data ) = i; ASSERT_MESSAGE( iter->user_data != 0, "tree model error" ); } static GType graph_tree_model_get_column_type( GtkTreeModel *tree_model, gint index ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); //GraphTreeModel *graph_tree_model = (GraphTreeModel *) tree_model; return G_TYPE_POINTER; } static gboolean graph_tree_model_get_iter( GtkTreeModel* tree_model, GtkTreeIter* iter, GtkTreePath* path ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); gint* indices = gtk_tree_path_get_indices( path ); gint depth = gtk_tree_path_get_depth( path ); g_return_val_if_fail( depth > 0, FALSE ); GraphTreeNode* graph = GRAPH_TREE_MODEL( tree_model )->m_graph; if ( graph->empty() ) { return FALSE; } GtkTreeIter tmp; GtkTreeIter* parent = 0; for ( gint i = 0; i < depth; i++ ) { if ( !gtk_tree_model_iter_nth_child( tree_model, iter, parent, indices[i] ) ) { return FALSE; } tmp = *iter; parent = &tmp; } return TRUE; } static GtkTreePath* graph_tree_model_get_path( GtkTreeModel* tree_model, GtkTreeIter* iter ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); GraphTreeNode* graph = GRAPH_TREE_MODEL( tree_model )->m_graph; GtkTreePath* path = ui::TreePath(); for ( GraphTreeNode* node = ( *graph_iterator_read_tree_iter( iter ) ).second; node != graph; node = node->m_parent ) { std::size_t index = 0; for ( GraphTreeNode::iterator i = node->m_parent->begin(); i != node->m_parent->end(); ++i, ++index ) { if ( ( *i ).second == node ) { gtk_tree_path_prepend_index( path, gint( index ) ); break; } } ASSERT_MESSAGE( index != node->m_parent->size(), "error resolving tree path" ); } return path; } static void graph_tree_model_get_value( GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); ASSERT_MESSAGE( column == 0 || column == 1, "tree model error" ); GraphTreeNode::iterator i = graph_iterator_read_tree_iter( iter ); g_value_init( value, G_TYPE_POINTER ); if ( column == 0 ) { g_value_set_pointer( value, reinterpret_cast( ( *i ).first.second ) ); } else { g_value_set_pointer( value, reinterpret_cast( &( *i ).second->m_instance.get() ) ); } } static gboolean graph_tree_model_iter_next( GtkTreeModel *tree_model, GtkTreeIter *iter ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); GraphTreeNode::iterator i = graph_iterator_read_tree_iter( iter ); GraphTreeNode& parent = *( *i ).second->m_parent; ASSERT_MESSAGE( i != parent.end(), "RUNTIME ERROR" ); if ( ++i == parent.end() ) { return FALSE; } graph_iterator_write_tree_iter( i, iter ); return TRUE; } static gboolean graph_tree_model_iter_children( GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); GraphTreeNode& node = ( parent == 0 ) ? *GRAPH_TREE_MODEL( tree_model )->m_graph : *( *graph_iterator_read_tree_iter( parent ) ).second; if ( !node.empty() ) { graph_iterator_write_tree_iter( node.begin(), iter ); return TRUE; } return FALSE; } static gboolean graph_tree_model_iter_has_child( GtkTreeModel *tree_model, GtkTreeIter *iter ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); GraphTreeNode& node = *( *graph_iterator_read_tree_iter( iter ) ).second; return !node.empty(); } static gint graph_tree_model_iter_n_children( GtkTreeModel *tree_model, GtkTreeIter *parent ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); GraphTreeNode& node = ( parent == 0 ) ? *GRAPH_TREE_MODEL( tree_model )->m_graph : *( *graph_iterator_read_tree_iter( parent ) ).second; return static_cast( node.size() ); } static gboolean graph_tree_model_iter_nth_child( GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); GraphTreeNode& node = ( parent == 0 ) ? *GRAPH_TREE_MODEL( tree_model )->m_graph : *( *graph_iterator_read_tree_iter( parent ) ).second; if ( static_cast( n ) < node.size() ) { GraphTreeNode::iterator i = node.begin(); std::advance( i, n ); graph_iterator_write_tree_iter( i, iter ); return TRUE; } return FALSE; } static gboolean graph_tree_model_iter_parent( GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child ){ ASSERT_MESSAGE( tree_model != 0, "RUNTIME ERROR" ); GraphTreeNode& node = *( *graph_iterator_read_tree_iter( child ) ).second; if ( node.m_parent != GRAPH_TREE_MODEL( tree_model )->m_graph ) { GraphTreeNode& parentParent = *node.m_parent->m_parent; for ( GraphTreeNode::iterator i = parentParent.begin(); i != parentParent.end(); ++i ) { if ( ( *i ).second == node.m_parent ) { graph_iterator_write_tree_iter( i, iter ); return TRUE; } } } return FALSE; } static GObjectClass *g_parent_class = 0; namespace { scene::Node* g_null_node = 0; } class NullInstance : public scene::Instance { public: NullInstance() : scene::Instance( scene::Path( makeReference( *g_null_node ) ), 0, 0, Static::instance() ){ } }; namespace { NullInstance g_null_instance; } static void graph_tree_model_init( GraphTreeModel *graph_tree_model ){ graph_tree_model->m_graph = new GraphTreeNode( g_null_instance ); } static void graph_tree_model_finalize( GObject* object ){ GraphTreeModel* graph_tree_model = GRAPH_TREE_MODEL( object ); delete graph_tree_model->m_graph; /* must chain up */ ( *g_parent_class->finalize )( object ); } static void graph_tree_model_class_init( GraphTreeModelClass *class_ ){ GObjectClass *object_class; g_parent_class = (GObjectClass*)g_type_class_peek_parent( class_ ); object_class = (GObjectClass *) class_; object_class->finalize = graph_tree_model_finalize; } static void graph_tree_model_tree_model_init( GtkTreeModelIface *iface ){ iface->get_flags = graph_tree_model_get_flags; iface->get_n_columns = graph_tree_model_get_n_columns; iface->get_column_type = graph_tree_model_get_column_type; iface->get_iter = graph_tree_model_get_iter; iface->get_path = graph_tree_model_get_path; iface->get_value = graph_tree_model_get_value; iface->iter_next = graph_tree_model_iter_next; iface->iter_children = graph_tree_model_iter_children; iface->iter_has_child = graph_tree_model_iter_has_child; iface->iter_n_children = graph_tree_model_iter_n_children; iface->iter_nth_child = graph_tree_model_iter_nth_child; iface->iter_parent = graph_tree_model_iter_parent; } GType graph_tree_model_get_type( void ){ static GType graph_tree_model_type = 0; if ( !graph_tree_model_type ) { static const GTypeInfo graph_tree_model_info = { sizeof( GraphTreeModelClass ), 0, /* base_init */ 0, /* base_finalize */ (GClassInitFunc) graph_tree_model_class_init, 0, /* class_finalize */ 0, /* class_data */ sizeof( GraphTreeModel ), 0, /* n_preallocs */ (GInstanceInitFunc) graph_tree_model_init, 0 }; static const GInterfaceInfo tree_model_info = { (GInterfaceInitFunc) graph_tree_model_tree_model_init, 0, 0 }; graph_tree_model_type = g_type_register_static( G_TYPE_OBJECT, "GraphTreeModel", &graph_tree_model_info, (GTypeFlags)0 ); g_type_add_interface_static( graph_tree_model_type, GTK_TYPE_TREE_MODEL, &tree_model_info ); } return graph_tree_model_type; } GraphTreeModel* graph_tree_model_new(){ GraphTreeModel* graph_tree_model = GRAPH_TREE_MODEL( g_object_new( graph_tree_model_get_type(), 0 ) ); return graph_tree_model; } void graph_tree_model_delete( GraphTreeModel* model ){ g_object_unref( G_OBJECT( model ) ); } void graph_tree_model_row_changed( GraphTreeModel* model, GraphTreeNode::iterator i ){ GtkTreeIter iter; graph_iterator_write_tree_iter( i, &iter ); GtkTreePath* tree_path = graph_tree_model_get_path( GTK_TREE_MODEL( model ), &iter ); gtk_tree_model_row_changed( GTK_TREE_MODEL( model ), tree_path, &iter ); gtk_tree_path_free( tree_path ); } void graph_tree_model_row_inserted( GraphTreeModel* model, GraphTreeNode::iterator i ){ GtkTreeIter iter; graph_iterator_write_tree_iter( i, &iter ); GtkTreePath* tree_path = graph_tree_model_get_path( GTK_TREE_MODEL( model ), &iter ); gtk_tree_model_row_inserted( GTK_TREE_MODEL( model ), tree_path, &iter ); gtk_tree_path_free( tree_path ); } void graph_tree_model_row_deleted( GraphTreeModel* model, GraphTreeNode::iterator i ){ GtkTreeIter iter; graph_iterator_write_tree_iter( i, &iter ); GtkTreePath* tree_path = graph_tree_model_get_path( GTK_TREE_MODEL( model ), &iter ); gtk_tree_model_row_deleted( GTK_TREE_MODEL( model ), tree_path ); gtk_tree_path_free( tree_path ); } void graph_tree_model_row_inserted( GraphTreeModel& model, GraphTreeNode::iterator i ){ graph_tree_model_row_inserted( &model, i ); } void graph_tree_model_row_deleted( GraphTreeModel& model, GraphTreeNode::iterator i ){ graph_tree_model_row_deleted( &model, i ); } const char* node_get_name( scene::Node& node ); const char* node_get_name_safe( scene::Node& node ){ volatile intptr_t n = (intptr_t)&node; // see the comment on line 650 if ( n == 0 ) { return ""; } return node_get_name( node ); } GraphTreeNode* graph_tree_model_find_parent( GraphTreeModel* model, const scene::Path& path ){ GraphTreeNode* parent = model->m_graph; for ( scene::Path::const_iterator i = path.begin(); i != path.end() - 1; ++i ) { GraphTreeNode::iterator child = parent->find( GraphTreeNode::key_type( node_get_name_safe( ( *i ).get() ), ( *i ).get_pointer() ) ); ASSERT_MESSAGE( child != parent->end(), "ERROR" ); parent = ( *child ).second; } return parent; } void node_attach_name_changed_callback( scene::Node& node, const NameCallback& callback ){ volatile intptr_t n = (intptr_t)&node; // see the comment on line 650 if ( n != 0 ) { Nameable* nameable = Node_getNameable( node ); if ( nameable != 0 ) { nameable->attach( callback ); } } } void node_detach_name_changed_callback( scene::Node& node, const NameCallback& callback ){ volatile intptr_t n = (intptr_t)&node; // see the comment on line 650 if ( n != 0 ) { Nameable* nameable = Node_getNameable( node ); if ( nameable != 0 ) { nameable->detach( callback ); } } } GraphTreeModel* scene_graph_get_tree_model(); // temp hack void graph_tree_node_foreach_pre( GraphTreeNode::iterator root, const Callback1& callback ){ callback( root ); for ( GraphTreeNode::iterator i = ( *root ).second->begin(); i != ( *root ).second->end(); ++i ) { graph_tree_node_foreach_pre( i, callback ); } } void graph_tree_node_foreach_post( GraphTreeNode::iterator root, const Callback1& callback ){ for ( GraphTreeNode::iterator i = ( *root ).second->begin(); i != ( *root ).second->end(); ++i ) { graph_tree_node_foreach_post( i, callback ); } callback( root ); } void graph_tree_model_row_changed( GraphTreeNode& node ){ GraphTreeModel* model = scene_graph_get_tree_model(); const scene::Instance& instance = node.m_instance.get(); GraphTreeNode::iterator i = node.m_parent->find( GraphTreeNode::key_type( node_get_name_safe( instance.path().top().get() ), instance.path().top().get_pointer() ) ); graph_tree_model_row_changed( model, i ); } void graph_tree_model_set_name( const scene::Instance& instance, const char* name ){ GraphTreeModel* model = scene_graph_get_tree_model(); GraphTreeNode* parent = graph_tree_model_find_parent( model, instance.path() ); GraphTreeNode::iterator oldNode = parent->find( GraphTreeNode::key_type( node_get_name_safe( instance.path().top().get() ), instance.path().top().get_pointer() ) ); graph_tree_node_foreach_post( oldNode, ReferenceCaller1( *model ) ); GraphTreeNode* node( ( *oldNode ).second ); parent->erase( oldNode ); GraphTreeNode::iterator newNode = parent->insert( GraphTreeNode::value_type( GraphTreeNode::key_type( name, &instance.path().top().get() ), node ) ); graph_tree_node_foreach_pre( newNode, ReferenceCaller1( *model ) ); } void graph_tree_model_insert( GraphTreeModel* model, const scene::Instance& instance ){ GraphTreeNode* parent = graph_tree_model_find_parent( model, instance.path() ); GraphTreeNode::iterator i = parent->insert( GraphTreeNode::value_type( GraphTreeNode::key_type( node_get_name_safe( instance.path().top().get() ), instance.path().top().get_pointer() ), new GraphTreeNode( const_cast( instance ) ) ) ); graph_tree_model_row_inserted( model, i ); node_attach_name_changed_callback( instance.path().top(), ConstReferenceCaller1( instance ) ); } void graph_tree_model_erase( GraphTreeModel* model, const scene::Instance& instance ){ node_detach_name_changed_callback( instance.path().top(), ConstReferenceCaller1( instance ) ); GraphTreeNode* parent = graph_tree_model_find_parent( model, instance.path() ); GraphTreeNode::iterator i = parent->find( GraphTreeNode::key_type( node_get_name_safe( instance.path().top().get() ), instance.path().top().get_pointer() ) ); graph_tree_model_row_deleted( model, i ); GraphTreeNode* node( ( *i ).second ); parent->erase( i ); delete node; } #endif #if 0 class TestGraphTreeModel { public: TestGraphTreeModel(){ gtk_init( 0, 0 ); graph_type graph; scene::Node* root = *(scene::Node*)0xa0000000; scene::Node* node1 = (scene::Node*)0xa0000001; scene::Node* node2 = (scene::Node*)0xa0000002; scene::Node* node3 = (scene::Node*)0xa0000003; scene::Node* node4 = (scene::Node*)0xa0000004; scene::Instance* instance = (scene::Instance*)0xaaaaaaaa; scene::Path rootpath( root ); graph.insert( graph_type::value_type( rootpath, instance ) ); rootpath.push( node1 ); graph.insert( graph_type::value_type( rootpath, instance ) ); rootpath.pop(); rootpath.push( node2 ); graph.insert( graph_type::value_type( rootpath, instance ) ); rootpath.push( node3 ); graph.insert( graph_type::value_type( rootpath, instance ) ); rootpath.pop(); rootpath.push( node4 ); graph.insert( graph_type::value_type( rootpath, instance ) ); rootpath.pop(); rootpath.pop(); GtkTreeModel* model = GTK_TREE_MODEL( graph_tree_model_new( &graph ) ); { gint n_columns = gtk_tree_model_get_n_columns( model ); ASSERT_MESSAGE( n_columns == 2, "test failed!" ); } { GType type = gtk_tree_model_get_column_type( model, 0 ); ASSERT_MESSAGE( type == G_TYPE_POINTER, "test failed!" ); } { GType type = gtk_tree_model_get_column_type( model, 1 ); ASSERT_MESSAGE( type == G_TYPE_POINTER, "test failed!" ); } { GtkTreeIter iter; gtk_tree_model_get_iter_first( model, &iter ); graph_type::iterator i = graph_iterator_read_tree_iter( &iter ); ASSERT_MESSAGE( ( *i ).first.get().size() == 2 && ( *i ).first.get().top() == node1, "test failed!" ); } { GtkTreeIter iter; gtk_tree_model_get_iter_first( model, &iter ); ASSERT_MESSAGE( gtk_tree_model_iter_has_child( model, &iter ) == FALSE, "test failed!" ); ASSERT_MESSAGE( gtk_tree_model_iter_n_children( model, &iter ) == 0, "test failed!" ); gtk_tree_model_iter_next( model, &iter ); ASSERT_MESSAGE( gtk_tree_model_iter_has_child( model, &iter ) != FALSE, "test failed!" ); ASSERT_MESSAGE( gtk_tree_model_iter_n_children( model, &iter ) == 2, "test failed!" ); { GtkTreeIter child; gtk_tree_model_iter_nth_child( model, &child, &iter, 0 ); scene::Node* test; gtk_tree_model_get_value( model, &child, 0, (GValue*)&test ); ASSERT_MESSAGE( test == node3, "test failed!" ); { GtkTreeIter parent; gtk_tree_model_iter_parent( model, &parent, &child ); scene::Node* test; gtk_tree_model_get_value( model, &parent, 0, (GValue*)&test ); ASSERT_MESSAGE( test == node2, "test failed!" ); } } { GtkTreeIter child; gtk_tree_model_iter_nth_child( model, &child, &iter, 1 ); scene::Node* test; gtk_tree_model_get_value( model, &child, 0, (GValue*)&test ); ASSERT_MESSAGE( test == node4, "test failed!" ); } } { GtkTreeIter iter; std::size_t count = 0; for ( gboolean good = gtk_tree_model_get_iter_first( model, &iter ); good; good = gtk_tree_model_iter_next( model, &iter ) ) { scene::Node* test; gtk_tree_model_get_value( model, &iter, 0, (GValue*)&test ); ASSERT_MESSAGE( ( count == 0 && test == node1 ) || ( count == 1 && test == node2 ), "test failed!" ); ++count; } ASSERT_MESSAGE( count == 2, "test failed!" ); } { GtkTreeIter iter; gtk_tree_model_get_iter_first( model, &iter ); scene::Node* test; gtk_tree_model_get_value( model, &iter, 0, (GValue*)&test ); ASSERT_MESSAGE( test == node1, "test failed!" ); } { GtkTreeIter iter; GtkTreePath* path = ui::TreePath( "0" ); gtk_tree_model_get_iter( model, &iter, path ); gtk_tree_path_free( path ); graph_type::iterator i = graph_iterator_read_tree_iter( &iter ); ASSERT_MESSAGE( ( *i ).first.get().size() == 2 && ( *i ).first.get().top() == node1, "test failed!" ); } { GtkTreeIter iter; GtkTreePath* path = ui::TreePath( "1" ); gtk_tree_model_get_iter( model, &iter, path ); gtk_tree_path_free( path ); graph_type::iterator i = graph_iterator_read_tree_iter( &iter ); ASSERT_MESSAGE( ( *i ).first.get().size() == 2 && ( *i ).first.get().top() == node2, "test failed!" ); } { GtkTreeIter iter; graph_type::iterator i = graph.begin(); ++i; graph_iterator_write_tree_iter( i, &iter ); GtkTreePath* path = gtk_tree_model_get_path( model, &iter ); gint depth = gtk_tree_path_get_depth( path ); gint* indices = gtk_tree_path_get_indices( path ); ASSERT_MESSAGE( depth == 1 && indices[0] == 0, "test failed!" ); gtk_tree_path_free( path ); } { GtkTreeIter iter; graph_type::iterator i = graph.begin(); ++i; ++i; graph_iterator_write_tree_iter( i, &iter ); GtkTreePath* path = gtk_tree_model_get_path( model, &iter ); gint depth = gtk_tree_path_get_depth( path ); gint* indices = gtk_tree_path_get_indices( path ); ASSERT_MESSAGE( depth == 1 && indices[0] == 1, "test failed!" ); gtk_tree_path_free( path ); } } }; TestGraphTreeModel g_TestGraphTreeModel; #endif