Branch: refs/heads/master Author: elextr elextr@gmail.com Committer: GitHub noreply@github.com Date: Mon, 01 Apr 2019 01:03:26 UTC Commit: 8ec8bb339a6e130c858401d9de4bf265a1a28b3e https://github.com/geany/geany-plugins/commit/8ec8bb339a6e130c858401d9de4bf2...
Log Message: ----------- Merge pull request #783 from techee/projorg_file_ops
Add file operations to Project Organizer v2
Modified Paths: -------------- projectorganizer/src/prjorg-project.c projectorganizer/src/prjorg-project.h projectorganizer/src/prjorg-sidebar.c projectorganizer/src/prjorg-utils.c projectorganizer/src/prjorg-utils.h
Modified: projectorganizer/src/prjorg-project.c 11 lines changed, 8 insertions(+), 3 deletions(-) =================================================================== @@ -118,6 +118,8 @@ static GSList *get_file_list(const gchar *utf8_path, GSList *patterns, ignored_file_patterns, visited_paths); if (lst) list = g_slist_concat(list, lst); + else + list = g_slist_prepend(list, g_build_path(G_DIR_SEPARATOR_S, utf8_filename, PROJORG_DIR_ENTRY, NULL)); } } else if (g_file_test(locale_filename, G_FILE_TEST_IS_REGULAR)) @@ -284,16 +286,19 @@ static void regenerate_tags(PrjOrgRoot *root, gpointer user_data) g_hash_table_iter_init(&iter, root->file_table); while (g_hash_table_iter_next(&iter, &key, &value)) { - TMSourceFile *sf; + TMSourceFile *sf = NULL; gchar *utf8_path = key; gchar *locale_path = utils_get_locale_from_utf8(utf8_path); + gchar *basename = g_path_get_basename(locale_path);
- sf = tm_source_file_new(locale_path, filetypes_detect(utf8_path)->name); - if (sf && !document_find_by_filename(utf8_path)) + if (g_strcmp0(PROJORG_DIR_ENTRY, basename) != 0) + sf = tm_source_file_new(locale_path, filetypes_detect(utf8_path)->name); + if (sf && !document_find_by_filename(utf8_path) ) g_ptr_array_add(source_files, sf);
g_hash_table_insert(file_table, g_strdup(utf8_path), sf); g_free(locale_path); + g_free(basename); } g_hash_table_destroy(root->file_table); root->file_table = file_table;
Modified: projectorganizer/src/prjorg-project.h 5 lines changed, 5 insertions(+), 0 deletions(-) =================================================================== @@ -63,4 +63,9 @@ void prjorg_project_remove_single_tm_file(gchar *utf8_filename);
gboolean prjorg_project_is_in_project(const gchar *utf8_filename);
+/* In the code we create a list of all files but we want to keep empty directories + * in the list for which we create a fake file name with the PROJORG_DIR_ENTRY + * value. */ +#define PROJORG_DIR_ENTRY "..." + #endif
Modified: projectorganizer/src/prjorg-sidebar.c 231 lines changed, 231 insertions(+), 0 deletions(-) =================================================================== @@ -105,6 +105,11 @@ static struct GtkWidget *find_tag; GtkWidget *expand; GtkWidget *remove_external_dir; + + GtkWidget *create_file; + GtkWidget *create_dir; + GtkWidget *rename; + GtkWidget *delete; } s_popup_menu;
@@ -350,6 +355,169 @@ static void on_remove_external_dir(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_U }
+// returned string must be freed +static gchar* parent_dir_for_create() +{ + GtkTreeSelection *treesel; + GtkTreeModel *model; + GtkTreeIter iter, parent; + gchar *path = NULL; + + treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(s_file_view)); + if (gtk_tree_selection_get_selected(treesel, &model, &iter)) + { + path = build_path(&iter); + if (!g_file_test(path, G_FILE_TEST_IS_DIR)) + { + g_free(path); + path = NULL; + if (gtk_tree_model_iter_parent(model, &parent, &iter)) + path = build_path(&parent); + } + } + return path; +} + + +static void on_create_file(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data) +{ + gchar *dir, *name; + + dir = parent_dir_for_create(); + if (dir == NULL) + return; + + name = dialogs_show_input(_("New File"), GTK_WINDOW(geany->main_widgets->window), + _("File name:"), _("newfile.txt")); + + if (name != NULL) + { + gchar *path = g_build_path(G_DIR_SEPARATOR_S, dir, name, NULL); + + if (create_file(path)) + { + open_file(path); + //TODO: don't rescan the whole project, only change the affected file + prjorg_project_rescan(); + prjorg_sidebar_update(TRUE); + } + else + dialogs_show_msgbox(GTK_MESSAGE_ERROR, _("Cannot create file '%s'."), path); + g_free(path); + } + g_free(name); + g_free(dir); +} + + +static void on_create_dir(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data) +{ + gchar *dir, *name; + + dir = parent_dir_for_create(); + if (dir == NULL) + return; + + name = dialogs_show_input(_("New Directory"), GTK_WINDOW(geany->main_widgets->window), + _("Directory name:"), _("newdir")); + + if (name != NULL) + { + gchar *path = g_build_path(G_DIR_SEPARATOR_S, dir, name, NULL); + + if (create_dir(path)) + { + //TODO: don't rescan the whole project, only change the affected directory + prjorg_project_rescan(); + prjorg_sidebar_update(TRUE); + } + else + dialogs_show_msgbox(GTK_MESSAGE_ERROR, _("Cannot create directory '%s'."), path); + g_free(path); + } + g_free(name); + g_free(dir); +} + + +static void on_rename(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data) +{ + GtkTreeSelection *treesel; + GtkTreeModel *model; + GtkTreeIter iter, parent; + gchar *name, *dir; + + treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(s_file_view)); + if (!gtk_tree_selection_get_selected(treesel, &model, &iter)) + return; + if (!gtk_tree_model_iter_parent(model, &parent, &iter)) + return; + dir = build_path(&parent); + if (dir == NULL) + return; + + gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_NAME, &name, -1); + if (name != NULL) + { + gchar *newname = dialogs_show_input(_("Rename"), GTK_WINDOW(geany->main_widgets->window), + _("New name:"), name); + + if (newname != NULL) + { + gchar *oldpath = g_build_path(G_DIR_SEPARATOR_S, dir, name, NULL); + gchar *newpath = g_build_path(G_DIR_SEPARATOR_S, dir, newname, NULL); + if (rename_file_or_dir(oldpath, newpath)) + { + //TODO: don't rescan the whole project, only change the affected file + prjorg_project_rescan(); + prjorg_sidebar_update(TRUE); + } + else + dialogs_show_msgbox(GTK_MESSAGE_ERROR, _("Cannot rename '%s' to '%s'."), + oldpath, newpath); + g_free(oldpath); + g_free(newpath); + } + g_free(newname); + } + g_free(dir); + g_free(name); +} + + +static void on_delete(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data) +{ + GtkTreeSelection *treesel; + GtkTreeModel *model; + GtkTreeIter iter; + gchar *name; + + treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(s_file_view)); + if (!gtk_tree_selection_get_selected(treesel, &model, &iter)) + return; + + gtk_tree_model_get(model, &iter, FILEVIEW_COLUMN_NAME, &name, -1); + + if (dialogs_show_question(_("Are you sure you want to delete '%s'?"), name)) + { + gchar *path = build_path(&iter); + + if (remove_file_or_dir(path)) + close_file(path); + else + dialogs_show_msgbox(GTK_MESSAGE_ERROR, _("Cannot delete file '%s'."), path); + + g_free(path); + + //TODO: don't rescan the whole project, only change the affected file + prjorg_project_rescan(); + prjorg_sidebar_update(TRUE); + } + + g_free(name); +} + + static void find_file_recursive(GtkTreeIter *iter, gboolean case_sensitive, gboolean full_path, GPatternSpec *pattern) { GtkTreeModel *model = GTK_TREE_MODEL(s_file_store); @@ -789,6 +957,8 @@ static gboolean on_button_release(G_GNUC_UNUSED GtkWidget * widget, GdkEventButt GtkTreeSelection *treesel; GtkTreeModel *model; GtkTreeIter iter; + gboolean delete_enabled = TRUE; + gchar *path;
treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(s_file_view));
@@ -798,6 +968,23 @@ static gboolean on_button_release(G_GNUC_UNUSED GtkWidget * widget, GdkEventButt gtk_widget_set_sensitive(s_popup_menu.expand, gtk_tree_model_iter_has_child(model, &iter)); gtk_widget_set_sensitive(s_popup_menu.remove_external_dir, topmost_selected(model, &iter, FALSE));
+ path = build_path(&iter); + SETPTR(path, utils_get_locale_from_utf8(path)); + if (g_file_test(path, G_FILE_TEST_IS_DIR)) + { + GDir *dir = g_dir_open(path, 0, NULL); + + delete_enabled = FALSE; + if (dir) + { + delete_enabled = g_dir_read_name(dir) == NULL; + g_dir_close(dir); + } + } + g_free(path); + + gtk_widget_set_sensitive(s_popup_menu.delete, delete_enabled); + gtk_menu_popup(GTK_MENU(s_popup_menu.widget), NULL, NULL, NULL, NULL, event->button, event->time); return TRUE; @@ -900,6 +1087,10 @@ static void create_branch(gint level, GSList *leaf_list, GtkTreeIter *parent, GtkTreeIter iter; gchar **path_arr = elem->data; GIcon *icon = NULL; + + if (g_strcmp0(PROJORG_DIR_ENTRY, path_arr[level]) == 0) + continue; + gchar *content_type = g_content_type_guess(path_arr[level], NULL, 0, NULL);
if (content_type) @@ -1472,6 +1663,46 @@ void prjorg_sidebar_init(void) gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item);
+ image = gtk_image_new_from_stock(GTK_STOCK_FILE, GTK_ICON_SIZE_MENU); + gtk_widget_show(image); + item = gtk_image_menu_item_new_with_mnemonic(_("New File...")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); + gtk_widget_show(item); + gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item); + g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_create_file), NULL); + s_popup_menu.create_file = item; + + image = gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU); + gtk_widget_show(image); + item = gtk_image_menu_item_new_with_mnemonic(_("New Directory...")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); + gtk_widget_show(item); + gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item); + g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_create_dir), NULL); + s_popup_menu.create_dir = item; + + image = gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU); + gtk_widget_show(image); + item = gtk_image_menu_item_new_with_mnemonic(_("Rename...")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); + gtk_widget_show(item); + gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item); + g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_rename), NULL); + s_popup_menu.rename = item; + + image = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU); + gtk_widget_show(image); + item = gtk_image_menu_item_new_with_mnemonic(_("Delete")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); + gtk_widget_show(item); + gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item); + g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_delete), NULL); + s_popup_menu.delete = item; + + item = gtk_separator_menu_item_new(); + gtk_widget_show(item); + gtk_container_add(GTK_CONTAINER(s_popup_menu.widget), item); + item = gtk_image_menu_item_new_with_mnemonic(_("H_ide Sidebar")); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU));
Modified: projectorganizer/src/prjorg-utils.c 77 lines changed, 77 insertions(+), 0 deletions(-) =================================================================== @@ -19,6 +19,11 @@ #include <string.h> #include <glib.h>
+#include <glib/gstdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + #include <geanyplugin.h>
#include "prjorg-utils.h" @@ -104,6 +109,78 @@ void open_file(gchar *utf8_name) }
+void close_file(gchar *utf8_name) +{ + GeanyDocument *doc = document_find_by_filename(utf8_name); + + if (doc) + { + document_set_text_changed(doc, FALSE); + document_close(doc); + } +} + + +gboolean create_file(gchar *utf8_name) +{ + gchar *name = utils_get_locale_from_utf8(utf8_name); + gint fd = g_open(name, O_CREAT|O_EXCL, 0660); // rw-rw---- + gboolean success = FALSE; + + if (fd != -1) + { + GError *err; + success = TRUE; + g_close(fd, &err); + } + g_free(name); + return success; +} + + +gboolean create_dir(char *utf8_name) +{ + gchar *name = utils_get_locale_from_utf8(utf8_name); + gboolean ret = g_mkdir_with_parents(name, 0770) == 0; // rwxrwx--- + g_free(name); + return ret; +} + + +gboolean remove_file_or_dir(char *utf8_name) +{ + gchar *name = utils_get_locale_from_utf8(utf8_name); + gboolean ret = g_remove(utf8_name) == 0; + g_free(name); + return ret; +} + + +static gboolean document_rename(GeanyDocument *document, gchar *utf8_name) +{ + // IMHO: this is wrong. If save as fails Geany's state becomes inconsistent. + document_rename_file(document, utf8_name); + return document_save_file_as(document, utf8_name); +} + + +gboolean rename_file_or_dir(gchar *utf8_oldname, gchar *utf8_newname) +{ + GeanyDocument *doc = document_find_by_filename(utf8_oldname); + if (doc) + return document_rename(doc, utf8_newname); + else + { + gchar *oldname = utils_get_locale_from_utf8(utf8_oldname); + gchar *newname = utils_get_locale_from_utf8(utf8_newname); + gint res = g_rename(oldname, newname); + g_free(oldname); + g_free(newname); + return res == 0; + } +} + + gchar *get_selection(void) { GeanyDocument *doc = document_get_current();
Modified: projectorganizer/src/prjorg-utils.h 7 lines changed, 7 insertions(+), 0 deletions(-) =================================================================== @@ -25,6 +25,13 @@ gboolean patterns_match(GSList *patterns, const gchar *str); GSList *get_precompiled_patterns(gchar **patterns);
void open_file(gchar *utf8_name); +void close_file(gchar *utf8_name); + +gboolean create_file(char *utf8_name); +gboolean create_dir(char *utf8_name); +gboolean remove_file_or_dir(char *utf8_name); +gboolean rename_file_or_dir(gchar *utf8_oldname, gchar *utf8_newname); + gchar *get_selection(void); gchar *get_project_base_path(void);
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).