[gthumb/ext] [file tools] added the "adjust colors" tool



commit 4772c7542b8ab5ce9540256e9f42c8755cfa4e86
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sun Sep 13 10:03:35 2009 +0200

    [file tools] added the "adjust colors" tool

 extensions/file_tools/Makefile.am                  |    2 +
 extensions/file_tools/data/ui/Makefile.am          |    2 +-
 .../file_tools/data/ui/adjust-colors-options.ui    |  246 ++++++++
 extensions/file_tools/data/ui/crop-options.ui      |    4 +-
 .../file_tools/gth-file-tool-adjust-colors.c       |  622 ++++++++++++++++++++
 .../file_tools/gth-file-tool-adjust-colors.h       |   54 ++
 extensions/file_tools/gth-file-tool-crop.c         |    2 +-
 extensions/file_tools/gth-file-tool-desaturate.c   |    2 +-
 extensions/file_tools/gth-file-tool-enhance.c      |   10 +-
 extensions/file_tools/gth-file-tool-equalize.c     |    2 +-
 extensions/file_tools/main.c                       |    6 +-
 extensions/image_viewer/gth-image-viewer-page.c    |   10 +-
 extensions/image_viewer/gth-image-viewer-page.h    |    3 +-
 extensions/red_eye_removal/gth-file-tool-red-eye.c |    2 +-
 gthumb/gth-pixbuf-task.c                           |  121 +++--
 gthumb/gth-pixbuf-task.h                           |   11 +-
 gthumb/gth-sidebar.c                               |    7 +-
 17 files changed, 1024 insertions(+), 82 deletions(-)
---
diff --git a/extensions/file_tools/Makefile.am b/extensions/file_tools/Makefile.am
index fbe3d19..4b2a25e 100644
--- a/extensions/file_tools/Makefile.am
+++ b/extensions/file_tools/Makefile.am
@@ -4,6 +4,8 @@ extensiondir = $(libdir)/gthumb-2.0/extensions
 extension_LTLIBRARIES = libfile_tools.la
 
 libfile_tools_la_SOURCES = 		\
+	gth-file-tool-adjust-colors.c	\
+	gth-file-tool-adjust-colors.h	\
 	gth-file-tool-crop.c		\
 	gth-file-tool-crop.h		\
 	gth-file-tool-desaturate.c	\
diff --git a/extensions/file_tools/data/ui/Makefile.am b/extensions/file_tools/data/ui/Makefile.am
index d3fd8dd..2fab922 100644
--- a/extensions/file_tools/data/ui/Makefile.am
+++ b/extensions/file_tools/data/ui/Makefile.am
@@ -1,5 +1,5 @@
 uidir = $(datadir)/gthumb-2.0/ui
-ui_DATA = crop-options.ui
+ui_DATA = adjust-colors-options.ui crop-options.ui
 EXTRA_DIST = $(ui_DATA)
 
 -include $(top_srcdir)/git.mk
diff --git a/extensions/file_tools/data/ui/adjust-colors-options.ui b/extensions/file_tools/data/ui/adjust-colors-options.ui
new file mode 100644
index 0000000..4d6e8d2
--- /dev/null
+++ b/extensions/file_tools/data/ui/adjust-colors-options.ui
@@ -0,0 +1,246 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkAlignment" id="options">
+    <property name="visible">True</property>
+    <property name="top_padding">6</property>
+    <child>
+      <object class="GtkVBox" id="vbox2">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="border_width">12</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkTable" id="table1">
+                <property name="visible">True</property>
+                <property name="n_rows">5</property>
+                <property name="n_columns">2</property>
+                <property name="column_spacing">6</property>
+                <property name="row_spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">_Brightness:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Con_trast:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="x_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">_Saturation:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="x_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="brightness_hbox">
+                    <property name="visible">True</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="contrast_hbox">
+                    <property name="visible">True</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="saturation_hbox">
+                    <property name="visible">True</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="hue_hbox">
+                    <property name="visible">True</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">4</property>
+                    <property name="bottom_attach">5</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label5">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">_Hue:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">4</property>
+                    <property name="bottom_attach">5</property>
+                    <property name="x_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="gamma_hbox">
+                    <property name="visible">True</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label4">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">_Intensity:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="x_options">GTK_FILL</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHSeparator" id="hseparator1">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="padding">6</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHButtonBox" id="hbuttonbox1">
+            <property name="visible">True</property>
+            <property name="spacing">12</property>
+            <property name="layout_style">center</property>
+            <child>
+              <object class="GtkButton" id="ok_button">
+                <property name="label">gtk-apply</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="padding">6</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkAdjustment" id="ratio_w_adjustment">
+    <property name="upper">9999</property>
+    <property name="step_increment">1</property>
+  </object>
+  <object class="GtkAdjustment" id="crop_x_adjustment">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+  </object>
+  <object class="GtkAdjustment" id="crop_y_adjustment">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+  </object>
+  <object class="GtkAdjustment" id="crop_width_adjustment">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+  </object>
+  <object class="GtkAdjustment" id="crop_height_adjustment">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+  </object>
+  <object class="GtkAdjustment" id="ratio_h_adjustment">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+  </object>
+</interface>
diff --git a/extensions/file_tools/data/ui/crop-options.ui b/extensions/file_tools/data/ui/crop-options.ui
index 236d0aa..1fd616b 100644
--- a/extensions/file_tools/data/ui/crop-options.ui
+++ b/extensions/file_tools/data/ui/crop-options.ui
@@ -363,7 +363,7 @@
             </child>
             <child>
               <object class="GtkButton" id="cancel_button">
-                <property name="label" translatable="yes">gtk-cancel</property>
+                <property name="label">gtk-cancel</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
@@ -387,7 +387,7 @@
     </child>
   </object>
   <object class="GtkAdjustment" id="ratio_w_adjustment">
-    <property name="upper">9999</property>
+    <property name="upper">99999</property>
     <property name="step_increment">1</property>
   </object>
   <object class="GtkAdjustment" id="crop_x_adjustment">
