New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SaveActions: Add configurable target directory for instantly saved files #2769
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
f96a84d
SaveActions: Add configurable target directory for instantly saved files
eht16 ead067b
SaveActions: Fix trailing dot on InstantSave filenames for filetype None
eht16 e74d71c
SaveActions: Fix resetting Instant Save target directory
eht16 0c4a356
SaveActions: Handle g_mkstemp() errors and prevent duplicate files
eht16 f64374e
SaveActions: Fix memory leak and possible invalid access
eht16 e024932
Use "ft" variable consistently
eht16 File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,6 +63,7 @@ static struct | |
GtkWidget *autosave_save_all_radio2; | ||
|
||
GtkWidget *instantsave_ft_combo; | ||
GtkWidget *instantsave_entry_dir; | ||
|
||
GtkWidget *backupcopy_entry_dir; | ||
GtkWidget *backupcopy_entry_time; | ||
|
@@ -82,6 +83,7 @@ static gboolean autosave_save_all; | |
static guint autosave_src_id = 0; | ||
|
||
static gchar *instantsave_default_ft; | ||
static gchar *instantsave_target_dir; | ||
|
||
static gchar *backupcopy_backup_dir; /* path to an existing directory in locale encoding */ | ||
static gchar *backupcopy_time_fmt; | ||
|
@@ -91,12 +93,12 @@ static gchar *config_file; | |
|
||
|
||
/* Ensures utf8_dir exists and is writable and | ||
* set backup_dir to the locale encoded form of utf8_dir */ | ||
static gboolean backupcopy_set_backup_dir(const gchar *utf8_dir) | ||
* set target to the locale encoded form of utf8_dir */ | ||
static gboolean store_target_directory(const gchar *utf8_dir, gchar **target) | ||
{ | ||
gchar *tmp; | ||
|
||
if (G_UNLIKELY(EMPTY(utf8_dir))) | ||
if (G_UNLIKELY(EMPTY(utf8_dir)) || target == NULL) | ||
return FALSE; | ||
|
||
tmp = utils_get_locale_from_utf8(utf8_dir); | ||
|
@@ -110,7 +112,7 @@ static gboolean backupcopy_set_backup_dir(const gchar *utf8_dir) | |
} | ||
/** TODO add utils_is_file_writeable() to the plugin API and make use of it **/ | ||
|
||
SETPTR(backupcopy_backup_dir, tmp); | ||
SETPTR(*target, tmp); | ||
|
||
return TRUE; | ||
} | ||
|
@@ -260,26 +262,40 @@ static void instantsave_document_new_cb(GObject *obj, GeanyDocument *doc, gpoint | |
{ | ||
if (enable_instantsave && doc->file_name == NULL) | ||
{ | ||
const gchar *directory; | ||
gchar *new_filename; | ||
gint fd; | ||
GeanyFiletype *ft = doc->file_type; | ||
|
||
fd = g_file_open_tmp("gis_XXXXXX", &new_filename, NULL); | ||
if (fd != -1) | ||
close(fd); /* close the returned file descriptor as we only need the filename */ | ||
|
||
if (ft == NULL || ft->id == GEANY_FILETYPES_NONE) | ||
/* ft is NULL when a new file without template was opened, so use the | ||
* configured default file type */ | ||
ft = filetypes_lookup_by_name(instantsave_default_ft); | ||
|
||
if (ft != NULL) | ||
/* add the filetype's default extension to the new filename */ | ||
/* construct filename */ | ||
directory = !EMPTY(instantsave_target_dir) ? instantsave_target_dir : g_get_tmp_dir(); | ||
new_filename = g_build_filename(directory, "gis_XXXXXX", NULL); | ||
if (ft != NULL && !EMPTY(ft->extension)) | ||
SETPTR(new_filename, g_strconcat(new_filename, ".", ft->extension, NULL)); | ||
|
||
/* create new file */ | ||
fd = g_mkstemp(new_filename); | ||
if (fd == -1) | ||
{ | ||
gchar *message = g_strdup_printf( | ||
_("Instant Save filename could not be generated (%s)."), g_strerror(errno)); | ||
ui_set_statusbar(TRUE, "%s", message); | ||
g_warning("%s", message); | ||
g_free(message); | ||
g_free(new_filename); | ||
return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. leaks There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, thanks for spotting. |
||
} | ||
|
||
close(fd); /* close the returned file descriptor as we only need the filename */ | ||
|
||
doc->file_name = new_filename; | ||
|
||
if (doc->file_type->id == GEANY_FILETYPES_NONE) | ||
if (ft != NULL && ft->id == GEANY_FILETYPES_NONE) | ||
document_set_filetype(doc, filetypes_lookup_by_name(instantsave_default_ft)); | ||
|
||
/* force saving the file to enable all the related actions(tab name, filetype, etc.) */ | ||
|
@@ -407,6 +423,9 @@ void plugin_init(GeanyData *data) | |
|
||
instantsave_default_ft = utils_get_setting_string(config, "instantsave", "default_ft", | ||
filetypes[GEANY_FILETYPES_NONE]->name); | ||
tmp = utils_get_setting_string(config, "instantsave", "target_dir", NULL); | ||
store_target_directory(tmp, &instantsave_target_dir); | ||
g_free(tmp); | ||
|
||
autosave_src_id = 0; /* mark as invalid */ | ||
autosave_interval = utils_get_setting_integer(config, "autosave", "interval", 300); | ||
|
@@ -419,21 +438,15 @@ void plugin_init(GeanyData *data) | |
backupcopy_time_fmt = utils_get_setting_string( | ||
config, "backupcopy", "time_fmt", "%Y-%m-%d-%H-%M-%S"); | ||
tmp = utils_get_setting_string(config, "backupcopy", "backup_dir", g_get_tmp_dir()); | ||
backupcopy_set_backup_dir(tmp); | ||
store_target_directory(tmp, &backupcopy_backup_dir); | ||
g_free(tmp); | ||
|
||
g_key_file_free(config); | ||
g_free(tmp); | ||
} | ||
|
||
|
||
static void backupcopy_dir_button_clicked_cb(GtkButton *button, gpointer item) | ||
static void target_directory_button_clicked_cb(GtkButton *button, gpointer item) | ||
{ | ||
/** TODO add win32_show_pref_file_dialog to the plugin API and use it **/ | ||
/* | ||
#ifdef G_OS_WIN32 | ||
win32_show_pref_file_dialog(item); | ||
#else | ||
*/ | ||
GtkWidget *dialog; | ||
gchar *text; | ||
|
||
|
@@ -471,7 +484,7 @@ static void configure_response_cb(GtkDialog *dialog, gint response, G_GNUC_UNUSE | |
{ | ||
GKeyFile *config = g_key_file_new(); | ||
gchar *str; | ||
const gchar *text_dir, *text_time; | ||
const gchar *backupcopy_text_dir, *instantsave_text_dir, *text_time; | ||
gchar *config_dir = g_path_get_dirname(config_file); | ||
|
||
enable_autosave = gtk_toggle_button_get_active( | ||
|
@@ -493,8 +506,9 @@ static void configure_response_cb(GtkDialog *dialog, gint response, G_GNUC_UNUSE | |
g_free(instantsave_default_ft); | ||
instantsave_default_ft = gtk_combo_box_text_get_active_text( | ||
GTK_COMBO_BOX_TEXT(pref_widgets.instantsave_ft_combo)); | ||
instantsave_text_dir = gtk_entry_get_text(GTK_ENTRY(pref_widgets.instantsave_entry_dir)); | ||
|
||
text_dir = gtk_entry_get_text(GTK_ENTRY(pref_widgets.backupcopy_entry_dir)); | ||
backupcopy_text_dir = gtk_entry_get_text(GTK_ENTRY(pref_widgets.backupcopy_entry_dir)); | ||
text_time = gtk_entry_get_text(GTK_ENTRY(pref_widgets.backupcopy_entry_time)); | ||
backupcopy_dir_levels = gtk_spin_button_get_value_as_int( | ||
GTK_SPIN_BUTTON(pref_widgets.backupcopy_spin_dir_levels)); | ||
|
@@ -513,15 +527,33 @@ static void configure_response_cb(GtkDialog *dialog, gint response, G_GNUC_UNUSE | |
|
||
if (instantsave_default_ft != NULL) | ||
g_key_file_set_string(config, "instantsave", "default_ft", instantsave_default_ft); | ||
if (enable_instantsave) | ||
{ | ||
if (EMPTY(instantsave_text_dir)) | ||
{ | ||
g_key_file_set_string(config, "instantsave", "target_dir", ""); | ||
SETPTR(instantsave_target_dir, NULL); | ||
} | ||
else if (store_target_directory(instantsave_text_dir, &instantsave_target_dir)) | ||
{ | ||
g_key_file_set_string(config, "instantsave", "target_dir", instantsave_target_dir); | ||
} | ||
else | ||
{ | ||
dialogs_show_msgbox(GTK_MESSAGE_ERROR, | ||
_("Instantsave directory does not exist or is not writable.")); | ||
} | ||
} | ||
|
||
g_key_file_set_integer(config, "backupcopy", "dir_levels", backupcopy_dir_levels); | ||
g_key_file_set_string(config, "backupcopy", "time_fmt", text_time); | ||
SETPTR(backupcopy_time_fmt, g_strdup(text_time)); | ||
if (enable_backupcopy) | ||
{ | ||
if (!EMPTY(text_dir) && backupcopy_set_backup_dir(text_dir)) | ||
if (!EMPTY(backupcopy_text_dir) && store_target_directory( | ||
backupcopy_text_dir, &backupcopy_backup_dir)) | ||
{ | ||
g_key_file_set_string(config, "backupcopy", "backup_dir", text_dir); | ||
g_key_file_set_string(config, "backupcopy", "backup_dir", backupcopy_text_dir); | ||
} | ||
else | ||
{ | ||
|
@@ -666,9 +698,10 @@ GtkWidget *plugin_configure(GtkDialog *dialog) | |
* Instant Save | ||
*/ | ||
{ | ||
GtkWidget *combo; | ||
GtkWidget *combo, *hbox, *entry_dir, *button, *image, *help_label; | ||
guint i; | ||
const GSList *node; | ||
gchar *entry_dir_label_text; | ||
|
||
notebook_vbox = gtk_vbox_new(FALSE, 2); | ||
inner_vbox = gtk_vbox_new(FALSE, 5); | ||
|
@@ -704,6 +737,38 @@ GtkWidget *plugin_configure(GtkDialog *dialog) | |
gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(combo), 3); | ||
gtk_label_set_mnemonic_widget(GTK_LABEL(label), combo); | ||
gtk_box_pack_start(GTK_BOX(inner_vbox), combo, FALSE, FALSE, 0); | ||
|
||
entry_dir_label_text = g_strdup_printf( | ||
_("_Directory to save files in (leave empty to use the default: %s):"), g_get_tmp_dir()); | ||
label = gtk_label_new_with_mnemonic(entry_dir_label_text); | ||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | ||
gtk_box_pack_start(GTK_BOX(inner_vbox), label, FALSE, FALSE, 0); | ||
g_free(entry_dir_label_text); | ||
|
||
pref_widgets.instantsave_entry_dir = entry_dir = gtk_entry_new(); | ||
gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_dir); | ||
if (!EMPTY(instantsave_target_dir)) | ||
gtk_entry_set_text(GTK_ENTRY(entry_dir), instantsave_target_dir); | ||
|
||
button = gtk_button_new(); | ||
g_signal_connect(button, "clicked", | ||
G_CALLBACK(target_directory_button_clicked_cb), entry_dir); | ||
|
||
image = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON); | ||
gtk_container_add(GTK_CONTAINER(button), image); | ||
|
||
hbox = gtk_hbox_new(FALSE, 6); | ||
gtk_box_pack_start(GTK_BOX(hbox), entry_dir, TRUE, TRUE, 0); | ||
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); | ||
gtk_box_pack_start(GTK_BOX(inner_vbox), hbox, FALSE, FALSE, 0); | ||
|
||
help_label = gtk_label_new( | ||
_("<i>If you set the Instant Save directory to a directory " | ||
"which is not automatically cleared,\nyou will need to cleanup instantly saved files " | ||
"manually. The Instant Save plugin will not delete the created files.</i>")); | ||
gtk_label_set_use_markup(GTK_LABEL(help_label), TRUE); | ||
gtk_misc_set_alignment(GTK_MISC(help_label), 0, 0.5); | ||
gtk_box_pack_start(GTK_BOX(inner_vbox), help_label, FALSE, FALSE, 0); | ||
} | ||
/* | ||
* Backup Copy | ||
|
@@ -737,7 +802,7 @@ GtkWidget *plugin_configure(GtkDialog *dialog) | |
|
||
button = gtk_button_new(); | ||
g_signal_connect(button, "clicked", | ||
G_CALLBACK(backupcopy_dir_button_clicked_cb), entry_dir); | ||
G_CALLBACK(target_directory_button_clicked_cb), entry_dir); | ||
|
||
image = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON); | ||
gtk_container_add(GTK_CONTAINER(button), image); | ||
|
@@ -793,6 +858,7 @@ void plugin_cleanup(void) | |
g_source_remove(autosave_src_id); | ||
|
||
g_free(instantsave_default_ft); | ||
g_free(instantsave_target_dir); | ||
|
||
g_free(backupcopy_backup_dir); | ||
g_free(backupcopy_time_fmt); | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ft == NULL
meansdoc->file_type
is NULL, but its not set before being dereferenced at here belowThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, you are uncovering very old bugs :). Should be fixed.