summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-07-22 10:03:00 +0300
committerPaul Buetow <paul@buetow.org>2026-07-22 10:03:00 +0300
commitd0a824c685db1567facaf08fcae19ee0636496da (patch)
tree4b2100be7133b013b91660436431cd6d3f940503 /src
parent00f9ad7f18349781a679e7b3281d7e8f84ebb5b1 (diff)
enhance: save hint in the panel + Save/Discard/Cancel dialog on navigation
Add a dim 's Save enhanced copy' hint at the bottom of the enhance panel so the save shortcut is visible right where the toggles are. When you navigate to another image (h/l/g/G) with an unsaved enhance preview active, a Save / Discard / Cancel alert now asks what to do: Save exports <stem>-enhanced.<ext> with the enabled-preset chain then moves on; Discard drops the preview and moves on; Cancel keeps the preview and stays on the current image. _action_prev/next/first/last route through _maybe_save_then; the s action and the dialog's Save button share _enhance_do_save. Non-GEGL builds get a passthrough gate.
Diffstat (limited to 'src')
-rw-r--r--src/window.c216
1 files changed, 162 insertions, 54 deletions
diff --git a/src/window.c b/src/window.c
index 529711b..65ef229 100644
--- a/src/window.c
+++ b/src/window.c
@@ -72,8 +72,156 @@ static void _show_info(GgazeWindow *p_win);
static void _hide_info(GgazeWindow *p_win);
static gboolean _slideshow_tick(gpointer p_data);
#if GGAZE_HAVE_GEGL
-static void _enhance_update_highlights(GgazeWindow *p_win);
-static void _enhance_panel_reparent(GgazeWindow *p_win, gboolean b_overlay);
+static void _enhance_update_highlights(GgazeWindow *p_win);
+static void _enhance_panel_reparent(GgazeWindow *p_win, gboolean b_overlay);
+static gboolean _enhance_do_save(GgazeWindow *p_win);
+#endif
+
+#if GGAZE_HAVE_GEGL
+/* Export the current image with the enabled-preset chain to
+ * <stem>-enhanced.<ext>. Returns TRUE on success; prints the saved name. */
+static gboolean
+_enhance_do_save(GgazeWindow *p_win) {
+ if (p_win->p_nav == NULL || p_win->p_enhancer == NULL ||
+ p_win->u_enhance_mask == 0) {
+ return (FALSE);
+ }
+ GFile *p_file = navigator_get_current(p_win->p_nav);
+ if (p_file == NULL) {
+ return (FALSE);
+ }
+ char *c_base = g_file_get_basename(p_file);
+ char *c_dot = strrchr(c_base, '.');
+ const char *c_ext = ".jpg";
+ if (c_dot != NULL && (g_ascii_strcasecmp(c_dot, ".jpg") == 0 ||
+ g_ascii_strcasecmp(c_dot, ".jpeg") == 0 ||
+ g_ascii_strcasecmp(c_dot, ".png") == 0 ||
+ g_ascii_strcasecmp(c_dot, ".webp") == 0)) {
+ c_ext = c_dot;
+ }
+ char *c_stem;
+ if (c_dot != NULL && c_ext == c_dot) {
+ c_stem = g_strndup(c_base, (gsize)(c_dot - c_base));
+ } else {
+ c_stem = g_strdup(c_base);
+ }
+ GFile *p_dir = g_file_get_parent(p_file);
+ char *c_outname = g_strdup_printf("%s-enhanced%s", c_stem, c_ext);
+ GFile *p_out = g_file_get_child(p_dir, c_outname);
+ g_free(c_outname);
+ g_free(c_stem);
+ g_free(c_base);
+ g_object_unref(p_dir);
+
+ GError *p_err = NULL;
+ GeglBuffer *p_buf = enhancer_load(p_file, &p_err);
+ gboolean b_ok = FALSE;
+ if (p_buf != NULL) {
+ const GPtrArray *p_presets = enhancer_get_presets(p_win->p_enhancer);
+ b_ok = enhancer_export_chain(p_win->p_enhancer, p_buf, p_presets,
+ p_win->u_enhance_mask, p_out, &p_err);
+ g_object_unref(p_buf);
+ }
+ char *c_saved = b_ok ? g_file_get_basename(p_out) : NULL;
+ g_object_unref(p_out);
+ if (b_ok) {
+ g_printerr("ggaze: saved %s\n", c_saved);
+ } else {
+ g_warning("ggaze: enhance-save failed: %s",
+ p_err != NULL ? p_err->message : "(no detail)");
+ }
+ g_free(c_saved);
+ g_clear_error(&p_err);
+ return (b_ok);
+}
+
+typedef struct {
+ GgazeWindow *p_win;
+ GSourceFunc fn;
+ gpointer data;
+} _SaveCtx;
+
+/* Alert-dialog response: 0=Cancel, 1=Discard, 2=Save. */
+static void
+_save_dialog_cb(GObject *p_dlg, GAsyncResult *p_res, gpointer p_data) {
+ _SaveCtx *p_ctx = (_SaveCtx *)p_data;
+ GgazeWindow *p_win = p_ctx->p_win;
+ GError *p_err = NULL;
+ gint i_btn =
+ gtk_alert_dialog_choose_finish(GTK_ALERT_DIALOG(p_dlg), p_res, &p_err);
+ g_object_unref(GTK_ALERT_DIALOG(p_dlg));
+ if (p_err != NULL) { /* dismissed / error -> treat as Cancel */
+ g_clear_error(&p_err);
+ g_free(p_ctx);
+ return;
+ }
+ if (i_btn == 2) { /* Save */
+ _enhance_do_save(p_win);
+ }
+ if (i_btn == 1 || i_btn == 2) { /* Discard or Save: clear + proceed */
+ p_win->u_enhance_mask = 0;
+ _enhance_update_highlights(p_win);
+ _update_header(p_win);
+ if (p_ctx->fn != NULL) {
+ p_ctx->fn(p_ctx->data);
+ }
+ } /* Cancel: keep the preview, do not navigate. */
+ g_free(p_ctx);
+}
+
+/* If an enhance preview is active (unsaved), ask Save / Discard / Cancel
+ * before proceeding with fn. If no preview, just run fn. */
+static void
+_maybe_save_then(GgazeWindow *p_win, GSourceFunc fn, gpointer data) {
+ if (p_win->u_enhance_mask == 0 || p_win->p_enhancer == NULL) {
+ if (fn != NULL) {
+ fn(data);
+ }
+ return;
+ }
+ GtkAlertDialog *p_dlg =
+ gtk_alert_dialog_new("Save the enhanced copy before leaving this image?");
+ static const char *const c_btns[] = {"Cancel", "Discard", "Save", NULL};
+ gtk_alert_dialog_set_buttons(p_dlg, c_btns);
+ gtk_alert_dialog_set_default_button(p_dlg, 2);
+ gtk_alert_dialog_set_cancel_button(p_dlg, 0);
+ gtk_alert_dialog_set_modal(p_dlg, TRUE);
+ _SaveCtx *p_ctx = g_new(_SaveCtx, 1);
+ p_ctx->p_win = p_win;
+ p_ctx->fn = fn;
+ p_ctx->data = data;
+ gtk_alert_dialog_choose(p_dlg, GTK_WINDOW(p_win), NULL, _save_dialog_cb,
+ p_ctx);
+}
+
+static gboolean
+_proceed_prev(gpointer d) {
+ ggaze_window_prev(GGAZE_WINDOW(d));
+ return (G_SOURCE_REMOVE);
+}
+static gboolean
+_proceed_next(gpointer d) {
+ ggaze_window_next(GGAZE_WINDOW(d));
+ return (G_SOURCE_REMOVE);
+}
+static gboolean
+_proceed_first(gpointer d) {
+ ggaze_window_first(GGAZE_WINDOW(d));
+ return (G_SOURCE_REMOVE);
+}
+static gboolean
+_proceed_last(gpointer d) {
+ ggaze_window_last(GGAZE_WINDOW(d));
+ return (G_SOURCE_REMOVE);
+}
+#else /* !GGAZE_HAVE_GEGL */
+static void
+_maybe_save_then(GgazeWindow *p_win, GSourceFunc fn, gpointer data) {
+ (void)p_win;
+ if (fn != NULL) {
+ fn(data);
+ }
+}
#endif
/* --- actions ------------------------------------------------------------- */
@@ -82,28 +230,28 @@ static void
_action_prev(GSimpleAction *p_a, GVariant *p_v, gpointer p_data) {
(void)p_a;
(void)p_v;
- ggaze_window_prev(GGAZE_WINDOW(p_data));
+ _maybe_save_then(GGAZE_WINDOW(p_data), _proceed_prev, p_data);
}
static void
_action_next(GSimpleAction *p_a, GVariant *p_v, gpointer p_data) {
(void)p_a;
(void)p_v;
- ggaze_window_next(GGAZE_WINDOW(p_data));
+ _maybe_save_then(GGAZE_WINDOW(p_data), _proceed_next, p_data);
}
static void
_action_first(GSimpleAction *p_a, GVariant *p_v, gpointer p_data) {
(void)p_a;
(void)p_v;
- ggaze_window_first(GGAZE_WINDOW(p_data));
+ _maybe_save_then(GGAZE_WINDOW(p_data), _proceed_first, p_data);
}
static void
_action_last(GSimpleAction *p_a, GVariant *p_v, gpointer p_data) {
(void)p_a;
(void)p_v;
- ggaze_window_last(GGAZE_WINDOW(p_data));
+ _maybe_save_then(GGAZE_WINDOW(p_data), _proceed_last, p_data);
}
static void
@@ -751,6 +899,13 @@ _build_enhance_panel(GgazeWindow *p_win) {
p_win);
gtk_box_append(GTK_BOX(p_box), p_btn0);
+ /* A dim hint that 's' saves the enhanced copy. */
+ GtkWidget *p_hint = gtk_label_new("s Save enhanced copy");
+ gtk_widget_set_halign(p_hint, GTK_ALIGN_START);
+ gtk_widget_set_margin_top(p_hint, 8);
+ gtk_widget_add_css_class(p_hint, "dim-label");
+ gtk_box_append(GTK_BOX(p_box), p_hint);
+
GtkWidget *p_rev = gtk_revealer_new();
gtk_revealer_set_transition_type(GTK_REVEALER(p_rev),
GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT);
@@ -844,54 +999,7 @@ _action_enhance_save(GSimpleAction *p_a, GVariant *p_v, gpointer p_data) {
g_warning("ggaze: nothing to save (no enhance preset enabled)");
return;
}
- GFile *p_file = navigator_get_current(p_win->p_nav);
- if (p_file == NULL) {
- return;
- }
-
- /* Build <stem>-enhanced.<ext>; default .jpg for unknown extensions. */
- char *c_base = g_file_get_basename(p_file);
- char *c_dot = strrchr(c_base, '.');
- const char *c_ext = ".jpg";
- if (c_dot != NULL && (g_ascii_strcasecmp(c_dot, ".jpg") == 0 ||
- g_ascii_strcasecmp(c_dot, ".jpeg") == 0 ||
- g_ascii_strcasecmp(c_dot, ".png") == 0 ||
- g_ascii_strcasecmp(c_dot, ".webp") == 0)) {
- c_ext = c_dot;
- }
- char *c_stem;
- if (c_dot != NULL && c_ext == c_dot) {
- c_stem = g_strndup(c_base, (gsize)(c_dot - c_base));
- } else {
- c_stem = g_strdup(c_base);
- }
- GFile *p_dir = g_file_get_parent(p_file);
- char *c_outname = g_strdup_printf("%s-enhanced%s", c_stem, c_ext);
- GFile *p_out = g_file_get_child(p_dir, c_outname);
- g_free(c_outname);
- g_free(c_stem);
- g_free(c_base);
- g_object_unref(p_dir);
-
- GError *p_err = NULL;
- GeglBuffer *p_buf = enhancer_load(p_file, &p_err);
- gboolean b_ok = FALSE;
- if (p_buf != NULL) {
- const GPtrArray *p_presets = enhancer_get_presets(p_win->p_enhancer);
- b_ok = enhancer_export_chain(p_win->p_enhancer, p_buf, p_presets,
- p_win->u_enhance_mask, p_out, &p_err);
- g_object_unref(p_buf);
- }
- char *c_saved = b_ok ? g_file_get_basename(p_out) : NULL;
- g_object_unref(p_out);
- if (b_ok) {
- g_printerr("ggaze: saved %s\n", c_saved);
- } else {
- g_warning("ggaze: enhance-save failed: %s",
- p_err != NULL ? p_err->message : "(no detail)");
- }
- g_free(c_saved);
- g_clear_error(&p_err);
+ _enhance_do_save(p_win);
}
#else /* !GGAZE_HAVE_GEGL */
static void