diff --git a/extensions/file_tools/gth-file-tool-adjust-colors.c b/extensions/file_tools/gth-file-tool-adjust-colors.c
new file mode 100644
index 0000000..03f632e
--- /dev/null
+++ b/extensions/file_tools/gth-file-tool-adjust-colors.c
@@ -0,0 +1,622 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <gthumb.h>
+#include <extensions/image_viewer/gth-image-viewer-page.h>
+#include "gth-file-tool-adjust-colors.h"
+
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
+#define APPLY_DELAY 250
+
+
+static gpointer parent_class = NULL;
+
+
+struct _GthFileToolAdjustColorsPrivate {
+	GdkPixbuf     *src_pixbuf;
+	GdkPixbuf     *dest_pixbuf;
+	GtkBuilder    *builder;
+	GtkAdjustment *brightness_adj;
+	GtkAdjustment *contrast_adj;
+	GtkAdjustment *saturation_adj;
+	GtkAdjustment *gamma_adj;
+	GthTask       *pixbuf_task;
+	guint          apply_event;
+};
+
+
+typedef struct {
+	double gamma[5];
+	int    low_input[5];
+	int    high_input[5];
+	int    low_output[5];
+	int    high_output[5];
+} Levels;
+
+
+
+typedef struct {
+	GtkWidget *viewer_page;
+	Levels     levels;
+	double     gamma;
+	double     brightness;
+	double     contrast;
+	double     saturation;
+} AdjustData;
+
+
+static void
+levels_init (Levels *levels,
+	     double  brightness,
+	     double  contrast,
+	     double  gamma)
+{
+	int    channel;
+	double slant;
+	double value;
+
+	for (channel = 0; channel < MAX_N_CHANNELS + 1; channel++) {
+		levels->gamma[channel] = 1.0;
+		levels->low_input[channel] = 0;
+		levels->high_input[channel] = 255;
+		levels->low_output[channel] = 0;
+		levels->high_output[channel] = 255;
+	}
+
+	/*  */
+
+	levels->gamma[0] = pow (10, - (gamma / 100.0));
+
+	brightness = brightness / 127.0 / 2.0;
+	contrast = contrast / 127.0;
+
+	slant = tan ((contrast + 1) * G_PI_4);
+	if (brightness >= 0) {
+		value = -0.5 * slant + brightness * slant + 0.5;
+		if (value < 0.0) {
+			value = 0.0;
+
+			/* this slightly convoluted math follows by inverting the
+			 * calculation of the brightness/contrast LUT in base/lut-funcs.h */
+
+			levels->low_input[0] = 255.0 * (- brightness * slant + 0.5 * slant - 0.5) / (slant - brightness * slant);
+		}
+
+		levels->low_output[0] = 255.0 * value;
+
+		value = 0.5 * slant + 0.5;
+		if (value > 1.0) {
+			value = 1.0;
+			levels->high_input[0] = 255.0 * (- brightness * slant + 0.5 * slant + 0.5) / (slant - brightness * slant);
+	        }
+
+		levels->high_output[0] = 255.0 * value;
+	}
+	else {
+		value = 0.5 - 0.5 * slant;
+		if (value < 0.0) {
+			value = 0.0;
+			levels->low_input[0] = 255.0 * (0.5 * slant - 0.5) / (slant + brightness * slant);
+		}
+
+		levels->low_output[0] = 255.0 * value;
+
+		value = slant * brightness + slant * 0.5 + 0.5;
+		if (value > 1.0) {
+			value = 1.0;
+			levels->high_input[0] = 255.0 * (0.5 * slant + 0.5) / (slant + brightness * slant);
+		}
+
+		levels->high_output[0] = 255.0 * value;
+	}
+}
+
+
+static void
+adjust_colors_init (GthPixbufTask *pixop)
+{
+	/* AdjustData *data = pixop->data; FIXME: delte if not used */
+}
+
+
+static guchar
+adjust_levels_func (guchar  value,
+		    Levels *levels,
+		    guchar  lightness,
+		    double  saturation,
+		    int     channel)
+{
+	double inten;
+	int    j;
+
+	inten = value;
+
+	/* For color  images this runs through the loop with j = channel + 1
+	 * the first time and j = 0 the second time
+	 *
+	 * For bw images this runs through the loop with j = 0 the first and
+	 *  only time
+	 */
+	for (j = channel + 1; j >= 0; j -= (channel + 1)) {
+		inten /= 255.0;
+
+		/*  determine input intensity  */
+
+		if (levels->high_input[j] != levels->low_input[j])
+			inten = (255.0 * inten - levels->low_input[j]) /
+				(levels->high_input[j] - levels->low_input[j]);
+		else
+			inten = 255.0 * inten - levels->low_input[j];
+
+		if (levels->gamma[j] != 0.0) {
+			if (inten >= 0.0)
+				inten =  pow ( inten, (1.0 / levels->gamma[j]));
+			else
+				inten = -pow (-inten, (1.0 / levels->gamma[j]));
+		}
+
+		inten = (saturation * lightness) + ((1.0 - saturation) * inten);
+
+		/*  determine the output intensity  */
+
+		if (levels->high_output[j] >= levels->low_output[j])
+			inten = inten * (levels->high_output[j] - levels->low_output[j]) +
+				levels->low_output[j];
+		else if (levels->high_output[j] < levels->low_output[j])
+			inten = levels->low_output[j] - inten *
+				(levels->low_output[j] - levels->high_output[j]);
+	}
+
+	if (inten < 0.0)
+		inten = 0.0;
+	else if (inten > 255.0)
+		inten = 255.0;
+
+	return (guchar) inten;
+}
+
+
+static guchar
+interpolate_value (guchar original,
+		   guchar reference,
+		   double distance)
+{
+	return CLAMP((distance * reference) + ((1.0 - distance) * original), 0, 255);
+}
+
+
+static guchar
+gamma_correction (guchar original,
+		  double gamma)
+{
+	double inten;
+
+	inten = (double) original / 255.0;
+
+	if (gamma != 0.0) {
+		if (inten >= 0.0)
+			inten =  pow ( inten, (1.0 / gamma));
+		else
+			inten = -pow (-inten, (1.0 / gamma));
+	}
+
+	return CLAMP (inten * 255.0, 0, 255);
+}
+
+
+static void
+adjust_colors_step (GthPixbufTask *pixop)
+{
+	AdjustData *data = pixop->data;
+	guchar      min, max, lightness;
+
+	pixop->dest_pixel[RED_PIX] = pixop->src_pixel[RED_PIX];
+	pixop->dest_pixel[GREEN_PIX] = pixop->src_pixel[GREEN_PIX];
+	pixop->dest_pixel[BLUE_PIX] = pixop->src_pixel[BLUE_PIX];
+	if (pixop->has_alpha)
+		pixop->dest_pixel[ALPHA_PIX] = pixop->src_pixel[ALPHA_PIX];
+
+	/* intensitiy / gamma correction */
+
+	pixop->dest_pixel[RED_PIX] = gamma_correction (pixop->dest_pixel[RED_PIX], data->gamma);
+	pixop->dest_pixel[GREEN_PIX] = gamma_correction (pixop->dest_pixel[GREEN_PIX], data->gamma);
+	pixop->dest_pixel[BLUE_PIX] = gamma_correction (pixop->dest_pixel[BLUE_PIX], data->gamma);
+
+	/* brightness */
+
+	pixop->dest_pixel[RED_PIX] = interpolate_value (pixop->dest_pixel[RED_PIX], 0, data->brightness);
+	pixop->dest_pixel[GREEN_PIX] = interpolate_value (pixop->dest_pixel[GREEN_PIX], 0, data->brightness);
+	pixop->dest_pixel[BLUE_PIX] = interpolate_value (pixop->dest_pixel[BLUE_PIX], 0, data->brightness);
+
+	/* contrast */
+
+	pixop->dest_pixel[RED_PIX] = interpolate_value (pixop->dest_pixel[RED_PIX], 127, data->contrast);
+	pixop->dest_pixel[GREEN_PIX] = interpolate_value (pixop->dest_pixel[GREEN_PIX], 127, data->contrast);
+	pixop->dest_pixel[BLUE_PIX] = interpolate_value (pixop->dest_pixel[BLUE_PIX], 127, data->contrast);
+
+	/* saturation */
+
+	max = MAX (pixop->dest_pixel[RED_PIX], pixop->dest_pixel[GREEN_PIX]);
+	max = MAX (max, pixop->dest_pixel[BLUE_PIX]);
+	min = MIN (pixop->dest_pixel[RED_PIX], pixop->dest_pixel[GREEN_PIX]);
+	min = MIN (min, pixop->dest_pixel[BLUE_PIX]);
+	lightness = (max + min) / 2;
+
+	pixop->dest_pixel[RED_PIX] = interpolate_value (pixop->dest_pixel[RED_PIX], lightness, data->saturation);
+	pixop->dest_pixel[GREEN_PIX] = interpolate_value (pixop->dest_pixel[GREEN_PIX], lightness, data->saturation);
+	pixop->dest_pixel[BLUE_PIX] = interpolate_value (pixop->dest_pixel[BLUE_PIX], lightness, data->saturation);
+
+	/*pixop->dest_pixel[RED_PIX]   = adjust_levels_func (pixop->src_pixel[RED_PIX], &data->levels, lightness, data->saturation, RED_PIX);
+	pixop->dest_pixel[GREEN_PIX] = adjust_levels_func (pixop->src_pixel[GREEN_PIX], &data->levels, lightness, data->saturation, GREEN_PIX);
+	pixop->dest_pixel[BLUE_PIX]  = adjust_levels_func (pixop->src_pixel[BLUE_PIX], &data->levels, lightness, data->saturation, BLUE_PIX);
+	if (pixop->has_alpha)
+		pixop->dest_pixel[ALPHA_PIX] = pixop->src_pixel[ALPHA_PIX];*/
+}
+
+
+static void
+adjust_colors_release (GthPixbufTask *pixop,
+		       GError        *error)
+{
+	AdjustData *data = pixop->data;
+
+	if (error == NULL)
+		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (data->viewer_page), pixop->dest, TRUE);
+
+	g_object_unref (data->viewer_page);
+	g_free (data);
+}
+
+
+static void
+gth_file_tool_adjust_colors_update_sensitivity (GthFileTool *base)
+{
+	GtkWidget *window;
+	GtkWidget *viewer_page;
+
+	window = gth_file_tool_get_window (base);
+	viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
+	if (! GTH_IS_IMAGE_VIEWER_PAGE (viewer_page))
+		gtk_widget_set_sensitive (GTK_WIDGET (base), FALSE);
+	else
+		gtk_widget_set_sensitive (GTK_WIDGET (base), TRUE);
+}
+
+
+static void
+ok_button_clicked_cb (GtkButton               *button,
+		      GthFileToolAdjustColors *self)
+{
+	GtkWidget *window;
+	GtkWidget *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_pixbuf (GTH_IMAGE_VIEWER_PAGE (viewer_page), self->priv->dest_pixbuf, TRUE);
+	gth_file_tool_hide_options (GTH_FILE_TOOL (self));
+}
+
+
+static void
+cancel_button_clicked_cb (GtkButton               *button,
+			  GthFileToolAdjustColors *self)
+{
+	GtkWidget   *window;
+	GthFileData *current_file;
+
+	if (self->priv->apply_event != 0) {
+		g_source_remove (self->priv->apply_event);
+		self->priv->apply_event = 0;
+	}
+
+	window = gth_file_tool_get_window (GTH_FILE_TOOL (self));
+	current_file = gth_browser_get_current_file (GTH_BROWSER (window));
+	if (current_file != NULL) {
+		g_file_info_set_attribute_boolean (current_file->info, "gth::file::is-modified", FALSE);
+		gth_monitor_metadata_changed (gth_main_get_default_monitor (), current_file);
+	}
+
+	gth_file_tool_hide_options (GTH_FILE_TOOL (self));
+}
+
+
+static void
+task_completed_cb (GthTask                 *task,
+		   GError                  *error,
+		   GthFileToolAdjustColors *self)
+{
+	if (self->priv->pixbuf_task == task)
+		self->priv->pixbuf_task = NULL;
+	g_object_unref (task);
+
+	if (error == NULL) {
+		GtkWidget *window;
+		GtkWidget *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_pixbuf (GTH_IMAGE_VIEWER_PAGE (viewer_page), self->priv->dest_pixbuf, FALSE);
+	}
+}
+
+
+static gboolean
+apply_cb (gpointer user_data)
+{
+	GthFileToolAdjustColors *self = user_data;
+	GtkWidget               *window;
+	AdjustData              *data;
+
+	if (self->priv->apply_event != 0) {
+		g_source_remove (self->priv->apply_event);
+		self->priv->apply_event = 0;
+	}
+
+	if (self->priv->pixbuf_task != NULL)
+		gth_task_cancel (self->priv->pixbuf_task);
+
+	window = gth_file_tool_get_window (GTH_FILE_TOOL (self));
+
+	data = g_new0 (AdjustData, 1);
+	data->viewer_page = g_object_ref (gth_browser_get_viewer_page (GTH_BROWSER (window)));
+
+	levels_init (&data->levels,
+		     gtk_adjustment_get_value (self->priv->brightness_adj),
+		     gtk_adjustment_get_value (self->priv->contrast_adj),
+		     gtk_adjustment_get_value (self->priv->gamma_adj));
+
+	data->gamma = pow (10, - (gtk_adjustment_get_value (self->priv->gamma_adj) / 100.0));
+	data->brightness = gtk_adjustment_get_value (self->priv->brightness_adj) / 100.0;
+	data->contrast = gtk_adjustment_get_value (self->priv->contrast_adj) / 100.0;
+	data->saturation = gtk_adjustment_get_value (self->priv->saturation_adj) / 100.0;
+
+	self->priv->pixbuf_task = gth_pixbuf_task_new (_("Applying changes"),
+						       self->priv->src_pixbuf,
+						       self->priv->dest_pixbuf,
+						       adjust_colors_init,
+						       adjust_colors_step,
+						       adjust_colors_release,
+						       data);
+	g_signal_connect (self->priv->pixbuf_task,
+			  "completed",
+			  G_CALLBACK (task_completed_cb),
+			  self);
+
+	gth_browser_exec_task (GTH_BROWSER (window), self->priv->pixbuf_task, FALSE);
+
+	return FALSE;
+}
+
+
+static void
+value_changed_cb (GtkAdjustment           *adj,
+		  GthFileToolAdjustColors *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 GtkAdjustment *
+gimp_scale_entry_new (GtkWidget *parent_box,
+		      float      value,
+		      float      lower,
+		      float      upper,
+		      float      step_increment,
+		      float      page_increment,
+		      int        digits)
+{
+	GtkWidget *hbox;
+	GtkWidget *scale;
+	GtkWidget *spinbutton;
+	GtkObject *adj;
+
+	adj = gtk_adjustment_new (value, lower, upper,
+				  step_increment, page_increment,
+				  0.0);
+
+	spinbutton = gtk_spin_button_new  (GTK_ADJUSTMENT (adj), 1.0, 0);
+	gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spinbutton), digits);
+	gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 4);
+
+	scale = gtk_hscale_new (GTK_ADJUSTMENT (adj));
+	gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
+	gtk_scale_set_digits (GTK_SCALE (scale), digits);
+
+	hbox = gtk_hbox_new (FALSE, 5);
+	gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+
+	gtk_box_pack_start (GTK_BOX (parent_box), hbox, TRUE, TRUE, 0);
+	gtk_widget_show_all (hbox);
+
+	return (GtkAdjustment *) adj;
+}
+
+
+static GtkWidget *
+gth_file_tool_adjust_colors_get_options (GthFileTool *base)
+{
+	GthFileToolAdjustColors *self;
+	GtkWidget               *window;
+	GtkWidget               *viewer_page;
+	GtkWidget               *viewer;
+	GtkWidget               *options;
+
+	self = (GthFileToolAdjustColors *) base;
+
+	window = gth_file_tool_get_window (base);
+	viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
+	if (! GTH_IS_IMAGE_VIEWER_PAGE (viewer_page))
+		return NULL;
+
+	_g_object_unref (self->priv->src_pixbuf);
+	_g_object_unref (self->priv->dest_pixbuf);
+
+	viewer = gth_image_viewer_page_get_image_viewer (GTH_IMAGE_VIEWER_PAGE (viewer_page));
+	self->priv->src_pixbuf = gth_image_viewer_get_current_pixbuf (GTH_IMAGE_VIEWER (viewer));
+	if (self->priv->src_pixbuf == NULL)
+		return NULL;
+
+	self->priv->src_pixbuf = g_object_ref (self->priv->src_pixbuf);
+	self->priv->dest_pixbuf = gdk_pixbuf_copy (self->priv->src_pixbuf);
+
+	self->priv->builder = _gtk_builder_new_from_file ("adjust-colors-options.ui", "file_tools");
+	options = _gtk_builder_get_widget (self->priv->builder, "options");
+	gtk_widget_show (options);
+
+	self->priv->brightness_adj = gimp_scale_entry_new (GET_WIDGET ("brightness_hbox"), 0.0, -100.0, 100.0, 1.0, 10.0, 0);
+	self->priv->contrast_adj = gimp_scale_entry_new (GET_WIDGET ("contrast_hbox"), 0.0, -100.0, 100.0, 1.0, 10.0, 0);
+	self->priv->gamma_adj = gimp_scale_entry_new (GET_WIDGET ("gamma_hbox"), 0.0, -100.0, 100.0, 1.0, 10.0, 0);
+	self->priv->saturation_adj = gimp_scale_entry_new (GET_WIDGET ("saturation_hbox"), 0.0, -100.0, 100.0, 1.0, 10.0, 0);
+
+	g_signal_connect (GET_WIDGET ("ok_button"),
+			  "clicked",
+			  G_CALLBACK (ok_button_clicked_cb),
+			  self);
+	g_signal_connect (GET_WIDGET ("cancel_button"),
+			  "clicked",
+			  G_CALLBACK (cancel_button_clicked_cb),
+			  self);
+	g_signal_connect (G_OBJECT (self->priv->brightness_adj),
+			  "value-changed",
+			  G_CALLBACK (value_changed_cb),
+			  self);
+	g_signal_connect (G_OBJECT (self->priv->contrast_adj),
+			  "value-changed",
+			  G_CALLBACK (value_changed_cb),
+			  self);
+	g_signal_connect (G_OBJECT (self->priv->gamma_adj),
+			  "value-changed",
+			  G_CALLBACK (value_changed_cb),
+			  self);
+	g_signal_connect (G_OBJECT (self->priv->saturation_adj),
+			  "value-changed",
+			  G_CALLBACK (value_changed_cb),
+			  self);
+
+	return options;
+}
+
+
+static void
+gth_file_tool_adjust_colors_destroy_options (GthFileTool *base)
+{
+	GthFileToolAdjustColors *self;
+
+	self = (GthFileToolAdjustColors *) base;
+
+	if (self->priv->apply_event != 0) {
+		g_source_remove (self->priv->apply_event);
+		self->priv->apply_event = 0;
+	}
+
+	_g_object_unref (self->priv->src_pixbuf);
+	_g_object_unref (self->priv->dest_pixbuf);
+	_g_object_unref (self->priv->builder);
+	self->priv->src_pixbuf = NULL;
+	self->priv->dest_pixbuf = NULL;
+	self->priv->builder = NULL;
+}
+
+
+static void
+gth_file_tool_adjust_colors_activate (GthFileTool *base)
+{
+	gth_file_tool_show_options (base);
+}
+
+
+static void
+gth_file_tool_adjust_colors_instance_init (GthFileToolAdjustColors *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_FILE_TOOL_ADJUST_COLORS, GthFileToolAdjustColorsPrivate);
+	gth_file_tool_construct (GTH_FILE_TOOL (self), GTK_STOCK_EDIT, _("Adjust Colors"), _("Adjust Colors"), TRUE);
+	gtk_widget_set_tooltip_text (GTK_WIDGET (self), _("Change brightness, contrast, saturation and gamma level of the image"));
+}
+
+
+static void
+gth_file_tool_adjust_colors_finalize (GObject *object)
+{
+	GthFileToolAdjustColors *self;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GTH_IS_FILE_TOOL_ADJUST_COLORS (object));
+
+	self = (GthFileToolAdjustColors *) object;
+
+	_g_object_unref (self->priv->src_pixbuf);
+	_g_object_unref (self->priv->dest_pixbuf);
+	_g_object_unref (self->priv->builder);
+
+	/* Chain up */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+gth_file_tool_adjust_colors_class_init (GthFileToolAdjustColorsClass *class)
+{
+	GObjectClass     *gobject_class;
+	GthFileToolClass *file_tool_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (GthFileToolAdjustColorsPrivate));
+
+	gobject_class = (GObjectClass*) class;
+	gobject_class->finalize = gth_file_tool_adjust_colors_finalize;
+
+	file_tool_class = (GthFileToolClass *) class;
+	file_tool_class->update_sensitivity = gth_file_tool_adjust_colors_update_sensitivity;
+	file_tool_class->activate = gth_file_tool_adjust_colors_activate;
+	file_tool_class->get_options = gth_file_tool_adjust_colors_get_options;
+	file_tool_class->destroy_options = gth_file_tool_adjust_colors_destroy_options;
+}
+
+
+GType
+gth_file_tool_adjust_colors_get_type (void) {
+	static GType type_id = 0;
+	if (type_id == 0) {
+		static const GTypeInfo g_define_type_info = {
+			sizeof (GthFileToolAdjustColorsClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gth_file_tool_adjust_colors_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (GthFileToolAdjustColors),
+			0,
+			(GInstanceInitFunc) gth_file_tool_adjust_colors_instance_init,
+			NULL
+		};
+		type_id = g_type_register_static (GTH_TYPE_FILE_TOOL, "GthFileToolAdjustColors", &g_define_type_info, 0);
+	}
+	return type_id;
+}
diff --git a/extensions/file_tools/gth-file-tool-adjust-colors.h b/extensions/file_tools/gth-file-tool-adjust-colors.h
new file mode 100644
index 0000000..1c2e4ee
--- /dev/null
+++ b/extensions/file_tools/gth-file-tool-adjust-colors.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_FILE_TOOL_ADJUST_COLORS_H
+#define GTH_FILE_TOOL_ADJUST_COLORS_H
+
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_FILE_TOOL_ADJUST_COLORS (gth_file_tool_adjust_colors_get_type ())
+#define GTH_FILE_TOOL_ADJUST_COLORS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_FILE_TOOL_ADJUST_COLORS, GthFileToolAdjustColors))
+#define GTH_FILE_TOOL_ADJUST_COLORS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_FILE_TOOL_ADJUST_COLORS, GthFileToolAdjustColorsClass))
+#define GTH_IS_FILE_TOOL_ADJUST_COLORS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_FILE_TOOL_ADJUST_COLORS))
+#define GTH_IS_FILE_TOOL_ADJUST_COLORS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_FILE_TOOL_ADJUST_COLORS))
+#define GTH_FILE_TOOL_ADJUST_COLORS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_FILE_TOOL_ADJUST_COLORS, GthFileToolAdjustColorsClass))
+
+typedef struct _GthFileToolAdjustColors GthFileToolAdjustColors;
+typedef struct _GthFileToolAdjustColorsClass GthFileToolAdjustColorsClass;
+typedef struct _GthFileToolAdjustColorsPrivate GthFileToolAdjustColorsPrivate;
+
+struct _GthFileToolAdjustColors {
+	GthFileTool parent_instance;
+	GthFileToolAdjustColorsPrivate *priv;
+};
+
+struct _GthFileToolAdjustColorsClass {
+	GthFileToolClass parent_class;
+};
+
+GType  gth_file_tool_adjust_colors_get_type  (void);
+
+G_END_DECLS
+
+#endif /* GTH_FILE_TOOL_ADJUST_COLORS_H */
diff --git a/extensions/file_tools/gth-file-tool-crop.c b/extensions/file_tools/gth-file-tool-crop.c
index 689c50f..6cb6974 100644
--- a/extensions/file_tools/gth-file-tool-crop.c
+++ b/extensions/file_tools/gth-file-tool-crop.c
@@ -106,7 +106,7 @@ crop_button_clicked_cb (GtkButton       *button,
 
 		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_pixbuf (GTH_IMAGE_VIEWER_PAGE (viewer_page), new_pixbuf);
+		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (viewer_page), new_pixbuf, TRUE);
 		gth_file_tool_hide_options (GTH_FILE_TOOL (self));
 
 		g_object_unref (new_pixbuf);
diff --git a/extensions/file_tools/gth-file-tool-desaturate.c b/extensions/file_tools/gth-file-tool-desaturate.c
index d639a24..b093f0b 100644
--- a/extensions/file_tools/gth-file-tool-desaturate.c
+++ b/extensions/file_tools/gth-file-tool-desaturate.c
@@ -68,7 +68,7 @@ desaturate_release (GthPixbufTask *pixop,
 		    GError        *error)
 {
 	if (error == NULL)
-		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (pixop->data), pixop->dest);
+		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (pixop->data), pixop->dest, TRUE);
 }
 
 
