diff options
| author | Paul Buetow <paul@buetow.org> | 2026-07-22 10:03:00 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-07-22 10:03:00 +0300 |
| commit | d0a824c685db1567facaf08fcae19ee0636496da (patch) | |
| tree | 4b2100be7133b013b91660436431cd6d3f940503 /src | |
| parent | 00f9ad7f18349781a679e7b3281d7e8f84ebb5b1 (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.c | 216 |
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 |
