/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Pix * * Copyright (C) 2014 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "gth-curve-editor.h" #include "gth-curve-preset.h" #include "gth-curve-preset-editor-dialog.h" #include "gth-file-tool-curves.h" #include "gth-curve.h" #include "gth-points.h" #include "gth-preview-tool.h" #define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x))) #define APPLY_DELAY 150 #define PREVIEW_SIZE 0.9 struct _GthFileToolCurvesPrivate { cairo_surface_t *destination; cairo_surface_t *preview; GtkBuilder *builder; GthTask *image_task; guint apply_event; GthImageViewerTool *preview_tool; GthHistogram *histogram; gboolean view_original; gboolean apply_to_original; gboolean closing; gboolean apply_current_curve; GtkWidget *curve_editor; GtkWidget *preview_button; GtkWidget *preview_channel_button; GtkWidget *stack; GtkWidget *show_presets_button; GtkWidget *reset_button; GtkWidget *add_preset_button; GthCurvePreset *preset; GtkWidget *filter_grid; }; G_DEFINE_TYPE_WITH_CODE (GthFileToolCurves, gth_file_tool_curves, GTH_TYPE_IMAGE_VIEWER_PAGE_TOOL, G_ADD_PRIVATE (GthFileToolCurves)) /* -- apply_changes -- */ typedef struct { long *value_map[GTH_HISTOGRAM_N_CHANNELS]; GthCurve *curve[GTH_HISTOGRAM_N_CHANNELS]; int current_curve; gboolean apply_current_curve; } TaskData; static void curves_setup (TaskData *task_data, cairo_surface_t *source) { int c, v; for (c = GTH_HISTOGRAM_CHANNEL_VALUE; c <= GTH_HISTOGRAM_CHANNEL_BLUE; c++) { task_data->value_map[c] = g_new (long, 256); for (v = 0; v <= 255; v++) { double u; if ((c != task_data->current_curve) || task_data->apply_current_curve) u = gth_curve_eval (task_data->curve[c], v); else u = v; if (c > GTH_HISTOGRAM_CHANNEL_VALUE) u = task_data->value_map[GTH_HISTOGRAM_CHANNEL_VALUE][(int)u]; task_data->value_map[c][v] = u; } } } static inline guchar value_func (TaskData *task_data, int n_channel, guchar value) { return (guchar) task_data->value_map[n_channel][value]; } static gpointer curves_exec (GthAsyncTask *task, gpointer user_data) { TaskData *task_data = user_data; cairo_surface_t *source; cairo_format_t format; int width; int height; int source_stride; cairo_surface_t *destination; int destination_stride; unsigned char *p_source_line; unsigned char *p_destination_line; unsigned char *p_source; unsigned char *p_destination; gboolean cancelled; double progress; int x, y, temp; unsigned char red, green, blue, alpha; /* initialize the extra data */ source = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task)); curves_setup (task_data, source); /* convert the image */ format = cairo_image_surface_get_format (source); width = cairo_image_surface_get_width (source); height = cairo_image_surface_get_height (source); source_stride = cairo_image_surface_get_stride (source); destination = cairo_image_surface_create (format, width, height); destination_stride = cairo_image_surface_get_stride (destination); p_source_line = _cairo_image_surface_flush_and_get_data (source); p_destination_line = _cairo_image_surface_flush_and_get_data (destination); for (y = 0; y < height; y++) { gth_async_task_get_data (task, NULL, &cancelled, NULL); if (cancelled) { cairo_surface_destroy (destination); cairo_surface_destroy (source); return NULL; } progress = (double) y / height; gth_async_task_set_data (task, NULL, NULL, &progress); p_source = p_source_line; p_destination = p_destination_line; for (x = 0; x < width; x++) { CAIRO_GET_RGBA (p_source, red, green, blue, alpha); red = value_func (task_data, GTH_HISTOGRAM_CHANNEL_RED, red); green = value_func (task_data, GTH_HISTOGRAM_CHANNEL_GREEN, green); blue = value_func (task_data, GTH_HISTOGRAM_CHANNEL_BLUE, blue); CAIRO_SET_RGBA (p_destination, red, green, blue, alpha); p_source += 4; p_destination += 4; } p_source_line += source_stride; p_destination_line += destination_stride; } cairo_surface_mark_dirty (destination); gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), destination); cairo_surface_destroy (destination); cairo_surface_destroy (source); return NULL; } static void apply_changes (GthFileToolCurves *self); static void image_task_completed_cb (GthTask *task, GError *error, gpointer user_data) { GthFileToolCurves *self = user_data; GthImage *destination_image; self->priv->image_task = NULL; if (self->priv->closing) { g_object_unref (task); gth_image_viewer_page_tool_reset_image (GTH_IMAGE_VIEWER_PAGE_TOOL (self)); return; } if (error != NULL) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) apply_changes (self); g_object_unref (task); return; } destination_image = gth_image_task_get_destination (GTH_IMAGE_TASK (task)); if (destination_image == NULL) { g_object_unref (task); return; } cairo_surface_destroy (self->priv->destination); self->priv->destination = gth_image_get_cairo_surface (destination_image); if (self->priv->apply_to_original) { if (self->priv->destination != NULL) { GtkWidget *window; GthViewerPage *viewer_page; window = gth_file_tool_get_window (GTH_FILE_TOOL (self)); viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window)); gth_image_viewer_page_set_image (GTH_IMAGE_VIEWER_PAGE (viewer_page), self->priv->destination, TRUE); } gth_file_tool_hide_options (GTH_FILE_TOOL (self)); } else { if (! self->priv->view_original) gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool), self->priv->destination); } g_object_unref (task); } static TaskData * task_data_new (GthPoints *points) { TaskData *task_data; int c; task_data = g_new (TaskData, 1); for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++) { task_data->value_map[c] = NULL; task_data->curve[c] = gth_curve_new (GTH_TYPE_BEZIER, points + c); } return task_data; } static void task_data_destroy (gpointer user_data) { TaskData *task_data = user_data; int c; if (task_data == NULL) return; for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++) g_object_unref (task_data->curve[c]); for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++) g_free (task_data->value_map[c]); g_free (task_data); } static GthTask * get_curves_task (GthPoints *points, int current_curve, gboolean apply_current_curve) { TaskData *task_data; task_data = task_data_new (points); task_data->current_curve = current_curve; task_data->apply_current_curve = apply_current_curve; return gth_image_task_new (_("Applying changes"), NULL, curves_exec, NULL, task_data, task_data_destroy); } static gboolean apply_cb (gpointer user_data) { GthFileToolCurves *self = user_data; GtkWidget *window; GthPoints points[GTH_HISTOGRAM_N_CHANNELS]; if (self->priv->apply_event != 0) { g_source_remove (self->priv->apply_event); self->priv->apply_event = 0; } if (self->priv->image_task != NULL) { gth_task_cancel (self->priv->image_task); return FALSE; } window = gth_file_tool_get_window (GTH_FILE_TOOL (self)); gth_points_array_init (points); gth_curve_editor_get_points (GTH_CURVE_EDITOR (self->priv->curve_editor), points); self->priv->image_task = get_curves_task (points, gth_curve_editor_get_current_channel (GTH_CURVE_EDITOR (self->priv->curve_editor)), self->priv->apply_current_curve); gth_points_array_dispose (points); if (self->priv->apply_to_original) gth_image_task_set_source_surface (GTH_IMAGE_TASK (self->priv->image_task), gth_image_viewer_page_tool_get_source (GTH_IMAGE_VIEWER_PAGE_TOOL (self))); else gth_image_task_set_source_surface (GTH_IMAGE_TASK (self->priv->image_task), self->priv->preview); g_signal_connect (self->priv->image_task, "completed", G_CALLBACK (image_task_completed_cb), self); gth_browser_exec_task (GTH_BROWSER (window), self->priv->image_task, GTH_TASK_FLAGS_DEFAULT); return FALSE; } static void apply_changes (GthFileToolCurves *self) { if (self->priv->apply_event != 0) { g_source_remove (self->priv->apply_event); self->priv->apply_event = 0; } self->priv->apply_event = g_timeout_add (APPLY_DELAY, apply_cb, self); } static void reset_button_clicked_cb (GtkButton *button, gpointer user_data) { GthFileToolCurves *self = user_data; gth_curve_editor_reset (GTH_CURVE_EDITOR (self->priv->curve_editor)); } static void add_to_presets_dialog_response_cb (GtkDialog *dialog, int response, gpointer user_data) { GthFileToolCurves *self = user_data; char *name; GthPoints points[GTH_HISTOGRAM_N_CHANNELS]; if (response != GTK_RESPONSE_OK) { gtk_widget_destroy (GTK_WIDGET (dialog)); return; } name = gth_request_dialog_get_normalized_text (GTH_REQUEST_DIALOG (dialog)); if (_g_utf8_all_spaces (name)) { g_free (name); gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, _("No name specified")); return; } if (g_regex_match_simple ("/", name, 0, 0)) { char *message; message = g_strdup_printf (_("Invalid name. The following characters are not allowed: %s"), "/"); gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, message); g_free (message); g_free (name); return; } gtk_widget_destroy (GTK_WIDGET (dialog)); gth_points_array_init (points); gth_curve_editor_get_points (GTH_CURVE_EDITOR (self->priv->curve_editor), points); gth_curve_preset_add (self->priv->preset, name, points); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->priv->show_presets_button), TRUE); gth_points_array_dispose (points); g_free (name); } static void add_to_presets_button_clicked_cb (GtkButton *button, gpointer user_data) { GthFileToolCurves *self = user_data; GtkWidget *dialog; dialog = gth_request_dialog_new (GTK_WINDOW (gth_file_tool_get_window (GTH_FILE_TOOL (self))), GTK_DIALOG_MODAL, _("Add to Presets"), _("Enter the preset name:"), _GTK_LABEL_CANCEL, _GTK_LABEL_SAVE); g_signal_connect (dialog, "response", G_CALLBACK (add_to_presets_dialog_response_cb), self); gtk_window_present (GTK_WINDOW (dialog)); } static void curve_editor_changed_cb (GthCurveEditor *curve_editor, gpointer user_data) { GthFileToolCurves *self = user_data; apply_changes (self); if (g_strcmp0 (gtk_stack_get_visible_child_name (GTK_STACK (self->priv->stack)), "presets") != 0) gth_filter_grid_activate (GTH_FILTER_GRID (self->priv->filter_grid), GTH_FILTER_GRID_NO_FILTER); } static void curve_editor_current_channel_changed_cb (GObject *gobject, GParamSpec *pspec, gpointer user_data) { GthFileToolCurves *self = user_data; if (! self->priv->apply_current_curve) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->priv->preview_channel_button), TRUE); } static void _gth_file_tool_curves_set_view_original (GthFileToolCurves *self, gboolean view_original, gboolean update_image) { self->priv->view_original = view_original; _g_signal_handlers_block_by_data (self->priv->preview_button, self); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->priv->preview_button), ! self->priv->view_original); _g_signal_handlers_unblock_by_data (self->priv->preview_button, self); gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (self->priv->preview_channel_button), self->priv->view_original); gtk_widget_set_sensitive (self->priv->preview_channel_button, ! self->priv->view_original); if (update_image) { if (self->priv->view_original) gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool), self->priv->preview); else gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool), self->priv->destination); } } static void preview_checkbutton_toggled_cb (GtkToggleButton *togglebutton, gpointer user_data) { GthFileToolCurves *self = user_data; _gth_file_tool_curves_set_view_original (self, ! gtk_toggle_button_get_active (togglebutton), TRUE); } static void preview_channel_checkbutton_toggled_cb (GtkToggleButton *togglebutton, gpointer user_data) { GthFileToolCurves *self = user_data; self->priv->apply_current_curve = gtk_toggle_button_get_active (togglebutton); apply_changes (self); } static void show_options_button_clicked_cb (GtkButton *button, gpointer user_data) { GthFileToolCurves *self = user_data; gtk_stack_set_visible_child_name (GTK_STACK (self->priv->stack), "options"); g_signal_handlers_block_matched (self->priv->show_presets_button, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->priv->show_presets_button), FALSE); g_signal_handlers_unblock_matched (self->priv->show_presets_button, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self); gtk_widget_set_visible (self->priv->reset_button, TRUE); gtk_widget_set_visible (self->priv->add_preset_button, TRUE); } static void edit_presets_button_clicked_cb (GtkButton *button, gpointer user_data) { GthFileToolCurves *self = user_data; GtkWidget *dialog; dialog = gth_curve_preset_editor_dialog_new (GTK_WINDOW (gth_file_tool_get_window (GTH_FILE_TOOL (self))), self->priv->preset); gtk_widget_show (dialog); } static void preset_changed_cb (GthCurvePreset *preset, GthPresetAction action, int preset_id, gpointer user_data) { GthFileToolCurves *self = user_data; const char *preset_name; GthPoints *points; GError *error = NULL; GList *order; if (! gth_curve_preset_save (self->priv->preset, &error)) { _gtk_error_dialog_from_gerror_show (NULL, _("Could not save the file"), error); g_clear_error (&error); return; } switch (action) { case GTH_PRESET_ACTION_ADDED: if (gth_curve_preset_get_by_id (preset, preset_id, &preset_name, &points)) { gth_filter_grid_add_filter (GTH_FILTER_GRID (self->priv->filter_grid), preset_id, get_curves_task (points, 0, TRUE), preset_name, NULL); gth_filter_grid_generate_preview (GTH_FILTER_GRID (self->priv->filter_grid), preset_id, self->priv->preview); } break; case GTH_PRESET_ACTION_REMOVED: gth_filter_grid_remove_filter (GTH_FILTER_GRID (self->priv->filter_grid), preset_id); break; case GTH_PRESET_ACTION_RENAMED: if (gth_curve_preset_get_by_id (preset, preset_id, &preset_name, NULL)) gth_filter_grid_rename_filter (GTH_FILTER_GRID (self->priv->filter_grid), preset_id, preset_name); break; case GTH_PRESET_ACTION_CHANGED_ORDER: order = gth_curve_preset_get_order (preset); gth_filter_grid_change_order (GTH_FILTER_GRID (self->priv->filter_grid), order); g_list_free (order); break; } } static void filter_grid_activated_cb (GthFilterGrid *filter_grid, int filter_id, gpointer user_data) { GthFileToolCurves *self = user_data; _gth_file_tool_curves_set_view_original (self, FALSE, FALSE); if (filter_id == GTH_FILTER_GRID_NO_FILTER) { if (g_strcmp0 (gtk_stack_get_visible_child_name (GTK_STACK (self->priv->stack)), "presets") == 0) { GthPoints points[GTH_HISTOGRAM_N_CHANNELS]; int c; /* reset the curve */ for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++) { GthPoints *p = points + c; gth_points_init (p, 2); gth_points_set_point (p, 0, 0, 0); gth_points_set_point (p, 1, 255, 255); } gth_curve_editor_set_points (GTH_CURVE_EDITOR (self->priv->curve_editor), points); gth_points_array_dispose (points); } } else { GthPoints *points; if (gth_curve_preset_get_by_id (GTH_CURVE_PRESET (self->priv->preset), filter_id, NULL, &points)) gth_curve_editor_set_points (GTH_CURVE_EDITOR (self->priv->curve_editor), points); } } static GtkWidget * gth_file_tool_curves_get_options (GthFileTool *base) { GthFileToolCurves *self; GthViewerPage *viewer_page; GtkWidget *viewer; cairo_surface_t *source; GtkWidget *options; int width, height; GtkAllocation allocation; GtkWidget *container; self = (GthFileToolCurves *) base; viewer_page = gth_image_viewer_page_tool_get_page (GTH_IMAGE_VIEWER_PAGE_TOOL (self)); if (viewer_page == NULL) return NULL; _cairo_clear_surface (&self->priv->destination); _cairo_clear_surface (&self->priv->preview); source = gth_image_viewer_page_tool_get_source (GTH_IMAGE_VIEWER_PAGE_TOOL (self)); if (source == NULL) return NULL; width = cairo_image_surface_get_width (source); height = cairo_image_surface_get_height (source); viewer = gth_image_viewer_page_get_image_viewer (GTH_IMAGE_VIEWER_PAGE (viewer_page)); gtk_widget_get_allocation (GTK_WIDGET (viewer), &allocation); if (scale_keeping_ratio (&width, &height, PREVIEW_SIZE * allocation.width, PREVIEW_SIZE * allocation.height, FALSE)) self->priv->preview = _cairo_image_surface_scale_fast (source, width, height); else self->priv->preview = cairo_surface_reference (source); self->priv->destination = cairo_surface_reference (self->priv->preview); self->priv->apply_to_original = FALSE; self->priv->view_original = FALSE; self->priv->closing = FALSE; container = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); self->priv->stack = gtk_stack_new (); gtk_stack_set_transition_type (GTK_STACK (self->priv->stack), GTK_STACK_TRANSITION_TYPE_CROSSFADE); gtk_box_pack_start (GTK_BOX (container), self->priv->stack, FALSE, FALSE, 0); gtk_widget_show (self->priv->stack); self->priv->builder = _gtk_builder_new_from_file ("curves-options.ui", "file_tools"); options = _gtk_builder_get_widget (self->priv->builder, "options"); gtk_widget_show (options); gtk_stack_add_named (GTK_STACK (self->priv->stack), options, "options"); self->priv->curve_editor = gth_curve_editor_new (self->priv->histogram); gtk_widget_show (self->priv->curve_editor); gtk_box_pack_start (GTK_BOX (GET_WIDGET ("curves_box")), self->priv->curve_editor, TRUE, TRUE, 0); g_signal_connect (self->priv->curve_editor, "changed", G_CALLBACK (curve_editor_changed_cb), self); g_signal_connect (self->priv->curve_editor, "notify::current-channel", G_CALLBACK (curve_editor_current_channel_changed_cb), self); self->priv->preview_button = GET_WIDGET ("preview_checkbutton"); g_signal_connect (self->priv->preview_button, "toggled", G_CALLBACK (preview_checkbutton_toggled_cb), self); self->priv->preview_channel_button = GET_WIDGET ("preview_channel_checkbutton"); g_signal_connect (self->priv->preview_channel_button, "toggled", G_CALLBACK (preview_channel_checkbutton_toggled_cb), self); { GtkWidget *header_bar; GtkWidget *button; GFile *file; int i; GtkWidget *presets; header_bar = gtk_header_bar_new (); gtk_header_bar_set_title (GTK_HEADER_BAR (header_bar), _("Presets")); button = gtk_button_new_from_icon_name ("go-previous-symbolic", GTK_ICON_SIZE_BUTTON); g_signal_connect (button, "clicked", G_CALLBACK (show_options_button_clicked_cb), self); gtk_widget_show (button); gtk_header_bar_pack_start (GTK_HEADER_BAR (header_bar), button); button = gtk_button_new_from_icon_name ("edit-symbolic", GTK_ICON_SIZE_BUTTON); g_signal_connect (button, "clicked", G_CALLBACK (edit_presets_button_clicked_cb), self); gtk_widget_show (button); gtk_header_bar_pack_end (GTK_HEADER_BAR (header_bar), button); gtk_widget_show (header_bar); file = gth_user_dir_get_file_for_write (GTH_DIR_CONFIG, "pix", "curves.xml", NULL); self->priv->preset = gth_curve_preset_new_from_file (file); g_object_unref (file); g_signal_connect (self->priv->preset, "preset_changed", G_CALLBACK (preset_changed_cb), self); self->priv->filter_grid = gth_filter_grid_new (); for (i = 0; i < gth_curve_preset_get_size (self->priv->preset); i++) { GthPoints *points; const char *name; int id; if (gth_curve_preset_get_nth (self->priv->preset, i, &id, &name, &points)) gth_filter_grid_add_filter (GTH_FILTER_GRID (self->priv->filter_grid), id, get_curves_task (points, 0, TRUE), name, NULL); } g_signal_connect (self->priv->filter_grid, "activated", G_CALLBACK (filter_grid_activated_cb), self); gtk_widget_show (self->priv->filter_grid); presets = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); gtk_box_pack_start (GTK_BOX (presets), header_bar, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (presets), self->priv->filter_grid, FALSE, FALSE, 0); gtk_widget_show (presets); gtk_stack_add_named (GTK_STACK (self->priv->stack), presets, "presets"); gth_filter_grid_generate_previews (GTH_FILTER_GRID (self->priv->filter_grid), self->priv->preview); } gtk_stack_set_visible_child_name (GTK_STACK (self->priv->stack), "options"); gtk_widget_show_all (container); self->priv->preview_tool = gth_preview_tool_new (); gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool), self->priv->preview); gth_image_viewer_set_tool (GTH_IMAGE_VIEWER (viewer), self->priv->preview_tool); gth_histogram_calculate_for_image (self->priv->histogram, self->priv->preview); apply_changes (self); return container; } static void gth_file_tool_curves_destroy_options (GthFileTool *base) { GthFileToolCurves *self; GthViewerPage *viewer_page; self = (GthFileToolCurves *) base; if (self->priv->apply_event != 0) { g_source_remove (self->priv->apply_event); self->priv->apply_event = 0; } viewer_page = gth_image_viewer_page_tool_get_page (GTH_IMAGE_VIEWER_PAGE_TOOL (self)); gth_image_viewer_page_reset_viewer_tool (GTH_IMAGE_VIEWER_PAGE (viewer_page)); gth_viewer_page_update_sensitivity (viewer_page); _cairo_clear_surface (&self->priv->preview); _cairo_clear_surface (&self->priv->destination); _g_clear_object (&self->priv->builder); } static void gth_file_tool_curves_apply_options (GthFileTool *base) { GthFileToolCurves *self; self = (GthFileToolCurves *) base; self->priv->apply_to_original = TRUE; apply_changes (self); } static void presets_toggled_cb (GtkToggleButton *button, GthFileToolCurves * self) { gboolean show_presets; show_presets = gtk_toggle_button_get_active (button); gtk_stack_set_visible_child_name (GTK_STACK (self->priv->stack), show_presets ? "presets" : "options"); gtk_widget_set_visible (self->priv->reset_button, ! show_presets); gtk_widget_set_visible (self->priv->add_preset_button, ! show_presets); } static void gth_file_tool_curves_populate_headerbar (GthFileTool *base, GthBrowser *browser) { GthFileToolCurves *self; GtkWidget *button; self = (GthFileToolCurves *) base; /* reset button */ button = gth_browser_add_header_bar_button (browser, GTH_BROWSER_HEADER_SECTION_EDITOR_COMMANDS, "edit-undo-symbolic", _("Reset"), NULL, NULL); self->priv->reset_button = button; g_signal_connect (button, "clicked", G_CALLBACK (reset_button_clicked_cb), self); /* add to presets */ button = gth_browser_add_header_bar_button (browser, GTH_BROWSER_HEADER_SECTION_EDITOR_COMMANDS, "list-add-symbolic", _("Add to presets"), NULL, NULL); self->priv->add_preset_button = button; g_signal_connect (button, "clicked", G_CALLBACK (add_to_presets_button_clicked_cb), self); /* presets */ button = gth_browser_add_header_bar_toggle_button (browser, GTH_BROWSER_HEADER_SECTION_EDITOR_COMMANDS, "presets-symbolic", _("Presets"), NULL, NULL); gtk_widget_set_margin_start (button, GTH_BROWSER_HEADER_BAR_BIG_MARGIN); self->priv->show_presets_button = button; g_signal_connect (button, "toggled", G_CALLBACK (presets_toggled_cb), self); } static void gth_file_tool_sharpen_reset_image (GthImageViewerPageTool *base) { GthFileToolCurves *self = (GthFileToolCurves *) base; if (self->priv->image_task != NULL) { self->priv->closing = TRUE; gth_task_cancel (self->priv->image_task); return; } if (self->priv->apply_event != 0) { g_source_remove (self->priv->apply_event); self->priv->apply_event = 0; } gth_image_viewer_page_reset (GTH_IMAGE_VIEWER_PAGE (gth_image_viewer_page_tool_get_page (GTH_IMAGE_VIEWER_PAGE_TOOL (self)))); gth_file_tool_hide_options (GTH_FILE_TOOL (self)); } static void gth_file_tool_curves_init (GthFileToolCurves *self) { self->priv = gth_file_tool_curves_get_instance_private (self); self->priv->preview = NULL; self->priv->destination = NULL; self->priv->builder = NULL; self->priv->image_task = NULL; self->priv->view_original = FALSE; self->priv->apply_current_curve = TRUE; self->priv->histogram = gth_histogram_new (); gth_file_tool_construct (GTH_FILE_TOOL (self), "curves-symbolic", _("Color Curves"), GTH_TOOLBOX_SECTION_COLORS); gtk_widget_set_tooltip_text (GTK_WIDGET (self), _("Adjust color curves")); } static void gth_file_tool_curves_finalize (GObject *object) { GthFileToolCurves *self; g_return_if_fail (object != NULL); g_return_if_fail (GTH_IS_FILE_TOOL_CURVES (object)); self = (GthFileToolCurves *) object; cairo_surface_destroy (self->priv->preview); cairo_surface_destroy (self->priv->destination); _g_object_unref (self->priv->builder); _g_object_unref (self->priv->histogram); G_OBJECT_CLASS (gth_file_tool_curves_parent_class)->finalize (object); } static void gth_file_tool_curves_class_init (GthFileToolCurvesClass *klass) { GObjectClass *gobject_class; GthFileToolClass *file_tool_class; GthImageViewerPageToolClass *image_viewer_page_tool_class; gobject_class = (GObjectClass*) klass; gobject_class->finalize = gth_file_tool_curves_finalize; file_tool_class = (GthFileToolClass *) klass; file_tool_class->get_options = gth_file_tool_curves_get_options; file_tool_class->destroy_options = gth_file_tool_curves_destroy_options; file_tool_class->apply_options = gth_file_tool_curves_apply_options; file_tool_class->populate_headerbar = gth_file_tool_curves_populate_headerbar; image_viewer_page_tool_class = (GthImageViewerPageToolClass *) klass; image_viewer_page_tool_class->reset_image = gth_file_tool_sharpen_reset_image; }