diff --git a/extensions/file_tools/gth-file-tool-enhance.c b/extensions/file_tools/gth-file-tool-enhance.c
index bbd35fd..38e47ae 100644
--- a/extensions/file_tools/gth-file-tool-enhance.c
+++ b/extensions/file_tools/gth-file-tool-enhance.c
@@ -190,9 +190,9 @@ adjust_levels_step (GthPixbufTask *pixop)
 {
 	EnhanceData *data = pixop->data;
 
-	pixop->dest_pixel[RED_PIX]   = levels_func (pixop->src_pixel[RED_PIX], data->levels, 0);
-	pixop->dest_pixel[GREEN_PIX] = levels_func (pixop->src_pixel[GREEN_PIX], data->levels, 1);
-	pixop->dest_pixel[BLUE_PIX]  = levels_func (pixop->src_pixel[BLUE_PIX], data->levels, 2);
+	pixop->dest_pixel[RED_PIX]   = levels_func (pixop->src_pixel[RED_PIX], data->levels, RED_PIX);
+	pixop->dest_pixel[GREEN_PIX] = levels_func (pixop->src_pixel[GREEN_PIX], data->levels, GREEN_PIX);
+	pixop->dest_pixel[BLUE_PIX]  = levels_func (pixop->src_pixel[BLUE_PIX], data->levels, BLUE_PIX);
 
 	if (pixop->has_alpha)
 		pixop->dest_pixel[ALPHA_PIX] = pixop->src_pixel[ALPHA_PIX];
@@ -206,11 +206,11 @@ adjust_levels_release (GthPixbufTask *pixop,
 	EnhanceData *data = pixop->data;
 
 	if (error == NULL)
-		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (data->viewer_page), pixop->dest);
+		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (data->viewer_page), pixop->dest, TRUE);
 
 	gth_histogram_free (data->hist);
 	g_free (data->levels);
