From cd344ba3bea51cce1ab6e76b50980d8191aa90b9 Mon Sep 17 00:00:00 2001 From: John Ellis Date: Sat, 25 Nov 2006 03:00:33 +0000 Subject: [PATCH] Fri Nov 24 21:37:01 2006 John Ellis * 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. --- ChangeLog | 18 ++ README | 11 +- TODO | 7 + configure.in | 27 +++ src/Makefile.am | 4 +- src/color-man.c | 403 +++++++++++++++++++++++++++++++++++++++++++ src/color-man.h | 56 ++++++ src/globals.c | 10 ++ src/gqview.h | 10 ++ src/image.c | 191 +++++++++++++++++++- src/image.h | 10 ++ src/layout.c | 186 +++++++++++++++++++- src/layout_image.c | 47 ++++- src/layout_image.h | 11 +- src/main.c | 11 ++ src/menu.c | 31 ++-- src/preferences.c | 85 ++++++++- src/rcfile.c | 61 ++++++- src/typedefs.h | 21 +++ src/ui_menu.c | 16 ++ src/ui_menu.h | 3 + src/ui_misc.c | 6 +- src/view_file_icon.c | 2 + src/view_file_list.c | 2 + 24 files changed, 1199 insertions(+), 30 deletions(-) create mode 100644 src/color-man.c create mode 100644 src/color-man.h diff --git a/ChangeLog b/ChangeLog index 56cdcf85..8c91a336 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +Fri Nov 24 21:37:01 2006 John Ellis + + * 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 * ui_fileops.[ch]: Add path_list_lstat() to obtain a path listing that diff --git a/README b/README index df09b5eb..8eb860b1 100644 --- 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 71cf578b..ded5ffb7 100644 --- 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. diff --git a/configure.in b/configure.in index eb4ba44a..599099b1 100644 --- a/configure.in +++ b/configure.in @@ -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) diff --git a/src/Makefile.am b/src/Makefile.am index 9a86f6fc..7ce47020 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 index 00000000..5c524570 --- /dev/null +++ b/src/color-man.c @@ -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 +#else + #include +#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 index 00000000..b5d3f7c2 --- /dev/null +++ b/src/color-man.h @@ -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 + diff --git a/src/globals.c b/src/globals.c index caa40b9a..b971b652 100644 --- a/src/globals.c +++ b/src/globals.c @@ -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; + + diff --git a/src/gqview.h b/src/gqview.h index 716bbdd6..e6e6bc76 100644 --- a/src/gqview.h +++ b/src/gqview.h @@ -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 diff --git a/src/image.c b/src/image.c index bb5f8c1d..086e2b0a 100644 --- a/src/image.c +++ b/src/image.c @@ -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; diff --git a/src/image.h b/src/image.h index 82224630..6f1574fb 100644 --- a/src/image.h +++ b/src/image.h @@ -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); diff --git a/src/layout.c b/src/layout.c index 169bea22..09980a0e 100644 --- a/src/layout.c +++ b/src/layout.c @@ -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; diff --git a/src/layout_image.c b/src/layout_image.c index b6945238..e53b2988 100644 --- a/src/layout_image.c +++ b/src/layout_image.c @@ -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; diff --git a/src/layout_image.h b/src/layout_image.h index fbc8b458..8c371404 100644 --- a/src/layout_image.h +++ b/src/layout_image.h @@ -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); diff --git a/src/main.c b/src/main.c index 38c36e05..c104aca3 100644 --- a/src/main.c +++ b/src/main.c @@ -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(); diff --git a/src/menu.c b/src/menu.c index 1803ba24..056dfa33 100644 --- a/src/menu.c +++ b/src/menu.c @@ -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) { diff --git a/src/preferences.c b/src/preferences.c index 69e87125..7c344161 100644 --- a/src/preferences.c +++ b/src/preferences.c @@ -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); diff --git a/src/rcfile.c b/src/rcfile.c index 368cb6d8..a777ac51 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -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) diff --git a/src/typedefs.h b/src/typedefs.h index d68a25a2..cc049d6a 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -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; diff --git a/src/ui_menu.c b/src/ui_menu.c index 198a9b78..f08b5d3a 100644 --- a/src/ui_menu.c +++ b/src/ui_menu.c @@ -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(); diff --git a/src/ui_menu.h b/src/ui_menu.h index 2ab7c446..90a2df31 100644 --- a/src/ui_menu.h +++ b/src/ui_menu.h @@ -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 */ diff --git a/src/ui_misc.c b/src/ui_misc.c index ce510e26..618df294 100644 --- a/src/ui_misc.c +++ b/src/ui_misc.c @@ -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; diff --git a/src/view_file_icon.c b/src/view_file_icon.c index a1d02f2e..78946297 100644 --- a/src/view_file_icon.c +++ b/src/view_file_icon.c @@ -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; diff --git a/src/view_file_list.c b/src/view_file_list.c index 41691717..651b8d12 100644 --- a/src/view_file_list.c +++ b/src/view_file_list.c @@ -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; -- 2.20.1