Fri Nov 24 21:37:01 2006 John Ellis <johne@verizon.net>
authorJohn Ellis <johne@verizon.net>
Sat, 25 Nov 2006 03:00:33 +0000 (03:00 +0000)
committerJohn Ellis <johne@verizon.net>
Sat, 25 Nov 2006 03:00:33 +0000 (03:00 +0000)
        * configure.in: Add test for lcms (little cms).
        * Makefile.am: Add color-man.[ch]:
        * color-man.[ch]: New files for color management support.
        * globals.c, gqview.h, main.c, rcfile.c, typedefs.h: Add color profile
        variables and option saving.
        * image.[ch]: Add color profile functions.
        * layout.c, layout_image.[ch]: Add color profile icon, popup menu, and
        fix sort menu to use radio buttons.
        * menu.c: Use radio buttons for sort menu when appropriate.
        * preferences.c: Add color profile options to preferences.
        * ui_menu.[ch]: Add menu_item_add_radio() for radio item menus.
        * ui_misc.c: Fix gtk_table_attach() arg for vertical expansion.
        * view_file_icon.c, view_file_list.c: Check for active state in sort
        menu callbacks.
        * README: Add info about lcms, and how to disable.

24 files changed:
ChangeLog
README
TODO
configure.in
src/Makefile.am
src/color-man.c [new file with mode: 0644]
src/color-man.h [new file with mode: 0644]
src/globals.c
src/gqview.h
src/image.c
src/image.h
src/layout.c
src/layout_image.c
src/layout_image.h
src/main.c
src/menu.c
src/preferences.c
src/rcfile.c
src/typedefs.h
src/ui_menu.c
src/ui_menu.h
src/ui_misc.c
src/view_file_icon.c
src/view_file_list.c

index 56cdcf8..8c91a33 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+Fri Nov 24 21:37:01 2006  John Ellis  <johne@verizon.net>
+
+       * configure.in: Add test for lcms (little cms).
+       * Makefile.am: Add color-man.[ch]:
+       * color-man.[ch]: New files for color management support.
+       * globals.c, gqview.h, main.c, rcfile.c, typedefs.h: Add color profile
+       variables and option saving.
+       * image.[ch]: Add color profile functions.
+       * layout.c, layout_image.[ch]: Add color profile icon, popup menu, and
+       fix sort menu to use radio buttons.
+       * menu.c: Use radio buttons for sort menu when appropriate.
+       * preferences.c: Add color profile options to preferences.
+       * ui_menu.[ch]: Add menu_item_add_radio() for radio item menus.
+       * ui_misc.c: Fix gtk_table_attach() arg for vertical expansion.
+       * view_file_icon.c, view_file_list.c: Check for active state in sort
+       menu callbacks.
+       * README: Add info about lcms, and how to disable.
+
 Fri Nov 17 19:06:19 2006  John Ellis  <johne@verizon.net>
 
        * ui_fileops.[ch]: Add path_list_lstat() to obtain a path listing that
diff --git a/README b/README
index df09b5e..8eb860b 100644 (file)
--- a/README
+++ b/README
@@ -38,7 +38,12 @@ homepage: http://gqview.sourceforge.net
 
 ======== Requirements
 
-  GTK+ 2.4.x: ftp://ftp.gtk.org/pub/gtk
+  Required libraries:
+    GTK+ 2.4.x: ftp://ftp.gtk.org/pub/gtk
+
+  Optional libraries:
+    lcms, for color management support: http://www.littlecms.com
+        (disable with configure option: '--without-lcms')
 
 ======== Notes and changes for this release            [section:release_notes]
 
@@ -81,6 +86,10 @@ homepage: http://gqview.sourceforge.net
 
     Version in perenthesis indicates first appearance of feature or change.
 
+    (2.1.5) Add support for color profiles when lcms is installed. To
+    disable color profiles and use of lcms, run configure
+    with '--without-lcms'.
+
     (2.1.1) Add support for viewing jpeg images and EXIF embedded within
     raw files for Canon (.crw, .cr2) Fujifilm (.raf), and Nikon (.nef).
     Note that not all cameras that support a raw format will necessarily
diff --git a/TODO b/TODO
index 71cf578..ded5ffb 100644 (file)
--- a/TODO
+++ b/TODO
@@ -21,6 +21,10 @@ d> figure out if crash when expanding a folder in the folder tree view when pess
  > cache-load.c:
    > should honor enable_thumbnails setting
 
+ > color profiles:
+   > support profiles embedded in images
+   > add support in img-view.c
+
   ---
 
  >raw + exif formats:
@@ -101,6 +105,7 @@ d> figure out if crash when expanding a folder in the folder tree view when pess
    > [Control]+V now shows image in new window
    > [Shift]+P print shortcut added to collection and img-view windows.
      (fixme, forgot to add it to find dialog).
+   > add color profile page
 
    > add blurb about moving images between collections with shift+drag
 
@@ -113,6 +118,8 @@ Minor (non blockers):
 
 d> update icon used for window to the (not so) new icon
 
+ > fix gtk_table_attach use to not use FALSE for fill vertical arg.
+
  > xv and xpaint are hardly used or even installed by any distro anymore - time
    to remove these (and find alternates?) seems silly to only have gimp.
 