-	g_object_ref (data->viewer_page);
+	g_object_unref (data->viewer_page);
 	g_free (data);
 }
 
diff --git a/extensions/file_tools/gth-file-tool-equalize.c b/extensions/file_tools/gth-file-tool-equalize.c
index edc43ff..395f20f 100644
--- a/extensions/file_tools/gth-file-tool-equalize.c
+++ b/extensions/file_tools/gth-file-tool-equalize.c
@@ -124,7 +124,7 @@ equalize_release (GthPixbufTask *pixop,
 	int           i;
 
 	if (error == NULL)
-		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (data->viewer_page), pixop->dest);
+		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (data->viewer_page), pixop->dest, TRUE);
 
 	for (i = 0; i < MAX_N_CHANNELS + 1; i++)
 		g_free (data->part[i]);
diff --git a/extensions/file_tools/main.c b/extensions/file_tools/main.c
index 8c3f75e..94d0c36 100644
--- a/extensions/file_tools/main.c
+++ b/extensions/file_tools/main.c
@@ -24,6 +24,7 @@
 #include <config.h>
 #include <gtk/gtk.h>
 #include <gthumb.h>
+#include "gth-file-tool-adjust-colors.h"
 #include "gth-file-tool-crop.h"
 #include "gth-file-tool-desaturate.h"
 #include "gth-file-tool-enhance.h"
