/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* e-tree-example-2.c - Test program for directory reading in the GNOME Virtual File System. Copyright (C) 1999 Free Software Foundation The Gnome Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The Gnome Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the Gnome Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Author: Ettore Perazzoli */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "e-hpaned.h" #include "gal/e-util/e-cursors.h" #include "e-table-header.h" #include "e-table-header-item.h" #include "e-table-item.h" #include "e-cell-text.h" #include "e-cell-tree.h" #include "e-cell-checkbox.h" #include "e-table-scrolled.h" #include "e-tree-simple.h" #include "art/tree-expanded.xpm" #include "art/tree-unexpanded.xpm" GdkPixbuf *tree_expanded_pixbuf; GdkPixbuf *tree_unexpanded_pixbuf; #define MINI_ICON_SIZE 16 #define RIGHT_COLS 4 #define LEFT_COLS 4 #define RIGHT_E_TABLE_SPEC \ " \ \ 0 \ 1 \ 2 \ 3 \ \ \ " #define LEFT_E_TABLE_SPEC \ " \ \ 0 \ \ \ " char *left_headers [LEFT_COLS] = { "Folder" }; char *right_headers [RIGHT_COLS] = { "Name", "Size", "Type", "Mime Type" }; GnomeVFSDirectoryFilter *left_filter; GnomeVFSDirectoryFilter *right_filter; GHashTable *mime_type_to_pixbuf; GnomeVFSDirectoryList *right_list = NULL; ETreePath *right_root; ETreeModel *right_model = NULL; ETreeModel *left_model = NULL; typedef struct { char *node_uri; GnomeVFSFileInfo *info; /* next two used only if the node is a directory */ /* used if the node is expanded */ GnomeVFSDirectoryList *list; /* used if the node is collapsed */ ETreePath *placeholder; } VFSInfo; /* * ETreeSimple callbacks * These are the callbacks that define the behavior of our custom model. */ static gchar * type_to_string (GnomeVFSFileType type) { switch (type) { case GNOME_VFS_FILE_TYPE_UNKNOWN: return "Unknown"; case GNOME_VFS_FILE_TYPE_REGULAR: return "Regular"; case GNOME_VFS_FILE_TYPE_DIRECTORY: return "Directory"; case GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK: return "Symbolic Link"; case GNOME_VFS_FILE_TYPE_FIFO: return "FIFO"; case GNOME_VFS_FILE_TYPE_SOCKET: return "Socket"; case GNOME_VFS_FILE_TYPE_CHARACTER_DEVICE: return "Character device"; case GNOME_VFS_FILE_TYPE_BLOCK_DEVICE: return "Block device"; default: return "???"; } } /* This function returns the value at a particular point in our ETreeModel. */ static void * tree_value_at (ETreeModel *etm, ETreePath *path, int col, void *model_data) { VFSInfo *vfs_info = e_tree_model_node_get_data (etm, path); switch (col) { case 0: /* filename */ if (vfs_info->info) return vfs_info->info->name; else return vfs_info->node_uri; case 1: /* size */ { static char buf[15]; if (vfs_info->info) { if (vfs_info->info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) return ""; else { g_snprintf (buf, sizeof(buf), "%ld", (glong) vfs_info->info->size); return buf; } } else return ""; } case 2: /* file type */ if (vfs_info->info) return type_to_string (vfs_info->info->type); else return ""; case 3: /* mime type */ if (vfs_info->info) { const char *mime_type = gnome_vfs_file_info_get_mime_type (vfs_info->info); if (mime_type == NULL) mime_type = "(Unknown)"; return (void*)mime_type; } else { return ""; } default: return ""; } } /* This function returns the number of columns in our ETableModel. */ static int tree_col_count (ETableModel *etc, void *data) { return RIGHT_COLS; } /* This function duplicates the value passed to it. */ static void * tree_duplicate_value (ETableModel *etc, int col, const void *value, void *data) { return g_strdup (value); } /* This function frees the value passed to it. */ static void tree_free_value (ETableModel *etc, int col, void *value, void *data) { g_free (value); } /* This function creates an empty value. */ static void * tree_initialize_value (ETableModel *etc, int col, void *data) { return g_strdup (""); } /* This function reports if a value is empty. */ static gboolean tree_value_is_empty (ETableModel *etc, int col, const void *value, void *data) { return !(value && *(char *)value); } /* This function reports if a value is empty. */ static char * tree_value_to_string (ETableModel *etc, int col, const void *value, void *data) { return g_strdup(value); } static GdkPixbuf * tree_icon_at (ETreeModel *etm, ETreePath *path, void *model_data) { VFSInfo *vfs_info = e_tree_model_node_get_data (etm, path); const char *mime_type; GdkPixbuf *scaled_pixbuf = NULL; if (vfs_info->info == NULL) return NULL; mime_type = gnome_vfs_file_info_get_mime_type (vfs_info->info); if (mime_type == NULL) mime_type = "(Unknown)"; scaled_pixbuf = g_hash_table_lookup (mime_type_to_pixbuf, mime_type); if (!scaled_pixbuf) { const char *icon_filename = gnome_vfs_mime_get_icon (mime_type); if (icon_filename) { GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (icon_filename); if (pixbuf) { scaled_pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (pixbuf), gdk_pixbuf_get_has_alpha (pixbuf), gdk_pixbuf_get_bits_per_sample (pixbuf), MINI_ICON_SIZE, MINI_ICON_SIZE); gdk_pixbuf_scale (pixbuf, scaled_pixbuf, 0, 0, MINI_ICON_SIZE, MINI_ICON_SIZE, 0.0, 0.0, (double) MINI_ICON_SIZE / gdk_pixbuf_get_width (pixbuf), (double) MINI_ICON_SIZE / gdk_pixbuf_get_height (pixbuf), GDK_INTERP_HYPER); g_hash_table_insert (mime_type_to_pixbuf, (char*)mime_type, scaled_pixbuf); gdk_pixbuf_unref (pixbuf); } } } return scaled_pixbuf; } /* This function sets the value at a particular point in our ETreeModel. */ static void tree_set_value_at (ETreeModel *etm, ETreePath *path, int col, const void *val, void *model_data) { } /* This function returns whether a particular cell is editable. */ static gboolean tree_is_editable (ETreeModel *etm, ETreePath *path, int col, void *model_data) { return FALSE; } static void sort_list (GnomeVFSDirectoryList *list) { GnomeVFSDirectorySortRule rules[] = { GNOME_VFS_DIRECTORY_SORT_DIRECTORYFIRST, GNOME_VFS_DIRECTORY_SORT_BYNAME, GNOME_VFS_DIRECTORY_SORT_NONE }; gnome_vfs_directory_list_sort (list, FALSE, rules); } static void node_collapsed (ETreeModel *etm, ETreePath *path, void *data) { VFSInfo *vfs_info = e_tree_model_node_get_data (etm, path); char *name; ETreePath **paths; int num_children, i; if (vfs_info->info) name = vfs_info->info->name; else name = vfs_info->node_uri; gnome_vfs_directory_list_destroy (vfs_info->list); /* remove the children of this node, and replace the placeholder */ num_children = e_tree_model_node_get_children (etm, path, &paths); for (i = 0; i < num_children; i ++) e_tree_model_node_remove (etm, paths[i]); vfs_info->placeholder = e_tree_model_node_insert (etm, path, 0, NULL); } static void node_expanded (ETreeModel *etm, ETreePath *path, gboolean *allow_expand, void *data) { VFSInfo *vfs_info = e_tree_model_node_get_data (etm, path); GnomeVFSFileInfo *info; GnomeVFSResult result; char *name; if (vfs_info->info) name = vfs_info->info->name; else name = vfs_info->node_uri; /* Load with no filters and without requesting any metadata. */ result = gnome_vfs_directory_list_load (&vfs_info->list, vfs_info->node_uri, (GNOME_VFS_FILE_INFO_GET_MIME_TYPE | GNOME_VFS_FILE_INFO_FORCE_FAST_MIME_TYPE | GNOME_VFS_FILE_INFO_FOLLOW_LINKS), left_filter); *allow_expand = (result == GNOME_VFS_OK); if (!*allow_expand) { char *buf = g_strdup_printf ("Cannot open directory %s : %s\n", vfs_info->info->name, gnome_vfs_result_to_string (result)); gnome_error_dialog (buf); g_free (buf); return; } sort_list (vfs_info->list); /* remove the placeholder and insert all the children of this node */ e_tree_model_node_remove (etm, vfs_info->placeholder); info = gnome_vfs_directory_list_first (vfs_info->list); if (info == NULL) { /* no files */ return; } while (info != NULL) { if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) { ETreePath *new_node; VFSInfo *new_vfs_info = g_new0(VFSInfo, 1); new_vfs_info->info = info; new_node = e_tree_model_node_insert (etm, path, -1, new_vfs_info); new_vfs_info->placeholder = e_tree_model_node_insert (etm, new_node, -1, NULL); new_vfs_info->node_uri = g_strdup_printf("%s%s/", vfs_info->node_uri, info->name); } info = gnome_vfs_directory_list_next (vfs_info->list); } } static void on_cursor_change (ETable *table, int row, gpointer user_data) { int num_children, i; GnomeVFSFileInfo *info; GnomeVFSResult result; ETreePath **paths; ETreePath *left_path = e_tree_model_node_at_row (left_model, row); VFSInfo *vfs_info = e_tree_model_node_get_data (left_model, left_path); if (right_list) { gnome_vfs_directory_list_destroy (right_list); right_list = NULL; } /* remove the children of this node, and replace the placeholder */ num_children = e_tree_model_node_get_children (right_model, right_root, &paths); for (i = 0; i < num_children; i ++) e_tree_model_node_remove (right_model, paths[i]); /* Load with no filters and without requesting any metadata. */ result = gnome_vfs_directory_list_load (&right_list, vfs_info->node_uri, (GNOME_VFS_FILE_INFO_GET_MIME_TYPE | GNOME_VFS_FILE_INFO_FORCE_FAST_MIME_TYPE | GNOME_VFS_FILE_INFO_FOLLOW_LINKS), right_filter); if (result != GNOME_VFS_OK) return; info = gnome_vfs_directory_list_first (right_list); if (!info) return; while (info != NULL) { ETreePath *new_node; VFSInfo *new_vfs_info = g_new0(VFSInfo, 1); new_vfs_info->info = info; new_node = e_tree_model_node_insert (right_model, right_root, -1, new_vfs_info); new_vfs_info->node_uri = g_strdup_printf("%s%s", vfs_info->node_uri, info->name); info = gnome_vfs_directory_list_next (right_list); } } static void on_double_click (ETable *etable, int row, void *data) { GnomeVFSMimeApplication *app; ETreePath *path = e_tree_model_node_at_row (right_model, row); VFSInfo *vfs_info = e_tree_model_node_get_data (right_model, path); const char *mime_type = gnome_vfs_file_info_get_mime_type (vfs_info->info); if (mime_type == NULL) mime_type = "(Unknown)"; app = gnome_vfs_mime_get_default_application (mime_type); if (app) printf ("exec %s\n", app->command); else printf ("no command for mime type %s\n", mime_type); } /* create the table on the right */ static GtkWidget* create_right_tree(GtkWidget *paned) { GtkWidget *e_table; GtkWidget *frame; ECell *cell_left_just; ECell *cell_tree; ETableHeader *e_table_header; int i; right_filter = gnome_vfs_directory_filter_new (GNOME_VFS_DIRECTORY_FILTER_NONE, GNOME_VFS_DIRECTORY_FILTER_NODIRS, NULL); /* here we create our model. This uses the functions we defined earlier. */ right_model = e_tree_simple_new (tree_col_count, tree_duplicate_value, tree_free_value, tree_initialize_value, tree_value_is_empty, tree_value_to_string, tree_icon_at, tree_value_at, tree_set_value_at, tree_is_editable, NULL); /* create the unexpanded root node and it's placeholder child. */ right_root = e_tree_model_node_insert (right_model, NULL, 0, NULL); e_tree_model_root_node_set_visible (right_model, FALSE); /* * Next we create a header. The ETableHeader is used in two * different way. The first is the full_header. This is the * list of possible columns in the view. The second use is * completely internal. Many of the ETableHeader functions are * for that purpose. The only functions we really need are * e_table_header_new and e_table_header_add_col. * * First we create the header. */ e_table_header = e_table_header_new (); /* * Next we have to build renderers for all of the columns. * Since all our columns are text columns, we can simply use * the same renderer over and over again. If we had different * types of columns, we could use a different renderer for * each column. */ cell_left_just = e_cell_text_new (E_TABLE_MODEL(right_model), NULL, GTK_JUSTIFY_LEFT); /* * This renderer is used for the tree column (the leftmost one), and * has as its subcell renderer the text renderer. this means that * text is displayed to the right of the tree pipes. */ cell_tree = e_cell_tree_new (E_TABLE_MODEL(right_model), tree_expanded_pixbuf, tree_unexpanded_pixbuf, TRUE, cell_left_just); /* * Next we create a column object for each view column and add * them to the header. We don't create a column object for * the importance column since it will not be shown. */ for (i = 0; i < RIGHT_COLS; i++) { /* Create the column. */ ETableCol *ecol = e_table_col_new ( i, right_headers [i], 80, 20, i == 0 ? cell_tree : cell_left_just, g_str_compare, TRUE); /* Add it to the header. */ e_table_header_add_column (e_table_header, ecol, i); } /* This frame is simply to get a bevel around our table. */ frame = gtk_frame_new (NULL); /* * Here we create the table. We give it the three pieces of * the table we've created, the header, the model, and the * initial layout. It does the rest. */ e_table = e_table_scrolled_new (e_table_header, E_TABLE_MODEL(right_model), RIGHT_E_TABLE_SPEC); gtk_signal_connect (GTK_OBJECT (e_table), "double_click", GTK_SIGNAL_FUNC (on_double_click), NULL); /* Build the gtk widget hierarchy. */ gtk_container_add (GTK_CONTAINER (frame), e_table); gtk_container_add (GTK_CONTAINER (paned), frame); return e_table; } /* We create a window containing our new tree. */ static GtkWidget * create_left_tree (GtkWidget *paned, char *root_uri) { GtkWidget *scrolled; GtkWidget *e_table; GtkWidget *frame; ECell *cell_left_just; ECell *cell_tree; ETableHeader *e_table_header; ETreePath *root_node; VFSInfo *root_vfs_info; ETableCol *ecol; left_filter = gnome_vfs_directory_filter_new (GNOME_VFS_DIRECTORY_FILTER_NONE, /* putting DIRSONLY doesn't work here, so we filter files out in node_expanded. */ (GNOME_VFS_DIRECTORY_FILTER_NOSELFDIR | GNOME_VFS_DIRECTORY_FILTER_NOPARENTDIR), NULL); /* here we create our model. This uses the functions we defined earlier. */ left_model = e_tree_simple_new (tree_col_count, tree_duplicate_value, tree_free_value, tree_initialize_value, tree_value_is_empty, tree_value_to_string, tree_icon_at, tree_value_at, tree_set_value_at, tree_is_editable, NULL); /* catch collapsed/expanded signals */ gtk_signal_connect (GTK_OBJECT (left_model), "node_expanded", GTK_SIGNAL_FUNC (node_expanded), NULL); gtk_signal_connect (GTK_OBJECT (left_model), "node_collapsed", GTK_SIGNAL_FUNC (node_collapsed), NULL); /* create the unexpanded root node and it's placeholder child. */ root_vfs_info = g_new0 (VFSInfo, 1); root_vfs_info->node_uri = g_strdup (root_uri); root_vfs_info->info = gnome_vfs_file_info_new(); gnome_vfs_get_file_info (root_uri, root_vfs_info->info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE | GNOME_VFS_FILE_INFO_FORCE_FAST_MIME_TYPE | GNOME_VFS_FILE_INFO_FOLLOW_LINKS); root_node = e_tree_model_node_insert (left_model, NULL, 0, root_vfs_info); e_tree_model_node_set_expanded (left_model, root_node, FALSE); root_vfs_info->placeholder = e_tree_model_node_insert (left_model, root_node, 0, NULL); e_tree_model_root_node_set_visible (left_model, TRUE); /* * Next we create a header. The ETableHeader is used in two * different way. The first is the full_header. This is the * list of possible columns in the view. The second use is * completely internal. Many of the ETableHeader functions are * for that purpose. The only functions we really need are * e_table_header_new and e_table_header_add_col. * * First we create the header. */ e_table_header = e_table_header_new (); /* * Next we have to build renderers for all of the columns. * Since all our columns are text columns, we can simply use * the same renderer over and over again. If we had different * types of columns, we could use a different renderer for * each column. */ cell_left_just = e_cell_text_new (E_TABLE_MODEL(left_model), NULL, GTK_JUSTIFY_LEFT); /* * This renderer is used for the tree column (the leftmost one), and * has as its subcell renderer the text renderer. this means that * text is displayed to the right of the tree pipes. */ cell_tree = e_cell_tree_new (E_TABLE_MODEL(left_model), tree_expanded_pixbuf, tree_unexpanded_pixbuf, TRUE, cell_left_just); /* Create the column. */ ecol = e_table_col_new (0, left_headers [0], 80, 20, cell_tree, g_str_compare, TRUE); e_table_header_add_column (e_table_header, ecol, 0); /* This frame is simply to get a bevel around our table. */ frame = gtk_frame_new (NULL); /* * Here we create the table. We give it the three pieces of * the table we've created, the header, the model, and the * initial layout. It does the rest. */ e_table = e_table_new (e_table_header, E_TABLE_MODEL(left_model), LEFT_E_TABLE_SPEC); gtk_object_set (GTK_OBJECT (e_table), "cursor_mode", E_TABLE_CURSOR_LINE, NULL); scrolled = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); /* Build the gtk widget hierarchy. */ gtk_container_add (GTK_CONTAINER (scrolled), e_table); gtk_container_add (GTK_CONTAINER (frame), scrolled); gtk_container_add (GTK_CONTAINER (paned), frame); return e_table; } static void create_window(char *root_uri) { GtkWidget *paned; GtkWidget *window, *left_tree, *right_tree; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); paned = e_hpaned_new (); gtk_container_add (GTK_CONTAINER (window), paned); left_tree = create_left_tree (paned, root_uri); right_tree = create_right_tree (paned); /* Show it all. */ gtk_widget_show_all (window); gtk_signal_connect (GTK_OBJECT (left_tree), "cursor_change", GTK_SIGNAL_FUNC (on_cursor_change), NULL); gtk_signal_connect (GTK_OBJECT (window), "delete-event", gtk_main_quit, NULL); /* kick things off by selecting the root node */ e_table_set_cursor_row (E_TABLE (left_tree), 0); on_cursor_change (E_TABLE(left_tree), 0, NULL); } int main (int argc, char **argv) { gchar *root_uri; if (argv[1] == NULL) root_uri = "file:///"; else root_uri = argv[1]; gnome_init ("TableExample", "TableExample", argc, argv); e_cursors_init (); gnome_vfs_init (); mime_type_to_pixbuf = g_hash_table_new (g_str_hash, g_str_equal); gtk_widget_push_visual (gdk_rgb_get_visual ()); gtk_widget_push_colormap (gdk_rgb_get_cmap ()); /* * Create our pixbuf for expanding/unexpanding */ tree_expanded_pixbuf = gdk_pixbuf_new_from_xpm_data((const char**)tree_expanded_xpm); tree_unexpanded_pixbuf = gdk_pixbuf_new_from_xpm_data((const char**)tree_unexpanded_xpm); create_window (root_uri); gtk_main (); e_cursors_shutdown (); return 0; }