index eb4ba44..599099b 100644 (file)
@@ -25,6 +25,33 @@ AC_DEFINE_UNQUOTED(GQVIEW_HTMLDIR, "$prefix/share/doc/gqview-$VERSION/html", [Lo
 dnl checks for functions
 AC_CHECK_FUNCS(strverscmp)
 
+dnl check for little cms (lcms, this test pulled from gimp)
+AC_ARG_WITH(lcms, [  --without-lcms          build without lcms support])
+
+have_lcms=no
+if test "x$with_lcms" != "xno"; then
+  AC_CHECK_LIB(lcms, cmsCreate_sRGBProfile, [
+    AC_CHECK_HEADER(lcms.h,
+      have_lcms=yes, [
+      AC_CHECK_HEADER(lcms/lcms.h,
+        have_lcms=yes
+        AC_DEFINE(HAVE_LCMS_LCMS_H, 1,
+          [Define to 1 if the lcms header must be included as lcms/lcms.h]))
+      ])
+  ])
+  if test "$have_lcms" = "yes"; then
+    LCMS_LIBS="-llcms"
+    AC_DEFINE(HAVE_LCMS, 1, [define to enable use of color profiles with lcms])
+  else
+    have_lcms="no (lcms not found or unusable)"
+  fi
+else
+  have_lcms="no (lcms support disabled)"
+fi
+
+AC_SUBST(LCMS_LIBS)
+AM_CONDITIONAL(HAVE_LCMS, test "$have_lcms" = "yes")
+
 ALL_LINGUAS="ar be bg ca cs da de eo es et eu fi fr hu id it ja ko nl no pl pt_BR ro ru sk sl sv th tr uk vi zh_CN.GB2312 zh_TW"
 GETTEXT_PACKAGE=$PACKAGE
 AC_SUBST(GETTEXT_PACKAGE)
index 9a86f6f..7ce4702 100644 (file)
@@ -74,6 +74,8 @@ gqview_SOURCES = \
        collect-io.h    \
        collect-table.c \
        collect-table.h \
+       color-man.c     \
+       color-man.h     \
        dnd.c           \
        dnd.h           \
        dupe.c          \
@@ -164,7 +166,7 @@ gqview_SOURCES = \
        view_file_icon.c        \
        view_file_icon.h
 
-gqview_LDADD = $(GTK_LIBS) $(INTLLIBS)
+gqview_LDADD = $(GTK_LIBS) $(INTLLIBS) $(LCMS_LIBS)
 
 EXTRA_DIST = \
        $(extra_SLIK)
diff --git a/src/color-man.c b/src/color-man.c
new file mode 100644 (file)
index 0000000..5c52457
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * GQview
+ * (C) 2006 John Ellis
+ *
+ * Author: John Ellis
+ *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
+ */
+
+
+#include "gqview.h"
+#include "color-man.h"
+
+#include "image.h"
+#include "ui_fileops.h"
+
+
+#ifdef HAVE_LCMS
+/*** color support enabled ***/
+
+#ifdef HAVE_LCMS_LCMS_H
+  #include <lcms/lcms.h>
+#else
+  #include <lcms.h>
+#endif
+
+
+typedef struct _ColorManCache ColorManCache;
+struct _ColorManCache {
+       cmsHPROFILE   profile_in;
+       cmsHPROFILE   profile_out;
+       cmsHTRANSFORM transform;
+
+       ColorManProfileType profile_in_type;
+       gchar *profile_in_file;
+
+       ColorManProfileType profile_out_type;
+       gchar *profile_out_file;
+
+       gint has_alpha;
+
+       gint refcount;
+};
+
+/* pixels to transform per idle call */
+#define COLOR_MAN_CHUNK_SIZE 81900
+
+
+static void color_man_lib_init(void)
+{
+       static gint init_done = FALSE;
+
+       if (init_done) return;
+       init_done = TRUE;
+
+       cmsErrorAction(LCMS_ERROR_IGNORE);
+}
+
+
+/*
+ *-------------------------------------------------------------------
+ * color transform cache
+ *-------------------------------------------------------------------
+ */
+
+static GList *cm_cache_list = NULL;
+
+
+static void color_man_cache_ref(ColorManCache *cc)
+{
+       if (!cc) return;
+
+       cc->refcount++;
+}
+
+static void color_man_cache_unref(ColorManCache *cc)
+{
+       if (!cc) return;
+
+       cc->refcount--;
+       if (cc->refcount < 1)
+               {
+               if (cc->transform) cmsDeleteTransform(cc->transform);
+               if (cc->profile_in) cmsCloseProfile(cc->profile_in);
+               if (cc->profile_out) cmsCloseProfile(cc->profile_out);
+
+               g_free(cc->profile_in_file);
+               g_free(cc->profile_out_file);
+
+               g_free(cc);
+               }
+}
+
+static cmsHPROFILE color_man_cache_load_profile(ColorManProfileType type, const gchar *file)
+{
+       cmsHPROFILE profile = NULL;
+
+       switch (type)
+               {
+               case COLOR_PROFILE_FILE:
+                       if (file)
+                               {
+                               gchar *pathl;
+
+                               pathl = path_from_utf8(file);
+                               profile = cmsOpenProfileFromFile(pathl, "r");
+                               g_free(pathl);
+                               }
+                       break;
+               case COLOR_PROFILE_SRGB:
+                       profile = cmsCreate_sRGBProfile();
+                       break;
+               case COLOR_PROFILE_NONE:
+               default:
+                       break;
+               }
+
+       return profile;
+}
+
+static ColorManCache *color_man_cache_new(ColorManProfileType in_type, const gchar *in_file,
+                                         ColorManProfileType out_type, const gchar *out_file,
+                                         gint has_alpha)
+{
+       ColorManCache *cc;
+
+       color_man_lib_init();
+
+       cc = g_new0(ColorManCache, 1);
+       cc->refcount = 1;
+
+       cc->profile_in_type = in_type;
+       cc->profile_in_file = g_strdup(in_file);
+
+       cc->profile_out_type = out_type;
+       cc->profile_out_file = g_strdup(out_file);
+
+       cc->has_alpha = has_alpha;
+
+       cc->profile_in = color_man_cache_load_profile(cc->profile_in_type, cc->profile_in_file);
+       cc->profile_out = color_man_cache_load_profile(cc->profile_out_type, cc->profile_out_file);
+
+       if (!cc->profile_in || !cc->profile_out)
+               {
+               if (debug) printf("failed to load color profile for %s: %d %s\n",
+                                 (!cc->profile_in) ? "input" : "screen",
+                                 (!cc->profile_in) ? cc->profile_in_type : cc->profile_out_type,
+                                 (!cc->profile_in) ? cc->profile_in_file : cc->profile_out_file);
+
+               color_man_cache_unref(cc);
+               return NULL;
+               }
+
+       cc->transform = cmsCreateTransform(cc->profile_in,
+                                          (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
+                                          cc->profile_out,
+                                          (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
+                                          INTENT_PERCEPTUAL, 0);
+
+       if (!cc->transform)
+               {
+               if (debug) printf("failed to create color profile transform\n");
+
+               color_man_cache_unref(cc);
+               return NULL;
+               }
+
+       cm_cache_list = g_list_append(cm_cache_list, cc);
+
+       return cc;
+}
+
+static void color_man_cache_free(ColorManCache *cc)
+{
+       if (!cc) return;
+
+       cm_cache_list = g_list_remove(cm_cache_list, cc);
+       color_man_cache_unref(cc);
+}
+
+static void color_man_cache_reset(void)
+{
+       while (cm_cache_list)
+               {
+               ColorManCache *cc;
+
+               cc = cm_cache_list->data;
+               color_man_cache_free(cc);
+               }
+}
+
+static ColorManCache *color_man_cache_find(ColorManProfileType in_type, const gchar *in_file,
+                                          ColorManProfileType out_type, const gchar *out_file,
+                                          gint has_alpha)
+{
+       GList *work;
+
+       work = cm_cache_list;
+       while (work)
+               {
+               ColorManCache *cc;
+               gint match = FALSE;
+
+               cc = work->data;
+               work = work->next;
+
+               if (cc->profile_in_type == in_type &&
+                   cc->profile_out_type == out_type &&
+                   cc->has_alpha == has_alpha)
+                       {
+                       match = TRUE;
+                       }
+
+               if (match && cc->profile_in_type == COLOR_PROFILE_FILE)
+                       {
+                       match = (cc->profile_in_file && in_file &&
+                                strcmp(cc->profile_in_file, in_file) == 0);
+                       }
+               if (match && cc->profile_out_type == COLOR_PROFILE_FILE)
+                       {
+                       match = (cc->profile_out_file && out_file &&
+                                strcmp(cc->profile_out_file, out_file) == 0);
+                       }
+
+               if (match) return cc;
+               }
+
+       return NULL;
+}
+
+static ColorManCache *color_man_cache_get(ColorManProfileType in_type, const gchar *in_file,
+                                         ColorManProfileType out_type, const gchar *out_file,
+                                         gint has_alpha)
+{
+       ColorManCache *cc;
+
+       cc = color_man_cache_find(in_type, in_file, out_type, out_file, has_alpha);
+
+       if (!cc)
+               {
+               cc = color_man_cache_new(in_type, in_file, out_type, out_file, has_alpha);
+               }
+
+       return cc;
+}
+
+
+/*
+ *-------------------------------------------------------------------
+ * color manager
+ *-------------------------------------------------------------------
+ */
+
+static void color_man_done(ColorMan *cm, ColorManReturnType type)
+{
+       if (cm->func_done)
+               {
+               cm->func_done(cm, type, cm->func_done_data);
+               }
+}
+
+static void color_man_correct_region(ColorMan *cm, gint x, gint y, gint w, gint h,
+                                    gint pixbuf_width, gint pixbuf_height)
+{
+       ColorManCache *cc;
+       guchar *pix;
+       gint rs;
+       gint i;
+
+       cc = cm->profile;
+
+       pix = gdk_pixbuf_get_pixels(cm->pixbuf);
+       rs = gdk_pixbuf_get_rowstride(cm->pixbuf);
+
+       w = MIN(w, pixbuf_width - x);
+       h = MIN(h, pixbuf_height - y);
+
+       pix += x * ((cc->has_alpha) ? 4 : 3);
+       for (i = 0; i < h; i++)
+               {
+               guchar *pbuf;
+
+               pbuf = pix + ((y + i) * rs);
+               cmsDoTransform(cc->transform, pbuf, pbuf, w);
+               }
+
+       image_area_changed(cm->imd, x, y, w, h);
+}
+
+static gint color_man_idle_cb(gpointer data)
+{
+       ColorMan *cm = data;
+       gint width, height;
+       gint rh;
+
+       if (cm->pixbuf != image_get_pixbuf(cm->imd))
+               {
+               cm->idle_id = -1;
+               color_man_done(cm, COLOR_RETURN_IMAGE_CHANGED);
+               return FALSE;
+               }
+
+       width = gdk_pixbuf_get_width(cm->pixbuf);
+       height = gdk_pixbuf_get_height(cm->pixbuf);
+
+       if (cm->row > height)
+               {
+               cm->idle_id = -1;
+               color_man_done(cm, COLOR_RETURN_SUCCESS);
+               return FALSE;
+               }
+
+       rh = COLOR_MAN_CHUNK_SIZE / width + 1;
+       color_man_correct_region(cm, 0, cm->row, width, rh, width, height);
+       cm->row += rh;
+
+       return TRUE;
+}
+
+ColorMan *color_man_new(ImageWindow *imd,
+                       ColorManProfileType input_type, const gchar *input_file,
+                       ColorManProfileType screen_type, const gchar *screen_file,
+                       ColorManDoneFunc done_func, gpointer done_data)
+{
+       ColorMan *cm;
+       GdkPixbuf *pixbuf;
+       gint has_alpha;
+
+       if (!imd) return NULL;
+       if (input_type == COLOR_PROFILE_NONE || screen_type == COLOR_PROFILE_NONE) return NULL;
+
+       pixbuf = image_get_pixbuf(imd);
+       if (!pixbuf) return NULL;
+
+       cm = g_new0(ColorMan, 1);
+       cm->imd = imd;
+       cm->pixbuf = pixbuf;
+       cm->row = 0;
+       cm->idle_id = -1;
+
+       cm->func_done = done_func;
+       cm->func_done_data = done_data;
+
+       has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);
+       cm->profile = color_man_cache_get(input_type, input_file, screen_type, screen_file, has_alpha);
+       if (!cm->profile)
+               {
+               color_man_free(cm);
+               return NULL;
+               }
+
+       color_man_cache_ref(cm->profile);
+
+       cm->idle_id = g_idle_add(color_man_idle_cb, cm);
+
+       return cm;
+}
+
+void color_man_free(ColorMan *cm)
+{
+       if (!cm) return;
+
+       if (cm->idle_id != -1) g_source_remove(cm->idle_id);
+
+       color_man_cache_unref(cm->profile);
+
+       g_free(cm);
+}
+
+void color_man_update(void)
+{
+       color_man_cache_reset();
+}
+
+#else
+/*** color support not enabled ***/
+
+
+ColorMan *color_man_new(ImageWindow *imd,
+                       ColorManProfileType input_type, const gchar *input_file,
+                       ColorManProfileType screen_type, const gchar *screen_file,
+                       ColorManDoneFunc don_func, gpointer done_data)
+{
+       /* no op */
+       return NULL;
+}
+
+void color_man_free(ColorMan *cm)
+{
+       /* no op */
+}
+
+void color_man_update(void)
+{
+       /* no op */
+}
+
+
+#endif
+
+
diff --git a/src/color-man.h b/src/color-man.h
new file mode 100644 (file)
index 0000000..b5d3f7c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * GQview
+ * (C) 2006 John Ellis
+ *
+ * Author: John Ellis
+ *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
+ */
+
+
+#ifndef COLOR_MAN_H
+#define COLOR_MAN_H
+
+typedef enum {
+       COLOR_PROFILE_NONE = 0,
+       COLOR_PROFILE_FILE,
+       COLOR_PROFILE_SRGB,
+} ColorManProfileType;
+
+typedef enum {
+       COLOR_RETURN_SUCCESS = 0,
+       COLOR_RETURN_ERROR,
+       COLOR_RETURN_IMAGE_CHANGED
+} ColorManReturnType;
+
+typedef struct _ColorMan ColorMan;
+typedef void (* ColorManDoneFunc)(ColorMan *cm, ColorManReturnType success, gpointer data);
+
+
+struct _ColorMan {
+       ImageWindow *imd;
+       GdkPixbuf *pixbuf;
+       gint row;
+
+       gpointer profile;
+
+       gint idle_id;
+
+       ColorManDoneFunc func_done;
+       gpointer func_done_data;
+};
+
+
+ColorMan *color_man_new(ImageWindow *imd,
+                       ColorManProfileType input_type, const gchar *input_file,
+                       ColorManProfileType screen_type, const gchar *screen_file,
+                       ColorManDoneFunc done_func, gpointer done_data);
+void color_man_free(ColorMan *cm);
+
+void color_man_update(void);
+
+
+#endif
+
index caa40b9..b971b65 100644 (file)
@@ -117,3 +117,13 @@ gint lazy_image_sync = FALSE;
 gint update_on_time_change = TRUE;
 gint exif_rotate_enable = FALSE;
 
+/* color profiles */
+gint color_profile_enabled = FALSE;
+gint color_profile_input_type = 0;
+gchar *color_profile_input_file[COLOR_PROFILE_INPUTS];
+gchar *color_profile_input_name[COLOR_PROFILE_INPUTS];
+gint color_profile_screen_type = 0;
+gchar *color_profile_screen_file = NULL;
+gint color_profile_use_image = TRUE;
+
+
index 716bbdd..e6e6bc7 100644 (file)
@@ -79,6 +79,8 @@
 
 #define GQVIEW_EDITOR_SLOTS 10
 
+#define COLOR_PROFILE_INPUTS 4
+
 /*
  *----------------------------------------------------------------------------
  * globals
@@ -194,6 +196,14 @@ extern gint lazy_image_sync;
 extern gint update_on_time_change;
 extern gint exif_rotate_enable;
 
+extern gint color_profile_enabled;
+extern gint color_profile_input_type;
+extern gchar *color_profile_input_file[];
+extern gchar *color_profile_input_name[];
+extern gint color_profile_screen_type;
+extern gchar *color_profile_screen_file;
+extern gint color_profile_use_image;
+
 /*
  *----------------------------------------------------------------------------
  * main.c
index bb5f8c1..086e2b0 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "image-load.h"
 #include "collect.h"
+#include "color-man.h"
 #include "exif.h"
 #include "pixbuf-renderer.h"
 #include "pixbuf_util.h"
@@ -43,6 +44,7 @@ static GList *image_list = NULL;
 
 
 static void image_update_title(ImageWindow *imd);
+static void image_post_process(ImageWindow *imd, gint clamp);
 
 /*
  *-------------------------------------------------------------------
@@ -247,10 +249,95 @@ static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp, gint
                }
 }
 
+static void image_post_process_color_cb(ColorMan *cm, ColorManReturnType type, gpointer data)
+{
+       ImageWindow *imd = data;
+
+       color_man_free((ColorMan *)imd->cm);
+       imd->cm = NULL;
+       imd->state |= IMAGE_STATE_COLOR_ADJ;
+
+       if (type != COLOR_RETURN_IMAGE_CHANGED)
+               {
+               image_post_process(imd, FALSE);
+               }
+}
+
+static gint image_post_process_color(ImageWindow *imd, gint start_row)
+{
+       ColorMan *cm;
+       ColorManProfileType input_type;
+       ColorManProfileType screen_type;
+       const gchar *input_file;
+       const gchar *screen_file;
+
+       if (imd->cm) return FALSE;
+
+       if (imd->color_profile_input >= 1 &&
+           imd->color_profile_input <= COLOR_PROFILE_INPUTS)
+               {
+               gint n;
+
+               n = imd->color_profile_input - 1;
+               if (!color_profile_input_file[n]) return FALSE;
+
+               input_type = COLOR_PROFILE_FILE;
+               input_file = color_profile_input_file[n];
+               }
+       else if (imd->color_profile_input == 0)
+               {
+               input_type = COLOR_PROFILE_SRGB;
+               input_file = NULL;
+               }
+       else
+               {
+               return FALSE;
+               }
+
+       if (imd->color_profile_screen == 1 &&
+           color_profile_screen_file)
+               {
+               screen_type = COLOR_PROFILE_FILE;
+               screen_file = color_profile_screen_file;
+               }
+       else if (imd->color_profile_screen == 0)
+               {
+               screen_type = COLOR_PROFILE_SRGB;
+               screen_file = NULL;
+               }
+       else
+               {
+               return FALSE;
+               }
+
+       cm = color_man_new(imd,
+                          input_type, input_file,
+                          screen_type, screen_file,
+                          image_post_process_color_cb, imd);
+       if (cm)
+               {
+               if (start_row > 0) cm->row = start_row;
+
+               imd->cm = (gpointer)cm;
+               return TRUE;
+               }
+
+       return FALSE;
+}
+
 static void image_post_process(ImageWindow *imd, gint clamp)
 {
        gint exif_rotated = FALSE;
 
+       if (imd->color_profile_enable &&
+           !(imd->state & IMAGE_STATE_COLOR_ADJ))
+               {
+               if (image_post_process_color(imd, 0)) return;
+
+               /* fixme: note error to user */
+               imd->state |= IMAGE_STATE_COLOR_ADJ;
+               }
+
        if (exif_rotate_enable && image_get_pixbuf(imd))
                {
                ExifData *ed;
@@ -405,7 +492,7 @@ static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
  *-------------------------------------------------------------------
  */
 
-static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf)
+static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf, gint color_row)
 {
        g_free(imd->prev_path);
        if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
@@ -416,11 +503,13 @@ static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf
                        
                g_object_ref(pixbuf);
                imd->prev_pixbuf = pixbuf;
+               imd->prev_color_row = color_row;
                }
        else
                {
                imd->prev_path = NULL;
                imd->prev_pixbuf = NULL;
+               imd->prev_color_row = -1;
                }
 
        if (debug) printf("post buffer set: %s\n", path);
@@ -434,6 +523,10 @@ static gint image_post_buffer_get(ImageWindow *imd)
            imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
                {
                image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
+               if (imd->prev_color_row >= 0)
+                       {
+                       image_post_process_color(imd, imd->prev_color_row);
+                       }
                success = TRUE;
                }
        else
@@ -656,7 +749,12 @@ static void image_reset(ImageWindow *imd)
        image_loader_free(imd->il);
        imd->il = NULL;
 
+       color_man_free((ColorMan *)imd->cm);
+       imd->cm = NULL;
+
        imd->delay_alter_type = ALTER_NONE;
+
+       imd->state = IMAGE_STATE_NONE;
 }
 
 /*
@@ -723,6 +821,7 @@ static void image_change_real(ImageWindow *imd, const gchar *path,
        GdkPixbuf *prev_pixbuf = NULL;
        gchar *prev_path = NULL;
        gint prev_clear = FALSE;
+       gint prev_color_row = -1;
 
        imd->collection = cd;
        imd->collection_info = info;
@@ -741,6 +840,14 @@ static void image_change_real(ImageWindow *imd, const gchar *path,
                        prev_path = g_strdup(imd->image_path);
                        prev_pixbuf = pixbuf;
                        g_object_ref(prev_pixbuf);
+
+                       if (imd->cm)
+                               {
+                               ColorMan *cm;
+
+                               cm = (ColorMan *)imd->cm;
+                               prev_color_row = cm->row;
+                               }
                        }
                }
 
@@ -752,13 +859,13 @@ static void image_change_real(ImageWindow *imd, const gchar *path,
 
        if (prev_pixbuf)
                {
-               image_post_buffer_set(imd, prev_path, prev_pixbuf);
+               image_post_buffer_set(imd, prev_path, prev_pixbuf, prev_color_row);
                g_free(prev_path);
                g_object_unref(prev_pixbuf);
                }
        else if (prev_clear)
                {
-               image_post_buffer_set(imd, NULL, NULL);
+               image_post_buffer_set(imd, NULL, NULL, -1);
                }
 
        image_update_title(imd);
@@ -1040,6 +1147,24 @@ void image_change_from_image(ImageWindow *imd, ImageWindow *source)
                source->delay_alter_type = ALTER_NONE;
                }
 
+       imd->color_profile_enable = source->color_profile_enable;
+       imd->color_profile_input = source->color_profile_input;
+       imd->color_profile_screen = source->color_profile_screen;
+       imd->color_profile_use_image = source->color_profile_use_image;
+       color_man_free((ColorMan *)imd->cm);
+       imd->cm = NULL;
+       if (source->cm)
+               {
+               ColorMan *cm;
+
+               imd->cm = source->cm;
+               source->cm = NULL;
+
+               cm = (ColorMan *)imd->cm;
+               cm->imd = imd;
+               cm->func_done_data = imd;
+               }
+
        image_loader_free(imd->read_ahead_il);
        imd->read_ahead_il = source->read_ahead_il;
        source->read_ahead_il = NULL;
@@ -1056,12 +1181,16 @@ void image_change_from_image(ImageWindow *imd, ImageWindow *source)
        if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
        imd->prev_pixbuf = source->prev_pixbuf;
        source->prev_pixbuf = NULL;
+       imd->prev_color_row = source->prev_color_row;
+       source->prev_color_row = -1;
 
        g_free(imd->prev_path);
        imd->prev_path = source->prev_path;
        source->prev_path = NULL;
 
        imd->completed = source->completed;
+       imd->state = source->state;
+       source->state = IMAGE_STATE_NONE;
 
        pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
 }
@@ -1095,7 +1224,7 @@ void image_alter(ImageWindow *imd, AlterType type)
 {
        if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
 
-       if (imd->il)
+       if (imd->il || imd->cm)
                {
                /* still loading, wait till done */
                imd->delay_alter_type = type;
