From 3748ea718e0e829f32b760677a2042aab4d7662d Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Wed, 6 May 2020 21:26:25 +0200 Subject: Add +1 save button --- data/window.ui | 314 +++++++++++++++++++++++++++++++-------------------- src/gui_app_window.c | 165 ++++++++++++++++++++++++--- 2 files changed, 340 insertions(+), 139 deletions(-) diff --git a/data/window.ui b/data/window.ui index 9eefa36..2ca8457 100644 --- a/data/window.ui +++ b/data/window.ui @@ -25,183 +25,255 @@ - + True False - - - True - False - - - - - True - True - 0 - - True False - vertical - 5 - - - Start - True - True - True - - - - False - True - 0 - - - - - True - False - - - - False - True - 1 - - - + True False - Exposure (ms) - - - False - True - 2 - - - - - True - True - digits - exposure_adj - if-valid + + - False + True True - 3 - - - - - True - False - Gain - - - False - True - 4 - - - - - True - True - digits - gain_adj - 2 - if-valid - - - False - True - 5 + 0 True False + vertical + 5 + + + Start + True + True + True + + + + False + True + 0 + + + + + True + False + + + + False + True + 1 + + + + + True + False + Exposure (ms) + + + False + True + 2 + + + + + True + True + digits + exposure_adj + if-valid + + + False + True + 3 + + + + + True + False + Gain + + + False + True + 4 + + + + + True + True + digits + gain_adj + 2 + if-valid + + + False + True + 5 + + + + + True + False + + + White Balance + True + True + False + True + + + + False + True + 0 + + + + + Cal. + True + True + True + + + + False + True + 1 + + + + + False + True + 6 + + - - White Balance + + Rotate True True False True - + False True - 0 + 7 - - Cal. + + Save image True + False True True - + False True - 1 + 8 + + + + + Save image (n+1) + True + False + True + True + + + + False + True + 9 False True - 6 + 1 - - - Rotate - True - True - False - True - + + + -1 + + + + + info_bar + True + False + True + end + False + + + False + 6 + end + + + False - True - 7 + False + 0 - - - Save image - True - False - True - True - + + + False + 16 + + + True + False + label + + + False + True + 0 + + False - True - 8 + False + 0 - - False - True - 1 - diff --git a/src/gui_app_window.c b/src/gui_app_window.c index 1068556..efde847 100644 --- a/src/gui_app_window.c +++ b/src/gui_app_window.c @@ -24,6 +24,7 @@ #include "utils.h" +#include #include #include @@ -45,6 +46,9 @@ struct _GuiAppWindowPrivate { GtkButton *wb_cal_button; GtkCheckButton *rotate_button; GtkButton *save_button; + GtkButton *savepp_button; + GtkInfoBar *info_bar; + GtkLabel *info_bar_label; GdkRectangle allocation; cairo_matrix_t transform; cairo_surface_t *surface; @@ -59,6 +63,9 @@ struct _GuiAppWindowPrivate { struct device *device; bool started; gulong video_ready_handler_id; + char *save_filename; + const char *save_type; + guint info_bar_timeout_id; }; G_DEFINE_TYPE_WITH_PRIVATE(GuiAppWindow, gui_app_window, @@ -163,6 +170,8 @@ video_ready_cb(GuiApp *app, gpointer user_data) image->height, image->stride); gtk_widget_queue_draw(GTK_WIDGET(priv->video)); gtk_widget_set_sensitive(GTK_WIDGET(priv->save_button), TRUE); + if (priv->save_filename) + gtk_widget_set_sensitive(GTK_WIDGET(priv->savepp_button), TRUE); } } } @@ -216,6 +225,7 @@ video_stop(GuiAppWindowPrivate *priv) gtk_widget_queue_draw(GTK_WIDGET(priv->video)); gtk_button_set_label(priv->start_stop_button, "Start"); gtk_widget_set_sensitive(GTK_WIDGET(priv->save_button), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(priv->savepp_button), FALSE); } static gboolean @@ -251,6 +261,15 @@ destroy_cb(GtkWidget *widget, gpointer user_data) { GuiAppWindow *win = GUI_APP_WINDOW(widget); GuiAppWindowPrivate *priv = gui_app_window_get_instance_private(win); + if (priv->info_bar_timeout_id) { + g_source_remove(priv->info_bar_timeout_id); + priv->info_bar_timeout_id = 0; + } + if (priv->save_filename) { + g_free(priv->save_filename); + priv->save_filename = NULL; + priv->save_type = NULL; + } if (priv->started) video_stop(priv); if (priv->device) { @@ -340,6 +359,56 @@ rotate_button_toggled_cb(GtkToggleButton *button, gpointer user_data) video_size_update(priv); } +static gboolean +info_bar_hide(gpointer user_data) +{ + GuiAppWindowPrivate *priv = user_data; + gtk_info_bar_set_revealed(priv->info_bar, FALSE); + priv->info_bar_timeout_id = 0; + return G_SOURCE_REMOVE; +} + +static GdkPixbuf * +save_get_pixbuf(GuiAppWindowPrivate *priv) +{ + GdkPixbuf *pixbuf = gdk_pixbuf_get_from_surface(priv->surface, 0, 0, + priv->width, priv->height); + if (priv->rotate && pixbuf) { + GdkPixbuf *rotated = gdk_pixbuf_rotate_simple(pixbuf, 180); + g_object_unref(pixbuf); + pixbuf = rotated; + } + return pixbuf; +} + +/* Save an image, take ownership of filename. */ +static void +save_image(GuiAppWindow *win, GuiAppWindowPrivate *priv, char *filename, + const char *type, GdkPixbuf *pixbuf) +{ + GError *error = NULL; + /* Save image. */ + if (!gdk_pixbuf_save(pixbuf, filename, type, &error, NULL)) { + utils_dialog_error(GTK_WINDOW(win), "can not save image: %s", error->message); + g_error_free(error); + } else { + /* Keep information to save +1. */ + if (priv->save_filename) + g_free(priv->save_filename); + priv->save_filename = filename; + priv->save_type = type; + gtk_widget_set_sensitive(GTK_WIDGET(priv->savepp_button), TRUE); + /* Display message. */ + char *save_message = g_strdup_printf("Saved image \"%s\"", filename); + gtk_label_set_text(priv->info_bar_label, save_message); + g_free(save_message); + gtk_info_bar_set_revealed(priv->info_bar, TRUE); + if (priv->info_bar_timeout_id) + g_source_remove(priv->info_bar_timeout_id); + priv->info_bar_timeout_id = g_timeout_add(3000, info_bar_hide, priv); + } +} + static void save_button_clicked_cb(GtkButton *button, gpointer user_data) { @@ -347,13 +416,7 @@ save_button_clicked_cb(GtkButton *button, gpointer user_data) GUI_APP_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))); GuiAppWindowPrivate *priv = gui_app_window_get_instance_private(win); if (priv->surface) { - GdkPixbuf *pixbuf = gdk_pixbuf_get_from_surface(priv->surface, 0, 0, - priv->width, priv->height); - if (priv->rotate && pixbuf) { - GdkPixbuf *rotated = gdk_pixbuf_rotate_simple(pixbuf, 180); - g_object_unref(pixbuf); - pixbuf = rotated; - } + GdkPixbuf *pixbuf = save_get_pixbuf(priv); if (pixbuf) { GtkWidget *dialog = gtk_file_chooser_dialog_new("Save image", GTK_WINDOW(win), @@ -385,17 +448,10 @@ save_button_clicked_cb(GtkButton *button, gpointer user_data) type = "png"; else if (filter == jpg_filter) type = "jpeg"; - if (type) { - GError *error = NULL; - if (!gdk_pixbuf_save(pixbuf, filename, type, &error, - NULL)) { - utils_dialog_error(GTK_WINDOW(win), - "can not save image: %s", - error->message); - g_error_free(error); - } - } - g_free(filename); + if (type) + save_image(win, priv, filename, type, pixbuf); + else + g_free(filename); } } gtk_widget_destroy(dialog); @@ -404,6 +460,68 @@ save_button_clicked_cb(GtkButton *button, gpointer user_data) } } +static void +savepp_button_clicked_cb(GtkButton *button, gpointer user_data) +{ + GuiAppWindow *win = + GUI_APP_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))); + GuiAppWindowPrivate *priv = gui_app_window_get_instance_private(win); + if (priv->surface && priv->save_filename) { + GdkPixbuf *pixbuf = save_get_pixbuf(priv); + if (pixbuf) { + /* Compute the new filename: + * - if there is a number, increment it, using the same number of + * digits, + * - else, add "_2". */ + char *dirname = g_path_get_dirname(priv->save_filename); + char *basename = g_path_get_basename(priv->save_filename); + char **parts = g_strsplit(basename, ".", 2); + char *root = parts[0]; + const char *extension = parts[1]; + char *root_end = root + strlen(root); + char *numbers = root_end; + while (numbers != root && g_ascii_isdigit(numbers[-1])) + numbers--; + unsigned long n = 2; + const char *sep = "_"; + int width = 1; + if (numbers != root_end) { + char *tail; + errno = 0; + /* n is unsigned so that it can be incremented even if value + * is maximum. */ + long v = strtol(numbers, &tail, 10); + if (tail == root_end && errno == 0) { + n = (unsigned long)v + 1; + *numbers = '\0'; + sep = ""; + width = root_end - numbers; + } + } + char *new_basename = g_strdup_printf("%s%s%0*lu%s%s", + root, sep, width, n, + extension ? "." : "", + extension ? extension : ""); + char *new_filename = g_build_filename(dirname, new_basename, + NULL); + /* Test for file existence. */ + if (g_file_test(new_filename, G_FILE_TEST_EXISTS)) { + utils_dialog_error(GTK_WINDOW(win), + "Will not overwrite %s: file exists.", new_filename); + g_free(new_filename); + } else { + /* Save image. */ + save_image(win, priv, new_filename, priv->save_type, pixbuf); + } + /* Cleanup. */ + g_free(dirname); + g_free(basename); + g_strfreev(parts); + g_object_unref(pixbuf); + } + } +} + static void gui_app_window_init(GuiAppWindow *win) { @@ -424,6 +542,9 @@ gui_app_window_init(GuiAppWindow *win) priv->device = NULL; priv->started = false; priv->video_ready_handler_id = 0; + priv->save_filename = NULL; + priv->save_type = NULL; + priv->info_bar_timeout_id = 0; } static void @@ -449,6 +570,12 @@ gui_app_window_class_init(GuiAppWindowClass *class) GuiAppWindow, rotate_button); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(class), GuiAppWindow, save_button); + gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(class), + GuiAppWindow, savepp_button); + gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(class), + GuiAppWindow, info_bar); + gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(class), + GuiAppWindow, info_bar_label); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), destroy_cb); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), @@ -471,6 +598,8 @@ gui_app_window_class_init(GuiAppWindowClass *class) rotate_button_toggled_cb); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), save_button_clicked_cb); + gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), + savepp_button_clicked_cb); } GuiAppWindow * -- cgit v1.2.3