/* $Id$ */ /* * Cantus Tag Editor * Copyright © 2002-2004 by Samuel Abels * Copyright © 2007 by Tim Huetz * * 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 3 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, see **/ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #define _(String) gettext (String) #define gettext_noop(String) (String) #define N_(String) gettext_noop (String) enum COLUMNS { DIR_COLUMN, PATH_COLUMN, N_COLUMNS }; #ifdef __cplusplus extern "C" { #endif /****************************************************************************** * STATICS ******************************************************************************/ /* * Returns TRUE if *path is a directory, FALSE if not. */ static gboolean is_dir(gchar *path) { struct stat pstat; if (stat(path, &pstat) < 0) return(FALSE); return(S_ISDIR(pstat.st_mode) != 0); } /* * Returns TRUE if *path has a subdirectory, FALSE if not. */ static gboolean has_subdir(gchar *path, gboolean showhidden) { char fullfilename[4096]; DIR *dirstream = NULL; struct dirent *dirstruct = NULL; if (!(dirstream = opendir(path))) return(FALSE); while ((dirstruct = readdir(dirstream))) { if (strcmp(dirstruct->d_name, "..") == 0 || strcmp(dirstruct->d_name, ".") == 0) continue; /* Show hidden files? */ if (strncmp(dirstruct->d_name, ".", 1) == 0 && !showhidden) continue; snprintf(fullfilename, 4095, "%s/%s", path, dirstruct->d_name); if (is_dir(fullfilename)) { closedir(dirstream); return(TRUE); } } closedir(dirstream); return(FALSE); } /* * Compare function for tree sorting. */ static gint compare_alphabetically_case_insensitive(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data) { gchar *dir1 = NULL, *dir2 = NULL; gtk_tree_model_get(model, a, DIR_COLUMN, &dir1, -1); gtk_tree_model_get(model, b, DIR_COLUMN, &dir2, -1); return (strcasecmp(dir1, dir2)); } /* * Compare function which opens a tree node of the same directory name. * Returns NULL if the directory name wasn't found, otherwise the path to the opened node. */ static GtkTreePath * open_node(GtkTreeView *treeview, GtkTreeModel *model, GtkTreeIter *parent, gchar *needledir) { GtkTreeIter child; GtkTreePath *path = NULL; gchar *dir = NULL; gtk_tree_model_iter_children(GTK_TREE_MODEL(model), &child, parent); do { gtk_tree_model_get(model, &child, DIR_COLUMN, &dir, -1); if (dir != NULL && strcmp(dir, needledir) == 0) { path = gtk_tree_model_get_path(model, &child); gtk_tree_view_expand_row(treeview, path, FALSE); return(path); } } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child)); return(NULL); } /****************************************************************************** * END STATICS ******************************************************************************/ /********************************************************************** * Initialize a treeview widget as directory tree and add the rootdir. * Arguments: treeview: The treeview widget to put this rootdir in. **********************************************************************/ GtkTreeStore *dirtree_create(GtkWidget *treeview) { GtkTreeStore *store = NULL; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkTreeIter iter; GtkTreeIter trash; g_assert(treeview != NULL); /* Create a model and connect signals. */ store = gtk_tree_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING); gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(store)); // gtk_signal_connect(GTK_OBJECT(treeview), "drag-drop", (GtkSignalFunc)on_dirtree_drag_drop, NULL); /* Create a cell render. */ renderer = gtk_cell_renderer_text_new(); /* Create a column, associating the "text" attribute of the */ /* cell_renderer to the first column of the model. */ column = gtk_tree_view_column_new_with_attributes(_("Directories"), renderer, "text", DIR_COLUMN, NULL); /* Add the column to the view. */ gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); /* Add the root node. */ gtk_tree_store_append(GTK_TREE_STORE(store), &iter, NULL); gtk_tree_store_set(store, &iter, DIR_COLUMN, "/", PATH_COLUMN, "/", -1); /* Show the expander by adding a dummy node. */ gtk_tree_store_append(GTK_TREE_STORE(store), &trash, &iter); /* Set sort column. */ gtk_tree_sortable_set_sort_func( GTK_TREE_SORTABLE(store), DIR_COLUMN, (GtkTreeIterCompareFunc)compare_alphabetically_case_insensitive, NULL, NULL); gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE(store), DIR_COLUMN, GTK_SORT_ASCENDING); /* Select the root node. */ gtk_tree_selection_select_iter( gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), &iter); return(store); } /********************************************************************** * Add a branch to the directory tree. * Arguments: model: the tree model in which to expand the branch. * path: the gtk tree path of the item to expand. * showhidden: A glist with pointers to alloced data **********************************************************************/ void dirtree_expand(GtkTreeModel *model, GtkTreePath *path, gboolean showhidden) { GtkTreeIter parent; GtkTreeIter child; GtkTreeIter trash; DIR *dirstream = NULL; struct dirent *dirstruct = NULL; gchar *directory = NULL; gchar subdirectory[4096]; gchar *name = NULL; gchar *dname = NULL; gchar *fname = NULL; /* Get the directory name of the given gtk path. */ if (!gtk_tree_model_get_iter(model, &parent, path)) { fprintf(stderr, "Error: dirtree_expand(): Failed receiving the tree iter."); return; } gtk_tree_model_get(model, &parent, PATH_COLUMN, &directory, -1); /* Look if there is only a dummy node under this parent. */ if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(model), &child, &parent)) { printf("Error: dirtree_expand(): Failed fetching the child iter.\n"); return; } gtk_tree_model_get(GTK_TREE_MODEL(model), &child, DIR_COLUMN, &name, -1); /* Open the directory. */ if (!(dirstream = opendir(directory))) { printf("Error: dirtree_expand(): Failed opening path. (%s)\n", directory); return; } /* Add each child-dir to the node. */ while ((dirstruct = readdir(dirstream))) { /* Don't show the relative directory links. */ if (strcmp(dirstruct->d_name, "..") == 0 || strcmp(dirstruct->d_name, ".") == 0) continue; /* Show hidden files? */ if (strncmp(dirstruct->d_name, ".", 1) == 0 && !showhidden) continue; /* Create a fullpath. */ snprintf(subdirectory, 4095, "%s%s", directory, dirstruct->d_name); if (!is_dir(subdirectory)) continue; strcpy(subdirectory + strlen(subdirectory), "/"); if (!name) { /* If there is only a dummy node under parent, replace it's name. */ dname = g_locale_to_utf8(subdirectory, -1, NULL, NULL, NULL); fname = g_locale_to_utf8(dirstruct->d_name, -1, NULL, NULL, NULL); gtk_tree_store_set(GTK_TREE_STORE(model), &child, DIR_COLUMN, dirstruct->d_name, PATH_COLUMN, subdirectory, -1); g_free(dname); g_free(fname); name = (gchar *)1; // Mark the variable as used. } else { /* Add the new node. */ gtk_tree_store_append(GTK_TREE_STORE(model), &child, &parent); dname = g_locale_to_utf8(subdirectory, -1, NULL, NULL, NULL); fname = g_locale_to_utf8(dirstruct->d_name, -1, NULL, NULL, NULL); gtk_tree_store_set(GTK_TREE_STORE(model), &child, DIR_COLUMN, dirstruct->d_name, PATH_COLUMN, subdirectory, -1); g_free(dname); g_free(fname); } /* If the current directory has a subdir, make the expander visible. */ if (!(has_subdir(subdirectory, showhidden))) continue; /* Show the expander by adding a dummy node. */ gtk_tree_store_append(GTK_TREE_STORE(model), &trash, &child); } gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), DIR_COLUMN, GTK_SORT_ASCENDING); closedir(dirstream); } /********************************************************************** * Remove a branch of the directory tree * Arguments: ctree: the ctree in which to collapse a branch. * row: the row number of the item to colapse. * data: A glist with pointers to alloced data **********************************************************************/ void dirtree_collapse(GtkTreeModel *model, GtkTreePath *path, gboolean keepdummy) { GtkTreeIter parent; GtkTreeIter child; g_return_if_fail(model != NULL); g_return_if_fail(path != NULL); /* Get the directory name of the given gtk path. */ if (!gtk_tree_model_get_iter(model, &parent, path)) { fprintf(stderr, "Error: dirtree_collapse(): Failed receiving the tree iter."); return; } /* Remove all children. */ // FIXME: Do we have to remove all children recursively or does handle GTK this properly? while (gtk_tree_model_iter_children(GTK_TREE_MODEL(model), &child, &parent)) gtk_tree_store_remove(GTK_TREE_STORE(model), &child); /* Should we append a dummy child? */ if (!keepdummy) return; /* Show the expander by adding a dummy node. */ gtk_tree_store_append(GTK_TREE_STORE(model), &child, &parent); } /********************************************************************** * Destroy the directory tree * Arguments: ctree: the ctree to destroy. **********************************************************************/ void dirtree_destroy(GtkTreeStore *model) { GtkTreeIter iter; g_return_if_fail(model != NULL); if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) return; // FIXME: Do i have to care about the children by myself or does GTK do it? gtk_tree_store_remove(GTK_TREE_STORE(model), &iter); } /********************************************************************** * Open a specific directory in the dirtree. **********************************************************************/ void dirtree_open_directory(GtkTreeView *treeview, GtkTreeModel *model, const gchar *dir) { GtkTreeIter parent; GtkTreePath *path = NULL; gchar *curdir = NULL; gchar dircp[4096]; const gchar delimiters[] = "/"; g_assert(dir != NULL); if (*dir == '\0') return; strncpy(dircp, dir, 4095); /* Unfold the root node. */ gtk_tree_model_get_iter_first(model, &parent); path = gtk_tree_model_get_path(model, &parent); gtk_tree_view_expand_row(treeview, path, FALSE); /* Init strtok. */ curdir = strtok(dircp, delimiters); /* Open the subdirs. */ do { if (!(path = open_node(treeview, model, &parent, curdir))) return; if (!gtk_tree_model_get_iter(model, &parent, path)) return; } while ((curdir = strtok(NULL, delimiters))); /* Select the opened node. */ gtk_tree_view_scroll_to_cell(treeview, path, 0, TRUE, 0.3, 0); gtk_tree_selection_select_iter( gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), &parent); } /* * Return a pointer to the full directory name of the given node. */ const gchar *dirtree_path_get(GtkTreeView *treeview, GtkTreePath *path) { const gchar * dir = NULL; GtkTreeModel *model = gtk_tree_view_get_model(treeview); GtkTreeIter iter; gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, PATH_COLUMN, &dir, -1); return(dir); } /* * Return a pointer to the full directory name of the current node. */ const gchar *dirtree_path_get_selected(GtkTreeView *treeview) { GtkTreeSelection *selection = NULL; GtkTreeModel *model = NULL; GtkTreeIter iter; const gchar *dir = NULL; /* Get the selected node. */ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); gtk_tree_selection_get_selected(selection, &model, &iter); if (iter.stamp == GTK_TREE_STORE(model)->stamp) gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, PATH_COLUMN, &dir, -1); return(dir); } #undef COLUMNS #ifdef __cplusplus } #endif