@@ -1304,6 +1433,52 @@ void image_background_set_color(ImageWindow *imd, GdkColor *color)
        pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
 }
 
+void image_color_profile_set(ImageWindow *imd,
+                            gint input_type, gint screen_type,
+                            gint use_image)
+{
+       if (!imd) return;
+
+       if (input_type < 0 || input_type > COLOR_PROFILE_INPUTS ||
+           screen_type < 0 || screen_type > 1)
+               {
+               return;
+               }
+
+       imd->color_profile_input = input_type;
+       imd->color_profile_screen = screen_type;
+       imd->color_profile_use_image = use_image;
+}
+
+gint image_color_profile_get(ImageWindow *imd,
+                            gint *input_type, gint *screen_type,
+                            gint *use_image)
+{
+       if (!imd) return FALSE;
+
+       if (input_type) *input_type = imd->color_profile_input;
+       if (screen_type) *screen_type = imd->color_profile_screen;
+       if (use_image) *use_image = imd->color_profile_use_image;
+
+       return TRUE;
+}
+
+void image_color_profile_set_use(ImageWindow *imd, gint enable)
+{
+       if (!imd) return;
+
+       if (imd->color_profile_enable == enable) return;
+
+       imd->color_profile_enable = enable;
+}
+
+gint image_color_profile_get_use(ImageWindow *imd)
+{
+       if (!imd) return FALSE;
+
+       return imd->color_profile_enable;
+}
+
 void image_set_delay_flip(ImageWindow *imd, gint delay)
 {
        if (!imd ||
@@ -1415,7 +1590,7 @@ static void image_free(ImageWindow *imd)
        image_reset(imd);
 
        image_read_ahead_cancel(imd);
-       image_post_buffer_set(imd, NULL, NULL);
+       image_post_buffer_set(imd, NULL, NULL, -1);
        image_auto_refresh(imd, -1);
 
        g_free(imd->image_path);
@@ -1454,6 +1629,12 @@ ImageWindow *image_new(gint frame)
        imd->read_ahead_path = NULL;
 
        imd->completed = FALSE;
+       imd->state = IMAGE_STATE_NONE;
+
+       imd->color_profile_enable = FALSE;
+       imd->color_profile_input = 0;
+       imd->color_profile_screen = 0;
+       imd->color_profile_use_image = FALSE;
 
        imd->auto_refresh_id = -1;
        imd->auto_refresh_interval = -1;
index 8222463..6f1574f 100644 (file)
@@ -86,6 +86,16 @@ void image_top_window_set_sync(ImageWindow *imd, gint allow_sync);
 void image_background_set_black(ImageWindow *imd, gint black);
 void image_background_set_color(ImageWindow *imd, GdkColor *color);
 
+/* color profiles */
+void image_color_profile_set(ImageWindow *imd,
+                            gint input_type, gint screen_type,
+                            gint use_embedded);
+gint image_color_profile_get(ImageWindow *imd,
+                            gint *input_type, gint *screen_type,
+                            gint *use_image);
+void image_color_profile_set_use(ImageWindow *imd, gint enable);
+gint image_color_profile_get_use(ImageWindow *imd);
+
 /* set delayed page flipping */
 void image_set_delay_flip(ImageWindow *imd, gint delay);
 
index 169bea2..09980a0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * GQview
- * (C) 2004 John Ellis
+ * (C) 2006 John Ellis
  *
  * Author: John Ellis
  *
@@ -19,6 +19,7 @@
 #include "menu.h"
 #include "pixbuf-renderer.h"
 #include "pixbuf_util.h"
+#include "utilops.h"
 #include "view_dir_list.h"
 #include "view_dir_tree.h"
 #include "view_file_list.h"
@@ -226,6 +227,8 @@ static void layout_sort_menu_cb(GtkWidget *widget, gpointer data)
        LayoutWindow *lw;
        SortType type;
 
+       if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
+
        lw = submenu_item_get_data(widget);
        if (!lw) return;
 
@@ -299,6 +302,180 @@ static GtkWidget *layout_sort_button(LayoutWindow *lw)
         return button;
 }
 
+/*
+ *-----------------------------------------------------------------------------
+ * color profile button (and menu)
+ *-----------------------------------------------------------------------------
+ */
+
+static void layout_color_menu_enable_cb(GtkWidget *widget, gpointer data)
+{
+       LayoutWindow *lw = data;
+
+       layout_image_color_profile_set_use(lw, (!layout_image_color_profile_get_use(lw)));
+       layout_image_refresh(lw);
+}
+
+#define COLOR_MENU_KEY "color_menu_key"
+
+static void layout_color_menu_input_cb(GtkWidget *widget, gpointer data)
+{
+       LayoutWindow *lw = data;
+       gint type;
+       gint input, screen, use_image;
+
+       if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
+
+       type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), COLOR_MENU_KEY));
+       if (type < 0 || type > COLOR_PROFILE_INPUTS) return;
+
+       if (!layout_image_color_profile_get(lw, &input, &screen, &use_image)) return;
+       if (type == input) return;
+
+       layout_image_color_profile_set(lw, type, screen, use_image);
+       layout_image_refresh(lw);
+}
+
+static void layout_color_menu_screen_cb(GtkWidget *widget, gpointer data)
+{
+       LayoutWindow *lw = data;
+       gint type;
+       gint input, screen, use_image;
+
+       if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
+
+       type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), COLOR_MENU_KEY));
+       if (type < 0 || type > 1) return;
+
+       if (!layout_image_color_profile_get(lw, &input, &screen, &use_image)) return;
+       if (type == screen) return;
+
+       layout_image_color_profile_set(lw, input, type, use_image);
+       layout_image_refresh(lw);
+}
+
+static gchar *layout_color_name_parse(const gchar *name)
+{
+       gchar *result;
+       gchar *p;
+
+       if (!name) name = _("Empty");
+
+       result = g_strdup(name);
+       p = result;
+       while (*p != '\0')
+               {
+               if (*p == '_') *p = '-';
+               p++;
+               }
+       return result;
+}
+
+static void layout_color_button_press_cb(GtkWidget *widget, gpointer data)
+{
+       LayoutWindow *lw = data;
+       GtkWidget *menu;
+       GtkWidget *item;
+       gchar *buf;
+       gchar *front;
+       gchar *end;
+       gint active;
+       gint input = 0;
+       gint screen = 0;
+       gint use_image = 0;
+       gint i;
+
+#ifndef HAVE_LCMS
+       file_util_warning_dialog(_("Color profiles not supported"),
+                                _("This installation of GQview was not built with support for color profiles."),
+                                GTK_STOCK_DIALOG_INFO, widget);
+       return;
+#endif
+
+       active = layout_image_color_profile_get_use(lw);
+       if (!layout_image_color_profile_get(lw, &input, &screen, &use_image)) return;
+
+       menu = popup_menu_short_lived();
+
+       menu_item_add_check(menu, _("Use _color profiles"), active,
+                           G_CALLBACK(layout_color_menu_enable_cb), lw);
+
+       menu_item_add_divider(menu);
+
+       front = g_strdup_printf(_("Input _%d:"), 0);
+       buf = g_strdup_printf("%s %s", front, "sRGB");
+       g_free(front);
+       item = menu_item_add_radio(menu, NULL,
+                                  buf, (color_profile_input_type == 0),
+                                  G_CALLBACK(layout_color_menu_input_cb), lw);
+       g_free(buf);
+       g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(0));
+       gtk_widget_set_sensitive(item, active);
+
+       for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
+               {
+               const gchar *name;
+
+               name = color_profile_input_name[i];
+               if (!name) name = filename_from_path(color_profile_input_file[i]);
+
+               front = g_strdup_printf(_("Input _%d:"), i + 1);
+               end = layout_color_name_parse(name);
+               buf = g_strdup_printf("%s %s", front, end);
+               g_free(front);
+               g_free(end);
+
+               item = menu_item_add_radio(menu, item,
+                                          buf, (i + 1 == input),
+                                          G_CALLBACK(layout_color_menu_input_cb), lw);
+               g_free(buf);
+               g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(i + 1));
+               gtk_widget_set_sensitive(item, active && color_profile_input_file[i]);
+               }
+
+       menu_item_add_divider(menu);
+
+       buf = g_strdup_printf("%s sRGB", _("Screen"));
+       item = menu_item_add_radio(menu, NULL,
+                                  buf, (screen == 0),
+                                  G_CALLBACK(layout_color_menu_screen_cb), lw);
+       g_free(buf);
+       g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(0));
+       gtk_widget_set_sensitive(item, active);
+
+       item = menu_item_add_radio(menu, item,
+                                  _("_Screen profile"), (screen == 1),
+                                  G_CALLBACK(layout_color_menu_screen_cb), lw);
+       g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(1));
+       gtk_widget_set_sensitive(item, active && color_profile_screen_file);
+
+       gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME);
+}
+
+static GtkWidget *layout_color_button(LayoutWindow *lw)
+{
+       GtkWidget *button;
+       GtkWidget *image;
+       gint enable;
+
+       button = gtk_button_new();
+       image = gtk_image_new_from_stock(GTK_STOCK_SELECT_COLOR, GTK_ICON_SIZE_MENU);
+       gtk_container_add(GTK_CONTAINER(button), image);
+       gtk_widget_show(image);
+       g_signal_connect(G_OBJECT(button), "clicked",
+                        G_CALLBACK(layout_color_button_press_cb), lw);
+        gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+
+       enable = (lw->image) ? lw->image->color_profile_enable : FALSE;
+#ifndef HAVE_LCMS
+       enable = FALSE;
+#endif
+       gtk_widget_set_sensitive(image, enable);
+
+       return button;
+}
+
+
 /*
  *-----------------------------------------------------------------------------
  * status bar
@@ -486,6 +663,11 @@ static void layout_status_setup(LayoutWindow *lw, GtkWidget *box, gint small_for
        gtk_box_pack_start(GTK_BOX(hbox), lw->info_sort, FALSE, FALSE, 0);
        gtk_widget_show(lw->info_sort);
 
+       lw->info_color = layout_color_button(lw);
+       gtk_widget_show(lw->info_color);
+
+       if (small_format) gtk_box_pack_end(GTK_BOX(hbox), lw->info_color, FALSE, FALSE, 0);
+
        lw->info_status = layout_status_label(NULL, lw->info_box, TRUE, 0, (!small_format));
 
        if (small_format)
@@ -499,6 +681,7 @@ static void layout_status_setup(LayoutWindow *lw, GtkWidget *box, gint small_for
                hbox = lw->info_box;
                }
        lw->info_details = layout_status_label(NULL, hbox, TRUE, 0, TRUE);
+       if (!small_format) gtk_box_pack_start(GTK_BOX(hbox), lw->info_color, FALSE, FALSE, 0);
        lw->info_zoom = layout_status_label(NULL, hbox, FALSE, ZOOM_LABEL_WIDTH, FALSE);
 }
 
@@ -1396,6 +1579,7 @@ void layout_style_set(LayoutWindow *lw, gint style, const gchar *order)
        lw->info_box = NULL;
        lw->info_progress_bar = NULL;
        lw->info_sort = NULL;
+       lw->info_color = NULL;
        lw->info_status = NULL;
        lw->info_details = NULL;
        lw->info_zoom = NULL;
index b694523..e53b298 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * GQview
- * (C) 2004 John Ellis
+ * (C) 2006 John Ellis
  *
  * Author: John Ellis
  *
@@ -1140,6 +1140,46 @@ void layout_image_refresh(LayoutWindow *lw)
        image_reload(lw->image);
 }
 
+void layout_image_color_profile_set(LayoutWindow *lw,
+                                   gint input_type, gint screen_type,
+                                   gint use_image)
+{
+       if (!layout_valid(&lw)) return;
+
+       image_color_profile_set(lw->image, input_type, screen_type, use_image);
+}
+
+gint layout_image_color_profile_get(LayoutWindow *lw,
+                                   gint *input_type, gint *screen_type,
+                                   gint *use_image)
+{
+       if (!layout_valid(&lw)) return FALSE;
+
+       return image_color_profile_get(lw->image, input_type, screen_type, use_image);
+}
+
+void layout_image_color_profile_set_use(LayoutWindow *lw, gint enable)
+{
+       if (!layout_valid(&lw)) return;
+
+       image_color_profile_set_use(lw->image, enable);
+
+       if (lw->info_color)
+               {
+#ifndef HAVE_LCMS
+               enable = FALSE;
+#endif
+               gtk_widget_set_sensitive(GTK_BIN(lw->info_color)->child, enable);
+               }
+}
+
+gint layout_image_color_profile_get_use(LayoutWindow *lw)
+{
+       if (!layout_valid(&lw)) return FALSE;
+
+       return image_color_profile_get_use(lw->image);
+}
+
 /*
  *----------------------------------------------------------------------------
  * list walkers
@@ -1393,6 +1433,11 @@ GtkWidget *layout_image_new(LayoutWindow *lw, const gchar *path)
                image_attach_window(lw->image, lw->window, NULL, "GQview", FALSE);
 
                image_auto_refresh(lw->image, 0);
+
+               image_color_profile_set(lw->image,
+                                       color_profile_input_type, color_profile_screen_type,
+                                       color_profile_use_image);
+               image_color_profile_set_use(lw->image, color_profile_enabled);
                }
 
        return lw->image->widget;
index fbc8b45..8c37140 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * GQview
- * (C) 2004 John Ellis
+ * (C) 2006 John Ellis
  *
  * Author: John Ellis
  *
@@ -23,6 +23,15 @@ void layout_image_set_collection(LayoutWindow *lw, CollectionData *cd, CollectIn
 
 void layout_image_refresh(LayoutWindow *lw);
 
+void layout_image_color_profile_set(LayoutWindow *lw,
+                                   gint input_type, gint screen_type,
+                                   gint use_image);
+gint layout_image_color_profile_get(LayoutWindow *lw,
+                                   gint *input_type, gint *screen_type,
+                                   gint *use_image);
+void layout_image_color_profile_set_use(LayoutWindow *lw, gint enable);
+gint layout_image_color_profile_get_use(LayoutWindow *lw);
+
 
 const gchar *layout_image_get_path(LayoutWindow *lw);
 const gchar *layout_image_get_name(LayoutWindow *lw);
index 38c36e0..c104aca 100644 (file)
@@ -1127,6 +1127,12 @@ static void setup_default_options(void)
 
        g_free(safe_delete_path);
        safe_delete_path = concat_dir_and_file(homedir(), GQVIEW_RC_DIR_TRASH);
+
+       for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
+               {
+               color_profile_input_file[i] = NULL;
+               color_profile_input_name[i] = NULL;
+               }
 }
 
 static void exit_gqview_final(void)
@@ -1161,6 +1167,11 @@ static void exit_gqview_final(void)
        layout_tools_float_get(NULL, &tools_float, &tools_hidden);
        toolbar_hidden = layout_toolbar_hidden(NULL);
 
+       color_profile_enabled = layout_image_color_profile_get_use(NULL);
+       layout_image_color_profile_get(NULL,
+                                      &color_profile_input_type, &color_profile_screen_type,
+                                      &color_profile_use_image);
+
        save_options();
        keys_save();
 
index 1803ba2..056dfa3 100644 (file)
@@ -135,19 +135,25 @@ gchar *sort_type_get_text(SortType method)
        return "";
 }
 
-static void submenu_add_sort_item(GtkWidget *menu, GCallback func, SortType type,
-                                 gint show_current, SortType show_type)
+static GtkWidget *submenu_add_sort_item(GtkWidget *menu, GtkWidget *parent,
+                                       GCallback func, SortType type,
+                                       gint show_current, SortType show_type)
 {
+       GtkWidget *item;
+
        if (show_current)
                {
-               menu_item_add_check(menu, sort_type_get_text(type), (type == show_type),
-                                   func, GINT_TO_POINTER((gint)type));
+               item = menu_item_add_radio(menu, parent,
+                                          sort_type_get_text(type), (type == show_type),
+                                          func, GINT_TO_POINTER((gint)type));
                }
        else
                {
-               menu_item_add(menu, sort_type_get_text(type),
-                             func, GINT_TO_POINTER((gint)type));
+               item = menu_item_add(menu, sort_type_get_text(type),
+                                    func, GINT_TO_POINTER((gint)type));
                }
+
+       return item;
 }
 
 GtkWidget *submenu_add_sort(GtkWidget *menu, GCallback func, gpointer data,
@@ -155,18 +161,19 @@ GtkWidget *submenu_add_sort(GtkWidget *menu, GCallback func, gpointer data,
                            gint show_current, SortType type)
 {
        GtkWidget *submenu;
+       GtkWidget *parent;
 
        submenu = gtk_menu_new();
        g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
 
-       submenu_add_sort_item(submenu, func, SORT_NAME, show_current, type);
+       parent = submenu_add_sort_item(submenu, NULL, func, SORT_NAME, show_current, type);
 #ifdef HAVE_STRVERSCMP
-       submenu_add_sort_item(submenu, func, SORT_NUMBER, show_current, type);
+       submenu_add_sort_item(submenu, parent, func, SORT_NUMBER, show_current, type);
 #endif
-       submenu_add_sort_item(submenu, func, SORT_TIME, show_current, type);
-       submenu_add_sort_item(submenu, func, SORT_SIZE, show_current, type);
-       if (include_path) submenu_add_sort_item(submenu, func, SORT_PATH, show_current, type);
-       if (include_none) submenu_add_sort_item(submenu, func, SORT_NONE, show_current, type);
+       submenu_add_sort_item(submenu, parent, func, SORT_TIME, show_current, type);
+       submenu_add_sort_item(submenu, parent, func, SORT_SIZE, show_current, type);
+       if (include_path) submenu_add_sort_item(submenu, parent, func, SORT_PATH, show_current, type);
+       if (include_none) submenu_add_sort_item(submenu, parent, func, SORT_NONE, show_current, type);
 
        if (menu)
                {
index 69e8712..7c34416 100644 (file)
@@ -138,6 +138,10 @@ static gint tree_descend_subdirs_c;
 static gint update_on_time_change_c;
 static gint exif_rotate_enable_c;
 
+static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
+static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
+static GtkWidget *color_profile_screen_file_entry;
+
 
 /*
  *-----------------------------------------------------------------------------
@@ -288,6 +292,25 @@ static void config_window_apply(void)
 
        tree_descend_subdirs = tree_descend_subdirs_c;
 
+#ifdef HAVE_LCMS
+       for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
+               {
+               g_free(color_profile_input_name[i]);
+               color_profile_input_name[i] = NULL;
+               buf = gtk_entry_get_text(GTK_ENTRY(color_profile_input_name_entry[i]));
+               if (buf && strlen(buf) > 0) color_profile_input_name[i] = g_strdup(buf);
+
+               g_free(color_profile_input_file[i]);
+               color_profile_input_file[i] = NULL;
+               buf = gtk_entry_get_text(GTK_ENTRY(color_profile_input_file_entry[i]));
+               if (buf && strlen(buf) > 0) color_profile_input_file[i] = g_strdup(buf);
+               }
+       g_free(color_profile_screen_file);
+       color_profile_screen_file = NULL;
+       buf = gtk_entry_get_text(GTK_ENTRY(color_profile_screen_file_entry));
+       if (buf && strlen(buf) > 0) color_profile_screen_file = g_strdup(buf);
+#endif
+
        l_conf = layout_config_get(layout_widget, &new_style);
 
        if (new_style != layout_style ||
@@ -403,7 +426,7 @@ static void add_quality_menu(GtkWidget *table, gint column, gint row, const gcha
                         G_CALLBACK(quality_menu_cb), option_c);
 
        gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
-                        GTK_EXPAND | GTK_FILL, FALSE, 0, 0);
+                        GTK_EXPAND | GTK_FILL, 0, 0, 0);
        gtk_widget_show(combo);
 }
 
@@ -514,7 +537,7 @@ static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *
                         G_CALLBACK(thumb_size_menu_cb), NULL);
 
        gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
-                        GTK_EXPAND | GTK_FILL, FALSE, 0, 0);
+                        GTK_EXPAND | GTK_FILL, 0, 0, 0);
        gtk_widget_show(combo);
 }
 
@@ -1103,7 +1126,7 @@ static void config_window_create(void)
                gtk_widget_set_size_request(editor_name_entry[i],80,-1);
                if (editor_name[i]) gtk_entry_set_text(GTK_ENTRY(editor_name_entry[i]),editor_name[i]);
                gtk_table_attach(GTK_TABLE (table),editor_name_entry[i],1,2,i+1,i+2,
-                                GTK_FILL | GTK_EXPAND, FALSE, 0, 0);
+                                GTK_FILL | GTK_EXPAND, 0, 0, 0);
                gtk_widget_show(editor_name_entry[i]);
 
                editor_command_entry[i] = gtk_entry_new();
@@ -1112,7 +1135,7 @@ static void config_window_create(void)
                tab_completion_add_to_entry(editor_command_entry[i], NULL, NULL);
                if (editor_command[i]) gtk_entry_set_text(GTK_ENTRY(editor_command_entry[i]), editor_command[i]);
                gtk_table_attach(GTK_TABLE (table),editor_command_entry[i],2,3,i+1,i+2,
-                                GTK_FILL | GTK_EXPAND, FALSE, 0, 0);
+                                GTK_FILL | GTK_EXPAND, 0, 0, 0);
                gtk_widget_show(editor_command_entry[i]);
                }
 
@@ -1227,6 +1250,60 @@ static void config_window_create(void)
        pref_spin_new_int(group, _("Offscreen cache size (Mb per image):"), NULL,
                          0, 128, 1, tile_cache_max, &tile_cache_max_c);
 
+       group =  pref_group_new(vbox, FALSE, _("Color profiles"), GTK_ORIENTATION_VERTICAL);
+#ifndef HAVE_LCMS
+       gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
+#endif
+
+       table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 2, FALSE, FALSE);
+       gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
+
+       label = pref_table_label(table, 0, 0, _("Type"), 0.0);
+       pref_label_bold(label, TRUE, FALSE);
+
+       label = pref_table_label(table, 1, 0, _("Menu name"), 0.0);
+       pref_label_bold(label, TRUE, FALSE);
+
+       label = pref_table_label(table, 2, 0, _("File"), 0.0);
+       pref_label_bold(label, TRUE, FALSE);
+
+       for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
+               {
+               GtkWidget *entry;
+               gchar *buf;
+
+               buf = g_strdup_printf("Input %d:", i + 1);
+               pref_table_label(table, 0, i + 1, buf, 1.0);
+               g_free(buf);
+
+               entry = gtk_entry_new();
+               gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
+               gtk_widget_set_size_request(editor_name_entry[i], 30, -1);
+               if (color_profile_input_name[i]) gtk_entry_set_text(GTK_ENTRY(entry), color_profile_input_name[i]);
+               gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
+                                GTK_FILL | GTK_EXPAND, 0, 0, 0);
+               gtk_widget_show(entry);
+               color_profile_input_name_entry[i] = entry;
+
+               tabcomp = tab_completion_new(&entry, color_profile_input_file[i], NULL, NULL);
+               tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
+               gtk_widget_set_size_request(entry, 160, -1);
+               gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
+                                GTK_FILL | GTK_EXPAND, 0, 0, 0);
+               gtk_widget_show(tabcomp);
+               color_profile_input_file_entry[i] = entry;
+               }
+
+       pref_table_label(table, 0, COLOR_PROFILE_INPUTS + 1, _("Screen:"), 1.0);
+       tabcomp = tab_completion_new(&color_profile_screen_file_entry,
+                                    color_profile_screen_file, NULL, NULL);
+       tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
+       gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
+       gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3,
+                        COLOR_PROFILE_INPUTS + 1, COLOR_PROFILE_INPUTS + 2,
+                        GTK_FILL | GTK_EXPAND, 0, 0, 0);
+       gtk_widget_show(tabcomp);
+
        gtk_widget_show(notebook);
 
        gtk_widget_show(configwindow);
index 368cb6d..a777ac5 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * GQview
- * (C) 2004 John Ellis
+ * (C) 2006 John Ellis
  *
  * Author: John Ellis
  *
@@ -305,6 +305,32 @@ void save_options(void)
        write_bool_option(f, "disable_filtering", file_filter_disable);
        filter_write_list(f);
 
+       fprintf(f,"\n##### Color Profiles #####\n\n");
+
+#ifndef HAVE_LCMS
+       fprintf(f,"# NOTICE: GQview was not built with support for color profiles,\n"
+                 "#         color profile options will have no effect.\n\n");
+#endif
+
+       write_bool_option(f, "color_profile_enabled", color_profile_enabled);
+       write_bool_option(f, "color_profile_use_image", color_profile_use_image);
+       write_int_option(f, "color_profile_input_type", color_profile_input_type);
+       for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
+               {
+               gchar *buf;
+
+               buf = g_strdup_printf("color_profile_input_file_%d", i + 1);
+               write_char_option(f, buf, color_profile_input_file[i]);
+               g_free(buf);
+
+               buf = g_strdup_printf("color_profile_input_name_%d", i + 1);
+               write_char_option(f, buf, color_profile_input_name[i]);
+               g_free(buf);
+               }
+       fprintf(f,"\n");
+       write_int_option(f, "color_profile_screen_type", color_profile_screen_type);
+       write_char_option(f, "color_profile_screen_file_1", color_profile_screen_file);
+
        fprintf(f,"\n##### External Programs #####\n");
        fprintf(f,"# Maximum of 10 programs (external_1 through external_10)\n");
        fprintf(f,"# format: external_n: \"menu name\" \"command line\"\n\n");
@@ -555,6 +581,39 @@ void load_options(void)
                        filter_parse(value_all);
                        }
 
+               /* Color Profiles */
+
+               color_profile_enabled = read_bool_option(f, option,
+                       "color_profile_enabled", value, color_profile_enabled);
+               color_profile_use_image = read_bool_option(f, option,
+                       "color_profile_use_image", value, color_profile_use_image);
+               color_profile_input_type = read_int_option(f, option,
+                       "color_profile_input_type", value, color_profile_input_type);
+
+               if (strncasecmp(option, "color_profile_input_file_", 25) == 0)
+                        {
+                        i = strtol(option + 25, NULL, 0) - 1;
+                       if (i >= 0 && i < COLOR_PROFILE_INPUTS)
+                               {
+                               color_profile_input_file[i] = read_char_option(f, option,
+                                       option, value, color_profile_input_file[i]);
+                               }
+                       }
+               if (strncasecmp(option, "color_profile_input_name_", 25) == 0)
+                        {
+                        i = strtol(option + 25, NULL, 0) - 1;
+                       if (i >= 0 && i < COLOR_PROFILE_INPUTS)
+                               {
+                               color_profile_input_name[i] = read_char_option(f, option,
+                                       option, value, color_profile_input_name[i]);
+                               }
+                       }
+
+               color_profile_screen_type = read_int_option(f, option,
+                       "color_profile_screen_type", value, color_profile_screen_type);
+               color_profile_screen_file = read_char_option(f, option,
+                       "color_profile_screen_file_1", value, color_profile_screen_file);
+
                /* External Programs */
 
                if (strncasecmp(option, "external_", 9) == 0)
