diff options
Diffstat (limited to 'saedit/treefolderview')
-rw-r--r-- | saedit/treefolderview/treefolderview.c | 400 | ||||
-rw-r--r-- | saedit/treefolderview/treefolderview.h | 64 | ||||
-rw-r--r-- | saedit/treefolderview/treefolderviewprivate.h | 45 | ||||
-rw-r--r-- | saedit/treefolderview/type.c | 250 |
4 files changed, 759 insertions, 0 deletions
diff --git a/saedit/treefolderview/treefolderview.c b/saedit/treefolderview/treefolderview.c new file mode 100644 index 0000000..e1b02f0 --- /dev/null +++ b/saedit/treefolderview/treefolderview.c @@ -0,0 +1,400 @@ +#include "treefolderview.h" +#include "treefolderviewprivate.h" + +enum { + STORE_COLUMN_FILE_NAME, + STORE_COLUMN_IS_FOLDER, + STORE_COLUMN_FILE_ICON, + STORE_COLUMN_FILE_INFO, + STORE_COLUMN_WAS_EXPANDED, + STORE_COLUMN_COUNT +}; + +enum { + SIGNAL_FILE_ACTIVATED, + SIGNAL_COUNT +}; + +struct _TreeFolderViewPrivate { + gchar *filename; + GtkFileFilter *file_filter; +}; + +static guint tfview_signals [SIGNAL_COUNT] = { 0 }; + +static GtkTreeStore * +tree_folder_view_get_store (TreeFolderView *tfview) { + GtkTreeModelFilter *model; + + model = GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (tfview))); + return GTK_TREE_STORE (gtk_tree_model_filter_get_model (model)); +} + +static gboolean +file_filter_filter_file_info (GtkFileFilter *filter, GFileInfo *info) { + gboolean result; + gchar *mimetype = NULL; + GtkFileFilterFlags required; + GtkFileFilterInfo filter_info = { 0, }; + + if (filter == NULL) + return TRUE; + + required = gtk_file_filter_get_needed (filter); + + filter_info.contains |= GTK_FILE_FILTER_DISPLAY_NAME; + filter_info.display_name = g_file_info_get_display_name (info); + + if (required & GTK_FILE_FILTER_MIME_TYPE) { + const gchar *ctype = g_file_info_get_content_type (info); + if (ctype != NULL) { + mimetype = g_content_type_get_mime_type (ctype); + if (mimetype != NULL) { + filter_info.contains |= GTK_FILE_FILTER_MIME_TYPE; + filter_info.mime_type = mimetype; + } + } + } + + if (required & GTK_FILE_FILTER_FILENAME) { + filter_info.filename = g_file_info_get_name (info); + filter_info.contains |= GTK_FILE_FILTER_FILENAME; + } + + result = gtk_file_filter_filter (filter, &filter_info); + + g_free(mimetype); + + return result; +} + +static gchar * +tree_folder_view_get_file_path_from_tree_path ( + TreeFolderView *tfview, + GtkTreePath *path +) { + GtkTreeIter iter; + GtkTreeModel *model; + + model = GTK_TREE_MODEL (tree_folder_view_get_store (tfview)); + gtk_tree_model_get_iter (model, &iter, path); + + return tree_folder_view_get_file_path_from_iter (tfview, &iter); +} + +static gchar * +tree_folder_view_get_file_path_from_iter ( + TreeFolderView *tfview, + GtkTreeIter *file_iter +) { + gchar *result, *data; + GPtrArray *names; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreePath *_path; + + model = GTK_TREE_MODEL (tree_folder_view_get_store (tfview)); + _path = gtk_tree_model_get_path (model, file_iter); + + names = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); + + while (gtk_tree_path_get_depth (_path) > 0) { + gtk_tree_model_get_iter (model, &iter, _path); + gtk_tree_model_get ( + model, &iter, + STORE_COLUMN_FILE_NAME, &data, -1 + ); + g_ptr_array_insert (names, 0, data); + gtk_tree_path_up (_path); + } + + g_ptr_array_insert (names, 0, g_strdup (tfview->priv->filename)); + g_ptr_array_add (names, NULL); + result = g_strjoinv ("/", (gchar **) names->pdata); + + g_ptr_array_free (names, TRUE); + gtk_tree_path_free (_path); + + return result; +} + +static void +tree_folder_view_row_expanded ( + GtkTreeView *tree_view, + GtkTreeIter *filter_iter, + GtkTreePath *path +) { + gboolean w_exp; + gchar *file_path; + GtkTreeIter iter, citer; + GtkTreeStore *store; + + gtk_tree_model_filter_convert_iter_to_child_iter ( + GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (tree_view)), + &iter, + filter_iter + ); + + store = tree_folder_view_get_store ( + TREE_FOLDER_VIEW (tree_view) + ); + + gtk_tree_model_get ( + GTK_TREE_MODEL (store), &iter, + STORE_COLUMN_WAS_EXPANDED, &w_exp, + -1 + ); + + if (w_exp) + return; + + gtk_tree_store_set ( + store, &iter, + STORE_COLUMN_WAS_EXPANDED, TRUE, + -1 + ); + + if (!gtk_tree_model_iter_has_child (GTK_TREE_MODEL (store), &iter)) + return; + + gtk_tree_model_iter_children (GTK_TREE_MODEL (store), &citer, &iter); + do { + file_path = tree_folder_view_get_file_path_from_iter ( + TREE_FOLDER_VIEW (tree_view), + &citer + ); + + tree_store_append_file_children ( + store, + &citer, + file_path, + FALSE + ); + + g_free (file_path); + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &citer)); +} + +static gboolean +tree_model_filter_file_visible_func ( + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data +) { + gboolean is_folder; + GFileInfo *info; + GtkFileFilter *filter; + TreeFolderView *tfview; + + tfview = TREE_FOLDER_VIEW (data); + + gtk_tree_model_get ( + model, iter, + STORE_COLUMN_FILE_INFO, &info, + STORE_COLUMN_IS_FOLDER, &is_folder, + -1 + ); + + if (info == NULL) + return FALSE; + + if (g_file_info_get_is_hidden (info)) + return FALSE; + + if (is_folder) + return TRUE; + + filter = tree_folder_view_get_filter (tfview); + return file_filter_filter_file_info (filter, info); +} + +static void +tree_store_append_file_children ( + GtkTreeStore *store, + GtkTreeIter *iter, + const gchar *path, + gboolean expanded +) { + gchar *npath; + const gchar *name; + GDir *dir; + + dir = g_dir_open (path, 0, NULL); + + if (dir == NULL) + return; + + while ((name = g_dir_read_name (dir)) != NULL) { + npath = g_strconcat (path, "/", name, NULL); + + tree_store_append_file_recursive ( + store, + iter, + npath, + name, + expanded + ); + + g_free(npath); + } + + g_dir_close (dir); +} + +static gint +tree_store_append_file_recursive ( + GtkTreeStore *store, + GtkTreeIter *parent_iter, + const gchar *path, + const gchar *display_name, + gboolean append_children +) { + GFile *file; + GFileInfo *info; + GtkTreeIter iter; + + file = g_file_new_for_path (path); + info = g_file_query_info (file, "*", 0, NULL, NULL); + g_object_unref (file); + + gtk_tree_store_append (store, &iter, parent_iter); + gtk_tree_store_set ( + store, &iter, + STORE_COLUMN_FILE_NAME, display_name, + STORE_COLUMN_FILE_ICON, g_content_type_get_icon (g_file_info_get_content_type (info)), + STORE_COLUMN_FILE_INFO, info, + STORE_COLUMN_IS_FOLDER, FALSE, + STORE_COLUMN_WAS_EXPANDED, FALSE, + -1 + ); + + if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY) + return 0; + + gtk_tree_store_set ( + store, &iter, + STORE_COLUMN_IS_FOLDER, TRUE, + STORE_COLUMN_FILE_ICON, g_themed_icon_new ("folder"), -1 + ); + + if (!append_children) + return 1; + + tree_store_append_file_children (store, &iter, path, FALSE); + + return 1; +} + +static gint +tree_store_iter_compare_func ( + GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data +) { + gboolean isf_a, isf_b; + gint cmp; + gchar *val_a, *val_b; + + gtk_tree_model_get (model, a, STORE_COLUMN_IS_FOLDER, &isf_a, -1); + gtk_tree_model_get (model, b, STORE_COLUMN_IS_FOLDER, &isf_b, -1); + + if (isf_a != isf_b) + return isf_b; + + gtk_tree_model_get (model, a, STORE_COLUMN_FILE_NAME, &val_a, -1); + gtk_tree_model_get (model, b, STORE_COLUMN_FILE_NAME, &val_b, -1); + + cmp = g_strcmp0 (val_a, val_b); + g_free (val_a); + g_free (val_b); + + return cmp; +} + +static void +tree_folder_view_row_activated ( + GtkTreeView *tree_view, + GtkTreePath *filter_path, + GtkTreeViewColumn *col +) { + gboolean is_folder; + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeStore *store; + + path = gtk_tree_model_filter_convert_path_to_child_path ( + GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (tree_view)), + filter_path + ); + + store = tree_folder_view_get_store (TREE_FOLDER_VIEW (tree_view)); + + gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path); + gtk_tree_model_get ( + GTK_TREE_MODEL (store), &iter, + STORE_COLUMN_IS_FOLDER, &is_folder, -1 + ); + + if (is_folder) + return; + + g_signal_emit ( + tree_view, + tfview_signals[SIGNAL_FILE_ACTIVATED], + 0, + tree_folder_view_get_file_path_from_tree_path ( + TREE_FOLDER_VIEW (tree_view), + path + ) + ); + + gtk_tree_path_free (path); +} + +void +tree_folder_view_set_filename ( + TreeFolderView *tfview, + const gchar *filename +) { + GtkTreeStore *store = tree_folder_view_get_store (tfview); + + /* TODO: values inside are not freed */ + gtk_tree_store_clear (store); + + g_free (tfview->priv->filename); + tfview->priv->filename = g_strdup (filename); + + if (filename != NULL) { + tree_store_append_file_children ( + store, + NULL, + filename, + TRUE + ); + } +} + +GtkFileFilter * +tree_folder_view_get_filter (TreeFolderView *tfview) { + return tfview->priv->file_filter; +} + +void +tree_folder_view_set_filter (TreeFolderView *tfview, GtkFileFilter *filter) { + if (G_IS_OBJECT (tfview->priv->file_filter)) + g_object_unref (tfview->priv->file_filter); + + tfview->priv->file_filter = filter; + g_object_ref (filter); + + gtk_tree_model_filter_refilter ( + GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (tfview)))); +} + +gchar * +tree_folder_view_get_filename (TreeFolderView *tfview) { + return g_strdup (tfview->priv->filename); +} + +#include "type.c" diff --git a/saedit/treefolderview/treefolderview.h b/saedit/treefolderview/treefolderview.h new file mode 100644 index 0000000..78aeefc --- /dev/null +++ b/saedit/treefolderview/treefolderview.h @@ -0,0 +1,64 @@ +#ifndef __TREE_FOLDER_VIEW_H__ +#define __TREE_FOLDER_VIEW_H__ + +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define TYPE_TREE_FOLDER_VIEW (tree_folder_view_get_type ()) +#define TREE_FOLDER_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TREE_FOLDER_VIEW, TreeFolderView)) +#define TREE_FOLDER_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_TREE_FOLDER_VIEW, TreeFolderViewClass)) +#define IS_TREE_FOLDER_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TREE_FOLDER_VIEW)) +#define IS_TREE_FOLDER_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_TREE_FOLDER_VIEW)) +#define TREE_FOLDER_VIEW_GET_CLASS(obj) ((obj), TYPE_TREE_FOLDER_VIEW, TreeFolderViewClass) + +typedef struct _TreeFolderView TreeFolderView; +typedef struct _TreeFolderViewPrivate TreeFolderViewPrivate; +typedef struct _TreeFolderViewClass TreeFolderViewClass; + +struct _TreeFolderView { + GtkTreeView tview; + + TreeFolderViewPrivate *priv; +}; + +struct _TreeFolderViewClass { + GtkTreeViewClass tview_class; + + void (* file_activated) ( + TreeFolderView *tfview, + gchar *filename + ); +}; + +GType +tree_folder_view_get_type (void); + +GtkWidget* +tree_folder_view_new (void); + +void +tree_folder_view_set_filename ( + TreeFolderView *tfview, + const gchar *filename +); + +GtkFileFilter* +tree_folder_view_get_filter (TreeFolderView *tfview); + +void +tree_folder_view_set_filter ( + TreeFolderView *tfview, + GtkFileFilter *filter +); + +gchar * +tree_folder_view_get_filename (TreeFolderView *tfview); + +gchar * +tree_folder_view_get_display_name (TreeFolderView *tfview); + +G_END_DECLS + +#endif diff --git a/saedit/treefolderview/treefolderviewprivate.h b/saedit/treefolderview/treefolderviewprivate.h new file mode 100644 index 0000000..01119fc --- /dev/null +++ b/saedit/treefolderview/treefolderviewprivate.h @@ -0,0 +1,45 @@ +#ifndef __TREE_FOLDER_VIEW_PRIVATE_H__ +#define __TREE_FOLDER_VIEW_PRIVATE_H__ + +enum { + PROP_0, + PROP_FILTER, + PROP_FILENAME, + PROP_MODEL, + PROP_COUNT +}; + +static GtkTreeStore * +tree_folder_view_get_store (TreeFolderView *tfview); + +static gchar * +tree_folder_view_get_file_path_from_tree_path ( + TreeFolderView *tfview, + GtkTreePath *path +); + +static gchar * +tree_folder_view_get_file_path_from_iter ( + TreeFolderView *tfview, + GtkTreeIter *file_iter +); + +static void +tree_store_append_file_children ( + GtkTreeStore *store, + GtkTreeIter *iter, + const gchar *path, + gboolean expanded +); + +static gint +tree_store_append_file_recursive ( + GtkTreeStore *store, + GtkTreeIter *parent_iter, + const gchar *path, + const gchar *display_name, + gboolean append_children +); + +#endif + diff --git a/saedit/treefolderview/type.c b/saedit/treefolderview/type.c new file mode 100644 index 0000000..fbb0f21 --- /dev/null +++ b/saedit/treefolderview/type.c @@ -0,0 +1,250 @@ +static void +tree_folder_view_init ( + TreeFolderView *tfview, + TreeFolderViewClass *klass +) { + GType *types; + GtkCellRenderer *renderer; + GtkTreeModel *model; + GtkTreeStore *store; + GtkTreeView *tview; + GtkTreeViewColumn *col; + + tview = GTK_TREE_VIEW (tfview); + + /* Setting up TreeView properties */ + gtk_tree_view_set_headers_visible (tview, FALSE); + gtk_tree_view_set_enable_tree_lines (tview, TRUE); + + /* Setting up only column */ + col = gtk_tree_view_column_new (); + + /* Filename renderer */ + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_end (col, renderer, TRUE); + gtk_tree_view_column_set_attributes ( + col, + renderer, + "text", STORE_COLUMN_FILE_NAME, + NULL + ); + + /* Filetype renderer */ + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_end (col, renderer, FALSE); + gtk_tree_view_column_set_attributes ( + col, + renderer, + "gicon", STORE_COLUMN_FILE_ICON, + NULL + ); + + gtk_tree_view_append_column (tview, col); + + /* Setting up TreeStore */ + types = g_new (GType, STORE_COLUMN_COUNT); + types [STORE_COLUMN_FILE_NAME] = G_TYPE_STRING; + types [STORE_COLUMN_FILE_ICON] = G_TYPE_ICON; + types [STORE_COLUMN_FILE_INFO] = G_TYPE_FILE_INFO; + types [STORE_COLUMN_IS_FOLDER] = G_TYPE_BOOLEAN; + types [STORE_COLUMN_WAS_EXPANDED] = G_TYPE_BOOLEAN; + + store = gtk_tree_store_newv (STORE_COLUMN_COUNT, types); + g_free(types); + + gtk_tree_sortable_set_sort_func ( + GTK_TREE_SORTABLE (store), + STORE_COLUMN_FILE_NAME, + (GtkTreeIterCompareFunc) tree_store_iter_compare_func, + NULL, + NULL + ); + + gtk_tree_sortable_set_sort_column_id ( + GTK_TREE_SORTABLE (store), + STORE_COLUMN_FILE_NAME, + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID + ); + + /* Setting up TreeModelFilter */ + model = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + gtk_tree_model_filter_set_visible_func ( + GTK_TREE_MODEL_FILTER (model), + (GtkTreeModelFilterVisibleFunc) tree_model_filter_file_visible_func, + (gpointer) tview, + NULL + ); + gtk_tree_view_set_model (tview, model); + + /* Setting up private */ + tfview->priv = G_TYPE_INSTANCE_GET_PRIVATE ( + tfview, + TYPE_TREE_FOLDER_VIEW, + TreeFolderViewPrivate + ); +} + +static void +tree_folder_view_set_property ( + GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec +) { + TreeFolderView *tfview = TREE_FOLDER_VIEW (object); + + switch (prop_id) { + case PROP_FILTER: + tree_folder_view_set_filter ( + tfview, + GTK_FILE_FILTER (g_value_get_object (value)) + ); + break; + case PROP_FILENAME: + tree_folder_view_set_filename ( + tfview, + g_value_get_string (value) + ); + break; + case PROP_MODEL: + g_warning( + "\"model\" property of GtkTreeView is overriden " + "in TreeFolderView and shouldn't be changed" + ); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +tree_folder_view_get_property ( + GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec +) { + TreeFolderView *tfview = TREE_FOLDER_VIEW (object); + + switch (prop_id) { + case PROP_FILTER: + g_value_set_object ( + value, tree_folder_view_get_filter (tfview) + ); + break; + case PROP_FILENAME: + g_value_set_string ( + value, tree_folder_view_get_filename (tfview) + ); + break; + case PROP_MODEL: + g_value_set_object ( + value, gtk_tree_view_get_model (GTK_TREE_VIEW (tfview)) + ); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +tree_folder_view_class_init ( + TreeFolderViewClass *klass, + gpointer class_data +) { + GObjectClass *object_class; + GtkTreeViewClass *tree_view_class; + + object_class = G_OBJECT_CLASS (klass); + tree_view_class = GTK_TREE_VIEW_CLASS (klass); + + tree_view_class->row_activated = tree_folder_view_row_activated; + tree_view_class->row_expanded = tree_folder_view_row_expanded; + + tfview_signals [SIGNAL_FILE_ACTIVATED] = g_signal_new ( + "file-activated", + TYPE_TREE_FOLDER_VIEW, + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (TreeFolderViewClass, file_activated), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_STRING + ); + + g_type_class_add_private (klass, sizeof (TreeFolderViewPrivate)); + + object_class->set_property = tree_folder_view_set_property; + object_class->get_property = tree_folder_view_get_property; + + g_object_class_install_property ( + object_class, + PROP_FILTER, + g_param_spec_object ( + "filter", + "File filter", + "File filter for selecting " + "which files should be displayed.", + GTK_TYPE_FILE_FILTER, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT + ) + ); + + g_object_class_install_property ( + object_class, + PROP_FILENAME, + g_param_spec_string ( + "filename", + "Folder filename", + "Full path to a folder " + "which contents are displayed.", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT + ) + ); + + g_object_class_override_property ( + object_class, + PROP_MODEL, + "model" + ); +} + +GType +tree_folder_view_get_type (void) { + static GType tfview_type = 0; + + if (tfview_type == 0) { + const GTypeInfo tfview_info = { + sizeof (TreeFolderViewClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) tree_folder_view_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (TreeFolderView), + 0, /* n_preallocs */ + (GInstanceInitFunc) tree_folder_view_init, + NULL, /* value_table */ + }; + + tfview_type = g_type_register_static ( + GTK_TYPE_TREE_VIEW, + "TreeFolderView", + &tfview_info, + 0 + ); + + } + + return tfview_type; +} + +GtkWidget * +tree_folder_view_new (void) { + GtkWidget *tfview = GTK_WIDGET ( + g_object_new (TYPE_TREE_FOLDER_VIEW, NULL) + ); + return tfview; +} |