diff options
author | Chris Toshok <toshok@helixcode.com> | 2000-09-01 17:05:56 +0800 |
---|---|---|
committer | Chris Toshok <toshok@src.gnome.org> | 2000-09-01 17:05:56 +0800 |
commit | 943c181c658d64e2e89026db65613c905ffe9f25 (patch) | |
tree | e1acb10a097618e739915936e1315b9966d8ac38 | |
parent | 0a380243b1363ab2477a8fd846d4fae92bb24912 (diff) | |
download | gsoc2013-evolution-943c181c658d64e2e89026db65613c905ffe9f25.tar.gz gsoc2013-evolution-943c181c658d64e2e89026db65613c905ffe9f25.tar.zst gsoc2013-evolution-943c181c658d64e2e89026db65613c905ffe9f25.zip |
new test, a home grown 2 hour hack of a file browser. uses
2000-09-01 Chris Toshok <toshok@helixcode.com>
* e-tree-example-2.c: new test, a home grown 2 hour hack of a file
browser. uses node_collapsed/emitted signals to populate the left
tree.
* Makefile.am: add tree-example-2.c stuff.
* .cvsignore: add tree-example-2
svn path=/trunk/; revision=5163
-rw-r--r-- | widgets/e-table/.cvsignore | 1 | ||||
-rw-r--r-- | widgets/e-table/ChangeLog | 10 | ||||
-rw-r--r-- | widgets/e-table/Makefile.am | 17 | ||||
-rw-r--r-- | widgets/e-table/e-tree-example-2.c | 673 | ||||
-rw-r--r-- | widgets/table/.cvsignore | 1 | ||||
-rw-r--r-- | widgets/table/e-tree-example-2.c | 673 |
6 files changed, 1374 insertions, 1 deletions
diff --git a/widgets/e-table/.cvsignore b/widgets/e-table/.cvsignore index ecfa617948..b1004fee2c 100644 --- a/widgets/e-table/.cvsignore +++ b/widgets/e-table/.cvsignore @@ -10,3 +10,4 @@ table-example-1 table-example-2 table-size-test tree-example-1 +tree-example-2 diff --git a/widgets/e-table/ChangeLog b/widgets/e-table/ChangeLog index 7de92bf0b2..a701b726ef 100644 --- a/widgets/e-table/ChangeLog +++ b/widgets/e-table/ChangeLog @@ -1,5 +1,15 @@ 2000-09-01 Chris Toshok <toshok@helixcode.com> + * e-tree-example-2.c: new test, a home grown 2 hour hack of a file + browser. uses node_collapsed/emitted signals to populate the left + tree. + + * Makefile.am: add tree-example-2.c stuff. + + * .cvsignore: add tree-example-2 + +2000-09-01 Chris Toshok <toshok@helixcode.com> + * e-tree-model.h: add signals/prototypes for e_tree_model_node_collapsed and e_tree_model_node_expanded. diff --git a/widgets/e-table/Makefile.am b/widgets/e-table/Makefile.am index 14bab7e2f4..1afe8d9f20 100644 --- a/widgets/e-table/Makefile.am +++ b/widgets/e-table/Makefile.am @@ -16,6 +16,7 @@ INCLUDES = \ $(GNOME_INCLUDEDIR) \ -DETABLE_GLADEDIR=\"$(gladedir)\" \ -I$(top_srcdir)/widgets/e-text \ + -I$(top_srcdir)/widgets/e-paned \ -I$(top_srcdir)/e-util \ -I$(top_srcdir) \ -DG_LOG_DOMAIN=\"e-table\" @@ -92,7 +93,7 @@ libetable_a_SOURCES = \ e-tree-simple.h noinst_PROGRAMS = \ - table-test table-example-1 table-example-2 table-size-test tree-example-1 + table-test table-example-1 table-example-2 table-size-test tree-example-1 tree-example-2 table_test_SOURCES = \ test-table.c \ @@ -163,6 +164,20 @@ tree_example_1_LDADD = \ $(top_builddir)/e-util/libeutil.la \ $(GNOME_PRINT_LIBS) +tree_example_2_SOURCES = \ + e-tree-example-2.c + +tree_example_2_LDFLAGS = `gnome-config --libs gdk_pixbuf` `gnome-config --libs vfs` + +tree_example_2_LDADD = \ + libetable.a \ + $(EXTRA_GNOME_LIBS) \ + $(top_builddir)/widgets/e-text/libetext.a \ + $(top_builddir)/widgets/e-paned/libepaned.a \ + $(top_builddir)/widgets/misc/libemiscwidgets.a \ + $(top_builddir)/e-util/libeutil.la \ + $(GNOME_PRINT_LIBS) + icons = \ arrow-down.xpm \ arrow-up.xpm \ diff --git a/widgets/e-table/e-tree-example-2.c b/widgets/e-table/e-tree-example-2.c new file mode 100644 index 0000000000..d7db861953 --- /dev/null +++ b/widgets/e-table/e-tree-example-2.c @@ -0,0 +1,673 @@ +/* -*- 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 <ettore@comm2000.it> */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <gnome.h> +#include <libgnomevfs/gnome-vfs.h> +#include <libgnomevfs/gnome-vfs-mime-handlers.h> +#include "e-hpaned.h" +#include "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 <gdk-pixbuf/gdk-pixbuf.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 \ +"<ETableSpecification> \ + <columns-shown> \ + <column> 0 </column> \ + <column> 1 </column> \ + <column> 2 </column> \ + <column> 3 </column> \ + </columns-shown> \ + <grouping></grouping> \ +</ETableSpecification>" + +#define LEFT_E_TABLE_SPEC \ +"<ETableSpecification no-header=\"1\"> \ + <columns-shown> \ + <column> 0 </column> \ + </columns-shown> \ + <grouping></grouping> \ +</ETableSpecification>" + +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 NULL; + else { + g_snprintf (buf, sizeof(buf), "%ld", (glong) vfs_info->info->size); + return buf; + } + } + else + return NULL; + } + case 2: /* file type */ + if (vfs_info->info) + return type_to_string (vfs_info->info->type); + else + return NULL; + 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 NULL; + } + default: return NULL; + } +} + +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_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_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; +} diff --git a/widgets/table/.cvsignore b/widgets/table/.cvsignore index ecfa617948..b1004fee2c 100644 --- a/widgets/table/.cvsignore +++ b/widgets/table/.cvsignore @@ -10,3 +10,4 @@ table-example-1 table-example-2 table-size-test tree-example-1 +tree-example-2 diff --git a/widgets/table/e-tree-example-2.c b/widgets/table/e-tree-example-2.c new file mode 100644 index 0000000000..d7db861953 --- /dev/null +++ b/widgets/table/e-tree-example-2.c @@ -0,0 +1,673 @@ +/* -*- 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 <ettore@comm2000.it> */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <gnome.h> +#include <libgnomevfs/gnome-vfs.h> +#include <libgnomevfs/gnome-vfs-mime-handlers.h> +#include "e-hpaned.h" +#include "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 <gdk-pixbuf/gdk-pixbuf.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 \ +"<ETableSpecification> \ + <columns-shown> \ + <column> 0 </column> \ + <column> 1 </column> \ + <column> 2 </column> \ + <column> 3 </column> \ + </columns-shown> \ + <grouping></grouping> \ +</ETableSpecification>" + +#define LEFT_E_TABLE_SPEC \ +"<ETableSpecification no-header=\"1\"> \ + <columns-shown> \ + <column> 0 </column> \ + </columns-shown> \ + <grouping></grouping> \ +</ETableSpecification>" + +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 NULL; + else { + g_snprintf (buf, sizeof(buf), "%ld", (glong) vfs_info->info->size); + return buf; + } + } + else + return NULL; + } + case 2: /* file type */ + if (vfs_info->info) + return type_to_string (vfs_info->info->type); + else + return NULL; + 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 NULL; + } + default: return NULL; + } +} + +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_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_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; +} |