index d68a25a..cc049d6 100644 (file)
@@ -42,6 +42,17 @@ typedef enum {
 } LayoutLocation;
 
 
+typedef enum {
+       IMAGE_STATE_NONE        = 0,
+       IMAGE_STATE_IMAGE       = 1 << 0,
+       IMAGE_STATE_LOADING     = 1 << 1,
+       IMAGE_STATE_ERROR       = 1 << 2,
+       IMAGE_STATE_COLOR_ADJ   = 1 << 3,
+       IMAGE_STATE_ROTATE_AUTO = 1 << 4,
+       IMAGE_STATE_ROTATE_USER = 1 << 5,
+       IMAGE_STATE_DELAY_FLIP  = 1 << 6
+} ImageState;
+
 typedef struct _ImageLoader ImageLoader;
 typedef struct _ThumbLoader ThumbLoader;
 
@@ -242,6 +253,7 @@ struct _ImageWindow
        gint title_show_zoom;   /* option to include zoom in window title */
 
        gint completed;
+       ImageState state;       /* mask of IMAGE_STATE_* flags about current image */
 
        void (*func_update)(ImageWindow *, gpointer);
        void (*func_complete)(ImageWindow *, gint preload, gpointer);
@@ -272,6 +284,13 @@ struct _ImageWindow
        CollectionData *collection;
        CollectInfo *collection_info;
 