@@ -41,10 +42,11 @@ gthumb_extension_activate (void)
 	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_SAVE_AS);
 	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_UNDO);
 	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_REDO);
-	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_CROP);
-	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_DESATURATE);
+	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_ADJUST_COLORS);
 	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_ENHANCE);
 	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_EQUALIZE);
+	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_DESATURATE);
+	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_CROP);
 }
 
 
diff --git a/extensions/image_viewer/gth-image-viewer-page.c b/extensions/image_viewer/gth-image-viewer-page.c
index 0b3243d..5df5828 100644
--- a/extensions/image_viewer/gth-image-viewer-page.c
+++ b/extensions/image_viewer/gth-image-viewer-page.c
@@ -1144,11 +1144,13 @@ gth_image_viewer_page_get_pixbuf (GthImageViewerPage *self)
 
 void
 gth_image_viewer_page_set_pixbuf (GthImageViewerPage *self,
-				  GdkPixbuf          *pixbuf)
+				  GdkPixbuf          *pixbuf,
+				  gboolean            add_to_history)
 {
-	gth_image_history_add_image (self->priv->history,
-				     gth_image_viewer_page_get_pixbuf (self),
-				     gth_browser_get_file_modified (GTH_BROWSER (self->priv->browser)));
+	if (add_to_history)
+		gth_image_history_add_image (self->priv->history,
+					     gth_image_viewer_page_get_pixbuf (self),
+					     gth_browser_get_file_modified (GTH_BROWSER (self->priv->browser)));
 	_gth_image_viewer_page_set_pixbuf (self, pixbuf, TRUE);
 }
 
