[gthumb] grayscale tool: show a preview of the different filters
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb] grayscale tool: show a preview of the different filters
- Date: Tue, 23 Dec 2014 11:33:11 +0000 (UTC)
commit 741c248a902b0b52deac43c30cf44c8017fafc21
Author: Paolo Bacchilega <paobac src gnome org>
Date: Sun Dec 21 19:56:56 2014 +0100
grayscale tool: show a preview of the different filters
extensions/file_tools/data/ui/grayscale-options.ui | 120 +----
extensions/file_tools/gth-file-tool-grayscale.c | 116 +++--
gthumb/Makefile.am | 2 +
gthumb/gth-filter-grid.c | 541 ++++++++++++++++++++
gthumb/gth-filter-grid.h | 80 +++
gthumb/resources/gthumb.css | 8 +
po/POTFILES.in | 2 +
7 files changed, 712 insertions(+), 157 deletions(-)
---
diff --git a/extensions/file_tools/data/ui/grayscale-options.ui
b/extensions/file_tools/data/ui/grayscale-options.ui
index 66c17f6..bf34634 100644
--- a/extensions/file_tools/data/ui/grayscale-options.ui
+++ b/extensions/file_tools/data/ui/grayscale-options.ui
@@ -1,109 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.16.0 on Sat Oct 12 13:11:33 2013 -->
+<!-- Generated with glade 3.18.3 -->
<interface>
- <!-- interface-requires gtk+ 3.0 -->
+ <requires lib="gtk+" version="3.12"/>
<object class="GtkAlignment" id="options">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="top_padding">6</property>
<child>
- <object class="GtkVBox" id="vbox2">
+ <object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkVBox" id="vbox1">
+ <object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
+ <property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
- <object class="GtkBox" id="box1">
+ <object class="GtkBox" id="filter_grid_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">12</property>
<child>
- <object class="GtkLabel" id="label1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Method</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkRadioButton" id="brightness_radiobutton">
- <property name="label" translatable="yes">_Brightness</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="xalign">0</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="saturation_radiobutton">
- <property name="label" translatable="yes">_Saturation</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="xalign">0</property>
- <property name="draw_indicator">True</property>
- <property name="group">brightness_radiobutton</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="average_radiobutton">
- <property name="label" translatable="yes">_Average</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="xalign">0</property>
- <property name="draw_indicator">True</property>
- <property name="group">brightness_radiobutton</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
+ <placeholder/>
</child>
</object>
<packing>
@@ -112,24 +31,6 @@
<property name="position">0</property>
</packing>
</child>
- <child>
- <object class="GtkCheckButton" id="preview_checkbutton">
- <property name="label" translatable="yes">_Preview</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="xalign">0</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
</object>
<packing>
<property name="expand">False</property>
@@ -138,7 +39,7 @@
</packing>
</child>
<child>
- <object class="GtkHSeparator" id="hseparator1">
+ <object class="GtkSeparator" id="separator1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
@@ -150,10 +51,11 @@
</packing>
</child>
<child>
- <object class="GtkHButtonBox" id="hbuttonbox1">
+ <object class="GtkButtonBox" id="buttonbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">12</property>
+ <property name="homogeneous">True</property>
<property name="layout_style">center</property>
<child>
<object class="GtkButton" id="ok_button">
diff --git a/extensions/file_tools/gth-file-tool-grayscale.c b/extensions/file_tools/gth-file-tool-grayscale.c
index 6361d0b..d75c0f0 100644
--- a/extensions/file_tools/gth-file-tool-grayscale.c
+++ b/extensions/file_tools/gth-file-tool-grayscale.c
@@ -50,6 +50,9 @@ struct _GthFileToolGrayscalePrivate {
guint apply_event;
gboolean apply_to_original;
gboolean closing;
+ Method method;
+ Method last_applied_method;
+ gboolean view_original;
};
@@ -162,7 +165,7 @@ image_task_completed_cb (GthTask *task,
gpointer user_data)
{
GthFileToolGrayscale *self = user_data;
- GthImage *destination_image;
+ GthImage *destination_image;
self->priv->image_task = NULL;
@@ -187,6 +190,7 @@ image_task_completed_cb (GthTask *task,
cairo_surface_destroy (self->priv->destination);
self->priv->destination = gth_image_get_cairo_surface (destination_image);
+ self->priv->last_applied_method = self->priv->method;
if (self->priv->apply_to_original) {
if (self->priv->destination != NULL) {
@@ -201,7 +205,7 @@ image_task_completed_cb (GthTask *task,
gth_file_tool_hide_options (GTH_FILE_TOOL (self));
}
else {
- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("preview_checkbutton"))))
+ if (! self->priv->view_original)
gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool),
self->priv->destination);
}
@@ -209,12 +213,28 @@ image_task_completed_cb (GthTask *task,
}
+static GthTask *
+get_image_task_for_method (Method method)
+{
+ GrayscaleData *grayscale_data;
+
+ grayscale_data = g_new0 (GrayscaleData, 1);
+ grayscale_data->method = method;
+
+ return gth_image_task_new (_("Applying changes"),
+ NULL,
+ grayscale_exec,
+ NULL,
+ grayscale_data,
+ grayscale_data_free);
+}
+
+
static gboolean
apply_cb (gpointer user_data)
{
GthFileToolGrayscale *self = user_data;
GtkWidget *window;
- GrayscaleData *grayscale_data;
if (self->priv->apply_event != 0) {
g_source_remove (self->priv->apply_event);
@@ -228,20 +248,7 @@ apply_cb (gpointer user_data)
window = gth_file_tool_get_window (GTH_FILE_TOOL (self));
- grayscale_data = g_new0 (GrayscaleData, 1);
- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("brightness_radiobutton"))))
- grayscale_data->method = METHOD_BRIGHTNESS;
- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("saturation_radiobutton"))))
- grayscale_data->method = METHOD_SATURATION;
- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("average_radiobutton"))))
- grayscale_data->method = METHOD_AVARAGE;
-
- self->priv->image_task = gth_image_task_new (_("Applying changes"),
- NULL,
- grayscale_exec,
- NULL,
- grayscale_data,
- grayscale_data_free);
+ self->priv->image_task = get_image_task_for_method (self->priv->method);
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
@@ -300,24 +307,23 @@ ok_button_clicked_cb (GtkButton *button,
static void
-preview_checkbutton_toggled_cb (GtkToggleButton *togglebutton,
- gpointer user_data)
+filter_grid_activated_cb (GthFilterGrid *filter_grid,
+ int filter_id,
+ gpointer user_data)
{
GthFileToolGrayscale *self = user_data;
- if (gtk_toggle_button_get_active (togglebutton))
- gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool),
self->priv->destination);
- else
+ self->priv->view_original = (filter_id == GTH_FILTER_GRID_NO_ACTIVE_FILTER);
+ if (self->priv->view_original) {
gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool), self->priv->preview);
-}
-
-
-static void
-method_changed_cb (GtkButton *button,
- gpointer user_data)
-{
- GthFileToolGrayscale *self = user_data;
- apply_changes (self);
+ }
+ else if (filter_id == self->priv->last_applied_method) {
+ gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool),
self->priv->destination);
+ }
+ else {
+ self->priv->method = filter_id;
+ apply_changes (self);
+ }
}
@@ -332,6 +338,7 @@ gth_file_tool_grayscale_get_options (GthFileTool *base)
GtkWidget *options;
int width, height;
GtkAllocation allocation;
+ GtkWidget *filter_grid;
self = (GthFileToolGrayscale *) base;
@@ -364,6 +371,19 @@ gth_file_tool_grayscale_get_options (GthFileTool *base)
options = _gtk_builder_get_widget (self->priv->builder, "options");
gtk_widget_show (options);
+ filter_grid = gth_filter_grid_new ();
+ gth_filter_grid_add_filter (GTH_FILTER_GRID (filter_grid), METHOD_BRIGHTNESS, NULL, _("_Brightness"),
NULL);
+ gth_filter_grid_add_filter (GTH_FILTER_GRID (filter_grid), METHOD_SATURATION, NULL, _("_Saturation"),
NULL);
+ gth_filter_grid_add_filter (GTH_FILTER_GRID (filter_grid), METHOD_AVARAGE, NULL, _("_Average"), NULL);
+
+ g_signal_connect (filter_grid,
+ "activated",
+ G_CALLBACK (filter_grid_activated_cb),
+ self);
+
+ gtk_widget_show (filter_grid);
+ gtk_box_pack_start (GTK_BOX (GET_WIDGET ("filter_grid_box")), filter_grid, TRUE, FALSE, 0);
+
g_signal_connect (GET_WIDGET ("ok_button"),
"clicked",
G_CALLBACK (ok_button_clicked_cb),
@@ -372,27 +392,21 @@ gth_file_tool_grayscale_get_options (GthFileTool *base)
"clicked",
G_CALLBACK (gth_file_tool_cancel),
self);
- g_signal_connect (GET_WIDGET ("brightness_radiobutton"),
- "clicked",
- G_CALLBACK (method_changed_cb),
- self);
- g_signal_connect (GET_WIDGET ("saturation_radiobutton"),
- "clicked",
- G_CALLBACK (method_changed_cb),
- self);
- g_signal_connect (GET_WIDGET ("average_radiobutton"),
- "clicked",
- G_CALLBACK (method_changed_cb),
- self);
- g_signal_connect (GET_WIDGET ("preview_checkbutton"),
- "toggled",
- G_CALLBACK (preview_checkbutton_toggled_cb),
- self);
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);
- apply_changes (self);
+ gth_filter_grid_activate (GTH_FILTER_GRID (filter_grid), METHOD_BRIGHTNESS);
+
+ gth_filter_grid_generate_previews (GTH_FILTER_GRID (filter_grid),
+ source,
+ METHOD_BRIGHTNESS,
+ get_image_task_for_method (METHOD_BRIGHTNESS),
+ METHOD_SATURATION,
+ get_image_task_for_method (METHOD_SATURATION),
+ METHOD_AVARAGE,
+ get_image_task_for_method (METHOD_AVARAGE),
+ -1);
return options;
}
@@ -420,6 +434,9 @@ gth_file_tool_grayscale_destroy_options (GthFileTool *base)
_cairo_clear_surface (&self->priv->preview);
_cairo_clear_surface (&self->priv->destination);
_g_clear_object (&self->priv->builder);
+
+ self->priv->method = -1;
+ self->priv->last_applied_method = -1;
}
@@ -469,6 +486,9 @@ gth_file_tool_grayscale_init (GthFileToolGrayscale *self)
self->priv->preview = NULL;
self->priv->destination = NULL;
self->priv->builder = NULL;
+ self->priv->method = -1;
+ self->priv->last_applied_method = -1;
+ self->priv->view_original = FALSE;
gth_file_tool_construct (GTH_FILE_TOOL (self),
"image-grayscale-symbolic",
diff --git a/gthumb/Makefile.am b/gthumb/Makefile.am
index b95625c..19b880a 100644
--- a/gthumb/Makefile.am
+++ b/gthumb/Makefile.am
@@ -54,6 +54,7 @@ PUBLIC_HEADER_FILES = \
gth-filterbar.h \
gth-filter-editor-dialog.h \
gth-filter-file.h \
+ gth-filter-grid.h \
gth-folder-tree.h \
gth-grid-view.h \
gth-histogram.h \
@@ -192,6 +193,7 @@ gthumb_SOURCES = \
gth-filterbar.c \
gth-filter-editor-dialog.c \
gth-filter-file.c \
+ gth-filter-grid.c \
gth-folder-tree.c \
gth-grid-view.c \
gth-histogram.c \
diff --git a/gthumb/gth-filter-grid.c b/gthumb/gth-filter-grid.c
new file mode 100644
index 0000000..53f798f
--- /dev/null
+++ b/gthumb/gth-filter-grid.c
@@ -0,0 +1,541 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2001-2014 The 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+#include "cairo-utils.h"
+#include "cairo-scale.h"
+#include "glib-utils.h"
+#include "gth-filter-grid.h"
+#include "gth-image-task.h"
+
+
+#define MAX_N_COLUMNS 10
+#define DEFAULT_N_COLUMNS 2
+#define PREVIEW_SIZE 110
+#define COLUMN_SPACING 20
+#define ROW_SPACING 20
+#define FILTER_ID_KEY "filter_id"
+
+
+/* Properties */
+enum {
+ PROP_0,
+ PROP_COLUMNS
+};
+
+
+/* Signals */
+enum {
+ ACTIVATED,
+ LAST_SIGNAL
+};
+
+
+typedef struct {
+ int filter_id;
+ GthTask *image_task;
+} PreviewTask;
+
+
+typedef struct {
+ GthFilterGrid *self;
+ GList *tasks;
+ GList *current_task;
+ GCancellable *cancellable;
+ cairo_surface_t *original;
+ GthTask *resize_task;
+} GeneratePreviewData;
+
+
+struct _GthFilterGridPrivate {
+ GtkWidget *grid;
+ int n_columns;
+ int current_column;
+ int current_row;
+ GHashTable *buttons;
+ GHashTable *previews;
+ GtkWidget *active_button;
+ GeneratePreviewData *gp_data;
+};
+
+
+static guint gth_filter_grid_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE (GthFilterGrid, gth_filter_grid, GTK_TYPE_BOX)
+
+
+static void
+_gth_filter_grid_set_n_columns (GthFilterGrid *self,
+ int n_columns)
+{
+ self->priv->grid = gtk_grid_new ();
+ gtk_grid_set_column_spacing (GTK_GRID (self->priv->grid), COLUMN_SPACING);
+ gtk_grid_set_row_spacing (GTK_GRID (self->priv->grid), ROW_SPACING);
+ gtk_widget_show (self->priv->grid);
+ gtk_container_add (GTK_CONTAINER (self), self->priv->grid);
+
+ self->priv->n_columns = n_columns;
+ self->priv->current_column = 0;
+ self->priv->current_row = 0;
+}
+
+
+static void
+gth_filter_grid_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GthFilterGrid *self;
+
+ self = GTH_FILTER_GRID (object);
+
+ switch (property_id) {
+ case PROP_COLUMNS:
+ _gth_filter_grid_set_n_columns (self, g_value_get_int (value));
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void
+gth_filter_grid_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GthFilterGrid *self;
+
+ self = GTH_FILTER_GRID (object);
+
+ switch (property_id) {
+ case PROP_COLUMNS:
+ g_value_set_int (value, self->priv->n_columns);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+static void generate_preview_data_cancel (GeneratePreviewData *data);
+
+
+static void
+gth_filter_grid_finalize (GObject *obj)
+{
+ GthFilterGrid *self;
+
+ self = GTH_FILTER_GRID (obj);
+ if (self->priv->gp_data != NULL) {
+ generate_preview_data_cancel (self->priv->gp_data);
+ self->priv->gp_data = NULL;
+ }
+ g_hash_table_destroy (self->priv->previews);
+ g_hash_table_destroy (self->priv->buttons);
+
+ G_OBJECT_CLASS (gth_filter_grid_parent_class)->finalize (obj);
+}
+
+
+static void
+gth_filter_grid_class_init (GthFilterGridClass *klass)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (klass, sizeof (GthFilterGridPrivate));
+
+ object_class = (GObjectClass*) klass;
+ object_class->set_property = gth_filter_grid_set_property;
+ object_class->get_property = gth_filter_grid_get_property;
+ object_class->finalize = gth_filter_grid_finalize;
+
+ /* properties */
+
+ g_object_class_install_property (object_class,
+ PROP_COLUMNS,
+ g_param_spec_int ("columns",
+ "Columns",
+ "Number of columns",
+ 1,
+ MAX_N_COLUMNS,
+ DEFAULT_N_COLUMNS,
+ G_PARAM_WRITABLE | G_PARAM_READABLE |
G_PARAM_CONSTRUCT_ONLY));
+
+ /* signals */
+
+ gth_filter_grid_signals[ACTIVATED] =
+ g_signal_new ("activated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GthFilterGridClass, activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+}
+
+
+static void
+gth_filter_grid_init (GthFilterGrid *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_FILTER_GRID, GthFilterGridPrivate);
+ self->priv->n_columns = DEFAULT_N_COLUMNS;
+ self->priv->previews = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
+ self->priv->buttons = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
+ self->priv->active_button = NULL;
+ self->priv->gp_data = NULL;
+}
+
+
+GtkWidget *
+gth_filter_grid_new (void)
+{
+ return (GtkWidget *) g_object_new (GTH_TYPE_FILTER_GRID, NULL);
+}
+
+
+static void
+button_toggled_cb (GtkWidget *toggle_button,
+ gpointer user_data)
+{
+ GthFilterGrid *self = user_data;
+ int filter_id = GTH_FILTER_GRID_NO_ACTIVE_FILTER;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle_button))) {
+ if (self->priv->active_button != toggle_button) {
+ if (self->priv->active_button != NULL) {
+ g_signal_handlers_block_by_data (self->priv->active_button, self);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->priv->active_button),
FALSE);
+ g_signal_handlers_unblock_by_data (self->priv->active_button, self);
+ }
+ self->priv->active_button = toggle_button;
+ }
+ filter_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (toggle_button), FILTER_ID_KEY));
+ }
+ else if (self->priv->active_button == toggle_button) {
+ self->priv->active_button = NULL;
+ }
+
+ g_signal_emit (self,
+ gth_filter_grid_signals[ACTIVATED],
+ 0,
+ filter_id);
+}
+
+
+static GtkWidget *
+_gth_filter_grid_cell_new (GthFilterGrid *self,
+ int filter_id,
+ cairo_surface_t *preview,
+ const char *label_text,
+ const char *tooltip)
+{
+ GtkWidget *cell;
+ GtkWidget *button;
+ GtkWidget *image;
+ GtkWidget *label;
+ GtkWidget *button_content;
+
+ cell = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+
+ button = gtk_toggle_button_new ();
+ g_object_set_data_full (G_OBJECT (button), FILTER_ID_KEY, GINT_TO_POINTER (filter_id), NULL);
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "filter-preview");
+ gtk_widget_set_tooltip_text (button, tooltip);
+ g_signal_connect (button, "toggled", G_CALLBACK (button_toggled_cb), self);
+
+ image = gtk_image_new_from_surface (preview);
+ gtk_widget_set_size_request (image, PREVIEW_SIZE, PREVIEW_SIZE);
+
+ label = gtk_label_new_with_mnemonic (label_text);
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+
+ button_content = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+
+ gtk_box_pack_start (GTK_BOX (button_content), image, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (button_content), label, FALSE, FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (button), button_content);
+ gtk_box_pack_start (GTK_BOX (cell), button, FALSE, FALSE, 0);
+ gtk_widget_show_all (cell);
+
+ g_hash_table_insert (self->priv->previews, GINT_TO_POINTER (filter_id), image);
+ g_hash_table_insert (self->priv->buttons, GINT_TO_POINTER (filter_id), button);
+
+ return cell;
+}
+
+
+void
+gth_filter_grid_add_filter (GthFilterGrid *self,
+ int filter_id,
+ cairo_surface_t *preview,
+ const char *label,
+ const char *tooltip)
+{
+ GtkWidget *cell;
+
+ cell = _gth_filter_grid_cell_new (self, filter_id, preview, label, tooltip);
+ gtk_grid_attach (GTK_GRID (self->priv->grid),
+ cell,
+ self->priv->current_column,
+ self->priv->current_row,
+ 1,
+ 1);
+
+ self->priv->current_column++;
+ if (self->priv->current_column >= self->priv->n_columns) {
+ self->priv->current_column = 0;
+ self->priv->current_row++;
+ }
+}
+
+
+void
+gth_filter_grid_set_filter_preview (GthFilterGrid *self,
+ int filter_id,
+ cairo_surface_t *preview)
+{
+ GtkWidget *image;
+
+ image = g_hash_table_lookup (self->priv->previews, GINT_TO_POINTER (filter_id));
+ g_return_if_fail (image != NULL);
+
+ gtk_image_set_from_surface (GTK_IMAGE (image), preview);
+}
+
+
+void
+gth_filter_grid_activate (GthFilterGrid *self,
+ int filter_id)
+{
+ GtkWidget *button;
+
+ button = g_hash_table_lookup (self->priv->buttons, GINT_TO_POINTER (filter_id));
+ g_return_if_fail (button != NULL);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+}
+
+
+/* -- gth_filter_grid_generate_previews -- */
+
+
+static void
+preview_task_free (PreviewTask *task)
+{
+ g_object_unref (task->image_task);
+ g_free (task);
+}
+
+
+static void
+generate_preview_data_free (GeneratePreviewData *data)
+{
+ if ((data->self != NULL) && (data->self->priv->gp_data == data))
+ data->self->priv->gp_data = NULL;
+ cairo_surface_destroy (data->original);
+ g_list_free_full (data->tasks, (GDestroyNotify) preview_task_free);
+ _g_object_unref (data->cancellable);
+ _g_object_unref (data->resize_task);
+ g_free (data);
+}
+
+
+static void
+generate_preview_data_cancel (GeneratePreviewData *data)
+{
+ if (data->cancellable != NULL)
+ g_cancellable_cancel (data->cancellable);
+}
+
+
+static void generate_preview (GeneratePreviewData *data);
+
+
+static void
+image_preview_completed_cb (GthTask *task,
+ GError *error,
+ gpointer user_data)
+{
+ GeneratePreviewData *data = user_data;
+ cairo_surface_t *preview;
+
+ if (error != NULL) {
+ generate_preview_data_free (data);
+ return;
+ }
+
+ preview = gth_image_task_get_destination_surface (GTH_IMAGE_TASK (task));
+ if (preview != NULL) {
+ PreviewTask *task = (PreviewTask *) data->current_task->data;
+ gth_filter_grid_set_filter_preview (data->self, task->filter_id, preview);
+ }
+
+ data->current_task = g_list_next (data->current_task);
+ generate_preview (data);
+}
+
+
+static void
+generate_preview (GeneratePreviewData *data)
+{
+ PreviewTask *task;
+
+ if (data->current_task == NULL) {
+ generate_preview_data_free (data);
+ return;
+ }
+
+ task = (PreviewTask *) data->current_task->data;
+
+ g_signal_connect (task->image_task,
+ "completed",
+ G_CALLBACK (image_preview_completed_cb),
+ data);
+ gth_image_task_set_source_surface (GTH_IMAGE_TASK (task->image_task), data->original);
+
+ _g_object_unref (data->cancellable);
+ data->cancellable = g_cancellable_new ();
+ gth_task_exec (task->image_task, data->cancellable);
+}
+
+
+static void
+resize_task_completed_cb (GthTask *task,
+ GError *error,
+ gpointer user_data)
+{
+ GeneratePreviewData *data = user_data;
+
+ _g_object_unref (data->resize_task);
+ data->resize_task = NULL;
+
+ if (error != NULL) {
+ generate_preview_data_free (data);
+ return;
+ }
+
+ if (data->original != NULL)
+ cairo_surface_destroy (data->original);
+ data->original = gth_image_task_get_destination_surface (GTH_IMAGE_TASK (task));
+ if (data->original == NULL) {
+ generate_preview_data_free (data);
+ return;
+ }
+
+ data->current_task = data->tasks;
+ generate_preview (data);
+}
+
+
+static gpointer
+resize_task_exec (GthAsyncTask *task,
+ gpointer user_data)
+{
+ cairo_surface_t *source;
+ cairo_surface_t *destination;
+
+ source = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
+ destination = _cairo_image_surface_scale_squared (source,
+ PREVIEW_SIZE,
+ SCALE_FILTER_GOOD,
+ task);
+ _cairo_image_surface_clear_metadata (destination);
+ gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), destination);
+
+ cairo_surface_destroy (destination);
+ cairo_surface_destroy (source);
+
+ return NULL;
+}
+
+
+void
+gth_filter_grid_generate_previews (GthFilterGrid *self,
+ cairo_surface_t *image,
+ int filter_id,
+ ...
+ /* series of:
+ int filter_id,
+ GthTask *image_task,
+ */)
+{
+ GeneratePreviewData *data;
+ GthTask *image_task;
+ va_list args;
+
+ if (self->priv->gp_data != NULL)
+ generate_preview_data_cancel (self->priv->gp_data);
+
+ data = g_new (GeneratePreviewData, 1);
+ data->self = self;
+ data->tasks = NULL;
+ data->cancellable = NULL;
+ data->original = NULL;
+
+ g_object_add_weak_pointer (G_OBJECT (self), &data->self);
+ self->priv->gp_data = data;
+
+ /* collect the (filter, task) pairs */
+
+ va_start (args, filter_id);
+ image_task = va_arg (args, GthTask *);
+ while ((filter_id >= 0) && (image_task != NULL)) {
+ PreviewTask *task;
+
+ task = g_new0 (PreviewTask, 1);
+ task->filter_id = filter_id;
+ task->image_task = image_task;
+ data->tasks = g_list_prepend (data->tasks, task);
+
+ filter_id = va_arg (args, int);
+ if (filter_id < 0)
+ break;
+ image_task = va_arg (args, GthTask *);
+ }
+ va_end (args);
+ data->tasks = g_list_reverse (data->tasks);
+
+ /* resize the original image */
+
+ data->resize_task = gth_image_task_new (_("Resizing images"),
+ NULL,
+ resize_task_exec,
+ NULL,
+ data,
+ NULL);
+ gth_image_task_set_source_surface (GTH_IMAGE_TASK (data->resize_task), image);
+ g_signal_connect (data->resize_task,
+ "completed",
+ G_CALLBACK (resize_task_completed_cb),
+ data);
+
+ data->cancellable = g_cancellable_new ();
+ gth_task_exec (data->resize_task, data->cancellable);
+}
diff --git a/gthumb/gth-filter-grid.h b/gthumb/gth-filter-grid.h
new file mode 100644
index 0000000..f6aded1
--- /dev/null
+++ b/gthumb/gth-filter-grid.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2001-2014 The 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_FILTER_GRID_H
+#define GTH_FILTER_GRID_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTH_FILTER_GRID_NO_ACTIVE_FILTER -1
+
+#define GTH_TYPE_FILTER_GRID (gth_filter_grid_get_type ())
+#define GTH_FILTER_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_FILTER_GRID, GthFilterGrid))
+#define GTH_FILTER_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_FILTER_GRID,
GthFilterGridClass))
+#define GTH_IS_FILTER_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_FILTER_GRID))
+#define GTH_IS_FILTER_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_FILTER_GRID))
+#define GTH_FILTER_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_FILTER_GRID,
GthFilterGridClass))
+
+typedef struct _GthFilterGrid GthFilterGrid;
+typedef struct _GthFilterGridClass GthFilterGridClass;
+typedef struct _GthFilterGridPrivate GthFilterGridPrivate;
+
+struct _GthFilterGrid {
+ GtkBox parent_instance;
+ GthFilterGridPrivate *priv;
+};
+
+struct _GthFilterGridClass {
+ GtkBoxClass parent_class;
+
+ /*< signals >*/
+
+ void (*activated) (GthFilterGrid *self,
+ int filter_id);
+};
+
+GType gth_filter_grid_get_type (void);
+GtkWidget * gth_filter_grid_new (void);
+void gth_filter_grid_add_filter (GthFilterGrid *self,
+ int filter_id,
+ cairo_surface_t *preview,
+ const char *label,
+ const char *tooltip);
+void gth_filter_grid_set_filter_preview (GthFilterGrid *self,
+ int filter_id,
+ cairo_surface_t *preview);
+void gth_filter_grid_activate (GthFilterGrid *self,
+ int filter_id);
+void gth_filter_grid_generate_previews (GthFilterGrid *self,
+ cairo_surface_t *image,
+ int filter_id,
+ ...
+ /* series of:
+ int filter_id,
+ GthTask *image_task,
+ */);
+
+G_END_DECLS
+
+#endif /* GTH_FILTER_GRID_H */
diff --git a/gthumb/resources/gthumb.css b/gthumb/resources/gthumb.css
index 9283f20..220155e 100644
--- a/gthumb/resources/gthumb.css
+++ b/gthumb/resources/gthumb.css
@@ -129,3 +129,11 @@ GtkLabel.extension-description {
.video-player {
background-color: #000;
}
+
+/* -- preview button in the filter grid -- */
+
+GtkButton.filter-preview:checked {
+ background-image: none;
+ background-color: @theme_unfocused_selected_bg_color;
+ color: @theme_unfocused_selected_fg_color;
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7d66752..ca4646b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -737,6 +737,8 @@ gthumb/gth-filter-editor-dialog.c
gthumb/gth-filter-editor-dialog.h
gthumb/gth-filter-file.c
gthumb/gth-filter-file.h
+gthumb/gth-filter-grid.c
+gthumb/gth-filter-grid.h
gthumb/gth-filter.h
gthumb/gth-folder-tree.c
gthumb/gth-folder-tree.h
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]