+       /* color profiles */
+       gint color_profile_enable;
+       gint color_profile_input;
+       gint color_profile_screen;
+       gint color_profile_use_image;
+       gpointer *cm;
+
        AlterType delay_alter_type;
 
        ImageLoader *read_ahead_il;
@@ -280,6 +299,7 @@ struct _ImageWindow
 
        GdkPixbuf *prev_pixbuf;
        gchar *prev_path;
+       gint prev_color_row;
 
        gint auto_refresh_id;
        gint auto_refresh_interval;
@@ -366,6 +386,7 @@ struct _LayoutWindow
        GtkWidget *info_box;
        GtkWidget *info_progress_bar;
        GtkWidget *info_sort;
+       GtkWidget *info_color;
        GtkWidget *info_status;
        GtkWidget *info_details;
        GtkWidget *info_zoom;
index 198a9b7..f08b5d3 100644 (file)
@@ -96,6 +96,22 @@ GtkWidget *menu_item_add_check(GtkWidget *menu, const gchar *label, gint active,
        return item;
 }
 
+GtkWidget *menu_item_add_radio(GtkWidget *menu, GtkWidget *parent,
+                              const gchar *label, gint active,
+                              GCallback func, gpointer data)
+{
+       GtkWidget *item;
+       GSList *group = NULL;
+
+       if (parent) group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(parent));
+
+       item = gtk_radio_menu_item_new_with_mnemonic(group, label);
+       if (active) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), active);
+       menu_item_finish(menu, item, func, data);
+
+       return item;
+}
+
 void menu_item_add_divider(GtkWidget *menu)
 {
        GtkWidget *item = gtk_menu_item_new();
index 2ab7c44..90a2df3 100644 (file)
@@ -24,6 +24,9 @@ GtkWidget *menu_item_add_stock_sensitive(GtkWidget *menu, const gchar *label, co
                                         GCallback func, gpointer data);
 GtkWidget *menu_item_add_check(GtkWidget *menu, const gchar *label, gint active,
                               GCallback func, gpointer data);
+GtkWidget *menu_item_add_radio(GtkWidget *menu, GtkWidget *parent,
+                              const gchar *label, gint active,
+                              GCallback func, gpointer data);
 void menu_item_add_divider(GtkWidget *menu);
 
 /* use to avoid mnemonics, for example filenames */
index ce510e2..618df29 100644 (file)
@@ -613,7 +613,7 @@ GtkWidget *pref_table_box(GtkWidget *table, gint column, gint row,
                }
 
        gtk_table_attach(GTK_TABLE(table), shell, column, column + 1, row, row + 1,
-                        GTK_EXPAND | GTK_FILL, FALSE, 0, 0);
+                        GTK_EXPAND | GTK_FILL, 0, 0, 0);
 
        gtk_widget_show(shell);
 