diff --git a/extensions/image_viewer/gth-image-viewer-page.h b/extensions/image_viewer/gth-image-viewer-page.h
index 2e6cda6..22a826b 100644
--- a/extensions/image_viewer/gth-image-viewer-page.h
+++ b/extensions/image_viewer/gth-image-viewer-page.h
@@ -51,7 +51,8 @@ GType              gth_image_viewer_page_get_type         (void);
 GtkWidget *        gth_image_viewer_page_get_image_viewer (GthImageViewerPage *page);
 GdkPixbuf *        gth_image_viewer_page_get_pixbuf       (GthImageViewerPage *page);
 void               gth_image_viewer_page_set_pixbuf       (GthImageViewerPage *page,
-							   GdkPixbuf          *pixbuf);
+							   GdkPixbuf          *pixbuf,
+							   gboolean            add_to_history);
 void               gth_image_viewer_page_undo             (GthImageViewerPage *page);
 void               gth_image_viewer_page_redo             (GthImageViewerPage *page);
 GthImageHistory *  gth_image_viewer_page_get_history      (GthImageViewerPage *self);
diff --git a/extensions/red_eye_removal/gth-file-tool-red-eye.c b/extensions/red_eye_removal/gth-file-tool-red-eye.c
index 3e73c5a..ef3cbea 100644
--- a/extensions/red_eye_removal/gth-file-tool-red-eye.c
+++ b/extensions/red_eye_removal/gth-file-tool-red-eye.c
@@ -339,7 +339,7 @@ selector_selected_cb (GthImageSelector  *selector,
 	new_pixbuf = gdk_pixbuf_copy (old_pixbuf);
 	init_is_red (self, new_pixbuf);
 	if (fix_redeye (new_pixbuf, self->priv->is_red, x, y)) {
-		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (viewer_page), new_pixbuf);
+		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (viewer_page), new_pixbuf, TRUE);
 		gth_file_tool_hide_options (GTH_FILE_TOOL (self));
 	}
 
