/***************************************************************************************** // Truevision - a 3d modeler for gnome and povray // // matlib.cc // // Vincent LE PRINCE // Copyright (C) 2000-2005 Vincent LE PRINCE // This file is part of the TRUEVISION Package // This program 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. // // This program 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 this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ //******************************************************************************************* #include "include/matlib.h" #include "include/matlist.h" #include "include/preferences.h" #include "config.h" #include "include/tvio.h" #include #include #include #include #include #include const char *sep = "/"; bool compare_items( MatLibItem *item1, MatLibItem *item2 ) { return ( strcmp( item1->name, item2->name ) < 0 ) ? true : false; } bool compare_nodes( MatLibNode *node1, MatLibNode *node2 ) { return ( strcmp( node1->name, node2->name ) < 0 ) ? true : false; } //******************************************************* // Material library tree node // // Represent a category of material // ie a directory on the hard disk //******************************************************* app_objs *MatLibNode::app_ref = NULL; GtkWidget *MatLibNode::tree_view = NULL; GtkTreeStore *MatLibNode::tree_store = NULL; GtkTreeSelection *MatLibNode::tree_selection = NULL; //******************************************************** // Constructor // // Load the tree from a directory tree on filesystem //******************************************************** MatLibNode::MatLibNode( char *syspath, char *hpath, char *nom, app_objs *appref ) { app_ref = appref; // Directory name if ( nom == NULL ) { // Root char *matlibname = _("Material Library"); name = new char[ strlen(matlibname) +1 ]; strcpy( name, matlibname ); if ( syspath != NULL ) { systempath = new char[ strlen(syspath) + 1 ]; strcpy( systempath, syspath ); } else systempath = NULL; if ( hpath != NULL ) { homepath = new char[ strlen(hpath) + 1 ]; strcpy( homepath, hpath ); } else homepath = NULL; } else { // SubNode int nsize = strlen( nom ) +2; name = new char[nsize-1]; strcpy( name, nom ); if ( syspath != NULL ) { systempath = new char[ strlen(syspath) + nsize ]; strcpy( systempath, syspath ); strcat( systempath, sep ); strcat( systempath, nom ); } else systempath = NULL; if ( hpath != NULL ) { homepath = new char[ strlen(hpath) + nsize ]; strcpy( homepath, hpath ); strcat( homepath, sep ); strcat( homepath, nom ); } else homepath = NULL; } // Load files and subdirectories list char *path[2] = { systempath, homepath }; for ( int i = 0 ; i < 2 ; i++ ) { if ( path[i] == NULL ) continue; int psize = strlen( path[i] ) + 2; DIR *dir = opendir( path[i] ); if ( dir == NULL ) continue; struct dirent *dp; do { dp = readdir( dir ); if ( dp == NULL ) break; if ( dp->d_name[0] == '.' ) continue; char *tmp = new char[ psize + strlen( dp->d_name ) ]; strcpy( tmp, path[i] ); strcat( tmp, sep ); strcat( tmp, dp->d_name ); DIR *subdir = opendir( tmp ); if ( subdir == NULL ) { MatLibItem *item = new MatLibItem( tmp, dp->d_name, app_ref ); Children.push_back( item ); } else { closedir( subdir ); bool exist = false; for ( register unsigned int i = 0 ; i < SubNodes.size() ; i ++ ) if ( !strcmp( SubNodes[i]->name, dp->d_name ) ) { exist = true ; break; } if ( ! exist ) { MatLibNode *node = new MatLibNode( path[0], path[1], dp->d_name, app_ref ); SubNodes.push_back( node ); } } delete tmp; } while ( dp != NULL ); closedir( dir ); } // Sort the list sort( Children.begin(), Children.end(), compare_items ); sort( SubNodes.begin(), SubNodes.end(), compare_nodes ); } //************************************************************* // Destructor // // Delete trees //************************************************************* MatLibNode::~MatLibNode() { // TREE !! delete name; if ( homepath != NULL ) delete homepath; if ( systempath != NULL ) delete systempath; } //************************************************************** // Add to tree // // Add the node to the tree view //************************************************************** void MatLibNode::add_to_tree( GtkWidget *view, GtkTreeStore *store, GtkTreeSelection *selection, GtkTreeIter *parent ) { tree_view = view; tree_store = store; tree_selection = selection; if ( parent != NULL ) gtk_tree_store_insert_before( tree_store, &node_iter, parent, NULL ); else gtk_tree_store_append( tree_store, &node_iter, NULL ); gtk_tree_store_set( tree_store, &node_iter, 0, name, 1, this, -1); for ( register unsigned int i = 0 ; i < SubNodes.size() ; i ++ ) SubNodes[i]->add_to_tree( view, store, selection, &node_iter ); } //************************************************************* // Expand // // expand node ( for root ) //************************************************************ void MatLibNode::expand() { GtkTreePath *path = gtk_tree_model_get_path( GTK_TREE_MODEL(tree_store), &node_iter ); gtk_tree_view_expand_row( GTK_TREE_VIEW(tree_view), path, FALSE ); } //************************************************************* // Draw thumbs // // Draw thumbnail preview for each item //************************************************************ void MatLibNode::draw_thumbs( GtkWidget *view, GtkListStore *store, GtkWidget *progress, GtkWidget *label ) { const char *mesg1 = N_("Display"); char *mesg = new char[ strlen( mesg1 ) + strlen(name) + 10 ]; sprintf( mesg, " %s %s ...", mesg1, name ); gtk_label_set_text( GTK_LABEL(label), mesg ); delete mesg; unsigned int node_size = Children.size(); gtk_list_store_clear( store ); for ( unsigned int i = 0 ; i < node_size ; i++ ) { Children[i]->draw_thumb( view, store ); gtk_progress_bar_set_fraction( GTK_PROGRESS_BAR(progress), (float)i/(float)node_size ); while ( gtk_events_pending() > 0 ) gtk_main_iteration(); } gtk_progress_bar_set_fraction( GTK_PROGRESS_BAR(progress), 0 ); const char *mesg2 = N_("Node :"); char *mesg3 = new char[ strlen( mesg1 ) + strlen(name) + 50 ]; sprintf( mesg3, " %s %s , %u materials", mesg2, name, node_size ); gtk_label_set_text( GTK_LABEL(label), mesg3 ); delete mesg3; } //******************************************************* // Material Library item //******************************************************* app_objs *MatLibItem::app_ref = NULL; //****************************************************** // Constructor //****************************************************** MatLibItem::MatLibItem( char *file, char *nom, app_objs *appref ) { app_ref = appref; path = new char[ strlen( file ) +1 ]; strcpy( path, file ); name = new char[ strlen( nom ) + 1 ]; strcpy( name, nom ); real_name = NULL; comment = NULL; author = NULL; } //****************************************************** // Destructor //****************************************************** MatLibItem::~MatLibItem() { delete path; delete name; } //***************************************************** // Draw thumb // // Get the preview from material file and add it to icon view //****************************************************** void MatLibItem::draw_thumb( GtkWidget *view, GtkListStore *store ) { // Get the preview buffer GdkPixbuf *image_buffer = NULL; // Open file and initialize bool has_preview = true; ifstream file( path, ios::binary ); if ( ! file ) has_preview = false; char * tag = NULL; char *val = NULL; unsigned long size = 0; // read the material file do { if ( !has_preview ) break; tag = tvio_get_next_tag( file ); if ( tag == NULL ) { has_preview = false; break; } // Look for a preview tag if ( ! strcmp( "PREVIEW", tag ) ) { do { val = tvio_get_next_val( file ); if ( val == NULL ) break; if ( ! strcmp( val, "SIZE" ) ) { size = tvio_get_value_as_int( file ); continue; } if ( !strcmp( val, "DATA" ) ) { if ( size == 0 ) { has_preview = false; break; } char *zdata = new char[ size+1 ]; for ( unsigned long i = 0 ; i < size ; i ++ ) zdata[i] = file.get(); unsigned long destlen = 19200; data = new char[ destlen ]; if ( uncompress( (Bytef*)data, (uLongf*)&destlen, (Bytef*)zdata, size ) != Z_OK ) { has_preview= false; break; } image_buffer = gdk_pixbuf_new_from_data( (guchar*)data, GDK_COLORSPACE_RGB, FALSE, 8, 80, 80, 79*3, (sign_thumb_data_free), this ); delete zdata; break; } } while( val != NULL ); break; tvio_skip_section( file ); } if ( !strcmp( tag, "MATERIAL" ) ) { has_preview = false; break; } tvio_skip_section( file ); } while( tag != NULL ); file.close(); // If we don't have a preview use a default icon if ( !has_preview ) { char *fname = tv_get_pixmap( "nopreview.xpm" ); image_buffer = gdk_pixbuf_new_from_file( fname, NULL ); delete fname; } // Get the name of the material int len = strlen( name ); char labtext[ len +1 ]; strcpy( labtext, name ); labtext[ len - 4 ] = '\0'; GtkTreeIter node_iter; gtk_list_store_append( store, &node_iter ); gtk_list_store_set( store, &node_iter, 0, image_buffer, 1, labtext, 2, this, -1); } //*********************************************************** // Load // // Load the material contained in the material file pointed by item //*********************************************************** void MatLibItem::load() { MATLIST_DEF matlist->mat_load_from_file( path ); } //*********************************************************** // Show infos // // Display informations about the material //*********************************************************** void MatLibItem::show_info(TvWidget_entry *name_label, TvWidget_entry *author_label, TvWidget_text *description ) { ifstream file( path, ios::binary ); if ( ! file ) return; char * tag = NULL; char *val = NULL; do { tag = tvio_get_next_tag( file ); if ( tag == NULL ) { break; } if (author_label->load( file , tag ) ) continue; if (description->load( file , tag ) ) continue; if ( ! strcmp( "MATERIAL", tag ) ) { do { val = tvio_get_next_tag( file ); if ( val == NULL ) break; if (name_label->load( file , val ) ) continue; tvio_skip_section( file ); } while( val != NULL ); } if ( ! strcmp( "PREVIEW", tag ) ) { do { val = tvio_get_next_val( file ); if ( val == NULL ) break; if ( ! strcmp( val, "SIZE" ) ) { int size = tvio_get_value_as_int( file ); file.seekg( size+6, ios::cur ); break; } } while( val != NULL ); tvio_skip_section( file ); continue; } tvio_skip_section( file ); } while( tag != NULL ); file.close(); author_label->update_widget(); name_label->update_widget(); description->update_widget(); } //******************************************************* // The Material library //******************************************************* //****************************************************** // Constructor //****************************************************** MatLib::MatLib( app_objs *appref ) { // Initialisations app_ref = appref; app_ref->matlib = this; dialog = NULL; PREF_DEF freeze = false; // Path to system wide material repository char *tmp = tv_get_data( "truevision/materials" ); if ( tmp != NULL ) { systemlib = new char[strlen( tmp ) + 1]; strcpy( systemlib, tmp ); delete tmp; } else systemlib = NULL; // Path to user material repository char *home = pref->get_home_dir(); if ( home != NULL ) { homelib = new char[ strlen(pref->get_home_dir()) + 12 ]; strcpy( homelib, pref->get_home_dir() ); strcat( homelib, "/materials" ); if ( access( homelib, F_OK ) == -1 ) mkdir( homelib, S_IRWXU ); } else homelib = NULL; // Create the material tree :) root = new MatLibNode( systemlib, homelib, NULL, app_ref ); } //****************************************************** // Destructor //****************************************************** MatLib::~MatLib() { if ( homelib != NULL ) delete homelib; if ( systemlib != NULL ) delete systemlib; delete root; } //********************************************************** // Raise dialog box // // open the material library dialog box //********************************************************** void MatLib::raise_dlg_box() { // Show it if already exists if ( GTK_IS_DIALOG(dialog) ) { gdk_window_raise( dialog->window ); return; } PREF_DEF bool tt = pref->tooltips->value(); // Create the dialog box dialog = gtk_dialog_new_with_buttons( _("Material Library"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, GTK_STOCK_HELP, GTK_RESPONSE_HELP, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,NULL ); g_signal_connect( G_OBJECT(dialog), "response", G_CALLBACK(sign_matlib_dlg_click), this ); g_signal_connect( G_OBJECT(dialog), "close", G_CALLBACK(sign_matlib_dlg_destroy), this ); // restore geometry if wanted if ( pref->save_dlg_geo->value() ) { if ( pref->mlib_xpos->value() < 0 ) pref->mlib_xpos->set(0); if ( pref->mlib_ypos->value() < 0 ) pref->mlib_ypos->set(0); gtk_widget_set_uposition( dialog, pref->mlib_xpos->value(), pref->mlib_ypos->value() ); gtk_window_set_default_size( GTK_WINDOW(dialog), pref->mlib_xsize->value(), pref->mlib_ysize->value() ); } GtkWidget *hbox = gtk_hbox_new( FALSE, 2 ); gtk_box_pack_start( GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 2 ); // The selection tree GtkWidget *scrolled = gtk_scrolled_window_new( NULL, NULL ); gtk_widget_set_usize( scrolled, 250, -1 ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_box_pack_start( GTK_BOX(hbox), scrolled, FALSE, TRUE, 4 ); material_tree_store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER, -1 ); material_tree_view = gtk_tree_view_new_with_model( GTK_TREE_MODEL(material_tree_store) ); gtk_container_add( GTK_CONTAINER(scrolled), material_tree_view ); GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes ( N_("Categories"), renderer, "text", 0, NULL); gtk_tree_view_append_column( GTK_TREE_VIEW(material_tree_view), column ); material_tree_selection = gtk_tree_view_get_selection( GTK_TREE_VIEW (material_tree_view) ); gtk_tree_selection_set_mode ( material_tree_selection, GTK_SELECTION_SINGLE ); g_signal_connect( G_OBJECT(material_tree_selection), "changed", G_CALLBACK(sign_matlib_tree_select), this ); gtk_signal_connect( GTK_OBJECT(material_tree_view), "row-activated", GTK_SIGNAL_FUNC(sign_matlib_tree_doubleclick), this ); root->add_to_tree( material_tree_view, material_tree_store, material_tree_selection, NULL ); // Icon view GtkWidget *vbox = gtk_vbox_new( FALSE, 3 ); gtk_box_pack_start( GTK_BOX(hbox), vbox, TRUE, TRUE, 2 ); GtkWidget *iframe = gtk_frame_new( NULL ); gtk_box_pack_start( GTK_BOX(vbox), iframe, TRUE, TRUE, 4 ); GtkWidget *ibox = gtk_vbox_new( FALSE, 3 ); gtk_container_set_border_width( GTK_CONTAINER(ibox), 3 ); gtk_container_add( GTK_CONTAINER(iframe), ibox ); // Thumbs scrolled = gtk_scrolled_window_new( NULL, NULL ); gtk_widget_set_usize( scrolled, 600, 280 ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_box_pack_start( GTK_BOX(ibox), scrolled, TRUE, TRUE, 4 ); material_icon_store = gtk_list_store_new( 3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER, -1 ); material_icon_view = gtk_icon_view_new_with_model( GTK_TREE_MODEL(material_icon_store) ); gtk_container_add( GTK_CONTAINER(scrolled), material_icon_view ); gtk_icon_view_set_text_column( GTK_ICON_VIEW(material_icon_view), 1 ); gtk_icon_view_set_pixbuf_column( GTK_ICON_VIEW(material_icon_view), 0 ); gtk_icon_view_set_selection_mode ( GTK_ICON_VIEW(material_icon_view), GTK_SELECTION_MULTIPLE ); gtk_signal_connect( GTK_OBJECT(material_icon_view), "selection-changed", GTK_SIGNAL_FUNC(sign_matlib_icon_select), this ); gtk_signal_connect( GTK_OBJECT(material_icon_view), "item-activated", GTK_SIGNAL_FUNC(sign_matlib_icon_doubleclick), this ); // Statusbar GtkWidget *stat_box = gtk_hbox_new( FALSE, 2 ); gtk_box_pack_start( GTK_BOX(ibox), stat_box, FALSE, TRUE, 2 ); progress_bar = gtk_progress_bar_new( ); gtk_box_pack_start( GTK_BOX(stat_box), progress_bar, FALSE, TRUE, 2 ); label = gtk_label_new( NULL ); gtk_box_pack_start( GTK_BOX(stat_box), label, FALSE, TRUE, 2 ); // Description GtkWidget *dframe = gtk_frame_new( NULL ); gtk_box_pack_start( GTK_BOX(vbox), dframe, FALSE, TRUE, 4 ); GtkWidget *dbox = gtk_vbox_new( FALSE, 5 ); gtk_container_set_border_width( GTK_CONTAINER(dbox), 3 ); gtk_container_add( GTK_CONTAINER(dframe), dbox ); name_label = new TvWidget_entry( _("Name"), "NAME", NULL, app_ref ); name_label->get_widget( dbox, tt ); author_label = new TvWidget_entry( _("Author"), "AUTHOR", NULL, app_ref ); author_label ->get_widget( dbox, tt ); description = new TvWidget_text( _("Comment"), "COMMENT", NULL, app_ref ); description->get_widget( dbox, tt ); gtk_widget_show_all(dialog); root->expand(); root->select(); } //*********************************************************** // Dialog clicked // // Dialog box callback //********************************************************** void MatLib::clicked_dlg( int button ) { switch( button ) { // Load & Close case GTK_RESPONSE_OK: load_selection(); clicked_dlg( -1 ); break; // Load ( apply ) case GTK_RESPONSE_APPLY: load_selection(); break; // Help case GTK_RESPONSE_HELP: truevision_help_display( "truevision.xml", "sect-gui-matlibrary", NULL ); break; // Close default: case GTK_RESPONSE_CLOSE: PREF_DEF if ( pref->save_dlg_geo->value() ) { gint x,y; gdk_window_get_root_origin( dialog->window, &x, &y ); pref->mlib_xpos->set( x ); pref->mlib_ypos->set( y ); pref->mlib_xsize->set( dialog->allocation.width ); pref->mlib_ysize->set( dialog->allocation.height ); } gtk_widget_destroy( dialog ); delete name_label; delete author_label; delete description; dialog = NULL; break; } } //******************************************************************* // Tree clicked // // Tree selection callback //******************************************************************* void MatLib::tree_select( GtkTreeSelection *selection ) { // Check if tree is currently freezed ( in update ) if ( freeze ) return; freeze = true; GtkTreeIter iter; GtkTreeModel *model; gpointer node_pointer = NULL; if (gtk_tree_selection_get_selected (selection, &model, &iter)) gtk_tree_model_get (model, &iter, 1, &node_pointer, -1); if ( node_pointer == NULL ) return; selected_node = (MatLibNode*)node_pointer; selected_node->draw_thumbs( material_icon_view, material_icon_store, progress_bar, label ); while ( gtk_events_pending() > 0 ) gtk_main_iteration(); freeze = false; } //******************************************************************* // Tree double clicked // // Tree ctivation callback : collapse / expand node //******************************************************************* void MatLib::tree_doubleclick() { GtkTreeIter iter; GtkTreeModel *model; gtk_tree_selection_get_selected (material_tree_selection, &model, &iter); GtkTreePath *path = gtk_tree_model_get_path( model, &iter ); if ( path == NULL ) return; bool isexp = gtk_tree_view_row_expanded( GTK_TREE_VIEW(material_tree_view), path ); if ( isexp ) gtk_tree_view_collapse_row( GTK_TREE_VIEW(material_tree_view), path ); else gtk_tree_view_expand_row( GTK_TREE_VIEW(material_tree_view), path, FALSE ); } //******************************************************************* // Icon select // // Material selection callback //******************************************************************* void MatLib::icon_select( ) { // Get first item in selection GList *list = gtk_icon_view_get_selected_items( GTK_ICON_VIEW(material_icon_view) ); list = g_list_first( list ); if ( list == NULL ) return; GtkTreePath *path = (GtkTreePath*)list->data; GtkTreeIter iter; gtk_tree_model_get_iter( GTK_TREE_MODEL(material_icon_store), &iter, path ); gpointer matitem_ptr; MatLibItem *selection = NULL; gtk_tree_model_get ( GTK_TREE_MODEL(material_icon_store), &iter, 2, &matitem_ptr, -1); if ( matitem_ptr != NULL ) selection = (MatLibItem*)matitem_ptr; // Display infos about selection if ( selection != NULL ) selection->show_info( name_label, author_label, description ); // Clear selection while ( list->next != NULL ) { gtk_tree_path_free( (GtkTreePath*)list->data ); list = g_list_next( list ); } gtk_tree_path_free( (GtkTreePath*)list->data ); g_list_free (list); } //******************************************************************* // Load selection // // Load selected materials //******************************************************************* void MatLib::load_selection( ) { // Get first item in selection GList *list = gtk_icon_view_get_selected_items( GTK_ICON_VIEW(material_icon_view) ); list = g_list_first( list ); if ( list == NULL ) return; // Load each item of the list, and clear the list while( 1 ) { GtkTreePath *path = (GtkTreePath*)list->data; GtkTreeIter iter; gtk_tree_model_get_iter( GTK_TREE_MODEL(material_icon_store), &iter, path ); gpointer matitem_ptr; MatLibItem *selection = NULL; gtk_tree_model_get ( GTK_TREE_MODEL(material_icon_store), &iter, 2, &matitem_ptr, -1); if ( matitem_ptr != NULL ) selection = (MatLibItem*)matitem_ptr; if ( selection != NULL ) selection->load(); gtk_tree_path_free( path ); if ( list-> next != NULL ) list = g_list_next( list ); else break; } g_list_free (list); } //******************************************************************* // Icon douibleclick // // Material selection callback //******************************************************************* void MatLib::icon_doubleclick( GtkTreePath *path ) { // get selection GtkTreeIter iter; gtk_tree_model_get_iter( GTK_TREE_MODEL(material_icon_store), &iter, path ); gpointer matitem_ptr; MatLibItem *selection = NULL; gtk_tree_model_get ( GTK_TREE_MODEL(material_icon_store), &iter, 2, &matitem_ptr, -1); if ( matitem_ptr != NULL ) selection = (MatLibItem*)matitem_ptr; // Load selection if ( selection == NULL ) return; selection->load(); }