@@ -628,7 +628,7 @@ GtkWidget *pref_table_label(GtkWidget *table, gint column, gint row,
 
        align = gtk_alignment_new(alignment, 0.50, 0.0, 0.0);
        gtk_table_attach(GTK_TABLE(table), align, column, column + 1, row, row + 1,
-                        GTK_FILL, FALSE, 0, 0);
+                        GTK_FILL, 0, 0, 0);
        gtk_widget_show(align);
        label = gtk_label_new(text);
        gtk_container_add(GTK_CONTAINER(align), label);
@@ -645,7 +645,7 @@ GtkWidget *pref_table_button(GtkWidget *table, gint column, gint row,
 
        button = pref_button_new(NULL, stock_id, text, hide_stock_text, func, data);
        gtk_table_attach(GTK_TABLE(table), button, column, column + 1, row, row + 1,
-                        GTK_FILL, FALSE, 0, 0);
+                        GTK_FILL, 0, 0, 0);
        gtk_widget_show(button);
 
        return button;
index a1d02f2..7894629 100644 (file)
@@ -206,6 +206,8 @@ static void vficon_pop_menu_sort_cb(GtkWidget *widget, gpointer data)
 {
        ViewFileIcon *vfi;
        SortType type;
+
+       if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
        
        vfi = submenu_item_get_data(widget);
        if (!vfi) return;
index 4169171..651b8d1 100644 (file)
@@ -306,6 +306,8 @@ static void vflist_pop_menu_sort_cb(GtkWidget *widget, gpointer data)
 {
        ViewFileList *vfl;
        SortType type;
+
+       if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
        
        vfl = submenu_item_get_data(widget);
        if (!vfl) return;