diff --git a/gthumb/gth-pixbuf-task.c b/gthumb/gth-pixbuf-task.c
index 5bc537b..f95ba74 100644
--- a/gthumb/gth-pixbuf-task.c
+++ b/gthumb/gth-pixbuf-task.c
@@ -24,9 +24,7 @@
 #include "gth-pixbuf-task.h"
 
 
-#define N_STEPS          20   /* number of lines to process in a single
-			       * timeout handler. */
-#define PROGRESS_STEP    5    /* notify progress each PROGRESS_STEP lines. */
+#define PROGRESS_DELAY 200 /* delay between progress notifications */
 
 
 static gpointer parent_class = NULL;
@@ -55,34 +53,40 @@ gth_pixbuf_task_finalize (GObject *object)
 	g_return_if_fail (GTH_IS_PIXBUF_TASK (object));
 	pixbuf_task = GTH_PIXBUF_TASK (object);
 
-	if (pixbuf_task->timeout_id != 0) {
-		g_source_remove (pixbuf_task->timeout_id);
-		pixbuf_task->timeout_id = 0;
+	if (pixbuf_task->progress_event != 0) {
+		g_source_remove (pixbuf_task->progress_event);
+		pixbuf_task->progress_event = 0;
 	}
+
 	release_pixbufs (pixbuf_task);
 	if (pixbuf_task->free_data_func != NULL)
 		(*pixbuf_task->free_data_func) (pixbuf_task);
 
+	g_mutex_free (pixbuf_task->data_mutex);
+
 	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 
 static gboolean
-one_step (gpointer data)
+update_progress (gpointer data)
 {
 	GthPixbufTask *pixbuf_task = data;
-	int            dir = 1;
+	gboolean       interrupt;
+	int            line;
 
-	if (! pixbuf_task->interrupt && pixbuf_task->single_step)
-		(*pixbuf_task->step_func) (pixbuf_task);
+	g_mutex_lock (pixbuf_task->data_mutex);
+	interrupt = pixbuf_task->interrupt;
+	line = pixbuf_task->line;
+	g_mutex_unlock (pixbuf_task->data_mutex);
 
-	if ((pixbuf_task->line >= pixbuf_task->height)
-	    || pixbuf_task->single_step
-	    || pixbuf_task->interrupt)
-	{
+	if ((line >= pixbuf_task->height) || interrupt) {
 		GError *error = NULL;
 
-		if (pixbuf_task->interrupt)
+		g_source_remove (pixbuf_task->progress_event);
+		pixbuf_task->progress_event = 0;
+
+		if (interrupt)
 			error = g_error_new_literal (GTH_TASK_ERROR, GTH_TASK_ERROR_CANCELLED, "");
 
 		if (pixbuf_task->release_func != NULL)
@@ -93,19 +97,37 @@ one_step (gpointer data)
 		return FALSE;
 	}
 
+	gth_task_progress (GTH_TASK (pixbuf_task),
+			   pixbuf_task->description,
+			   NULL,
+			   FALSE,
+			   (double) line / pixbuf_task->height);
+
+	return TRUE;
+}
+
+
+static gboolean
+execute_step (GthPixbufTask *pixbuf_task)
+{
+	int      dir = 1;
+	gboolean interrupt;
+	int      line;
+
+	g_mutex_lock (pixbuf_task->data_mutex);
+	interrupt = pixbuf_task->interrupt;
+	line = pixbuf_task->line;
+	g_mutex_unlock (pixbuf_task->data_mutex);
+
+	if ((line >= pixbuf_task->height) || interrupt)
+		return FALSE;
+
 	pixbuf_task->src_pixel = pixbuf_task->src_line;
 	pixbuf_task->src_line += pixbuf_task->rowstride;
 
 	pixbuf_task->dest_pixel = pixbuf_task->dest_line;
 	pixbuf_task->dest_line += pixbuf_task->rowstride;
 
-	if (pixbuf_task->line % PROGRESS_STEP == 0)
-		gth_task_progress (GTH_TASK (pixbuf_task),
-				   pixbuf_task->description,
-				   NULL,
-				   FALSE,
-				   (double) pixbuf_task->line / pixbuf_task->height);
-
 	if (! pixbuf_task->ltr) { /* right to left */
 		int ofs = (pixbuf_task->width - 1) * pixbuf_task->bytes_per_pixel;
 		pixbuf_task->src_pixel += ofs;
@@ -125,30 +147,29 @@ one_step (gpointer data)
 		pixbuf_task->line_step++;
 	}
 
+	g_mutex_lock (pixbuf_task->data_mutex);
 	pixbuf_task->line++;
+	g_mutex_unlock (pixbuf_task->data_mutex);
 
 	return TRUE;
 }
 
 
-static gboolean
-step (gpointer data)
+static gpointer
+execute_task (gpointer user_data)
 {
-	GthPixbufTask *pixbuf_task = data;
-	int          i;
+	GthPixbufTask *pixbuf_task = user_data;
 
-	if (pixbuf_task->timeout_id != 0) {
-		g_source_remove (pixbuf_task->timeout_id);
-		pixbuf_task->timeout_id = 0;
-	}
+	g_mutex_lock (pixbuf_task->data_mutex);
+	pixbuf_task->line = 0;
+	g_mutex_unlock (pixbuf_task->data_mutex);
 
-	for (i = 0; i < N_STEPS; i++)
-		if (! one_step (data))
-			return FALSE;
+	if (pixbuf_task->init_func != NULL)
+		(*pixbuf_task->init_func) (pixbuf_task);
 
-	pixbuf_task->timeout_id = g_idle_add (step, pixbuf_task);
+	while (execute_step (pixbuf_task)) /* void */;
 
-	return FALSE;
+	return NULL;
 }
 
 
@@ -157,17 +178,12 @@ gth_pixbuf_task_exec (GthTask *task)
 {
 	GthPixbufTask *pixbuf_task;
 
-	g_return_if_fail (GTH_IS_PIXBUF_TASK (task));
-
 	pixbuf_task = GTH_PIXBUF_TASK (task);
 
 	g_return_if_fail (pixbuf_task->src != NULL);
 
-	pixbuf_task->line = 0;
-	if (pixbuf_task->init_func != NULL)
-		(*pixbuf_task->init_func) (pixbuf_task);
-
-	step (pixbuf_task);
+	g_thread_create (execute_task, pixbuf_task, FALSE, NULL);
+	pixbuf_task->progress_event = g_timeout_add (PROGRESS_DELAY, update_progress, pixbuf_task);
 }
 
 
@@ -179,7 +195,10 @@ gth_pixbuf_task_cancel (GthTask *task)
 	g_return_if_fail (GTH_IS_PIXBUF_TASK (task));
 
 	pixbuf_task = GTH_PIXBUF_TASK (task);
+
+	g_mutex_lock (pixbuf_task->data_mutex);
 	pixbuf_task->interrupt = TRUE;
+	g_mutex_unlock (pixbuf_task->data_mutex);
 }
 
 
@@ -219,9 +238,11 @@ gth_pixbuf_task_init (GthPixbufTask *pixbuf_task)
 
 	pixbuf_task->ltr = TRUE;
 
-	pixbuf_task->timeout_id = 0;
+	pixbuf_task->progress_event = 0;
 	pixbuf_task->line = 0;
 	pixbuf_task->interrupt = FALSE;
+
+	pixbuf_task->data_mutex = g_mutex_new ();
 }
 
 
@@ -279,17 +300,9 @@ gth_pixbuf_task_new (const char     *description,
 
 
 void
-gth_pixbuf_task_set_single_step (GthPixbufTask *pixbuf_task,
-			       gboolean     single_step)
-{
-	pixbuf_task->single_step = single_step;
-}
-
-
-void
-gth_pixbuf_task_set_pixbufs (GthPixbufTask  *pixbuf_task,
-			     GdkPixbuf    *src,
-			     GdkPixbuf    *dest)
+gth_pixbuf_task_set_pixbufs (GthPixbufTask *pixbuf_task,
+			     GdkPixbuf     *src,
+			     GdkPixbuf     *dest)
 {
 	if (src == NULL)
 		return;
diff --git a/gthumb/gth-pixbuf-task.h b/gthumb/gth-pixbuf-task.h
index e3b0d83..4f8d251 100644
--- a/gthumb/gth-pixbuf-task.h
+++ b/gthumb/gth-pixbuf-task.h
@@ -61,8 +61,6 @@ struct _GthPixbufTask {
 	PixbufDoneFunc  release_func;
 	PixbufOpFunc    free_data_func;
 
-	gboolean        single_step;
-
 	gboolean        has_alpha;
 	int             bytes_per_pixel;
 	int             width, height;
@@ -71,13 +69,16 @@ struct _GthPixbufTask {
 	guchar         *dest_line, *dest_pixel;
 
 	gboolean        ltr, first_step, last_step;
-	guint           timeout_id;
 	int             line;
 	int             line_step;
 	int             column;
 	gboolean        interrupt;
-
 	const char     *description;
+
+	/*< private >*/
+
+	GMutex         *data_mutex;
+	guint           progress_event;
 };
 
 struct _GthPixbufTaskClass {
@@ -92,8 +93,6 @@ GthTask *     gth_pixbuf_task_new             (const char     *description,
 					       PixbufOpFunc    step_func,
 					       PixbufDoneFunc  release_func,
 					       gpointer        data);
-void          gth_pixbuf_task_set_single_step (GthPixbufTask  *pixbuf_task,
-					       gboolean        single_step);
 void          gth_pixbuf_task_set_pixbufs     (GthPixbufTask  *pixbuf_task,
 					       GdkPixbuf      *src,
 					       GdkPixbuf      *dest);
diff --git a/gthumb/gth-sidebar.c b/gthumb/gth-sidebar.c
index e63d12d..8727875 100644
--- a/gthumb/gth-sidebar.c
+++ b/gthumb/gth-sidebar.c
@@ -146,7 +146,8 @@ gth_sidebar_set_file (GthSidebar  *sidebar,
 	GList *children;
 	GList *scan;
 
-	gth_toolbox_deactivate_tool (GTH_TOOLBOX (sidebar->priv->toolbox));
+	if (! g_file_info_get_attribute_boolean (file_data->info, "gth::file::is-modified"))
+		gth_toolbox_deactivate_tool (GTH_TOOLBOX (sidebar->priv->toolbox));
 
 	children = gth_multipage_get_children (GTH_MULTIPAGE (sidebar->priv->properties));
 	for (scan = children; scan; scan = scan->next) {
@@ -165,8 +166,8 @@ gth_sidebar_set_file (GthSidebar  *sidebar,
 void
 gth_sidebar_show_properties (GthSidebar *sidebar)
 {
-	if (gtk_notebook_get_current_page (GTK_NOTEBOOK (sidebar)) == GTH_SIDEBAR_PAGE_TOOLS)
-		gth_toolbox_deactivate_tool (GTH_TOOLBOX (sidebar->priv->toolbox));
+	/*if (gtk_notebook_get_current_page (GTK_NOTEBOOK (sidebar)) == GTH_SIDEBAR_PAGE_TOOLS)
+		gth_toolbox_deactivate_tool (GTH_TOOLBOX (sidebar->priv->toolbox)); FIXME */
 	gtk_notebook_set_current_page (GTK_NOTEBOOK (sidebar), GTH_SIDEBAR_PAGE_PROPERTIES);
 }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]