summaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authorMattia Rizzolo <mattia@mapreri.org>2014-10-03 14:04:58 +0000
committerMattia Rizzolo <mattia@mapreri.org>2014-10-03 14:04:58 +0000
commit2d113e8792747151bf5d830f1a1485f2f951f940 (patch)
treef29e273d53fe5735cdac171504d798bf45ea2007 /frontend
Imported Upstream version 0.50upstream/0.50
Diffstat (limited to 'frontend')
-rw-r--r--frontend/Makefile.in85
-rw-r--r--frontend/cursor/cursor_pipette_black8
-rw-r--r--frontend/cursor/cursor_pipette_gray8
-rw-r--r--frontend/cursor/cursor_pipette_mask8
-rw-r--r--frontend/cursor/cursor_pipette_white8
-rw-r--r--frontend/xsane-back-gtk.c1426
-rw-r--r--frontend/xsane-back-gtk.h188
-rw-r--r--frontend/xsane-device-preferences.c699
-rw-r--r--frontend/xsane-device-preferences.c.old761
-rw-r--r--frontend/xsane-device-preferences.h42
-rw-r--r--frontend/xsane-front-gtk.c798
-rw-r--r--frontend/xsane-front-gtk.h73
-rw-r--r--frontend/xsane-gamma.c1541
-rw-r--r--frontend/xsane-gamma.h48
-rw-r--r--frontend/xsane-icons.c1751
-rw-r--r--frontend/xsane-icons.h87
-rw-r--r--frontend/xsane-logo.xpm372
-rw-r--r--frontend/xsane-preferences.c292
-rw-r--r--frontend/xsane-preferences.h122
-rw-r--r--frontend/xsane-preview.c3257
-rw-r--r--frontend/xsane-preview.h178
-rw-r--r--frontend/xsane-rc-io.c903
-rw-r--r--frontend/xsane-rc-io.h123
-rw-r--r--frontend/xsane-save.c975
-rw-r--r--frontend/xsane-save.h79
-rw-r--r--frontend/xsane-scan.c2369
-rw-r--r--frontend/xsane-scan.h35
-rw-r--r--frontend/xsane-setup.c1565
-rw-r--r--frontend/xsane-setup.h33
-rw-r--r--frontend/xsane-style.rc34
-rw-r--r--frontend/xsane-text.h417
-rw-r--r--frontend/xsane.c4794
-rw-r--r--frontend/xsane.h513
33 files changed, 23592 insertions, 0 deletions
diff --git a/frontend/Makefile.in b/frontend/Makefile.in
new file mode 100644
index 0000000..439369d
--- /dev/null
+++ b/frontend/Makefile.in
@@ -0,0 +1,85 @@
+SHELL = /bin/sh
+
+VPATH = @srcdir@
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = ..
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+configdir = ${sysconfdir}/sane.d
+sanedatadir = ${datadir}/sane
+
+MKINSTALLDIRS = @MKINSTALLDIRS@
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+CC = @CC@
+INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include \
+ @GTK_CFLAGS@ @INCLUDES@ \
+ -DLOCALEDIR=\""$(datadir)/locale"\"
+DEFS = @DEFS@
+CPPFLAGS = @CPPFLAGS@
+CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @INTLLIBS@ @LIBS@
+GTK_LIBS = @GTK_LIBS@
+GIMP_LIBS = @GIMP_LIBS@
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)
+LINK = $(CC) $(LDFLAGS) -o $@
+
+BINPROGS = @BINPROGS@
+
+@SET_MAKE@
+
+PROGRAMS = $(BINPROGS)
+LIBLIB = ../lib/liblib.a
+
+XSANE_OBJS = xsane-back-gtk.o xsane-front-gtk.o xsane-gamma.o xsane-preview.o \
+ xsane-rc-io.o xsane-device-preferences.o xsane-preferences.o \
+ xsane-setup.o xsane-save.o xsane-scan.o xsane-icons.o xsane.o
+
+
+.c.o:
+ $(COMPILE) $<
+
+all: $(PROGRAMS)
+
+install: $(PROGRAMS)
+ $(MKINSTALLDIRS) $(bindir) $(sbindir) $(datadir) $(sanedatadir) $(sanedatadir)/xsane
+ @for program in $(BINPROGS); do \
+ $(INSTALL_PROGRAM) $${program} $(bindir)/$${program}; \
+ done
+ $(INSTALL_DATA) $(srcdir)/xsane-style.rc $(sanedatadir)/xsane/xsane-style.rc
+ $(INSTALL_DATA) $(srcdir)/xsane-logo.xpm $(sanedatadir)/xsane-logo.xpm
+
+xsane: $(XSANE_OBJS) $(LIBLIB)
+ $(LINK) $(XSANE_OBJS) \
+ $(LIBLIB) $(GIMP_LIBS) $(GTK_LIBS) $(LIBS)
+
+
+clean:
+ rm -f *.o *~ .*~ *.bak
+ rm -rf .libs
+
+distclean: clean
+ rm -f Makefile $(PROGRAMS)
+
+depend:
+ makedepend $(INCLUDES) *.c
+
+.PHONY: all install depend clean distclean
diff --git a/frontend/cursor/cursor_pipette_black b/frontend/cursor/cursor_pipette_black
new file mode 100644
index 0000000..9fc4d56
--- /dev/null
+++ b/frontend/cursor/cursor_pipette_black
@@ -0,0 +1,8 @@
+#define cursor_pipette_black_width 16
+#define cursor_pipette_black_height 16
+#define cursor_pipette_black_x_hot 1
+#define cursor_pipette_black_y_hot 14
+static unsigned char cursor_pipette_black_bits[] = {
+ 0x00, 0x70, 0x00, 0xf8, 0x80, 0xff, 0x00, 0xfe, 0x00, 0x7d, 0x80, 0x38,
+ 0x40, 0x18, 0xe0, 0x17, 0xf0, 0x13, 0xf8, 0x01, 0xfc, 0x00, 0x7c, 0x00,
+ 0x3e, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x00};
diff --git a/frontend/cursor/cursor_pipette_gray b/frontend/cursor/cursor_pipette_gray
new file mode 100644
index 0000000..7cbea80
--- /dev/null
+++ b/frontend/cursor/cursor_pipette_gray
@@ -0,0 +1,8 @@
+#define cursor_pipette_gray_width 16
+#define cursor_pipette_gray_height 16
+#define cursor_pipette_gray_x_hot 1
+#define cursor_pipette_gray_y_hot 14
+static unsigned char cursor_pipette_gray_bits[] = {
+ 0x00, 0x70, 0x00, 0xf8, 0x80, 0xff, 0x00, 0xfe, 0x00, 0x7d, 0x80, 0x38,
+ 0x40, 0x18, 0x20, 0x14, 0x10, 0x12, 0xf8, 0x01, 0xfc, 0x00, 0x7c, 0x00,
+ 0x3e, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x00};
diff --git a/frontend/cursor/cursor_pipette_mask b/frontend/cursor/cursor_pipette_mask
new file mode 100644
index 0000000..36c7757
--- /dev/null
+++ b/frontend/cursor/cursor_pipette_mask
@@ -0,0 +1,8 @@
+#define cursor_pipette_mask_width 16
+#define cursor_pipette_mask_height 16
+#define cursor_pipette_mask_x_hot 1
+#define cursor_pipette_mask_y_hot 14
+static unsigned char cursor_pipette_mask_bits[] = {
+ 0x00, 0x70, 0x00, 0xf8, 0x80, 0xff, 0x00, 0xfe, 0x00, 0x7f, 0x80, 0x3f,
+ 0xc0, 0x1f, 0xe0, 0x17, 0xf0, 0x13, 0xf8, 0x01, 0xfc, 0x00, 0x7c, 0x00,
+ 0x3e, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x00};
diff --git a/frontend/cursor/cursor_pipette_white b/frontend/cursor/cursor_pipette_white
new file mode 100644
index 0000000..9952441
--- /dev/null
+++ b/frontend/cursor/cursor_pipette_white
@@ -0,0 +1,8 @@
+#define cursor_pipette_white_width 16
+#define cursor_pipette_white_height 16
+#define cursor_pipette_white_x_hot 1
+#define cursor_pipette_white_y_hot 14
+static unsigned char cursor_pipette_white_bits[] = {
+ 0x00, 0x70, 0x00, 0xf8, 0x80, 0xff, 0x00, 0xfe, 0x00, 0x7d, 0x80, 0x38,
+ 0x40, 0x18, 0x20, 0x14, 0x10, 0x12, 0x08, 0x01, 0x84, 0x00, 0x44, 0x00,
+ 0x32, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00};
diff --git a/frontend/xsane-back-gtk.c b/frontend/xsane-back-gtk.c
new file mode 100644
index 0000000..8bde762
--- /dev/null
+++ b/frontend/xsane-back-gtk.c
@@ -0,0 +1,1426 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-back-gtk.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+#include "xsane-back-gtk.h"
+#include "xsane-preferences.h"
+#include "xsane-text.h"
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+/* extern declarations */
+extern void xsane_panel_build(GSGDialog *dialog);
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+/* forward declarations: */
+static void xsane_back_gtk_panel_rebuild(GSGDialog *dialog);
+void xsane_set_sensitivity(SANE_Int sensitivity);
+void xsane_set_window_icon(GtkWidget *gtk_window, gchar **xpm_d);
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+const char *xsane_back_gtk_unit_string(SANE_Unit unit)
+{
+ double d;
+
+ switch (unit)
+ {
+ case SANE_UNIT_NONE: return "none";
+ case SANE_UNIT_PIXEL: return "pixel";
+ case SANE_UNIT_BIT: return "bit";
+ case SANE_UNIT_DPI: return "dpi";
+ case SANE_UNIT_PERCENT: return "%";
+ case SANE_UNIT_MM:
+ d = preferences.length_unit;
+ if (d > 9.9 && d < 10.1)
+ {
+ return "cm";
+ }
+ else if (d > 25.3 && d < 25.5)
+ {
+ return "in";
+ }
+ return "mm";
+ case SANE_UNIT_MICROSECOND: return "\265s";
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_set_tooltip(GtkTooltips *tooltips, GtkWidget *widget, const char *desc)
+{
+ if (desc && desc[0])
+ {
+ gtk_tooltips_set_tip(tooltips, widget, desc, 0);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+int xsane_back_gtk_make_path(size_t buf_size, char *buf,
+ const char *prog_name,
+ const char *dir_name,
+ const char *prefix, const char *dev_name,
+ const char *postfix,
+ int location)
+{
+ struct passwd *pw;
+ size_t len, extra;
+ int i;
+
+ if (location == XSANE_PATH_LOCAL_SANE) /* make path to local file */
+ {
+ pw = getpwuid(getuid()); /* get homedirectory */
+ if (!pw)
+ {
+ snprintf(buf, buf_size, "%s %s", ERR_HOME_DIR, strerror(errno));
+ xsane_back_gtk_error(buf, FALSE);
+ return -1;
+ }
+
+ snprintf(buf, buf_size, "%s/.sane", pw->pw_dir);
+ mkdir(buf, 0777); /* ensure ~/.sane directory exists */
+ }
+ else if (location == XSANE_PATH_SYSTEM) /* make path to system file */
+ {
+ snprintf(buf, buf_size, "%s", STRINGIFY(PATH_SANE_DATA_DIR));
+ }
+ else /* make path to temporary file */
+ {
+ snprintf(buf, buf_size, "%s", PATH_SANE_TMP);
+ }
+
+ len = strlen(buf);
+
+ if (prog_name)
+ {
+ extra = strlen(prog_name);
+ if (len + extra + 1 >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ buf[len++] = '/';
+ memcpy(buf + len, prog_name, extra);
+ len += extra;
+ buf[len] = '\0';
+ mkdir(buf, 0777); /* ensure ~/.sane/PROG_NAME directory exists */
+ }
+ if (len >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ buf[len++] = '/';
+
+
+ if (dir_name)
+ {
+ extra = strlen(dir_name);
+ if (len + extra + 1 >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ buf[len++] = '/';
+ memcpy(buf + len, dir_name, extra);
+ len += extra;
+ buf[len] = '\0';
+ mkdir(buf, 0777); /* ensure DIR_NAME directory exists */
+ }
+
+ if (len >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ buf[len++] = '/';
+
+
+ if (prefix)
+ {
+ extra = strlen(prefix);
+ if (len + extra >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ memcpy(buf + len, prefix, extra);
+ len += extra;
+ }
+
+ if (location == XSANE_PATH_TMP) /* system tmp dir, add uid */
+ {
+ char uid_prefix[256];
+ uid_t uid;
+
+ uid = getuid();
+ snprintf(uid_prefix, sizeof(uid_prefix), "%d-", uid);
+
+ extra = strlen(uid_prefix);
+ if (len + extra >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ memcpy(buf + len, uid_prefix, extra);
+ len += extra;
+ }
+
+ if (dev_name)
+ {
+ /* Turn devicename into valid filename by replacing slashes by "_", "_" gets "__", spaces are erased */
+
+ for (i = 0; dev_name[i]; ++i)
+ {
+ if (len + 2 >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ switch (dev_name[i])
+ {
+ case '/': /* "/" -> "_" */
+ buf[len++] = '_';
+ break;
+
+ case ' ': /* erase " " */
+ break;
+
+ case '_': /* "_" -> "__" */
+ buf[len++] = '_';
+ /* fall through */
+ default:
+ buf[len++] = dev_name[i];
+ break;
+ }
+ }
+ }
+
+ if (postfix)
+ {
+ extra = strlen(postfix);
+ if (len + extra >= buf_size)
+ {
+ goto filename_too_long;
+ }
+ memcpy(buf + len, postfix, extra);
+ len += extra;
+ }
+ if (len >= buf_size)
+ goto filename_too_long;
+
+ buf[len++] = '\0';
+ return 0;
+
+filename_too_long:
+ xsane_back_gtk_error(ERR_FILENAME_TOO_LONG, FALSE);
+ errno = E2BIG;
+ return -1;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_set_option(GSGDialog * dialog, int opt_num, void *val, SANE_Action action)
+{
+ SANE_Status status;
+ SANE_Int info;
+ char buf[256];
+
+ status = sane_control_option(dialog->dev, opt_num, action, val, &info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf(buf, sizeof(buf), "%s %s: %s.", ERR_SET_OPTION, sane_get_option_descriptor(dialog->dev, opt_num)->name,
+ XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, FALSE);
+ return;
+ }
+
+ if ((info & SANE_INFO_RELOAD_PARAMS) && dialog->param_change_callback)
+ {
+ (*dialog->param_change_callback) (dialog, dialog->param_change_arg);
+ }
+
+ if (info & SANE_INFO_RELOAD_OPTIONS)
+ {
+ xsane_back_gtk_panel_rebuild(dialog);
+ if (dialog->option_reload_callback)
+ {
+ (*dialog->option_reload_callback) (dialog, dialog->option_reload_arg);
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_close_dialog_callback(GtkWidget * widget, gpointer data)
+{
+ gtk_widget_destroy(data);
+ xsane_back_gtk_message_dialog_active = 0;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static gint decision_flag;
+static GtkWidget *decision_dialog;
+
+void xsane_back_gtk_decision_callback(GtkWidget * widget, gpointer data)
+{
+ gtk_widget_destroy(decision_dialog);
+ xsane_back_gtk_message_dialog_active = 0;
+ decision_flag = (long) data;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+gint xsane_back_gtk_decision(gchar *title, gchar **xpm_d, gchar *message, gchar *oktext, gchar *rejecttext, gint wait)
+{
+ GtkWidget *main_vbox, *hbox, *label, *button;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkWidget *pixmapwidget;
+
+ if (xsane_back_gtk_message_dialog_active)
+ {
+ fprintf(stderr, "%s: %s\n", title, message);
+ return TRUE;
+ }
+ xsane_back_gtk_message_dialog_active = 1;
+ decision_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
+ gtk_window_set_position(GTK_WINDOW(decision_dialog), GTK_WIN_POS_MOUSE);
+ gtk_window_set_title(GTK_WINDOW(decision_dialog), title);
+ gtk_signal_connect(GTK_OBJECT(decision_dialog), "delete_event",
+ GTK_SIGNAL_FUNC(xsane_back_gtk_decision_callback), (void *) -1); /* -1 = cancel */
+
+ xsane_set_window_icon(decision_dialog, 0);
+
+ /* create the main vbox */
+ main_vbox = gtk_vbox_new(TRUE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 5);
+ gtk_widget_show(main_vbox);
+
+ gtk_container_add(GTK_CONTAINER(decision_dialog), main_vbox);
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
+ gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, FALSE, 0);
+
+ /* the info icon */
+ if (xpm_d)
+ {
+ pixmap = gdk_pixmap_create_from_xpm_d(decision_dialog->window, &mask, xsane.bg_trans, xpm_d);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_box_pack_start(GTK_BOX(hbox), pixmapwidget, FALSE, FALSE, 10);
+ gtk_widget_show(pixmapwidget);
+ gdk_pixmap_unref(pixmap);
+ }
+
+ /* the message */
+ label = gtk_label_new(message);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ gtk_widget_show(hbox);
+
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
+ gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, FALSE, 0);
+
+ /* the confirmation button */
+ button = gtk_button_new_with_label(oktext);
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_back_gtk_decision_callback, (void *) 1 /* confirm */);
+ gtk_container_add(GTK_CONTAINER(hbox), button);
+ gtk_widget_grab_default(button);
+ gtk_widget_show(button);
+
+
+ if (rejecttext) /* the rejection button */
+ {
+ button = gtk_button_new_with_label(rejecttext);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_back_gtk_decision_callback, (void *) -1 /* reject */);
+ gtk_container_add(GTK_CONTAINER(hbox), button);
+ gtk_widget_show(button);
+ }
+ gtk_widget_show(hbox);
+
+ gtk_widget_show(decision_dialog);
+
+ if (!wait)
+ {
+ return TRUE;
+ }
+
+ decision_flag = 0;
+
+ while (decision_flag == 0)
+ {
+ gtk_main_iteration();
+ }
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ if (decision_flag == 1)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_message(gchar *title, gchar **icon_xpm, gchar *message, gint wait)
+{
+ xsane_back_gtk_decision(title, icon_xpm, message, ERR_BUTTON_OK, 0 /* no reject text */, wait);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_error(gchar *error, gint wait)
+{
+ if (wait)
+ {
+ SANE_Int old_sensitivity = xsane.sensitivity;
+
+ xsane_set_sensitivity(FALSE);
+ xsane_back_gtk_message(ERR_HEADER_ERROR, (gchar**) error_xpm, error, wait);
+ xsane_set_sensitivity(old_sensitivity);
+ }
+ else
+ {
+ xsane_back_gtk_message(ERR_HEADER_ERROR, (gchar **) error_xpm, error, wait);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_warning(gchar *warning, gint wait)
+{
+ if (wait)
+ {
+ SANE_Int old_sensitivity = xsane.sensitivity;
+
+ xsane_set_sensitivity(FALSE);
+ xsane_back_gtk_message(ERR_HEADER_WARNING, (gchar**) warning_xpm, warning, wait);
+ xsane_set_sensitivity(old_sensitivity);
+ }
+ else
+ {
+ xsane_back_gtk_message(ERR_HEADER_WARNING, (gchar**) warning_xpm, warning, wait);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_get_filename_button_clicked(GtkWidget *w, gpointer data)
+{
+ int *clicked = data;
+ *clicked = 1;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+int xsane_back_gtk_get_filename(const char *label, const char *default_name, size_t max_len, char *filename, int show_fileopts)
+{
+ int cancel = 0, ok = 0, destroy = 0;
+ GtkWidget *fileselection;
+
+ fileselection = gtk_file_selection_new((char *) label);
+
+ gtk_signal_connect(GTK_OBJECT(fileselection),
+ "destroy", GTK_SIGNAL_FUNC(xsane_back_gtk_get_filename_button_clicked), &destroy);
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fileselection)->cancel_button),
+ "clicked", (GtkSignalFunc) xsane_back_gtk_get_filename_button_clicked, &cancel);
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fileselection)->ok_button),
+ "clicked", (GtkSignalFunc) xsane_back_gtk_get_filename_button_clicked, &ok);
+ if (default_name)
+ {
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection), (char *) default_name);
+ }
+
+ if (show_fileopts)
+ {
+ gtk_file_selection_show_fileop_buttons(GTK_FILE_SELECTION(fileselection));
+ }
+ else
+ {
+ gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(fileselection));
+ }
+
+ gtk_widget_show(fileselection);
+
+ while (!cancel && !ok && !destroy)
+ {
+ if (!gtk_events_pending())
+ {
+ usleep(100000);
+ }
+ gtk_main_iteration();
+ }
+
+ if (ok)
+ {
+ size_t len, cwd_len;
+ char *cwd;
+
+ strncpy(filename, gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection)), max_len - 1);
+ filename[max_len - 1] = '\0';
+
+ len = strlen(filename);
+ cwd = alloca(len + 2);
+ getcwd(cwd, len + 1);
+ cwd_len = strlen(cwd);
+ cwd[cwd_len++] = '/';
+ cwd[cwd_len] = '\0';
+ if (strncmp(filename, cwd, cwd_len) == 0)
+ {
+ memcpy(filename, filename + cwd_len, len - cwd_len + 1);
+ }
+ }
+
+ if (!destroy)
+ {
+ gtk_widget_destroy(fileselection);
+ }
+
+ return ok ? 0 : -1;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static gint xsane_back_gtk_autobutton_update(GtkWidget *widget, GSGDialogElement *elem)
+{
+ GSGDialog *dialog = elem->dialog;
+ int opt_num = elem - dialog->element;
+ const SANE_Option_Descriptor *opt;
+ SANE_Status status;
+ SANE_Word val;
+ char buf[256];
+
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+ if (GTK_TOGGLE_BUTTON(widget)->active)
+ {
+ xsane_back_gtk_set_option(dialog, opt_num, 0, SANE_ACTION_SET_AUTO);
+ }
+ else
+ {
+ status = sane_control_option(dialog->dev, opt_num, SANE_ACTION_GET_VALUE, &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf(buf, sizeof(buf), "%s %s: %s.", ERR_GET_OPTION, opt->name, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, FALSE);
+ }
+ xsane_back_gtk_set_option(dialog, opt_num, &val, SANE_ACTION_SET_VALUE);
+ }
+ return FALSE;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_autobutton_new(GtkWidget *parent, GSGDialogElement *elem,
+ GtkWidget *label, GtkTooltips *tooltips)
+{
+ GtkWidget *button, *alignment;
+
+ button = gtk_check_button_new();
+ gtk_container_set_border_width(GTK_CONTAINER(button), 0);
+ gtk_widget_set_usize(button, 20, 20);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_back_gtk_autobutton_update, elem);
+ xsane_back_gtk_set_tooltip(tooltips, button, "Turns on automatic mode.");
+
+ alignment = gtk_alignment_new(0.0, 1.0, 0.5, 0.5);
+ gtk_container_add(GTK_CONTAINER(alignment), button);
+
+ gtk_box_pack_end(GTK_BOX(parent), label, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(parent), alignment, FALSE, FALSE, 2);
+
+ gtk_widget_show(alignment);
+ gtk_widget_show(button);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static gint xsane_back_gtk_button_update(GtkWidget * widget, GSGDialogElement * elem)
+{
+ GSGDialog *dialog = elem->dialog;
+ int opt_num = elem - dialog->element;
+ const SANE_Option_Descriptor *opt;
+ SANE_Word val = SANE_FALSE;
+
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+ if (GTK_TOGGLE_BUTTON(widget)->active)
+ {
+ val = SANE_TRUE;
+ }
+ xsane_back_gtk_set_option(dialog, opt_num, &val, SANE_ACTION_SET_VALUE);
+
+ return FALSE;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_button_new(GtkWidget * parent, const char *name, SANE_Word val,
+ GSGDialogElement * elem, GtkTooltips *tooltips, const char *desc, SANE_Int settable)
+{
+ GtkWidget *button;
+
+ button = gtk_check_button_new_with_label((char *) name);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), val);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_back_gtk_button_update, elem);
+ gtk_box_pack_start(GTK_BOX(parent), button, FALSE, TRUE, 0);
+ gtk_widget_show(button);
+ xsane_back_gtk_set_tooltip(tooltips, button, desc);
+
+ gtk_widget_set_sensitive(button, settable);
+
+ elem->widget = button;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_scale_update(GtkAdjustment * adj_data, GSGDialogElement * elem)
+{
+ const SANE_Option_Descriptor *opt;
+ GSGDialog *dialog = elem->dialog;
+ SANE_Word val, new_val;
+ int opt_num;
+ double d;
+
+ opt_num = elem - dialog->element;
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+ switch(opt->type)
+ {
+ case SANE_TYPE_INT:
+ val = adj_data->value + 0.5;
+ break;
+
+ case SANE_TYPE_FIXED:
+ d = adj_data->value;
+ if (opt->unit == SANE_UNIT_MM)
+ {
+ d *= preferences.length_unit;
+ }
+ val = SANE_FIX(d);
+ break;
+
+ default:
+ fprintf(stderr, "xsane_back_gtk_scale_update: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ return;
+ }
+
+ xsane_back_gtk_set_option(dialog, opt_num, &val, SANE_ACTION_SET_VALUE);
+ sane_control_option(dialog->dev, opt_num, SANE_ACTION_GET_VALUE, &new_val, 0);
+ if (new_val != val)
+ {
+ val = new_val;
+ goto value_changed;
+ }
+ return; /* value didn't change */
+
+value_changed:
+ switch(opt->type)
+ {
+ case SANE_TYPE_INT:
+ adj_data->value = val;
+ break;
+
+ case SANE_TYPE_FIXED:
+ d = SANE_UNFIX(val);
+ if (opt->unit == SANE_UNIT_MM)
+ {
+ d /= preferences.length_unit;
+ }
+ adj_data->value = d;
+ break;
+
+ default:
+ break;
+ }
+ /* Let widget know that value changed _again_. This must converge
+ quickly---otherwise things would get very slow very quickly (as
+ in "infinite recursion"): */
+ gtk_signal_emit_by_name(GTK_OBJECT(adj_data), "value_changed");
+ return;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_scale_new(GtkWidget * parent, const char *name, gfloat val,
+ gfloat min, gfloat max, gfloat quant, int automatic,
+ GSGDialogElement * elem, GtkTooltips *tooltips, const char *desc, SANE_Int settable)
+{
+ GtkWidget *hbox, *label, *scale;
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 0);
+ gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new((char *) name);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+
+ elem->data = gtk_adjustment_new(val, min, max, quant, 1.0, 0.0);
+ scale = gtk_hscale_new(GTK_ADJUSTMENT(elem->data));
+ xsane_back_gtk_set_tooltip(tooltips, scale, desc);
+ gtk_widget_set_usize(scale, 150, 0);
+
+ if (automatic)
+ {
+ xsane_back_gtk_autobutton_new(hbox, elem, scale, tooltips);
+ }
+ else
+ {
+ gtk_box_pack_end(GTK_BOX(hbox), scale, FALSE, FALSE, 0); /* make scales fixed */
+/* gtk_box_pack_end(GTK_BOX(hbox), scale, TRUE, TRUE, 0); */ /* make scales sizeable */
+ }
+
+ gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS);
+ gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_TOP);
+ if (quant - (int) quant == 0.0)
+ {
+ gtk_scale_set_digits(GTK_SCALE(scale), 0);
+ }
+ else
+ {
+ /* one place behind decimal point */
+ gtk_scale_set_digits(GTK_SCALE(scale), 1);
+ }
+
+ gtk_signal_connect(elem->data, "value_changed", (GtkSignalFunc) xsane_back_gtk_scale_update, elem);
+
+ gtk_widget_show(label);
+ gtk_widget_show(scale);
+ gtk_widget_show(hbox);
+
+ gtk_widget_set_sensitive(scale, settable);
+
+ elem->widget = scale;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_push_button_callback(GtkWidget * widget, gpointer data)
+{
+ GSGDialogElement *elem = data;
+ GSGDialog *dialog = elem->dialog;
+ int opt_num;
+
+ opt_num = elem - dialog->element;
+ xsane_back_gtk_set_option(dialog, opt_num, 0, SANE_ACTION_SET_VALUE);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_back_gtk_option_menu_lookup(GSGMenuItem menu_items[], const char *string)
+{
+ int i;
+
+ for (i = 0; strcmp(menu_items[i].label, string) != 0; ++i);
+
+ return i;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_option_menu_callback(GtkWidget * widget, gpointer data)
+{
+ GSGMenuItem *menu_item = data;
+ GSGDialogElement *elem = menu_item->elem;
+ const SANE_Option_Descriptor *opt;
+ GSGDialog *dialog = elem->dialog;
+ int opt_num;
+ double dval;
+ SANE_Word val;
+ void *valp = &val;
+
+ opt_num = elem - dialog->element;
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+ switch(opt->type)
+ {
+ case SANE_TYPE_INT:
+ sscanf(menu_item->label, "%d", &val);
+ break;
+
+ case SANE_TYPE_FIXED:
+ sscanf(menu_item->label, "%lg", &dval);
+ val = SANE_FIX(dval);
+ break;
+
+ case SANE_TYPE_STRING:
+ valp = menu_item->label;
+ break;
+
+ default:
+ fprintf(stderr, "xsane_back_gtk_option_menu_callback: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ break;
+ }
+ xsane_back_gtk_set_option(dialog, opt_num, valp, SANE_ACTION_SET_VALUE);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_option_menu_new(GtkWidget *parent, const char *name, char *str_list[],
+ const char *val, GSGDialogElement * elem,
+ GtkTooltips *tooltips, const char *desc, SANE_Int settable)
+{
+ GtkWidget *hbox, *label, *option_menu, *menu, *item;
+ GSGMenuItem *menu_items;
+ int i, num_items;
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 0);
+ gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new((char *) name);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+
+ for (num_items = 0; str_list[num_items]; ++num_items);
+ menu_items = malloc(num_items * sizeof(menu_items[0]));
+
+ menu = gtk_menu_new();
+ for (i = 0; i < num_items; ++i)
+ {
+ item = gtk_menu_item_new_with_label(_BGT(str_list[i]));
+ gtk_container_add(GTK_CONTAINER(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_back_gtk_option_menu_callback, menu_items + i);
+
+ gtk_widget_show(item);
+
+ menu_items[i].label = str_list[i];
+ menu_items[i].elem = elem;
+ menu_items[i].index = i;
+ }
+
+ option_menu = gtk_option_menu_new();
+ gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 2);
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), xsane_back_gtk_option_menu_lookup(menu_items, val));
+ xsane_back_gtk_set_tooltip(tooltips, option_menu, desc);
+
+ gtk_widget_show(label);
+ gtk_widget_show(option_menu);
+ gtk_widget_show(hbox);
+
+ gtk_widget_set_sensitive(option_menu, settable);
+
+ elem->widget = option_menu;
+ elem->menu_size = num_items;
+ elem->menu = menu_items;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_text_entry_callback(GtkWidget *w, gpointer data)
+{
+ GSGDialogElement *elem = data;
+ const SANE_Option_Descriptor *opt;
+ GSGDialog *dialog = elem->dialog;
+ gchar *text;
+ int opt_num;
+ char *buf;
+
+ opt_num = elem - dialog->element;
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+
+ buf = alloca(opt->size);
+ buf[0] = '\0';
+
+ text = gtk_entry_get_text(GTK_ENTRY(elem->widget));
+ if (text)
+ {
+ strncpy(buf, text, opt->size);
+ }
+ buf[opt->size - 1] = '\0';
+
+ xsane_back_gtk_set_option(dialog, opt_num, buf, SANE_ACTION_SET_VALUE);
+
+ if (strcmp(buf, text) != 0) /* the backend modified the option value; update widget: */
+ {
+ gtk_entry_set_text(GTK_ENTRY(elem->widget), buf);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_text_entry_new(GtkWidget * parent, const char *name, const char *val, GSGDialogElement *elem,
+ GtkTooltips *tooltips, const char *desc, SANE_Int settable)
+{
+ GtkWidget *hbox, *text, *label;
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 0);
+ gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new((char *) name);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+
+ text = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) val);
+/* gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, TRUE, 0); */ /* text entry fixed */
+ gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0); /* text entry sizeable */
+ gtk_signal_connect(GTK_OBJECT(text), "changed", (GtkSignalFunc) xsane_back_gtk_text_entry_callback, elem);
+ xsane_back_gtk_set_tooltip(tooltips, text, desc);
+
+ gtk_widget_show(hbox);
+ gtk_widget_show(label);
+ gtk_widget_show(text);
+
+ gtk_widget_set_sensitive(text, settable);
+
+ elem->widget = text;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+GtkWidget *xsane_back_gtk_group_new(GtkWidget *parent, const char * title)
+{
+ GtkWidget * frame, * vbox;
+
+ frame = gtk_frame_new((char *) title);
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start(GTK_BOX(parent), frame, FALSE, FALSE, 0);
+
+ vbox = gtk_vbox_new(FALSE, 4);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_widget_show(vbox);
+ return vbox;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+#if 0
+static GtkWidget* xsane_back_gtk_curve_new(GSGDialog *dialog, int optnum)
+{
+ const SANE_Option_Descriptor * opt;
+ gfloat fmin, fmax, val, *vector;
+ SANE_Word *optval, min, max;
+ GtkWidget *curve, *gamma;
+ SANE_Status status;
+ SANE_Handle dev;
+ int i, optlen;
+
+ gamma = gtk_gamma_curve_new();
+ curve = GTK_GAMMA_CURVE(gamma)->curve;
+ dev = dialog->dev;
+
+ opt = sane_get_option_descriptor(dev, optnum);
+ optlen = opt->size / sizeof(SANE_Word);
+ vector = alloca(optlen * (sizeof(vector[0]) + sizeof(optval[0])));
+ optval = (SANE_Word *) (vector + optlen);
+
+ min = max = 0;
+ switch(opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ min = opt->constraint.range->min;
+ max = opt->constraint.range->max;
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ if (opt->constraint.word_list[0] > 1)
+ {
+ min = max = opt->constraint.word_list[1];
+ for (i = 2; i < opt->constraint.word_list[0]; ++i)
+ {
+ if (opt->constraint.word_list[i] < min)
+ {
+ min = opt->constraint.word_list[i];
+ }
+
+ if (opt->constraint.word_list[i] > max)
+ {
+ max = opt->constraint.word_list[i];
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (min == max)
+ {
+ fprintf(stderr, "xsane_back_gtk_curve_new: %s: `%s'\n", WARN_NO_VALUE_CONSTRAINT, opt->name);
+ fmin = 0;
+ fmax = 255;
+ }
+ else if (opt->type == SANE_TYPE_FIXED)
+ {
+ fmin = SANE_UNFIX(min);
+ fmax = SANE_UNFIX(max);
+ }
+ else
+ {
+ fmin = min;
+ fmax = max;
+ }
+ gtk_curve_set_range(GTK_CURVE(curve), 0, optlen - 1, fmin, fmax);
+
+ status = sane_control_option(dev, optnum, SANE_ACTION_GET_VALUE, optval, 0);
+ if (status == SANE_STATUS_GOOD)
+ {
+ for (i = 0; i < optlen; ++i)
+ {
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ val = SANE_UNFIX(optval[i]);
+ }
+ else
+ {
+ val = optval[i];
+ }
+ vector[i] = val;
+ }
+ gtk_curve_set_vector(GTK_CURVE(curve), optlen, vector);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(gamma, FALSE);
+ }
+
+ return gamma;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_vector_new(GSGDialog * dialog, GtkWidget *vbox, int num_vopts, int *vopts)
+{
+ GtkWidget *notebook, *label, *curve;
+ const SANE_Option_Descriptor *opt;
+ int i;
+
+ notebook = gtk_notebook_new();
+ gtk_container_set_border_width(GTK_CONTAINER(notebook), 4);
+ gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
+
+ for (i = 0; i < num_vopts; ++i)
+ {
+ opt = sane_get_option_descriptor(dialog->dev, vopts[i]);
+
+ label = gtk_label_new((char *) opt->title);
+ vbox = gtk_vbox_new(/* homogeneous */ FALSE, 0);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);
+ gtk_widget_show(vbox);
+ gtk_widget_show(label);
+
+ curve = xsane_back_gtk_curve_new(dialog, vopts[i]);
+ gtk_container_set_border_width(GTK_CONTAINER(curve), 4);
+ gtk_box_pack_start(GTK_BOX(vbox), curve, TRUE, TRUE, 0);
+ gtk_widget_show(curve);
+
+ dialog->element[vopts[i]].widget = curve;
+ }
+ gtk_widget_show(notebook);
+}
+#endif
+/* ----------------------------------------------------------------------------------------------------------------- */
+#if 0
+static void tooltips_destroy(GSGDialog * dialog)
+{
+ gtk_object_unref(GTK_OBJECT(dialog->tooltips));
+ dialog->tooltips = 0;
+}
+#endif
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_panel_destroy(GSGDialog * dialog)
+{
+ const SANE_Option_Descriptor *opt;
+ GSGDialogElement *elem;
+ int i, j;
+
+ gtk_widget_destroy(dialog->xsane_hbox);
+ gtk_widget_destroy(dialog->standard_hbox);
+ gtk_widget_destroy(dialog->advanced_hbox);
+
+ /* free the menu labels of integer/fix-point word-lists: */
+ for (i = 0; i < dialog->num_elements; ++i)
+ {
+ if (dialog->element[i].menu)
+ {
+ opt = sane_get_option_descriptor(dialog->dev, i);
+ elem = dialog->element + i;
+ if (opt->type != SANE_TYPE_STRING)
+ {
+ for (j = 0; j < elem->menu_size; ++j)
+ {
+ if (elem->menu[j].label)
+ {
+ free(elem->menu[j].label);
+ elem->menu[j].label = 0;
+ }
+ }
+ free(elem->menu);
+ elem->menu = 0;
+ }
+ }
+ }
+ memset(dialog->element, 0, dialog->num_elements * sizeof(dialog->element[0]));
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+/* When an setting an option changes the dialog, everything may
+ change: the option titles, the activity-status of the option, its
+ constraints or what not. Thus, rather than trying to be clever in
+ detecting what exactly changed, we use a brute-force method of
+ rebuilding the entire dialog. */
+
+static void xsane_back_gtk_panel_rebuild(GSGDialog * dialog)
+{
+ xsane_back_gtk_panel_destroy(dialog);
+ xsane_panel_build(dialog);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_refresh_dialog(GSGDialog *dialog)
+{
+ xsane_back_gtk_panel_rebuild(dialog);
+ if (dialog->param_change_callback)
+ {
+ (*dialog->param_change_callback) (dialog, dialog->param_change_arg);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_update_scan_window(GSGDialog *dialog)
+{
+ const SANE_Option_Descriptor *opt;
+ double old_val, new_val;
+ GSGDialogElement *elem;
+ SANE_Status status;
+ SANE_Word word;
+ int i, optnum;
+ char str[64];
+
+ for (i = 0; i < 4; ++i)
+ {
+ if (dialog->well_known.coord[i] > 0)
+ {
+ optnum = dialog->well_known.coord[i];
+ elem = dialog->element + optnum;
+ opt = sane_get_option_descriptor(dialog->dev, optnum);
+
+ status = sane_control_option(dialog->dev, optnum, SANE_ACTION_GET_VALUE, &word, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ continue; /* sliently ignore errors */
+ }
+
+ switch(opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ if (opt->type == SANE_TYPE_INT)
+ {
+ old_val = GTK_ADJUSTMENT(elem->data)->value;
+ new_val = word;
+ GTK_ADJUSTMENT(elem->data)->value = new_val;
+ }
+ else
+ {
+ old_val = GTK_ADJUSTMENT(elem->data)->value;
+ new_val = SANE_UNFIX(word);
+ if (opt->unit == SANE_UNIT_MM)
+ {
+ new_val /= preferences.length_unit;
+ }
+ GTK_ADJUSTMENT(elem->data)->value = new_val;
+ }
+
+ if (old_val != new_val)
+ {
+ gtk_signal_emit_by_name(GTK_OBJECT(elem->data), "value_changed");
+ }
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ if (opt->type == SANE_TYPE_INT)
+ {
+ sprintf(str, "%d", word);
+ }
+ else
+ {
+ sprintf(str, "%g", SANE_UNFIX(word));
+ }
+ /* XXX maybe we should call this only when the value changes... */
+ gtk_option_menu_set_history(GTK_OPTION_MENU(elem->widget), xsane_back_gtk_option_menu_lookup(elem->menu, str));
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+/* Ensure sure the device has up-to-date option values. Except for
+ vectors, all option values are kept current. Vectors are
+ downloaded into the device during this call. */
+void xsane_back_gtk_sync(GSGDialog *dialog)
+{
+ const SANE_Option_Descriptor *opt;
+ gfloat val, *vector;
+ SANE_Word *optval;
+ int i, j, optlen;
+ GtkWidget *curve;
+
+ for (i = 1; i < dialog->num_elements; ++i)
+ {
+ opt = sane_get_option_descriptor(dialog->dev, i);
+
+ if (!SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ continue;
+ }
+
+ if (opt->type != SANE_TYPE_INT && opt->type != SANE_TYPE_FIXED)
+ {
+ continue;
+ }
+
+ if (opt->size == sizeof(SANE_Word))
+ {
+ continue;
+ }
+
+ /* ok, we're dealing with an active vector */
+
+ optlen = opt->size / sizeof(SANE_Word);
+ optval = alloca(optlen * sizeof(optval[0]));
+ vector = alloca(optlen * sizeof(vector[0]));
+
+ curve = GTK_GAMMA_CURVE(dialog->element[i].widget)->curve;
+ gtk_curve_get_vector(GTK_CURVE(curve), optlen, vector);
+ for (j = 0; j < optlen; ++j)
+ {
+ val = vector[j];
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ optval[j] = SANE_FIX(val);
+ }
+ else
+ {
+ optval[j] = val + 0.5;
+ }
+ }
+
+ xsane_back_gtk_set_option(dialog, i, optval, SANE_ACTION_SET_VALUE);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_update_vector(GSGDialog *dialog, int opt_num, SANE_Int *vector)
+{
+ const SANE_Option_Descriptor *opt;
+ gfloat val;
+ SANE_Word *optval;
+ int j, optlen;
+
+ if (opt_num < 1)
+ return; /* not defined */
+
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+ if (!SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ return; /* inactive */
+ }
+
+ if (opt->type != SANE_TYPE_INT && opt->type != SANE_TYPE_FIXED)
+ {
+ return;
+ }
+
+ if (opt->size == sizeof(SANE_Word))
+ {
+ return;
+ }
+
+ /* ok, we're dealing with an active vector */
+
+ optlen = opt->size / sizeof(SANE_Word);
+ optval = alloca(optlen * sizeof(optval[0]));
+ for (j = 0; j < optlen; ++j)
+ {
+ val = vector[j];
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ optval[j] = SANE_FIX(val);
+ }
+ else
+ {
+ optval[j] = val + 0.5;
+ }
+ }
+
+ xsane_back_gtk_set_option(dialog, opt_num, optval, SANE_ACTION_SET_VALUE);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_set_tooltips(GSGDialog *dialog, int enable)
+{
+ if (!dialog->tooltips)
+ {
+ return;
+ }
+
+ if (enable)
+ {
+ gtk_tooltips_enable(dialog->tooltips);
+ }
+ else
+ {
+ gtk_tooltips_disable(dialog->tooltips);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_set_sensitivity(GSGDialog *dialog, int sensitive)
+{
+ const SANE_Option_Descriptor *opt;
+ int i;
+
+ for (i = 0; i < dialog->num_elements; ++i)
+ {
+ opt = sane_get_option_descriptor(dialog->dev, i);
+
+ if (!SANE_OPTION_IS_ACTIVE(opt->cap) || !SANE_OPTION_IS_SETTABLE(opt->cap) ||
+ opt->type == SANE_TYPE_GROUP || !dialog->element[i].widget)
+ {
+ continue;
+ }
+
+ if (!(opt->cap & SANE_CAP_ALWAYS_SETTABLE))
+ {
+ gtk_widget_set_sensitive(dialog->element[i].widget, sensitive);
+ }
+ }
+
+ if (dialog)
+ {
+ if (dialog->xsanemode_widget)
+ {
+ gtk_widget_set_sensitive(dialog->xsanemode_widget, sensitive);
+ }
+ }
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+}
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_set_sensitivity(SANE_Int sensitivity)
+{
+ if (xsane.shell)
+ {
+ gtk_widget_set_sensitive(xsane.shell, sensitivity);
+ gtk_widget_set_sensitive(xsane.standard_options_shell, sensitivity);
+ gtk_widget_set_sensitive(xsane.advanced_options_shell, sensitivity);
+ gtk_widget_set_sensitive(xsane.histogram_dialog, sensitivity);
+ }
+
+ if (xsane.preview)
+ {
+ gtk_widget_set_sensitive(xsane.preview->button_box, sensitivity); /* button box at top of window */
+#if 0
+ gtk_widget_set_sensitive(xsane.preview->viewport, sensitivity); /* Preview image selection */
+#endif
+ gtk_widget_set_sensitive(xsane.preview->start, sensitivity); /* Acquire preview button */
+ }
+
+ if (xsane.fax_dialog)
+ {
+ gtk_widget_set_sensitive(xsane.fax_dialog, sensitivity);
+ }
+
+ if (dialog)
+ {
+ xsane_back_gtk_set_sensitivity(dialog, sensitivity);
+ }
+
+ while (gtk_events_pending()) /* make sure set_sensitivity is displayed */
+ {
+ gtk_main_iteration();
+ }
+
+ xsane.sensitivity = sensitivity;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_destroy_dialog(GSGDialog * dialog)
+{
+ SANE_Handle dev = dialog->dev;
+
+ xsane_back_gtk_panel_destroy(dialog);
+ free((void *) dialog->dev_name);
+ free(dialog->element);
+ free(dialog);
+
+ sane_close(dev);
+}
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_set_window_icon(GtkWidget *gtk_window, gchar **xpm_d)
+{
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+
+ gtk_widget_realize(gtk_window);
+ if (xpm_d)
+ {
+ pixmap = gdk_pixmap_create_from_xpm_d(gtk_window->window, &mask, xsane.bg_trans, xpm_d);
+ }
+ else
+ {
+ if (xsane.window_icon_pixmap)
+ {
+ pixmap = xsane.window_icon_pixmap;
+ mask = xsane.window_icon_mask;
+ }
+ else
+ {
+ pixmap = gdk_pixmap_create_from_xpm_d(gtk_window->window, &mask, xsane.bg_trans, (gchar **) xsane_window_icon_xpm);
+ }
+ }
+
+ gdk_window_set_icon(gtk_window->window, 0, pixmap, mask);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
diff --git a/frontend/xsane-back-gtk.h b/frontend/xsane-back-gtk.h
new file mode 100644
index 0000000..af1d55a
--- /dev/null
+++ b/frontend/xsane-back-gtk.h
@@ -0,0 +1,188 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-back-gtk.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifndef xsane_back_gtk_h
+#define xsane_back_gtk_h
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include <sys/types.h>
+
+#include <gtk/gtk.h>
+
+#include <sane/config.h>
+#include <sane/sane.h>
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+enum
+{
+ XSANE_PATH_LOCAL_SANE = 0,
+ XSANE_PATH_SYSTEM,
+ XSANE_PATH_TMP
+};
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+struct GSGDialog;
+
+typedef void (*GSGCallback) (struct GSGDialog *dialog, void *arg);
+typedef GtkWidget *(*XSANECallback) (void);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+typedef enum
+ {
+ xsane_back_gtk_TL_X, /* top-left x */
+ xsane_back_gtk_TL_Y, /* top-left y */
+ xsane_back_gtk_BR_X, /* bottom-right x */
+ xsane_back_gtk_BR_Y /* bottom-right y */
+ }
+GSGCornerCoordinates;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+typedef struct
+ {
+ /* The option number of the well-known options. Each of these may
+ be -1 in case the backend doesn't define the respective option. */
+ int scanmode;
+ int scansource;
+ int preview;
+ int dpi;
+ int dpi_x;
+ int dpi_y;
+ int coord[4];
+ int gamma_vector;
+ int gamma_vector_r;
+ int gamma_vector_g;
+ int gamma_vector_b;
+ int bit_depth;
+ }
+GSGWellKnownOptions;
+
+typedef struct
+ {
+ gchar *label;
+ struct GSGDialogElement *elem;
+ gint index;
+ }
+GSGMenuItem;
+
+typedef struct GSGDialogElement
+ {
+ struct GSGDialog *dialog; /* wasteful, but is there a better solution? */
+ GtkWidget *automatic; /* auto button for options that support this */
+ GtkWidget *widget;
+ GtkObject *data;
+ int menu_size; /* # of items in menu (if any) */
+ GSGMenuItem *menu;
+ }
+GSGDialogElement;
+
+typedef struct GSGDialog
+ {
+ GtkWidget *xsane_window;
+ GtkWidget *standard_window;
+ GtkWidget *advanced_window;
+ GtkWidget *xsane_hbox;
+ GtkWidget *standard_hbox;
+ GtkWidget *advanced_hbox;
+ GtkWidget *xsanemode_widget;
+ GtkTooltips *tooltips;
+ GdkColor tooltips_fg;
+ GdkColor tooltips_bg;
+ SANE_Handle *dev;
+ const char *dev_name;
+ GSGWellKnownOptions well_known;
+ int num_elements;
+ GSGDialogElement *element;
+ gint idle_id;
+ u_int rebuild : 1;
+ /* This callback gets invoked whenever the backend notifies us
+ that the option descriptors have changed. */
+ GSGCallback option_reload_callback;
+ void *option_reload_arg;
+ /* This callback gets invoked whenever the backend notifies us
+ that the parameters have changed. */
+ GSGCallback param_change_callback;
+ void *param_change_arg;
+ XSANECallback update_xsane_callback;
+ void *update_xsane_arg;
+ int pixelcolor;
+ }
+GSGDialog;
+
+extern int xsane_back_gtk_message_dialog_active;
+
+/* Construct the path and return it in filename_ret (this buffer must
+ be at least max_len bytes long). The path is constructed as
+ follows:
+
+ ~/.sane/${PROG_NAME}/${PREFIX}${DEV_NAME}${POSTFIX}
+
+ If PROG_NAME is NULL, an empty string is used and the leading slash
+ is removed. On success, 0 is returned, on error a negative number and
+ ERRNO is set to the appropriate value. */
+extern int xsane_back_gtk_make_path(size_t max_len, char *filename_ret,
+ const char *prog_name,
+ const char *dir_name,
+ const char *prefix, const char *dev_name,
+ const char *postfix,
+ int local);
+extern gint xsane_back_gtk_decision(gchar *title, gchar** icon_xpm, gchar *message, gchar *oktext, gchar *rejecttext, gint wait);
+extern void xsane_back_gtk_message(gchar *title, gchar** icon_xpm, gchar *message, gint wait);
+extern void xsane_back_gtk_error(gchar *error_message, gint wait);
+extern void xsane_back_gtk_warning(gchar *warning_message, gint wait);
+extern int xsane_back_gtk_get_filename(const char *label, const char *default_name,
+ size_t max_len, char *filename, int show_fileopts);
+
+extern void xsane_back_gtk_sync(GSGDialog *dialog);
+extern void xsane_back_gtk_update_vector(GSGDialog *dialog, int opt_num, SANE_Int *vector);
+extern void xsane_back_gtk_refresh_dialog(GSGDialog *dialog);
+extern void xsane_back_gtk_update_scan_window(GSGDialog *dialog);
+extern void xsane_back_gtk_set_advanced(GSGDialog *dialog, int advanced);
+extern void xsane_back_gtk_set_tooltips(GSGDialog *dialog, int enable);
+extern void xsane_back_gtk_set_tooltip(GtkTooltips *tooltips, GtkWidget *widget, const char *desc);
+extern void xsane_back_gtk_set_sensitivity(GSGDialog *dialog, int sensitive);
+extern void xsane_set_sensitivity(SANE_Int sensitivity);
+extern void xsane_back_gtk_destroy_dialog(GSGDialog *dialog);
+extern void xsane_back_gtk_set_option(GSGDialog * dialog, int opt_num, void *val, SANE_Action action);
+extern GtkWidget *xsane_back_gtk_group_new (GtkWidget *parent, const char * title);
+extern void xsane_back_gtk_button_new(GtkWidget * parent, const char *name, SANE_Word val,
+ GSGDialogElement *elem, GtkTooltips *tooltips, const char *desc, SANE_Int settable);
+extern void xsane_back_gtk_scale_new(GtkWidget * parent, const char *name, gfloat val,
+ gfloat min, gfloat max, gfloat quant, int automatic,
+ GSGDialogElement *elem, GtkTooltips *tooltips, const char *desc, SANE_Int settable);
+extern void xsane_back_gtk_option_menu_new(GtkWidget *parent, const char *name, char *str_list[],
+ const char *val, GSGDialogElement * elem, GtkTooltips *tooltips, const char *desc, SANE_Int settable);
+extern void xsane_back_gtk_text_entry_new(GtkWidget *parent, const char *name, const char *val,
+ GSGDialogElement *elem, GtkTooltips *tooltips, const char *desc, SANE_Int settable);
+extern void xsane_back_gtk_push_button_callback(GtkWidget * widget, gpointer data);
+extern const char *xsane_back_gtk_unit_string(SANE_Unit unit);
+void xsane_set_window_icon(GtkWidget *gtk_window, gchar **xpm_d);
+
+#define xsane_back_gtk_dialog_get_device(dialog) ((dialog)->dev)
+
+#endif /* gtkglue_h */
diff --git a/frontend/xsane-device-preferences.c b/frontend/xsane-device-preferences.c
new file mode 100644
index 0000000..cce2007
--- /dev/null
+++ b/frontend/xsane-device-preferences.c
@@ -0,0 +1,699 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-device-preferences.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+#include "xsane-rc-io.h"
+#include "xsane-front-gtk.h"
+#include "xsane-gamma.h"
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+#define BITS_PER_LONG (8*sizeof(u_long))
+
+#define SET(set, bit) ((set)[(bit)/BITS_PER_LONG] |= (1UL << (bit)%BITS_PER_LONG))
+#define IS_SET(set, bit) (((set)[(bit)/BITS_PER_LONG] & (1UL << (bit)%BITS_PER_LONG)) != 0)
+
+#define DPOFFSET(field) ((char *) &((Xsane *) 0)->field - (char *) 0)
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+static struct
+{
+ SANE_String name;
+ void (*codec) (Wire *w, void *p, long offset);
+ long offset;
+}
+desc_xsane_device[] =
+{
+ {"xsane-main-window-x-position", xsane_rc_pref_int, DPOFFSET(shell_posx)},
+ {"xsane-main-window-y-position", xsane_rc_pref_int, DPOFFSET(shell_posy)},
+ {"xsane-main-window-width", xsane_rc_pref_int, DPOFFSET(shell_width)},
+ {"xsane-main-window-height", xsane_rc_pref_int, DPOFFSET(shell_height)},
+ {"xsane-standard-options-window-x-position", xsane_rc_pref_int, DPOFFSET(standard_options_shell_posx)},
+ {"xsane-standard-options-window-y-position", xsane_rc_pref_int, DPOFFSET(standard_options_shell_posy)},
+ {"xsane-advanced-options-window-x-position", xsane_rc_pref_int, DPOFFSET(advanced_options_shell_posx)},
+ {"xsane-advanced-options-window-y-position", xsane_rc_pref_int, DPOFFSET(advanced_options_shell_posy)},
+ {"xsane-histogram-window-x-position", xsane_rc_pref_int, DPOFFSET(histogram_dialog_posx)},
+ {"xsane-histogram-window-y-position", xsane_rc_pref_int, DPOFFSET(histogram_dialog_posy)},
+ {"xsane-preview-window-x-position", xsane_rc_pref_int, DPOFFSET(preview_dialog_posx)},
+ {"xsane-preview-window-y-position", xsane_rc_pref_int, DPOFFSET(preview_dialog_posy)},
+ {"xsane-preview-window-width", xsane_rc_pref_int, DPOFFSET(preview_dialog_width)},
+ {"xsane-preview-window-height", xsane_rc_pref_int, DPOFFSET(preview_dialog_height)},
+
+ {"xsane-gamma", xsane_rc_pref_double, DPOFFSET(gamma)},
+ {"xsane-gamma-red", xsane_rc_pref_double, DPOFFSET(gamma_red)},
+ {"xsane-gamma-green", xsane_rc_pref_double, DPOFFSET(gamma_green)},
+ {"xsane-gamma-blue", xsane_rc_pref_double, DPOFFSET(gamma_blue)},
+
+ {"xsane-brightness", xsane_rc_pref_double, DPOFFSET(brightness)},
+ {"xsane-brightness-red", xsane_rc_pref_double, DPOFFSET(brightness_red)},
+ {"xsane-brightness-green", xsane_rc_pref_double, DPOFFSET(brightness_green)},
+ {"xsane-brightness-blue", xsane_rc_pref_double, DPOFFSET(brightness_blue)},
+
+ {"xsane-contrast", xsane_rc_pref_double, DPOFFSET(contrast)},
+ {"xsane-contrast-red", xsane_rc_pref_double, DPOFFSET(contrast_red)},
+ {"xsane-contrast-green", xsane_rc_pref_double, DPOFFSET(contrast_green)},
+ {"xsane-contrast-blue", xsane_rc_pref_double, DPOFFSET(contrast_blue)},
+
+ {"xsane-enhancement-rgb-default", xsane_rc_pref_int, DPOFFSET(enhancement_rgb_default)},
+ {"xsane-negative", xsane_rc_pref_int, DPOFFSET(negative)},
+ {"xsane-show-preview", xsane_rc_pref_int, DPOFFSET(show_preview)},
+};
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_widget_get_uposition(GtkWidget *gtk_window, gint *x, gint *y)
+{
+#ifdef XSANE_BUGGY_WINDOWMANAGER_WINDOW_POSITION
+ gdk_window_get_root_origin(gtk_window->window, x, y);
+#else
+ gdk_window_get_deskrelative_origin(gtk_window->window, x, y);
+#endif
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_device_preferences_load_values(Wire *w, SANE_Handle device)
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Word *word_array;
+ SANE_String name, str;
+ u_long *caused_reload;
+ SANE_Int num_options;
+ SANE_Status status;
+ int i, keep_going;
+ SANE_Word word;
+ SANE_Int info;
+ off_t offset;
+ size_t size;
+ char *buf;
+
+ lseek(w->io.fd, 1, SEEK_SET); /* rewind file */
+ xsane_rc_io_w_flush(w);
+
+ offset = lseek(w->io.fd, 0, SEEK_CUR); /* remeber file position */
+
+ keep_going = 0;
+
+ sane_control_option(device, 0, SANE_ACTION_GET_VALUE, &num_options, 0);
+ size = (num_options + BITS_PER_LONG - 1) / BITS_PER_LONG * sizeof(long);
+ caused_reload = alloca(size);
+ memset(caused_reload, 0, size);
+
+ while (1)
+ {
+ xsane_rc_io_w_space(w, 3);
+ if (!w->status)
+ {
+ xsane_rc_io_w_string(w, &name);
+ }
+
+ if (w->status == XSANE_EOF) /* eof */
+ {
+ if (keep_going) /* we had a reload otpions? */
+ {
+ lseek(w->io.fd, offset, SEEK_SET); /* rewind file to position of first run */
+ xsane_rc_io_w_flush(w);
+ keep_going = 0;
+ continue;
+ }
+ return 0;
+ }
+ else if (w->status) /* error: skip line */
+ {
+ w->status = 0;
+ xsane_rc_io_w_free(w, (WireCodecFunc) xsane_rc_io_w_string, &name); /* free string memory */
+ xsane_rc_io_w_skip_newline(w); /* skip this line */
+ continue;
+ }
+
+ status = SANE_STATUS_GOOD;
+ info = 0;
+ for (i = 1; (opt = sane_get_option_descriptor(device, i)); ++i) /* search all options */
+ {
+ if (!opt->name || strcmp(opt->name, name) != 0) /* test if option names are equal */
+ {
+ continue; /* not equal, continue the search */
+ }
+
+ if (IS_SET(caused_reload, i))
+ {
+ continue; /* option caused a reload, continue search ??? why? ??? */
+ }
+
+ switch (opt->type)
+ {
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ if (opt->size == sizeof(SANE_Word))
+ {
+ xsane_rc_io_w_word(w, &word);
+ status = sane_control_option(device, i, SANE_ACTION_SET_VALUE, &word, &info);
+ }
+ else
+ {
+ SANE_Int len;
+
+ xsane_rc_io_w_array(w, &len, (void **) &word_array, (WireCodecFunc) xsane_rc_io_w_word, sizeof(SANE_Word));
+ status = sane_control_option(device, i, SANE_ACTION_SET_VALUE, word_array, &info);
+ w->direction = WIRE_FREE;
+ xsane_rc_io_w_array(w, &len, (void **) &word_array, (WireCodecFunc) xsane_rc_io_w_word, sizeof(SANE_Word));
+ w->direction = WIRE_DECODE;
+ }
+ break;
+
+ case SANE_TYPE_STRING:
+ xsane_rc_io_w_string(w, &str);
+ buf = malloc(opt->size);
+ if (!w->status) /* got a string ? */
+ {
+ strncpy(buf, str, opt->size);
+ buf[opt->size - 1] = '\0';
+ xsane_rc_io_w_free(w, (WireCodecFunc) xsane_rc_io_w_string, &str);
+ status = sane_control_option(device, i, SANE_ACTION_SET_VALUE, buf, &info);
+ }
+ break;
+
+ case SANE_TYPE_BUTTON:
+ case SANE_TYPE_GROUP:
+ /* nothing to read for button and group */
+ break;
+ }
+ break; /* option is set: do not continue search */
+ }
+ xsane_rc_io_w_free(w, (WireCodecFunc) xsane_rc_io_w_string, &name); /* free string memory */
+
+ if (status == SANE_STATUS_GOOD && (info & SANE_INFO_RELOAD_OPTIONS))
+ {
+ SET(caused_reload, i);
+ keep_going = 1;
+ }
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_device_preferences_save_values(Wire *w, SANE_Handle device)
+{
+ const SANE_Option_Descriptor *opt;
+ size_t word_array_size = 0;
+ SANE_Word *word_array = 0;
+ size_t str_size = 0;
+ SANE_String str = 0;
+ SANE_Word word;
+ int i;
+
+ for (i = 0; (opt = sane_get_option_descriptor(device, i)); ++i)
+ {
+ if ((opt->cap & (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT)) != (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT) || !opt->name)
+ /* if we can't query AND set the option, don't bother saving it */
+ {
+ continue;
+ }
+
+ switch (opt->type)
+ {
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ if (opt->size == sizeof(SANE_Word))
+ {
+ if (sane_control_option(device, i, SANE_ACTION_GET_VALUE, &word, 0) != SANE_STATUS_GOOD)
+ {
+ continue;
+ }
+ xsane_rc_io_w_string(w, (SANE_String *) &opt->name);
+ xsane_rc_io_w_word(w, &word);
+ }
+ else
+ {
+ SANE_Int len = opt->size / sizeof(SANE_Word);
+
+ if (opt->size > word_array_size)
+ {
+ word_array_size = ((opt->size + 32*sizeof(SANE_Word)) & ~(32*sizeof(SANE_Word) - 1));
+ if (word_array)
+ {
+ word_array = realloc(word_array, word_array_size);
+ }
+ else
+ {
+ word_array = malloc(word_array_size);
+ }
+
+ if (word_array == 0)
+ {
+ /* Malloc failed, so return an error. */
+ w->status = ENOMEM;
+ return 1;
+ }
+ }
+
+ if (sane_control_option(device, i, SANE_ACTION_GET_VALUE, word_array, 0) != SANE_STATUS_GOOD)
+ {
+ continue;
+ }
+
+ xsane_rc_io_w_string(w, (SANE_String *) &opt->name);
+ xsane_rc_io_w_array(w, &len, (void **) &word_array, (WireCodecFunc) xsane_rc_io_w_word, sizeof(SANE_Word));
+ }
+ break;
+
+ case SANE_TYPE_STRING:
+ if (opt->size > str_size)
+ {
+ str_size = (opt->size + 1024) & ~1023;
+
+ if (str)
+ {
+ str = realloc(str, str_size);
+ }
+ else
+ {
+ str = malloc(str_size);
+ }
+
+ if (str == 0)
+ {
+ /* Malloc failed, so return an error. */
+ w->status = ENOMEM;
+ return 1;
+ }
+ }
+
+ if (sane_control_option(device, i, SANE_ACTION_GET_VALUE, str, 0) != SANE_STATUS_GOOD)
+ {
+ continue;
+ }
+
+ xsane_rc_io_w_string(w, (SANE_String *) &opt->name);
+ xsane_rc_io_w_string(w, &str);
+ break;
+
+ case SANE_TYPE_BUTTON:
+ case SANE_TYPE_GROUP:
+ break;
+ }
+ }
+
+ if (word_array)
+ {
+ free(word_array);
+ }
+ if (str)
+ {
+ free(str);
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_device_preferences_load_file(char *filename)
+{
+ int fd;
+ char buf[256];
+#if 0
+ char *version = 0;
+#endif
+ Wire w;
+ SANE_String name;
+ int i;
+
+ /* set geometry and position to standard values */
+ xsane.shell_posx = XSANE_SHELL_POS_X;
+ xsane.shell_posy = XSANE_SHELL_POS_Y;
+ xsane.shell_width = XSANE_SHELL_WIDTH;
+ xsane.shell_height = XSANE_SHELL_HEIGHT;
+
+ xsane.standard_options_shell_posx = XSANE_STD_OPTIONS_POS_X;
+ xsane.standard_options_shell_posy = XSANE_STD_OPTIONS_POS_Y;
+
+ xsane.advanced_options_shell_posx = XSANE_ADV_OPTIONS_POS_X;
+ xsane.advanced_options_shell_posy = XSANE_ADV_OPTIONS_POS_Y;
+
+ xsane.histogram_dialog_posx = XSANE_HISTOGRAM_POS_X;
+ xsane.histogram_dialog_posy = XSANE_HISTOGRAM_POS_Y;
+
+ xsane.preview_dialog_posx = XSANE_PREVIEW_POS_X;
+ xsane.preview_dialog_posy = XSANE_PREVIEW_POS_Y;
+ xsane.preview_dialog_width = XSANE_PREVIEW_WIDTH;
+ xsane.preview_dialog_height = XSANE_PREVIEW_HEIGHT;
+
+ xsane.gamma = 1.0;
+ xsane.gamma_red = 1.0;
+ xsane.gamma_green = 1.0;
+ xsane.gamma_blue = 1.0;
+
+ xsane.brightness = 0.0;
+ xsane.brightness_red = 0.0;
+ xsane.brightness_green = 0.0;
+ xsane.brightness_blue = 0.0;
+
+ xsane.contrast = 0.0;
+ xsane.contrast_red = 0.0;
+ xsane.contrast_green = 0.0;
+ xsane.contrast_blue = 0.0;
+
+ xsane.enhancement_rgb_default = 1;
+ xsane.negative = 0;
+ xsane.show_preview = 1;
+
+ fd = open(filename, O_RDONLY);
+ if (fd >= 0)
+ {
+ /* prepare wire */
+ w.io.fd = fd;
+ w.io.read = read;
+ w.io.write = write;
+ xsane_rc_io_w_init(&w);
+ xsane_rc_io_w_set_dir(&w, WIRE_DECODE);
+
+ xsane_rc_io_w_space(&w, 3);
+ if (!w.status)
+ {
+ xsane_rc_io_w_string(&w, &name); /* get string */
+ if (!w.status)
+ {
+ if (strcmp(name, "XSANE_DEVICE_RC")) /* no real *.drc file */
+ {
+ w.status = -1; /* no *.drc file => error */
+ }
+ }
+ }
+
+ if (w.status)
+ {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%s\n%s %s", ERR_LOAD_DEVICE_SETTINGS, filename, ERR_NO_DRC_FILE);
+ xsane_back_gtk_error(buf, TRUE);
+ close(fd);
+ return;
+ }
+
+ xsane_rc_io_w_space(&w, 3);
+ if (!w.status)
+ {
+ xsane_rc_io_w_string(&w, &name); /* get string */
+ if (!w.status)
+ {
+ if (strcmp(name, xsane.device_set_filename))
+ {
+ snprintf(buf, sizeof(buf), "%s \"%s\"\n"
+ "%s \"%s\",\n"
+ "%s \"%s\",\n"
+ "%s",
+ TEXT_FILE, filename,
+ ERR_CREATED_FOR_DEVICE, name,
+ ERR_USED_FOR_DEVICE, xsane.device_set_filename,
+ ERR_MAY_CAUSE_PROBLEMS);
+ if (xsane_back_gtk_decision(ERR_HEADER_WARNING, (gchar **) error_xpm, buf, ERR_BUTTON_OK, BUTTON_CANCEL, TRUE) == FALSE)
+ { /* cancel */
+ close(fd);
+ return;
+ }
+ }
+ }
+ }
+
+ if (w.status)
+ {
+ /* may be we should pop up a window here */
+ close(fd);
+ return;
+ }
+
+
+#if 0
+/* add here: read version info */
+#if 0
+ while (!feof(file))
+ {
+ fgets(option, sizeof(option), file); /* get option name */
+ option[strlen(option)-1] = 0; /* remove cr */
+ if (strcmp(option, "\"xsane-version\"") == 0)
+ {
+ fgets(option, sizeof(option), file); /* get version */
+ option[strlen(option)-1] = 0; /* remove cr */
+ len = strlen(option);
+ if (len)
+ {
+ if (option[len-1] == 34)
+ {
+ option[len-1] = 0; /* remove " */
+ }
+ }
+ version = strdup(option+1);
+ }
+ else
+ {
+ fgets(option, sizeof(option), file); /* skip option */
+ }
+ }
+#endif
+
+
+ if (version)
+ {
+ if (strcmp(version, XSANE_VERSION))
+ {
+ snprintf(buf, sizeof(buf), "File: \"%s\"\n"
+ "has been saved with xsane-%s,\n"
+ "this may cause problems!", filename, version);
+ xsane_back_gtk_warning(buf, TRUE);
+ }
+ free(version);
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), "File: \"%s\"\n"
+ "has been saved with xsane before version 0.40,\n"
+ "this may cause problems!", filename);
+ xsane_back_gtk_warning(buf, TRUE);
+ }
+#endif
+
+
+
+ while (1) /* read device dependant xsane options */
+ {
+ xsane_rc_io_w_space(&w, 3);
+ if (w.status)
+ {
+ break;
+ }
+
+ xsane_rc_io_w_string(&w, &name);
+
+ if (!w.status && name)
+ {
+ for (i = 0; i < NELEMS (desc_xsane_device); ++i)
+ {
+ if (strcmp(name, desc_xsane_device[i].name) == 0)
+ {
+ (*desc_xsane_device[i].codec) (&w, &xsane, desc_xsane_device[i].offset);
+ break; /* leave for loop */
+ }
+ }
+ }
+ w.status = 0;
+ }
+
+ xsane_device_preferences_load_values(&w, dialog->dev); /* read device preferences */
+ close(fd);
+
+ if (dialog->well_known.dpi > 0)
+ {
+ const SANE_Option_Descriptor *opt;
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.dpi);
+
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ {
+ SANE_Int dpi;
+ sane_control_option(dialog->dev, dialog->well_known.dpi, SANE_ACTION_GET_VALUE, &dpi, 0);
+ xsane.resolution = dpi;
+ }
+ break;
+
+ case SANE_TYPE_FIXED:
+ {
+ SANE_Fixed dpi;
+ sane_control_option(dialog->dev, dialog->well_known.dpi, SANE_ACTION_GET_VALUE, &dpi, 0);
+ xsane.resolution = (int) SANE_UNFIX(dpi);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "xsane_pref_load_file: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ return;
+ }
+ }
+ }
+
+ gtk_widget_set_uposition(xsane.shell, xsane.shell_posx, xsane.shell_posy);
+ gtk_window_set_default_size(GTK_WINDOW(xsane.shell), xsane.shell_width, xsane.shell_height);
+ gtk_widget_set_uposition(xsane.standard_options_shell, xsane.standard_options_shell_posx, xsane.standard_options_shell_posy);
+ gtk_widget_set_uposition(xsane.advanced_options_shell, xsane.advanced_options_shell_posx, xsane.advanced_options_shell_posy);
+ gtk_widget_set_uposition(xsane.histogram_dialog, xsane.histogram_dialog_posx, xsane.histogram_dialog_posy);
+ gtk_widget_set_uposition(xsane.preview->top, xsane.preview_dialog_posx, xsane.preview_dialog_posy);
+ gtk_window_set_default_size(GTK_WINDOW(xsane.preview->top), xsane.preview_dialog_width, xsane.preview_dialog_height);
+
+ xsane_refresh_dialog(dialog);
+ xsane_enhancement_by_gamma();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_device_preferences_restore(void)
+{
+ char filename[PATH_MAX];
+ struct stat st;
+
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, 0, xsane.device_set_filename, ".drc", XSANE_PATH_LOCAL_SANE);
+
+ if (stat(filename, &st) >= 0)
+ {
+ xsane_device_preferences_load_file(filename);
+ }
+ else /* no local sane file, look for system file */
+ {
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, 0, xsane.device_set_filename, ".drc", XSANE_PATH_SYSTEM);
+ xsane_device_preferences_load_file(filename);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_device_preferences_load(void)
+{
+ char filename[PATH_MAX];
+ char windowname[256];
+
+ xsane_set_sensitivity(FALSE);
+
+ sprintf(windowname, "%s %s %s", prog_name, WINDOW_LOAD_SETTINGS, device_text);
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, 0, xsane.device_set_filename, ".drc", XSANE_PATH_LOCAL_SANE);
+ xsane_back_gtk_get_filename(windowname, filename, sizeof(filename), filename, FALSE);
+ xsane_device_preferences_load_file(filename);
+ xsane_set_sensitivity(TRUE);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#define XSANE_RC_IO_W_STRINGCONST(wire, string) { SANE_String str=string; xsane_rc_io_w_string(wire, &str); }
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_device_preferences_save(GtkWidget *widget, gpointer data)
+{
+ char filename[PATH_MAX];
+ char windowname[256];
+ int fd;
+ Wire w;
+ int i;
+
+ xsane_set_sensitivity(FALSE);
+
+ sprintf(windowname, "%s %s %s", prog_name, WINDOW_SAVE_SETTINGS, device_text);
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, 0, xsane.device_set_filename, ".drc", XSANE_PATH_LOCAL_SANE);
+ xsane_back_gtk_get_filename(windowname, filename, sizeof(filename), filename, FALSE);
+
+ umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_CREATE_FILE, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ xsane_set_sensitivity(TRUE);
+ return;
+ }
+
+ /* prepare wire */
+ w.io.fd = fd;
+ w.io.read = read;
+ w.io.write = write;
+ xsane_rc_io_w_init(&w);
+ xsane_rc_io_w_set_dir(&w, WIRE_ENCODE);
+
+ XSANE_RC_IO_W_STRINGCONST(&w, "XSANE_DEVICE_RC");
+ xsane_rc_io_w_string(&w, &xsane.device_set_filename);
+
+ XSANE_RC_IO_W_STRINGCONST(&w, "xsane-version");
+ XSANE_RC_IO_W_STRINGCONST(&w, XSANE_VERSION);
+
+ /* make geometry and position values up to date */
+ xsane_widget_get_uposition(xsane.shell, &xsane.shell_posx, &xsane.shell_posy);
+#if 0
+/* other possibillities: */
+ gdk_window_get_deskrelative_origin(xsane.shell->window, &xsane.shell_posx, &xsane.shell_posy);
+ gdk_window_get_origin(xsane.shell->window, &xsane.shell_posx, &xsane.shell_posy);
+ gdk_window_get_position(xsane.shell->window, &xsane.shell_posx, &xsane.shell_posy);
+ gdk_window_get_geometry(xsane.shell->window, ?);
+#endif
+ gdk_window_get_size(xsane.shell->window, &xsane.shell_width, &xsane.shell_height);
+ gtk_widget_set_uposition(xsane.shell, xsane.shell_posx, xsane.shell_posy); /* geometry used when window closed and opened again */
+ gtk_window_set_default_size(GTK_WINDOW(xsane.shell), xsane.shell_width, xsane.shell_height);
+
+ xsane_widget_get_uposition(xsane.standard_options_shell, &xsane.standard_options_shell_posx, &xsane.standard_options_shell_posy);
+ gtk_widget_set_uposition(xsane.standard_options_shell, xsane.standard_options_shell_posx, xsane.standard_options_shell_posy);
+
+ xsane_widget_get_uposition(xsane.advanced_options_shell, &xsane.advanced_options_shell_posx, &xsane.advanced_options_shell_posy);
+ gtk_widget_set_uposition(xsane.advanced_options_shell, xsane.advanced_options_shell_posx, xsane.advanced_options_shell_posy);
+
+ xsane_widget_get_uposition(xsane.histogram_dialog, &xsane.histogram_dialog_posx, &xsane.histogram_dialog_posy);
+ gtk_widget_set_uposition(xsane.histogram_dialog, xsane.histogram_dialog_posx, xsane.histogram_dialog_posy);
+
+ if (xsane.preview)
+ {
+ xsane_widget_get_uposition(xsane.preview->top, &xsane.preview_dialog_posx, &xsane.preview_dialog_posy);
+ gdk_window_get_size(xsane.preview->top->window, &xsane.preview_dialog_width, &xsane.preview_dialog_height);
+ gtk_widget_set_uposition(xsane.preview->top, xsane.preview_dialog_posx, xsane.preview_dialog_posy);
+ gtk_window_set_default_size(GTK_WINDOW(xsane.preview->top), xsane.preview_dialog_width, xsane.preview_dialog_height);
+ }
+
+ xsane_device_preferences_save_values(&w, dialog->dev);
+
+ for (i = 0; i < NELEMS(desc_xsane_device); ++i) /* save device preferences xsane values */
+ {
+ xsane_rc_io_w_string(&w, &desc_xsane_device[i].name);
+ (*desc_xsane_device[i].codec) (&w, &xsane, desc_xsane_device[i].offset);
+ }
+
+ xsane_rc_io_w_flush(&w);
+ close(fd);
+
+ xsane_set_sensitivity(TRUE);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
diff --git a/frontend/xsane-device-preferences.c.old b/frontend/xsane-device-preferences.c.old
new file mode 100644
index 0000000..aa4d8b8
--- /dev/null
+++ b/frontend/xsane-device-preferences.c.old
@@ -0,0 +1,761 @@
+
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1999 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+#include "xsane-rc-io.h"
+#include "xsane-front-gtk.h"
+#include "xsane-gamma.h"
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+#define BITS_PER_LONG (8*sizeof(u_long))
+
+#define SET(set, bit) ((set)[(bit)/BITS_PER_LONG] |= (1UL << (bit)%BITS_PER_LONG))
+#define IS_SET(set, bit) (((set)[(bit)/BITS_PER_LONG] & (1UL << (bit)%BITS_PER_LONG)) != 0)
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+int xsane_device_preferences_load_values(int fd, SANE_Handle device)
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Word *word_array;
+ SANE_String name, str;
+ u_long *caused_reload;
+ SANE_Int num_options;
+ SANE_Status status;
+ int i, keep_going;
+ SANE_Word word;
+ SANE_Int info;
+ off_t offset;
+ size_t size;
+ char *buf;
+ Wire w;
+
+ offset = lseek(fd, 0, SEEK_CUR);
+ w.io.fd = fd;
+ w.io.read = read;
+ w.io.write = write;
+ xsane_rc_io_w_init(&w);
+ xsane_rc_io_w_set_dir(&w, WIRE_DECODE);
+ keep_going = 0;
+
+ sane_control_option(device, 0, SANE_ACTION_GET_VALUE, &num_options, 0);
+ size = (num_options + BITS_PER_LONG - 1) / BITS_PER_LONG * sizeof(long);
+ caused_reload = alloca(size);
+ memset(caused_reload, 0, size);
+
+ while (1)
+ {
+ xsane_rc_io_w_space(&w, 3);
+
+ if (!w.status)
+ {
+ xsane_rc_io_w_string(&w, &name);
+ }
+
+ if (w.status)
+ {
+ if (keep_going)
+ {
+ lseek(fd, offset, SEEK_SET);
+ xsane_rc_io_w_set_dir(&w, WIRE_DECODE);
+ keep_going = 0;
+ continue;
+ }
+ return 0;
+ }
+
+ status = SANE_STATUS_GOOD;
+ info = 0;
+ for (i = 1; (opt = sane_get_option_descriptor(device, i)); ++i)
+ {
+ if (!opt->name || strcmp(opt->name, name) != 0)
+ {
+ continue;
+ }
+
+ if (IS_SET(caused_reload, i))
+ {
+ continue;
+ }
+
+ switch (opt->type)
+ {
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ if (opt->size == sizeof(SANE_Word))
+ {
+ xsane_rc_io_w_word(&w, &word);
+ status = sane_control_option(device, i, SANE_ACTION_SET_VALUE, &word, &info);
+ }
+ else
+ {
+ SANE_Int len;
+
+ xsane_rc_io_w_array(&w, &len, (void **) &word_array, (WireCodecFunc) xsane_rc_io_w_word, sizeof(SANE_Word));
+ status = sane_control_option(device, i, SANE_ACTION_SET_VALUE, word_array, &info);
+ w.direction = WIRE_FREE;
+ xsane_rc_io_w_array(&w, &len, (void **) &word_array, (WireCodecFunc) xsane_rc_io_w_word, sizeof(SANE_Word));
+ w.direction = WIRE_DECODE;
+ }
+ break;
+
+ case SANE_TYPE_STRING:
+ xsane_rc_io_w_string(&w, &str);
+ buf = malloc(opt->size);
+ if (!w.status) /* got a string ? */
+ {
+ strncpy(buf, str, opt->size);
+ buf[opt->size - 1] = '\0';
+ xsane_rc_io_w_free(&w, (WireCodecFunc) xsane_rc_io_w_string, &str);
+ status = sane_control_option(device, i, SANE_ACTION_SET_VALUE, buf, &info);
+ }
+ break;
+
+ case SANE_TYPE_BUTTON:
+ case SANE_TYPE_GROUP:
+ /* nothing to read for button and group */
+ break;
+ }
+ break;
+ }
+ xsane_rc_io_w_free(&w, (WireCodecFunc) xsane_rc_io_w_string, &name);
+
+ if (status == SANE_STATUS_GOOD && (info & SANE_INFO_RELOAD_OPTIONS))
+ {
+ SET(caused_reload, i);
+ keep_going = 1;
+ }
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+int xsane_device_preferences_save_values(int fd, SANE_Handle device)
+{
+ const SANE_Option_Descriptor *opt;
+ size_t word_array_size = 0;
+ SANE_Word *word_array = 0;
+ size_t str_size = 0;
+ SANE_String str = 0;
+ SANE_Word word;
+ Wire w;
+ int i;
+
+ w.io.fd = fd;
+ w.io.read = read;
+ w.io.write = write;
+ xsane_rc_io_w_init(&w);
+ xsane_rc_io_w_set_dir(&w, WIRE_ENCODE);
+
+ for (i = 0; (opt = sane_get_option_descriptor(device, i)); ++i)
+ {
+ if ((opt->cap & (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT)) != (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT) || !opt->name)
+ /* if we can't query AND set the option, don't bother saving it */
+ {
+ continue;
+ }
+
+ switch (opt->type)
+ {
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ if (opt->size == sizeof(SANE_Word))
+ {
+ if (sane_control_option(device, i, SANE_ACTION_GET_VALUE, &word, 0) != SANE_STATUS_GOOD)
+ {
+ continue;
+ }
+ xsane_rc_io_w_string(&w, (SANE_String *) &opt->name);
+ xsane_rc_io_w_word(&w, &word);
+ }
+ else
+ {
+ SANE_Int len = opt->size / sizeof(SANE_Word);
+
+ if (opt->size > word_array_size)
+ {
+ word_array_size = ((opt->size + 32*sizeof(SANE_Word)) & ~(32*sizeof(SANE_Word) - 1));
+ if (word_array)
+ {
+ word_array = realloc(word_array, word_array_size);
+ }
+ else
+ {
+ word_array = malloc(word_array_size);
+ }
+
+ if (word_array == 0)
+ {
+ /* Malloc failed, so return an error. */
+ w.status = ENOMEM;
+ return 1;
+ }
+ }
+
+ if (sane_control_option(device, i, SANE_ACTION_GET_VALUE, word_array, 0) != SANE_STATUS_GOOD)
+ {
+ continue;
+ }
+
+ xsane_rc_io_w_string(&w, (SANE_String *) &opt->name);
+ xsane_rc_io_w_array(&w, &len, (void **) &word_array, (WireCodecFunc) xsane_rc_io_w_word, sizeof(SANE_Word));
+ }
+ break;
+
+ case SANE_TYPE_STRING:
+ if (opt->size > str_size)
+ {
+ str_size = (opt->size + 1024) & ~1023;
+
+ if (str)
+ {
+ str = realloc(str, str_size);
+ }
+ else
+ {
+ str = malloc(str_size);
+ }
+
+ if (str == 0)
+ {
+ /* Malloc failed, so return an error. */
+ w.status = ENOMEM;
+ return 1;
+ }
+ }
+
+ if (sane_control_option(device, i, SANE_ACTION_GET_VALUE, str, 0) != SANE_STATUS_GOOD)
+ {
+ continue;
+ }
+
+ xsane_rc_io_w_string(&w, (SANE_String *) &opt->name);
+ xsane_rc_io_w_string(&w, &str);
+ break;
+
+ case SANE_TYPE_BUTTON:
+ case SANE_TYPE_GROUP:
+ break;
+ }
+ }
+ xsane_rc_io_w_set_dir(&w, WIRE_DECODE);
+
+ if (word_array)
+ {
+ free(word_array);
+ }
+ if (str)
+ {
+ free(str);
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_device_preferences_load_file(char *filename)
+{
+ int fd;
+ FILE *file;
+ char buf[256];
+ char option[256];
+ char *optionp;
+ char *version = 0;
+ int len;
+
+ int main_posx = XSANE_DIALOG_POS_X;
+ int main_posy = XSANE_DIALOG_POS_Y;
+ int main_width = XSANE_DIALOG_WIDTH;
+ int main_height = XSANE_DIALOG_HEIGHT;
+
+ int standard_options_posx = XSANE_DIALOG_POS_X;
+ int standard_options_posy = XSANE_DIALOG_POS_Y2;
+
+ int advanced_options_posx = XSANE_DIALOG_POS_X2;
+ int advanced_options_posy = XSANE_DIALOG_POS_Y2;
+
+ int histogram_posx = XSANE_DIALOG_POS_X2;
+ int histogram_posy = XSANE_DIALOG_POS_Y;
+
+ int preview_posx = 0;
+ int preview_posy = 0;
+ int preview_width = 0;
+ int preview_height = 0;
+
+ file = fopen(filename, "r");
+ if (file == 0) /* error ? */
+ {
+ return;
+ }
+
+ if (!feof(file))
+ {
+ fgets(option, sizeof(option), file); /* get first line */
+ option[strlen(option)-1] = 0; /* remove cr */
+
+ if (strcmp(option, "\"XSANE_DEVICE_RC\"") != 0) /* wrong file format ? */
+ {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%s\n%s %s", ERR_LOAD_DEVICE_SETTINGS, filename, ERR_NO_DRC_FILE);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ if (!feof(file))
+ {
+ fgets(option, sizeof(option), file); /* get version */
+ option[strlen(option)-1] = 0; /* remove cr */
+ len = strlen(option);
+ if (len)
+ {
+ if (option[len-1] == 34)
+ {
+ option[len-1] = 0; /* remove " */
+ }
+ }
+ optionp = option+1;
+
+ if (strcmp(optionp, xsane.device_set_filename))
+ {
+ snprintf(buf, sizeof(buf), "%s \"%s\"\n"
+ "%s \"%s\",\n"
+ "%s \"%s\",\n"
+ "%s",
+ TEXT_FILE, filename,
+ ERR_CREATED_FOR_DEVICE, optionp,
+ ERR_USED_FOR_DEVICE, xsane.device_set_filename,
+ ERR_MAY_CAUSE_PROBLEMS);
+ if (xsane_back_gtk_decision(ERR_HEADER_WARNING, buf, ERR_BUTTON_OK, BUTTON_CANCEL, TRUE) == FALSE)
+ { /* cancel */
+ fclose(file);
+ return;
+ }
+ }
+ }
+ }
+
+ while (!feof(file))
+ {
+ fgets(option, sizeof(option), file); /* get option name */
+ option[strlen(option)-1] = 0; /* remove cr */
+ if (strcmp(option, "\"xsane-version\"") == 0)
+ {
+ fgets(option, sizeof(option), file); /* get version */
+ option[strlen(option)-1] = 0; /* remove cr */
+ len = strlen(option);
+ if (len)
+ {
+ if (option[len-1] == 34)
+ {
+ option[len-1] = 0; /* remove " */
+ }
+ }
+ version = strdup(option+1);
+ }
+ else if (strcmp(option, "\"xsane-gamma\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.gamma);
+ }
+ else if (strcmp(option, "\"xsane-gamma-red\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.gamma_red);
+ }
+ else if (strcmp(option, "\"xsane-gamma-green\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.gamma_green);
+ }
+ else if (strcmp(option, "\"xsane-gamma-blue\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.gamma_blue);
+ }
+ else if (strcmp(option, "\"xsane-brightness\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.brightness);
+ }
+ else if (strcmp(option, "\"xsane-brightness-red\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.brightness_red);
+ }
+ else if (strcmp(option, "\"xsane-brightness-green\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.brightness_green);
+ }
+ else if (strcmp(option, "\"xsane-brightness-blue\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.brightness_blue);
+ }
+ else if (strcmp(option, "\"xsane-contrast\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.contrast);
+ }
+ else if (strcmp(option, "\"xsane-contrast-red\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.contrast_red);
+ }
+ else if (strcmp(option, "\"xsane-contrast-green\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.contrast_green);
+ }
+ else if (strcmp(option, "\"xsane-contrast-blue\"") == 0)
+ {
+ fscanf(file, "%lf\n", &xsane.contrast_blue);
+ }
+ else if (strcmp(option, "\"xsane-enhancement-rgb-default\"") == 0)
+ {
+ fscanf(file, "%d\n", &xsane.enhancement_rgb_default);
+ }
+ else if (strcmp(option, "\"xsane-negative\"") == 0)
+ {
+ fscanf(file, "%d\n", &xsane.negative);
+ }
+ else if (strcmp(option, "\"xsane-show-preview\"") == 0)
+ {
+ fscanf(file, "%d\n", &xsane.show_preview);
+ }
+ else if (strcmp(option, "\"xsane-main-window-x-position\"") == 0)
+ {
+ fscanf(file, "%d\n", &main_posx);
+ }
+ else if (strcmp(option, "\"xsane-main-window-y-position\"") == 0)
+ {
+ fscanf(file, "%d\n", &main_posy);
+ }
+ else if (strcmp(option, "\"xsane-main-window-width\"") == 0)
+ {
+ fscanf(file, "%d\n", &main_width);
+ }
+ else if (strcmp(option, "\"xsane-main-window-height\"") == 0)
+ {
+ fscanf(file, "%d\n", &main_height);
+ }
+ else if (strcmp(option, "\"xsane-standard-options-window-x-position\"") == 0)
+ {
+ fscanf(file, "%d\n", &standard_options_posx);
+ }
+ else if (strcmp(option, "\"xsane-standard-options-window-y-position\"") == 0)
+ {
+ fscanf(file, "%d\n", &standard_options_posy);
+ }
+ else if (strcmp(option, "\"xsane-advanced-options-window-x-position\"") == 0)
+ {
+ fscanf(file, "%d\n", &advanced_options_posx);
+ }
+ else if (strcmp(option, "\"xsane-advanced-options-window-y-position\"") == 0)
+ {
+ fscanf(file, "%d\n", &advanced_options_posy);
+ }
+ else if (strcmp(option, "\"xsane-histogram-window-x-position\"") == 0)
+ {
+ fscanf(file, "%d\n", &histogram_posx);
+ }
+ else if (strcmp(option, "\"xsane-histogram-window-y-position\"") == 0)
+ {
+ fscanf(file, "%d\n", &histogram_posy);
+ }
+ else if (strcmp(option, "\"xsane-preview-window-x-position\"") == 0)
+ {
+ fscanf(file, "%d\n", &preview_posx);
+ }
+ else if (strcmp(option, "\"xsane-preview-window-y-position\"") == 0)
+ {
+ fscanf(file, "%d\n", &preview_posy);
+ }
+ else if (strcmp(option, "\"xsane-preview-window-width\"") == 0)
+ {
+ fscanf(file, "%d\n", &preview_width);
+ }
+ else if (strcmp(option, "\"xsane-preview-window-height\"") == 0)
+ {
+ fscanf(file, "%d\n", &preview_height);
+ }
+ else
+ {
+ fgets(option, sizeof(option), file); /* skip option */
+ }
+ }
+ fclose(file);
+
+#if 0
+ if (version)
+ {
+ if (strcmp(version, XSANE_VERSION))
+ {
+ snprintf(buf, sizeof(buf), "File: \"%s\"\n"
+ "has been saved with xsane-%s,\n"
+ "this may cause problems!", filename, version);
+ xsane_back_gtk_warning(buf, TRUE);
+ }
+ free(version);
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), "File: \"%s\"\n"
+ "has been saved with xsane before version 0.40,\n"
+ "this may cause problems!", filename);
+ xsane_back_gtk_warning(buf, TRUE);
+ }
+#endif
+
+
+ gtk_widget_set_uposition(xsane.shell, main_posx, main_posy);
+ gtk_widget_set_uposition(xsane.standard_options_shell, standard_options_posx, standard_options_posy);
+ gtk_widget_set_uposition(xsane.advanced_options_shell, advanced_options_posx, advanced_options_posy);
+ gtk_widget_set_uposition(xsane.histogram_dialog, histogram_posx, histogram_posy);
+
+ if (xsane.preview)
+ {
+ gtk_widget_set_uposition(xsane.preview->top, preview_posx, preview_posy);
+ }
+
+ gtk_window_set_default_size(GTK_WINDOW(xsane.shell), main_width, main_height);
+
+ if (xsane.preview)
+ {
+ gtk_window_set_default_size(GTK_WINDOW(xsane.preview->top), preview_width, preview_height);
+ }
+
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ {
+ return;
+ }
+ xsane_device_preferences_load_values(fd, dialog->dev);
+ close(fd);
+
+ if (dialog->well_known.dpi > 0)
+ {
+ const SANE_Option_Descriptor *opt;
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.dpi);
+
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ {
+ SANE_Int dpi;
+ sane_control_option(dialog->dev, dialog->well_known.dpi, SANE_ACTION_GET_VALUE, &dpi, 0);
+ xsane.resolution = dpi;
+ }
+ break;
+
+ case SANE_TYPE_FIXED:
+ {
+ SANE_Fixed dpi;
+ sane_control_option(dialog->dev, dialog->well_known.dpi, SANE_ACTION_GET_VALUE, &dpi, 0);
+ xsane.resolution = (int) SANE_UNFIX(dpi);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "xsane_pref_load_file: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ return;
+ }
+ }
+
+ xsane_refresh_dialog(dialog);
+ xsane_enhancement_by_gamma();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_device_preferences_restore(void)
+{
+ char filename[PATH_MAX];
+
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, 0, xsane.device_set_filename, ".drc");
+ xsane_device_preferences_load_file(filename);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_device_preferences_load(void)
+{
+ char filename[PATH_MAX];
+ char windowname[256];
+
+ xsane_set_sensitivity(FALSE);
+
+ sprintf(windowname, "%s %s %s", prog_name, WINDOW_LOAD_SETTINGS, device_text);
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, 0, xsane.device_set_filename, ".drc");
+ xsane_back_gtk_get_filename(windowname, filename, sizeof(filename), filename);
+ xsane_device_preferences_load_file(filename);
+ xsane_set_sensitivity(TRUE);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_device_preferences_save(GtkWidget *widget, gpointer data)
+{
+ char filename[PATH_MAX];
+ char windowname[256];
+ int fd;
+ FILE *file;
+ int posx, posy, width, height;
+
+ xsane_set_sensitivity(FALSE);
+
+ sprintf(windowname, "%s %s %s", prog_name, WINDOW_SAVE_SETTINGS, device_text);
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, 0, xsane.device_set_filename, ".drc");
+ xsane_back_gtk_get_filename(windowname, filename, sizeof(filename), filename);
+
+ file = fopen(filename, "w");
+ if (file == 0)
+ {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_CREATE_FILE, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ xsane_set_sensitivity(TRUE);
+ return;
+ }
+
+ fprintf(file, "\"XSANE_DEVICE_RC\"\n");
+ fprintf(file, "\"%s\"\n", xsane.device_set_filename);
+ fclose(file);
+
+ fd = open(filename, O_WRONLY | O_APPEND , 0666);
+ if (fd < 0)
+ {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_CREATE_FILE, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ xsane_set_sensitivity(TRUE);
+ return;
+ }
+ xsane_device_preferences_save_values(fd, dialog->dev);
+ close(fd);
+
+
+ file = fopen(filename, "a");
+ if (file == 0)
+ {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_CREATE_FILE, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ xsane_set_sensitivity(TRUE);
+ return;
+ }
+
+ fprintf(file, "\"xsane-version\"\n");
+ fprintf(file, "\"" XSANE_VERSION "\"\n");
+ fprintf(file, "\"xsane-gamma\"\n");
+ fprintf(file, "%f\n", xsane.gamma);
+ fprintf(file, "\"xsane-gamma-red\"\n");
+ fprintf(file, "%f\n", xsane.gamma_red);
+ fprintf(file, "\"xsane-gamma-green\"\n");
+ fprintf(file, "%f\n", xsane.gamma_green);
+ fprintf(file, "\"xsane-gamma-blue\"\n");
+ fprintf(file, "%f\n", xsane.gamma_blue);
+
+ fprintf(file, "\"xsane-brightness\"\n");
+ fprintf(file, "%f\n", xsane.brightness);
+ fprintf(file, "\"xsane-brightness-red\"\n");
+ fprintf(file, "%f\n", xsane.brightness_red);
+ fprintf(file, "\"xsane-brightness-green\"\n");
+ fprintf(file, "%f\n", xsane.brightness_green);
+ fprintf(file, "\"xsane-brightness-blue\"\n");
+ fprintf(file, "%f\n", xsane.brightness_blue);
+
+ fprintf(file, "\"xsane-contrast\"\n");
+ fprintf(file, "%f\n", xsane.contrast);
+ fprintf(file, "\"xsane-contrast-red\"\n");
+ fprintf(file, "%f\n", xsane.contrast_red);
+ fprintf(file, "\"xsane-contrast-green\"\n");
+ fprintf(file, "%f\n", xsane.contrast_green);
+ fprintf(file, "\"xsane-contrast-blue\"\n");
+ fprintf(file, "%f\n", xsane.contrast_blue);
+
+ fprintf(file, "\"xsane-enhancement-rgb-default\"\n");
+ fprintf(file, "%d\n", xsane.enhancement_rgb_default);
+
+ fprintf(file, "\"xsane-negative\"\n");
+ fprintf(file, "%d\n", xsane.negative);
+
+ gdk_window_get_root_origin(xsane.shell->window, &posx, &posy);
+ gdk_window_get_size(xsane.shell->window, &width, &height);
+ fprintf(file, "\"xsane-main-window-x-position\"\n");
+ fprintf(file, "%d\n", posx);
+ fprintf(file, "\"xsane-main-window-y-position\"\n");
+ fprintf(file, "%d\n", posy);
+ fprintf(file, "\"xsane-main-window-width\"\n");
+ fprintf(file, "%d\n", width);
+ fprintf(file, "\"xsane-main-window-height\"\n");
+ fprintf(file, "%d\n", height);
+ gtk_widget_set_uposition(xsane.shell, posx, posy); /* set default geometry used when window is closed and opened again */
+ gtk_window_set_default_size(GTK_WINDOW(xsane.shell), width, height);
+
+ gdk_window_get_root_origin(xsane.standard_options_shell->window, &posx, &posy);
+ fprintf(file, "\"xsane-standard-options-window-x-position\"\n");
+ fprintf(file, "%d\n", posx);
+ fprintf(file, "\"xsane-standard-options-window-y-position\"\n");
+ fprintf(file, "%d\n", posy);
+ gtk_widget_set_uposition(xsane.standard_options_shell, posx, posy);
+
+ gdk_window_get_root_origin(xsane.advanced_options_shell->window, &posx, &posy);
+ fprintf(file, "\"xsane-advanced-options-window-x-position\"\n");
+ fprintf(file, "%d\n", posx);
+ fprintf(file, "\"xsane-advanced-options-window-y-position\"\n");
+ fprintf(file, "%d\n", posy);
+ gtk_widget_set_uposition(xsane.advanced_options_shell, posx, posy);
+
+ gdk_window_get_root_origin(xsane.histogram_dialog->window, &posx, &posy);
+ fprintf(file, "\"xsane-histogram-window-x-position\"\n");
+ fprintf(file, "%d\n", posx);
+ fprintf(file, "\"xsane-histogram-window-y-position\"\n");
+ fprintf(file, "%d\n", posy);
+ gtk_widget_set_uposition(xsane.histogram_dialog, posx, posy);
+
+ fprintf(file, "\"xsane-show-preview\"\n");
+ fprintf(file, "%d\n", xsane.show_preview);
+
+ if (xsane.preview)
+ {
+ gdk_window_get_root_origin(xsane.preview->top->window, &posx, &posy);
+ gdk_window_get_size(xsane.preview->top->window, &width, &height);
+ fprintf(file, "\"xsane-preview-window-x-position\"\n");
+ fprintf(file, "%d\n", posx);
+ fprintf(file, "\"xsane-preview-window-y-position\"\n");
+ fprintf(file, "%d\n", posy);
+ fprintf(file, "\"xsane-preview-window-width\"\n");
+ fprintf(file, "%d\n", width);
+ fprintf(file, "\"xsane-preview-window-height\"\n");
+ fprintf(file, "%d\n", height);
+ gtk_widget_set_uposition(xsane.preview->top, posx, posy);
+ gtk_window_set_default_size(GTK_WINDOW(xsane.preview->top), width, height);
+ }
+
+ fclose(file);
+
+ xsane_set_sensitivity(TRUE);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
diff --git a/frontend/xsane-device-preferences.h b/frontend/xsane-device-preferences.h
new file mode 100644
index 0000000..a372f2e
--- /dev/null
+++ b/frontend/xsane-device-preferences.h
@@ -0,0 +1,42 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-device-preferences.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifndef xsane_device_preferences_h
+#define xsane_device_preferences_h
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include <sane/sane.h>
+#include "xsane.h"
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+extern void xsane_device_preferences_save(GtkWidget *widget, gpointer data);
+extern void xsane_device_preferences_restore(void);
+extern void xsane_device_preferences_load(void);
+extern void xsane_device_preferences_load_file(char *filename);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#endif
diff --git a/frontend/xsane-front-gtk.c b/frontend/xsane-front-gtk.c
new file mode 100644
index 0000000..fca864e
--- /dev/null
+++ b/frontend/xsane-front-gtk.c
@@ -0,0 +1,798 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-front-gtk.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+#include "xsane-back-gtk.h"
+#include "xsane-preferences.h"
+#include "xsane-preview.h"
+#include "xsane-save.h"
+#include "xsane-text.h"
+#include "xsane-gamma.h"
+#include "xsane-setup.h"
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+#include <png.h>
+#include <zlib.h>
+#endif
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* forward declarations: */
+
+void xsane_get_bounds(const SANE_Option_Descriptor *opt, double *minp, double *maxp);
+int xsane_set_resolution(int well_known_option, int resolution);
+void xsane_set_all_resolutions(void);
+void xsane_define_maximum_output_size();
+void xsane_close_dialog_callback(GtkWidget *widget, gpointer data);
+void xsane_authorization_button_callback(GtkWidget *widget, gpointer data);
+gint xsane_authorization_callback(SANE_String_Const resource,
+ SANE_Char username[SANE_MAX_USERNAME_LEN],
+ SANE_Char password[SANE_MAX_PASSWORD_LEN]);
+void xsane_progress_cancel(GtkWidget *widget, gpointer data);
+XsaneProgress_t *xsane_progress_new(char *title, char *text, GtkSignalFunc callback, gpointer callback_data);
+void xsane_progress_free(XsaneProgress_t *p);
+void xsane_progress_update(XsaneProgress_t *p, gfloat newval);
+void xsane_toggle_button_new_with_pixmap(GtkWidget *parent, const char *xpm_d[], const char *desc,
+ int *state, void *xsane_toggle_button_callback);
+GtkWidget *xsane_button_new_with_pixmap(GtkWidget *parent, const char *xpm_d[], const char *desc,
+ void *xsane_button_callback, gpointer data);
+void xsane_option_menu_new(GtkWidget *parent, char *str_list[], const char *val, int option_number, const char *desc,
+ void *option_menu_callback, SANE_Int settable, const gchar *widget_name);
+void xsane_option_menu_new_with_pixmap(GtkBox *parent, const char *xpm_d[], const char *desc,
+ char *str_list[], const char *val,
+ GtkObject **data, int option,
+ void *option_menu_callback, SANE_Int settable, const gchar *widget_name);
+void xsane_scale_new(GtkBox *parent, char *labeltext, const char *desc,
+ float min, float max, float quant, float step, float page_step,
+ int digits, double *val, GtkObject **data, void *xsane_scale_callback, SANE_Int settable);
+void xsane_scale_new_with_pixmap(GtkBox *parent, const char *xpm_d[], const char *desc,
+ float min, float max, float quant, float step, float page_step, int digits,
+ double *val, GtkObject **data, int option, void *xsane_scale_callback, SANE_Int settable);
+void xsane_separator_new(GtkWidget *xsane_parent, int dist);
+GtkWidget *xsane_info_table_text_new(GtkWidget *table, gchar *text, int row, int colomn);
+GtkWidget *xsane_info_text_new(GtkWidget *parent, gchar *text);
+void xsane_refresh_dialog(void *nothing);
+void xsane_set_sensitivity(SANE_Int sensitivity);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_get_bounds(const SANE_Option_Descriptor *opt, double *minp, double *maxp)
+{
+ double min, max;
+ int i;
+
+ min = -INF;
+ max = INF;
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ min = opt->constraint.range->min;
+ max = opt->constraint.range->max;
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ min = INF;
+ max = -INF;
+
+ for (i = 1; i <= opt->constraint.word_list[0]; ++i)
+ {
+ if (opt->constraint.word_list[i] < min)
+ {
+ min = opt->constraint.word_list[i];
+ }
+ if (opt->constraint.word_list[i] > max)
+ {
+ max = opt->constraint.word_list[i];
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ if (min > -INF && min < INF)
+ {
+ min = SANE_UNFIX (min);
+ }
+ if (max > -INF && max < INF)
+ {
+ max = SANE_UNFIX (max);
+ }
+ }
+ *minp = min;
+ *maxp = max;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+int xsane_set_resolution(int well_known_option, int resolution)
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Word dpi;
+ SANE_Word bestdpi;
+ SANE_Word diff;
+ SANE_Word val;
+ int items;
+ int i;
+
+ opt = sane_get_option_descriptor(dialog->dev, well_known_option);
+
+ if (!opt)
+ {
+ return -1; /* option does not exits */
+ }
+
+ if (opt->constraint_type == SANE_CONSTRAINT_RANGE)
+ {
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ dpi = resolution;
+ break;
+
+ case SANE_TYPE_FIXED:
+ dpi = SANE_FIX(resolution);
+ break;
+
+ default:
+ fprintf(stderr, "set_resolution: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ return 1; /* error */
+ }
+ }
+ else if (opt->constraint_type == SANE_CONSTRAINT_WORD_LIST)
+ {
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ dpi = resolution;
+ break;
+
+ case SANE_TYPE_FIXED:
+ dpi = SANE_FIX(resolution);
+ break;
+
+ default:
+ fprintf(stderr, "set_resolution: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ return 1; /* error */
+ }
+
+ items = opt->constraint.word_list[0];
+ bestdpi = opt->constraint.word_list[1];
+ diff = abs(bestdpi - dpi);
+
+ for (i=1; i<=items; i++)
+ {
+ val = opt->constraint.word_list[i];
+ if (abs(val - dpi) < diff)
+ {
+ diff = abs(val - dpi);
+ bestdpi = val;
+ }
+ }
+
+ if (bestdpi == -1)
+ {
+ fprintf(stderr, "set_resolution: %s\n", ERR_FAILED_SET_RESOLUTION);
+ return -1;
+ }
+ dpi = bestdpi;
+ }
+ else
+ {
+ fprintf(stderr, "set_resolution: %s %d\n", ERR_UNKNOWN_CONSTRAINT_TYPE, opt->constraint_type);
+ return 1; /* error */
+ }
+
+ sane_control_option(dialog->dev, well_known_option, SANE_ACTION_SET_VALUE, &dpi, 0);
+ return 0; /* everything is ok */
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_set_all_resolutions(void)
+{
+ xsane_set_resolution(dialog->well_known.dpi_y, xsane.resolution_y); /* set y resolution if possible */
+ if (xsane_set_resolution(dialog->well_known.dpi_x, xsane.resolution_x)) /* set x resolution if possible */
+ {
+ xsane_set_resolution(dialog->well_known.dpi, xsane.resolution); /* set common resolution if necessary */
+ xsane.resolution_x = xsane.resolution;
+ xsane.resolution_y = xsane.resolution;
+ }
+
+ xsane.zoom = xsane.resolution / preferences.printer[preferences.printernr]->resolution;
+ xsane.zoom_x = xsane.resolution_x / preferences.printer[preferences.printernr]->resolution;
+ xsane.zoom_y = xsane.resolution_y / preferences.printer[preferences.printernr]->resolution;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_define_maximum_output_size()
+{
+ const SANE_Option_Descriptor *opt;
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.coord[0]);
+
+ if ( (opt) && (opt->unit== SANE_UNIT_MM) )
+ {
+ if (xsane.xsane_mode == XSANE_COPY)
+ {
+ if (preferences.psrotate) /* rotate: landscape */
+ {
+ preview_set_maximum_output_size(xsane.preview,
+ preferences.printer[preferences.printernr]->height / xsane.zoom_y,
+ preferences.printer[preferences.printernr]->width / xsane.zoom_x);
+ }
+ else /* do not rotate: portrait */
+ {
+ preview_set_maximum_output_size(xsane.preview,
+ preferences.printer[preferences.printernr]->width / xsane.zoom_x,
+ preferences.printer[preferences.printernr]->height / xsane.zoom_y);
+ }
+ }
+ else if (xsane.xsane_mode == XSANE_FAX)
+ {
+ preview_set_maximum_output_size(xsane.preview, preferences.fax_width, preferences.fax_height);
+ }
+ else
+ {
+ preview_set_maximum_output_size(xsane.preview, INF, INF);
+ }
+ }
+ else
+ {
+ preview_set_maximum_output_size(xsane.preview, INF, INF);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_close_dialog_callback(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *dialog = data;
+
+ gtk_widget_destroy(dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+int authorization_flag;
+
+void xsane_authorization_button_callback(GtkWidget *widget, gpointer data)
+{
+ authorization_flag = (long) data;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+gint xsane_authorization_callback(SANE_String_Const resource,
+ SANE_Char username[SANE_MAX_USERNAME_LEN],
+ SANE_Char password[SANE_MAX_PASSWORD_LEN])
+{
+ GtkWidget *authorize_dialog, *vbox, *hbox, *button, *label;
+ GtkWidget *username_widget, *password_widget;
+ char buf[256];
+ char *input;
+ int len;
+
+ authorize_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
+ gtk_window_set_position(GTK_WINDOW(authorize_dialog), GTK_WIN_POS_CENTER);
+ gtk_window_set_policy(GTK_WINDOW(authorize_dialog), FALSE, FALSE, FALSE);
+ gtk_signal_connect(GTK_OBJECT(authorize_dialog), "delete_event",
+ GTK_SIGNAL_FUNC(xsane_authorization_button_callback), (void *) -1); /* -1 = cancel */
+ snprintf(buf, sizeof(buf), "%s %s", prog_name, WINDOW_AUTHORIZE);
+ gtk_window_set_title(GTK_WINDOW(authorize_dialog), buf);
+ xsane_set_window_icon(authorize_dialog, 0);
+
+ vbox = gtk_vbox_new(/* not homogeneous */ FALSE, 10); /* y-space between all box items */
+ gtk_container_add(GTK_CONTAINER(authorize_dialog), vbox);
+ gtk_widget_show(vbox);
+
+ snprintf(buf, sizeof(buf), "\n\n%s %s\n", TEXT_AUTHORIZATION_REQ, resource);
+ label = gtk_label_new(buf);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); /* y-space around authorization text */
+ gtk_widget_show(label);
+
+ /* ask for username */
+ hbox = gtk_hbox_new(FALSE, 10); /* x-space between label and input filed */
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); /* y-space around inner items */
+
+ label = gtk_label_new(TEXT_USERNAME);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10); /* x-space around label */
+ gtk_widget_show(label);
+
+ username_widget = gtk_entry_new_with_max_length(SANE_MAX_USERNAME_LEN-1);
+ gtk_widget_set_usize(username_widget, 250, 0);
+ gtk_entry_set_text(GTK_ENTRY(username_widget), "");
+ gtk_box_pack_end(GTK_BOX(hbox), username_widget, FALSE, FALSE, 10); /* x-space around input filed */
+ gtk_widget_show(username_widget);
+ gtk_widget_show(hbox);
+
+
+ /* ask for password */
+ hbox = gtk_hbox_new(FALSE, 10); /* x-space between label and input filed */
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); /* y-space around inner items */
+
+ label = gtk_label_new(TEXT_PASSWORD);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10); /* x-space around label */
+ gtk_widget_show(label);
+
+ password_widget = gtk_entry_new_with_max_length(SANE_MAX_PASSWORD_LEN-1);
+ gtk_entry_set_visibility(GTK_ENTRY(password_widget), FALSE); /* make entered text invisible */
+ gtk_widget_set_usize(password_widget, 250, 0);
+ gtk_entry_set_text(GTK_ENTRY(password_widget), "");
+ gtk_box_pack_end(GTK_BOX(hbox), password_widget, FALSE, FALSE, 10); /* x-space around input filed */
+ gtk_widget_show(password_widget);
+ gtk_widget_show(hbox);
+
+ /* buttons */
+ hbox = gtk_hbox_new(TRUE, 10); /* x-space between buttons */
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 10); /* y-space around buttons */
+
+ button = gtk_button_new_with_label(BUTTON_OK);
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(xsane_authorization_button_callback), (void *) 1);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 10); /* x-space around OK-button */
+ gtk_widget_grab_default(button);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label(BUTTON_CANCEL);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(xsane_authorization_button_callback), (void *) -1);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 10); /* x-space around cancel-button */
+ gtk_widget_show(button);
+
+ gtk_widget_show(hbox);
+
+ gtk_widget_show(authorize_dialog);
+
+
+ username[0]=0;
+ password[0]=0;
+
+ authorization_flag = 0;
+
+ /* wait for ok or cancel */
+ while (authorization_flag == 0)
+ {
+ gtk_main_iteration();
+ }
+
+ if (authorization_flag == 1) /* 1=ok, -1=cancel */
+ {
+ input = gtk_entry_get_text(GTK_ENTRY(username_widget));
+ len = strlen(input);
+ memcpy(username, input, len);
+ username[len] = 0;
+
+ input = gtk_entry_get_text(GTK_ENTRY(password_widget));
+ len = strlen(input);
+ memcpy(password, input, len);
+ password[len] = 0;
+ }
+ gtk_widget_destroy(authorize_dialog);
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_progress_cancel(GtkWidget *widget, gpointer data)
+{
+ XsaneProgress_t *p = (XsaneProgress_t *) data;
+
+ (*p->callback) ();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+XsaneProgress_t *xsane_progress_new(char *title, char *text, GtkSignalFunc callback, gpointer callback_data)
+{
+ GtkWidget *button, *label;
+ GtkBox *vbox, *hbox;
+ XsaneProgress_t *p;
+ static const int progress_x = 5;
+ static const int progress_y = 5;
+
+ p = (XsaneProgress_t *) malloc(sizeof(XsaneProgress_t));
+ p->callback = callback;
+
+ p->shell = gtk_dialog_new();
+ gtk_widget_set_uposition(p->shell, progress_x, progress_y);
+ gtk_window_set_title(GTK_WINDOW (p->shell), title);
+
+ xsane_set_window_icon(p->shell, 0);
+
+ vbox = GTK_BOX(GTK_DIALOG(p->shell)->vbox);
+ hbox = GTK_BOX(GTK_DIALOG(p->shell)->action_area);
+
+ gtk_container_set_border_width(GTK_CONTAINER (vbox), 7);
+
+ label = gtk_label_new(text);
+ gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start(vbox, label, FALSE, TRUE, 0);
+
+ p->pbar = gtk_progress_bar_new();
+ gtk_widget_set_usize(p->pbar, 200, 20);
+ gtk_box_pack_start(vbox, p->pbar, TRUE, TRUE, 0);
+
+ button = gtk_toggle_button_new_with_label(BUTTON_CANCEL);
+ gtk_signal_connect(GTK_OBJECT (button), "clicked", (GtkSignalFunc) xsane_progress_cancel, p);
+ gtk_box_pack_start(hbox, button, TRUE, TRUE, 0);
+
+ gtk_widget_show(label);
+ gtk_widget_show(p->pbar);
+ gtk_widget_show(button);
+ gtk_widget_show(GTK_WIDGET (p->shell));
+ return p;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_progress_free(XsaneProgress_t *p)
+{
+ if (p)
+ {
+ gtk_widget_destroy(p->shell);
+ free (p);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_progress_update(XsaneProgress_t *p, gfloat newval)
+{
+ if (p)
+ {
+ gtk_progress_bar_update(GTK_PROGRESS_BAR(p->pbar), newval);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_toggle_button_new_with_pixmap(GtkWidget *parent, const char *xpm_d[], const char *desc,
+ int *state, void *xsane_toggle_button_callback)
+{
+ GtkWidget *button;
+ GtkWidget *pixmapwidget;
+ GdkBitmap *mask;
+ GdkPixmap *pixmap;
+
+ button = gtk_toggle_button_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, button, desc);
+
+ pixmap = gdk_pixmap_create_from_xpm_d(xsane.histogram_dialog->window, &mask, xsane.bg_trans, (gchar **) xpm_d);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_container_add(GTK_CONTAINER(button), pixmapwidget);
+ gtk_widget_show(pixmapwidget);
+ gdk_pixmap_unref(pixmap);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *state);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_toggle_button_callback, (GtkObject *)state);
+ gtk_box_pack_start(GTK_BOX(parent), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+GtkWidget *xsane_button_new_with_pixmap(GtkWidget *parent, const char *xpm_d[], const char *desc,
+ void *xsane_button_callback, gpointer data)
+{
+ GtkWidget *button;
+ GtkWidget *pixmapwidget;
+ GdkBitmap *mask;
+ GdkPixmap *pixmap;
+
+ button = gtk_button_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, button, desc);
+
+ pixmap = gdk_pixmap_create_from_xpm_d(xsane.histogram_dialog->window, &mask, xsane.bg_trans, (gchar **) xpm_d);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_container_add(GTK_CONTAINER(button), pixmapwidget);
+ gtk_widget_show(pixmapwidget);
+ gdk_pixmap_unref(pixmap);
+
+ if (xsane_button_callback)
+ {
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_button_callback, data);
+ }
+ gtk_box_pack_start(GTK_BOX(parent), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+
+ return(button);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_option_menu_lookup(GSGMenuItem menu_items[], const char *string)
+{
+ int i;
+
+ for (i = 0; strcmp(menu_items[i].label, string) != 0; ++i);
+ return i;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_option_menu_callback(GtkWidget *widget, gpointer data)
+{
+ GSGMenuItem *menu_item = data;
+ GSGDialogElement *elem = menu_item->elem;
+ const SANE_Option_Descriptor *opt;
+ GSGDialog *dialog = elem->dialog;
+ int opt_num;
+ double dval;
+ SANE_Word val;
+ void *valp = &val;
+
+ opt_num = elem - dialog->element;
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ sscanf(menu_item->label, "%d", &val);
+ break;
+
+ case SANE_TYPE_FIXED:
+ sscanf(menu_item->label, "%lg", &dval);
+ val = SANE_FIX(dval);
+ break;
+
+ case SANE_TYPE_STRING:
+ valp = menu_item->label;
+ break;
+
+ default:
+ fprintf(stderr, "xsane_option_menu_callback: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ break;
+ }
+ xsane_back_gtk_set_option(dialog, opt_num, valp, SANE_ACTION_SET_VALUE);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_option_menu_new(GtkWidget *parent, char *str_list[], const char *val, int option_number, const char *desc,
+ void *option_menu_callback, SANE_Int settable, const gchar *widget_name)
+{
+ GtkWidget *option_menu, *menu, *item;
+ GSGMenuItem *menu_items;
+ GSGDialogElement *elem;
+ int i, num_items;
+
+ elem = dialog->element + option_number;
+
+ for (num_items = 0; str_list[num_items]; ++num_items);
+ menu_items = malloc(num_items * sizeof(menu_items[0]));
+
+ menu = gtk_menu_new();
+ if (widget_name)
+ {
+ gtk_widget_set_name(menu, widget_name);
+ }
+
+ for (i = 0; i < num_items; ++i)
+ {
+ item = gtk_menu_item_new_with_label(_BGT(str_list[i]));
+ gtk_container_add(GTK_CONTAINER(menu), item);
+
+ if (option_menu_callback)
+ {
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) option_menu_callback, menu_items + i);
+ }
+ else
+ {
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_option_menu_callback, menu_items + i);
+ }
+
+ gtk_widget_show(item);
+
+ menu_items[i].label = str_list[i];
+ menu_items[i].elem = elem;
+ menu_items[i].index = i;
+ }
+
+ option_menu = gtk_option_menu_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, option_menu, desc);
+ gtk_box_pack_end(GTK_BOX(parent), option_menu, FALSE, FALSE, 2);
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), xsane_option_menu_lookup(menu_items, val));
+
+ gtk_widget_show(option_menu);
+
+ gtk_widget_set_sensitive(option_menu, settable);
+
+ elem->widget = option_menu;
+ elem->menu_size = num_items;
+ elem->menu = menu_items;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_option_menu_new_with_pixmap(GtkBox *parent, const char *xpm_d[], const char *desc,
+ char *str_list[], const char *val,
+ GtkObject **data, int option,
+ void *option_menu_callback, SANE_Int settable, const gchar *widget_name)
+{
+ GtkWidget *hbox;
+ GtkWidget *pixmapwidget;
+ GdkBitmap *mask;
+ GdkPixmap *pixmap;
+
+ hbox = gtk_hbox_new(FALSE, 5);
+ gtk_box_pack_start(parent, hbox, FALSE, FALSE, 0);
+
+ pixmap = gdk_pixmap_create_from_xpm_d(xsane.histogram_dialog->window, &mask, xsane.bg_trans, (gchar **) xpm_d);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_box_pack_start(GTK_BOX(hbox), pixmapwidget, FALSE, FALSE, 2);
+ gtk_widget_show(pixmapwidget);
+
+ xsane_option_menu_new(hbox, str_list, val, option, desc, option_menu_callback, settable, widget_name);
+ gtk_widget_show(hbox);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_scale_new(GtkBox *parent, char *labeltext, const char *desc,
+ float min, float max, float quant, float step, float page_step,
+ int digits, double *val, GtkObject **data, void *xsane_scale_callback, SANE_Int settable)
+{
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *scale;
+
+ hbox = gtk_hbox_new(FALSE, 5);
+ gtk_box_pack_start(parent, hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new(labeltext);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+
+ *data = gtk_adjustment_new(*val, min, max, quant, step, page_step);
+ scale = gtk_hscale_new(GTK_ADJUSTMENT(*data));
+ xsane_back_gtk_set_tooltip(dialog->tooltips, scale, desc);
+ gtk_widget_set_usize(scale, 201, 0); /* minimum scale with = 201 pixels */
+ gtk_range_set_update_policy(GTK_RANGE(scale), preferences.gtk_update_policy);
+ /* GTK_UPDATE_CONTINUOUS, GTK_UPDATE_DISCONTINUOUS, GTK_UPDATE_DELAYED */
+ gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_TOP);
+ gtk_scale_set_digits(GTK_SCALE(scale), digits);
+ gtk_box_pack_end(GTK_BOX(hbox), scale, FALSE, TRUE, 5); /* make scale not sizeable */
+
+ if (xsane_scale_callback)
+ {
+ gtk_signal_connect(*data, "value_changed", (GtkSignalFunc) xsane_scale_callback, val);
+ }
+
+ gtk_widget_show(label);
+ gtk_widget_show(scale);
+ gtk_widget_show(hbox);
+
+ gtk_widget_set_sensitive(scale, settable);
+
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_scale_new_with_pixmap(GtkBox *parent, const char *xpm_d[], const char *desc,
+ float min, float max, float quant, float step, float page_step, int digits,
+ double *val, GtkObject **data, int option, void *xsane_scale_callback, SANE_Int settable)
+{
+ GtkWidget *hbox;
+ GtkWidget *scale;
+ GtkWidget *pixmapwidget;
+ GdkBitmap *mask;
+ GdkPixmap *pixmap;
+
+ hbox = gtk_hbox_new(FALSE, 5);
+ gtk_box_pack_start(parent, hbox, FALSE, FALSE, 0);
+
+ pixmap = gdk_pixmap_create_from_xpm_d(xsane.histogram_dialog->window, &mask, xsane.bg_trans, (gchar **) xpm_d);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_box_pack_start(GTK_BOX(hbox), pixmapwidget, FALSE, FALSE, 2);
+
+ *data = gtk_adjustment_new(*val, min, max, quant, step, page_step);
+ scale = gtk_hscale_new(GTK_ADJUSTMENT(*data));
+ xsane_back_gtk_set_tooltip(dialog->tooltips, scale, desc);
+ gtk_widget_set_usize(scale, 201, 0); /* minimum scale with = 201 pxiels */
+ gtk_range_set_update_policy(GTK_RANGE(scale), preferences.gtk_update_policy);
+ /* GTK_UPDATE_CONTINUOUS, GTK_UPDATE_DISCONTINUOUS, GTK_UPDATE_DELAYED */
+ gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_TOP);
+ gtk_scale_set_digits(GTK_SCALE(scale), digits);
+ gtk_box_pack_end(GTK_BOX(hbox), scale, TRUE, TRUE, 5); /* make scale sizeable */
+
+ if (xsane_scale_callback)
+ {
+ gtk_signal_connect(*data, "value_changed", (GtkSignalFunc) xsane_scale_callback, val);
+ }
+
+ gtk_widget_show(pixmapwidget);
+ gtk_widget_show(scale);
+ gtk_widget_show(hbox);
+
+ gtk_widget_set_sensitive(scale, settable);
+
+ gdk_pixmap_unref(pixmap);
+
+ if ( (dialog) && (option) )
+ {
+ GSGDialogElement *elem;
+
+ elem=dialog->element + option;
+ elem->data = *data;
+ elem->widget = scale;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_separator_new(GtkWidget *xsane_parent, int dist)
+{
+ GtkWidget *xsane_separator;
+
+ xsane_separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(xsane_parent), xsane_separator, FALSE, FALSE, dist);
+ gtk_widget_show(xsane_separator);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+GtkWidget *xsane_info_table_text_new(GtkWidget *table, gchar *text, int row, int colomn)
+{
+ GtkWidget *hbox, *label;
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_table_attach_defaults(GTK_TABLE(table), hbox, row, row+1, colomn, colomn+1);
+ gtk_widget_show(hbox);
+
+ label = gtk_label_new(text);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
+ gtk_widget_show(label);
+
+ return label;
+}
+/* ---------------------------------------------------------------------------------------------------------------------- */
+#if 0
+GtkWidget *xsane_info_text_new(GtkWidget *parent, gchar *text)
+{
+ GtkWidget *hbox, *label;
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(parent), hbox, TRUE, TRUE, 5);
+ gtk_widget_show(hbox);
+
+ label = gtk_label_new(text);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
+ gtk_widget_show(label);
+
+ return label;
+}
+#endif
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_refresh_dialog(void *nothing)
+{
+ xsane_back_gtk_refresh_dialog(dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
diff --git a/frontend/xsane-front-gtk.h b/frontend/xsane-front-gtk.h
new file mode 100644
index 0000000..600fa28
--- /dev/null
+++ b/frontend/xsane-front-gtk.h
@@ -0,0 +1,73 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-front-gtk.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include <sane/sane.h>
+#include "xsane.h"
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifndef xsane_front_gtk_h
+#define xsane_front_gtk_h
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+extern void xsane_get_bounds(const SANE_Option_Descriptor *opt, double *minp, double *maxp);
+extern int xsane_set_resolution(int well_known_option, int resolution);
+extern void xsane_set_all_resolutions(void);
+extern void xsane_define_maximum_output_size();
+extern void xsane_close_dialog_callback(GtkWidget *widget, gpointer data);
+extern void xsane_authorization_button_callback(GtkWidget *widget, gpointer data);
+extern gint xsane_authorization_callback(SANE_String_Const resource,
+ SANE_Char username[SANE_MAX_USERNAME_LEN],
+ SANE_Char password[SANE_MAX_PASSWORD_LEN]);
+extern void xsane_progress_cancel(GtkWidget *widget, gpointer data);
+extern XsaneProgress_t *xsane_progress_new(char *title, char *text, GtkSignalFunc callback, gpointer callback_data);
+extern void xsane_progress_free(XsaneProgress_t *p);
+extern void xsane_progress_update(XsaneProgress_t *p, gfloat newval);
+extern void xsane_toggle_button_new_with_pixmap(GtkWidget *parent, const char *xpm_d[], const char *desc,
+ int *state, void *xsane_toggle_button_callback);
+extern GtkWidget *xsane_button_new_with_pixmap(GtkWidget *parent, const char *xpm_d[], const char *desc,
+ void *xsane_button_callback, gpointer data);
+extern void xsane_pixmap_new(GtkWidget *parent, char *title, int width, int height, XsanePixmap *hist);
+extern void xsane_option_menu_new(GtkWidget *parent, char *str_list[], const char *val, int option_number, const char *desc,
+ void *option_menu_callback, SANE_Int settable, const gchar *widget_name);
+extern void xsane_option_menu_new_with_pixmap(GtkBox *parent, const char *xpm_d[], const char *desc,
+ char *str_list[], const char *val,
+ GtkObject **data, int option,
+ void *option_menu_callback, SANE_Int settable, const gchar *widget_name);
+extern void xsane_scale_new(GtkBox *parent, char *labeltext, const char *desc,
+ float min, float max, float quant, float step, float xxx,
+ int digits, double *val, GtkObject **data, void *xsane_scale_callback, SANE_Int settable);
+extern void xsane_scale_new_with_pixmap(GtkBox *parent, const char *xpm_d[], const char *desc,
+ float min, float max, float quant, float step, float xxx, int digits,
+ double *val, GtkObject **data, int option, void *xsane_scale_callback, SANE_Int settable);
+extern void xsane_separator_new(GtkWidget *xsane_parent, int dist);
+extern GtkWidget *xsane_info_table_text_new(GtkWidget *table, gchar *text, int row, int colomn);
+extern GtkWidget *xsane_info_text_new(GtkWidget *parent, gchar *text);
+extern void xsane_refresh_dialog(void *nothing);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#endif
+
diff --git a/frontend/xsane-gamma.c b/frontend/xsane-gamma.c
new file mode 100644
index 0000000..90573f5
--- /dev/null
+++ b/frontend/xsane-gamma.c
@@ -0,0 +1,1541 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-gamma.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+#include "xsane-front-gtk.h"
+#include "xsane-back-gtk.h"
+#include "xsane-preferences.h"
+#include "xsane-preview.h"
+#include "xsane-save.h"
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+#include <png.h>
+#include <zlib.h>
+#endif
+#endif
+
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* forward declarations: */
+
+static void xsane_bound_double(double *value, double min, double max);
+void xsane_clear_histogram(XsanePixmap *hist);
+static void xsane_draw_histogram_with_points(XsanePixmap *hist, int invert,
+ SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue,
+ int show_red, int show_green, int show_blue, int show_inten, double scale);
+static void xsane_draw_histogram_with_lines(XsanePixmap *hist, int invert,
+ SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue,
+ int show_red, int show_green, int show_blue, int show_inten, double scale);
+void xsane_draw_slider_level(XsaneSlider *slider);
+static void xsane_set_slider(XsaneSlider *slider, double min, double mid, double max);
+void xsane_update_slider(XsaneSlider *slider);
+void xsane_update_sliders(void);
+static gint xsane_slider_callback(GtkWidget *widget, GdkEvent *event, XsaneSlider *slider);
+void xsane_create_slider(XsaneSlider *slider);
+void xsane_create_histogram(GtkWidget *parent, const char *title, int width, int height, XsanePixmap *hist);
+static void xsane_calculate_auto_enhancement(int negative,
+ SANE_Int *count_raw, SANE_Int *count_raw_red, SANE_Int *count_raw_green, SANE_Int *count_raw_blue);
+void xsane_calculate_histogram(void);
+void xsane_update_histogram(void);
+void xsane_histogram_toggle_button_callback(GtkWidget *widget, gpointer data);
+void xsane_create_gamma_curve(SANE_Int *gammadata, int negative, double gamma,
+ double brightness, double contrast, int numbers, int maxout);
+void xsane_update_gamma(void);
+static void xsane_enhancement_update(void);
+static void xsane_gamma_to_histogram(double *min, double *mid, double *max,
+ double contrast, double brightness, double gamma);
+void xsane_enhancement_by_gamma(void);
+void xsane_enhancement_restore_default(void);
+void xsane_enhancement_restore(void);
+void xsane_enhancement_store(void);
+static void xsane_histogram_to_gamma(XsaneSlider *slider, double *contrast, double *brightness, double *gamma);
+void xsane_enhancement_by_histogram(void);
+static gint xsane_histogram_win_delete(GtkWidget *widget, gpointer data);
+void xsane_create_histogram_dialog(const char *devicetext);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_bound_double(double *value, double min, double max)
+{
+ if (*value < min)
+ {
+ *value = min;
+ }
+
+ if (*value > max)
+ {
+ *value = max;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_clear_histogram(XsanePixmap *hist)
+{
+ GdkRectangle rect;
+
+ if(hist->pixmap)
+ {
+ rect.x=0;
+ rect.y=0;
+ rect.width = HIST_WIDTH;
+ rect.height = HIST_HEIGHT;
+
+ gdk_draw_rectangle(hist->pixmap, xsane.gc_backg, TRUE, 0, 0, HIST_WIDTH, HIST_HEIGHT);
+ gtk_widget_draw(hist->pixmapwid, &rect);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_draw_histogram_with_points(XsanePixmap *hist, int invert,
+ SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue,
+ int show_red, int show_green, int show_blue, int show_inten, double scale)
+{
+ GdkRectangle rect;
+ int i;
+ int inten, red, green, blue;
+ int colval;
+
+#define XD 1
+#define YD 2
+
+ if(hist->pixmap)
+ {
+ rect.x=0;
+ rect.y=0;
+ rect.width = HIST_WIDTH;
+ rect.height = HIST_HEIGHT;
+
+ gdk_draw_rectangle(hist->pixmap, xsane.gc_backg, TRUE, 0, 0, HIST_WIDTH, HIST_HEIGHT);
+
+ red = 0;
+ green = 0;
+ blue = 0;
+
+ for (i=0; i < HIST_WIDTH; i++)
+ {
+ if (invert)
+ {
+ colval = 255-i;
+ }
+ else
+ {
+ colval = i;
+ }
+
+ inten = show_inten * count[colval] * scale;
+
+ if (xsane.xsane_color)
+ {
+ red = show_red * count_red[colval] * scale;
+ green = show_green * count_green[colval] * scale;
+ blue = show_blue * count_blue[colval] * scale;
+ }
+
+ if (inten > HIST_HEIGHT)
+ inten = HIST_HEIGHT;
+
+ if (red > HIST_HEIGHT)
+ red = HIST_HEIGHT;
+
+ if (green > HIST_HEIGHT)
+ green = HIST_HEIGHT;
+
+ if (blue > HIST_HEIGHT)
+ blue = HIST_HEIGHT;
+
+
+ gdk_draw_rectangle(hist->pixmap, xsane.gc_red, TRUE, i, HIST_HEIGHT - red, XD, YD);
+ gdk_draw_rectangle(hist->pixmap, xsane.gc_green, TRUE, i, HIST_HEIGHT - green, XD, YD);
+ gdk_draw_rectangle(hist->pixmap, xsane.gc_blue, TRUE, i, HIST_HEIGHT - blue, XD, YD);
+ gdk_draw_rectangle(hist->pixmap, xsane.gc_black, TRUE, i, HIST_HEIGHT - inten, XD, YD);
+ }
+
+ gtk_widget_draw(hist->pixmapwid, &rect);
+ }
+}
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_draw_histogram_with_lines(XsanePixmap *hist, int invert,
+ SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue,
+ int show_red, int show_green, int show_blue, int show_inten, double scale)
+{
+ GdkRectangle rect;
+ int i, j, k;
+ int inten, red, green, blue;
+ int inten0=0, red0=0, green0=0, blue0=0;
+ int val[4];
+ int val2[4];
+ int color[4];
+ int val_swap;
+ int color_swap;
+ int colval;
+
+ if (hist->pixmap)
+ {
+ rect.x=0;
+ rect.y=0;
+ rect.width = HIST_WIDTH;
+ rect.height = HIST_HEIGHT;
+
+ gdk_draw_rectangle(hist->pixmap, xsane.gc_backg, TRUE, 0, 0, HIST_WIDTH, HIST_HEIGHT);
+
+ red = 0;
+ green = 0;
+ blue = 0;
+
+ for (i=0; i < HIST_WIDTH; i++)
+ {
+ if (invert)
+ {
+ colval = 255-i;
+ }
+ else
+ {
+ colval = i;
+ }
+
+ inten = show_inten * count[colval] * scale;
+
+ if (xsane.xsane_color)
+ {
+ red = show_red * count_red[colval] * scale;
+ green = show_green * count_green[colval] * scale;
+ blue = show_blue * count_blue[colval] * scale;
+ }
+
+ if (inten > HIST_HEIGHT)
+ inten = HIST_HEIGHT;
+
+ if (red > HIST_HEIGHT)
+ red = HIST_HEIGHT;
+
+ if (green > HIST_HEIGHT)
+ green = HIST_HEIGHT;
+
+ if (blue > HIST_HEIGHT)
+ blue = HIST_HEIGHT;
+
+ val[0] = red; color[0] = 0;
+ val[1] = green; color[1] = 1;
+ val[2] = blue; color[2] = 2;
+ val[3] = inten; color[3] = 3;
+
+ for (j=0; j<3; j++)
+ {
+ for (k=j+1; k<4; k++)
+ {
+ if (val[j] < val[k])
+ {
+ val_swap = val[j];
+ color_swap = color[j];
+ val[j] = val[k];
+ color[j] = color[k];
+ val[k] = val_swap;
+ color[k] = color_swap;
+ }
+ }
+ }
+ val2[0]=val[1]+1;
+ val2[1]=val[2]+1;
+ val2[2]=val[3]+1;
+ val2[3]=0;
+
+ for (j=0; j<4; j++)
+ {
+ switch(color[j])
+ {
+ case 0: red0 = val2[j];
+ break;
+ case 1: green0 = val2[j];
+ break;
+ case 2: blue0 = val2[j];
+ break;
+ case 3: inten0 = val2[j];
+ break;
+ }
+ }
+
+
+ gdk_draw_line(hist->pixmap, xsane.gc_red, i, HIST_HEIGHT - red, i, HIST_HEIGHT - red0);
+ gdk_draw_line(hist->pixmap, xsane.gc_green, i, HIST_HEIGHT - green, i, HIST_HEIGHT - green0);
+ gdk_draw_line(hist->pixmap, xsane.gc_blue, i, HIST_HEIGHT - blue, i, HIST_HEIGHT - blue0);
+ gdk_draw_line(hist->pixmap, xsane.gc_black, i, HIST_HEIGHT - inten, i, HIST_HEIGHT - inten0);
+ }
+
+ gtk_widget_draw(hist->pixmapwid, &rect);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_establish_slider(XsaneSlider *slider)
+{
+ int x, y, pos, len;
+ guchar buf[XSANE_SLIDER_WIDTH*3];
+ GdkRectangle rect;
+
+ buf[0] = buf[1] = buf[2] = 0;
+ buf[3+0] = buf[3+1] = buf[3+2]= 0;
+
+ for (x=0; x<256; x++)
+ {
+ buf[3*x+0+6] = x * slider->r;
+ buf[3*x+1+6] = x * slider->g;
+ buf[3*x+2+6] = x * slider->b;
+ }
+
+ buf[258*3+0] = 255 * slider->r;
+ buf[258*3+1] = 255 * slider->g;
+ buf[258*3+2] = 255 * slider->b;
+
+ buf[259*3+0] = 255 * slider->r;
+ buf[259*3+1] = 255 * slider->g;
+ buf[259*3+2] = 255 * slider->b;
+
+ for (y=0; y<XSANE_SLIDER_HEIGHT; y++)
+ {
+ pos = slider->position[0]-y/2;
+ len = y;
+ if (pos<-2)
+ {
+ len = len + pos + 2;
+ pos = -2;
+ }
+ pos = pos * 3 + 6;
+
+ for (x=0; x<=len; x++)
+ {
+ if ((x == 0) || (x == len) || (y == XSANE_SLIDER_HEIGHT-1))
+ {
+ buf[pos++] = 255;
+ buf[pos++] = 255;
+ buf[pos++] = 255;
+ }
+ else
+ {
+ buf[pos++] = 0;
+ buf[pos++] = 0;
+ buf[pos++] = 0;
+ }
+ }
+
+
+ pos = slider->position[1]-y/2;
+ len = y;
+ pos = pos * 3 + 6;
+
+ for (x=0; x<=len; x++)
+ {
+ if ((x == 0) || (x == len) || (y == XSANE_SLIDER_HEIGHT-1))
+ {
+ buf[pos++] = 255;
+ buf[pos++] = 255;
+ buf[pos++] = 255;
+ }
+ else
+ {
+ buf[pos++] = 128;
+ buf[pos++] = 128;
+ buf[pos++] = 128;
+ }
+ }
+
+
+ pos = slider->position[2]-y/2;
+ len = y;
+ if (pos+len>257)
+ {
+ len = 257 - pos;
+ }
+ pos = pos * 3 + 6;
+
+ for (x=0; x<=len; x++)
+ {
+ if ((x == 0) || (x == len) || (y == XSANE_SLIDER_HEIGHT-1))
+ {
+ buf[pos++] = 0;
+ buf[pos++] = 0;
+ buf[pos++] = 0;
+ }
+ else
+ {
+ buf[pos++] = 255;
+ buf[pos++] = 255;
+ buf[pos++] = 255;
+ }
+ }
+
+ gtk_preview_draw_row(GTK_PREVIEW(slider->preview),buf, 0, y, XSANE_SLIDER_WIDTH);
+ }
+
+ rect.x=0;
+ rect.y=0;
+ rect.width = XSANE_SLIDER_WIDTH;
+ rect.height = XSANE_SLIDER_HEIGHT;
+
+ gtk_widget_draw(slider->preview, &rect);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_draw_slider_level(XsaneSlider *slider)
+{
+ int i;
+ guchar buf[XSANE_SLIDER_WIDTH*3];
+ GdkRectangle rect;
+
+ buf[0] = buf[1] = buf[2] = 0;
+ buf[3+0] = buf[3+1] = buf[3+2]= 0;
+
+ for (i=0; i<256; i++)
+ {
+ buf[3*i+0+6] = i * slider->r;
+ buf[3*i+1+6] = i * slider->g;
+ buf[3*i+2+6] = i * slider->b;
+ }
+
+ buf[258*3+0] = 255 * slider->r;
+ buf[258*3+1] = 255 * slider->g;
+ buf[258*3+2] = 255 * slider->b;
+
+ buf[259*3+0] = 255 * slider->r;
+ buf[259*3+1] = 255 * slider->g;
+ buf[259*3+2] = 255 * slider->b;
+
+ for (i=0; i<XSANE_SLIDER_HEIGHT; i++)
+ {
+ gtk_preview_draw_row(GTK_PREVIEW(slider->preview),buf, 0, i, XSANE_SLIDER_WIDTH);
+ }
+
+ rect.x=0;
+ rect.y=0;
+ rect.width = XSANE_SLIDER_WIDTH;
+ rect.height = XSANE_SLIDER_HEIGHT;
+
+ gtk_widget_draw(slider->preview, &rect);
+}
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_set_slider(XsaneSlider *slider, double min, double mid, double max)
+{
+ slider->value[0] = min;
+ slider->value[1] = mid;
+ slider->value[2] = max;
+
+ slider->position[0] = min * 2.55;
+ slider->position[1] = mid * 2.55;
+ slider->position[2] = max * 2.55;
+
+ xsane_establish_slider(slider);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_update_slider(XsaneSlider *slider)
+{
+ slider->position[0] = 2.55 * slider->value[0];
+ slider->position[1] = 2.55 * slider->value[1];
+ slider->position[2] = 2.55 * slider->value[2];
+
+ xsane_establish_slider(slider);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_update_sliders()
+{
+ xsane_update_slider(&xsane.slider_gray);
+
+ if ( (xsane.xsane_color) && (!xsane.enhancement_rgb_default) )
+ {
+ xsane_update_slider(&xsane.slider_red);
+ xsane_update_slider(&xsane.slider_green);
+ xsane_update_slider(&xsane.slider_blue);
+
+ xsane.slider_gray.active &= ~XSANE_SLIDER_INACTIVE; /* mark slider active */
+ xsane.slider_red.active &= ~XSANE_SLIDER_INACTIVE; /* mark slider active */
+ xsane.slider_green.active &= ~XSANE_SLIDER_INACTIVE; /* mark slider active */
+ xsane.slider_blue.active &= ~XSANE_SLIDER_INACTIVE; /* mark slider active */
+ }
+ else
+ {
+ xsane_draw_slider_level(&xsane.slider_red); /* remove slider */
+ xsane_draw_slider_level(&xsane.slider_green); /* remove slider */
+ xsane_draw_slider_level(&xsane.slider_blue); /* remove slider */
+
+ xsane.slider_red.active = XSANE_SLIDER_INACTIVE; /* mark slider inactive */
+ xsane.slider_green.active = XSANE_SLIDER_INACTIVE; /* mark slider inactive */
+ xsane.slider_blue.active = XSANE_SLIDER_INACTIVE; /* mark slider inactive */
+
+ if (xsane.param.depth == 1)
+ {
+ xsane_draw_slider_level(&xsane.slider_gray); /* remove slider */
+ xsane.slider_gray.active = XSANE_SLIDER_INACTIVE; /* mark slider inactive */
+ }
+ else
+ {
+ xsane.slider_gray.active &= ~XSANE_SLIDER_INACTIVE; /* mark slider active */
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static gint xsane_slider_callback(GtkWidget *widget, GdkEvent *event, XsaneSlider *slider)
+{
+ GdkEventButton *button_event;
+ GdkEventMotion *motion_event;
+ int distance;
+ int i = 0;
+ static int update = FALSE;
+ static int event_count = 0;
+ static int x;
+
+ if (slider->active == XSANE_SLIDER_INACTIVE)
+ {
+ return 0;
+ }
+
+ event_count++;
+
+ switch(event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ gtk_grab_add(widget);
+ button_event = (GdkEventButton *) event;
+
+ distance = G_MAXINT;
+ for (i=0; i<3; i++)
+ {
+ if (fabs(button_event->x - slider->position[i]) < distance)
+ {
+ slider->active = i + 1;
+ distance = fabs(button_event->x - slider->position[i]);
+ }
+ }
+ if (distance<10)
+ {
+ x = button_event->x;
+ update = TRUE;
+ }
+ else
+ {
+ slider->active = XSANE_SLIDER_ACTIVE;
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ gtk_grab_remove(widget);
+ xsane_enhancement_by_histogram(); /* slider->active must be unchanged !!! */
+ slider->active = XSANE_SLIDER_ACTIVE; /* ok, now we can reset it */
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ motion_event = (GdkEventMotion *) event;
+ gdk_window_get_pointer(widget->window, &x, 0, 0);
+ update = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ if (update)
+ {
+ update = FALSE;
+ switch(slider->active)
+ {
+ case 1:
+ slider->value[0] = (x-XSANE_SLIDER_OFFSET) / 2.55;
+ xsane_bound_double(&slider->value[0], 0.0, slider->value[1] - 1);
+ break;
+
+ case 2:
+ slider->value[1] = (x-XSANE_SLIDER_OFFSET) / 2.55;
+ xsane_bound_double(&slider->value[1], slider->value[0] + 1, slider->value[2] - 1);
+ break;
+
+ case 3:
+ slider->value[2] = (x-XSANE_SLIDER_OFFSET) / 2.55;
+ xsane_bound_double(&slider->value[2], slider->value[1] + 1, 100.0);
+ break;
+
+ default:
+ break;
+ }
+ xsane_set_slider(slider, slider->value[0], slider->value[1], slider->value[2]);
+
+ if ((preferences.gtk_update_policy == GTK_UPDATE_CONTINUOUS) && (event_count == 1))
+ {
+ xsane_enhancement_by_histogram();
+ }
+ else if ((preferences.gtk_update_policy == GTK_UPDATE_DELAYED) && (event_count == 1))
+ {
+ xsane_enhancement_by_histogram();
+ }
+ }
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ event_count--;
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_create_slider(XsaneSlider *slider)
+{
+ slider->preview = gtk_preview_new(GTK_PREVIEW_COLOR);
+ gtk_preview_size(GTK_PREVIEW(slider->preview), XSANE_SLIDER_WIDTH, XSANE_SLIDER_HEIGHT);
+ gtk_widget_set_events(slider->preview, XSANE_SLIDER_EVENTS);
+ gtk_signal_connect(GTK_OBJECT(slider->preview), "event", GTK_SIGNAL_FUNC(xsane_slider_callback), slider);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_create_histogram(GtkWidget *parent, const char *title, int width, int height, XsanePixmap *hist)
+{
+ GdkBitmap *mask=NULL;
+
+ hist->frame = gtk_frame_new(title);
+ hist->pixmap = gdk_pixmap_new(xsane.histogram_dialog->window, width, height, -1);
+ hist->pixmapwid = gtk_pixmap_new(hist->pixmap, mask);
+ gtk_container_add(GTK_CONTAINER(hist->frame), hist->pixmapwid);
+ gdk_draw_rectangle(hist->pixmap, xsane.gc_backg, TRUE, 0, 0, width, height);
+
+ gtk_box_pack_start(GTK_BOX(parent), hist->frame, FALSE, FALSE, 2);
+ gtk_widget_show(hist->pixmapwid);
+ gtk_widget_show(hist->frame);
+ }
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_calculate_auto_enhancement(int negative,
+ SANE_Int *count_raw, SANE_Int *count_raw_red, SANE_Int *count_raw_green, SANE_Int *count_raw_blue)
+{ /* calculate white, medium and black values for auto enhancement */
+ int limit, limit_mid;
+ int points, points_mix, points_red, points_green, points_blue;
+ int min, mid, max;
+ int min_red, mid_red, max_red;
+ int min_green, mid_green, max_green;
+ int min_blue, mid_blue, max_blue;
+ int val;
+ int i;
+
+ if (xsane.preview)
+ {
+ points = 0;
+ points_mix = 0;
+ points_red = 0;
+ points_green = 0;
+ points_blue = 0;
+
+ for (i=0; i<256; i++)
+ {
+ points += count_raw[i];
+ points_mix += 10 * log(1 + count_raw[i] + count_raw_red[i] + count_raw_green[i] + count_raw_blue[i]);
+ points_red += 10 * log(1 + count_raw_red[i]);
+ points_green += 10 * log(1 + count_raw_green[i]);
+ points_blue += 10 * log(1 + count_raw_blue[i]);
+ }
+
+ limit = 1 + points / 5000;
+
+ /* ----- gray ----- */
+
+ min = -1;
+ val = 0;
+ while ( (val/4 < limit) && (min < 254) )
+ {
+ min++;
+ val += count_raw[min] + count_raw_red[min] + count_raw_green[min] + count_raw_blue[min];
+ }
+
+ max = HIST_WIDTH;
+ val = 0;
+ while ( (val/4 < limit) && (max > min + 1) )
+ {
+ max--;
+ val += count_raw[max] + count_raw_red[max] + count_raw_green[max] + count_raw_blue[max];
+ }
+
+ limit_mid = points_mix / 2.0;
+
+ mid = 0;
+ val = 0;
+ while ( (val < limit_mid) && (mid < max - 2) )
+ {
+ mid++;
+ val += 10 * log(1 + count_raw[mid] + count_raw_red[mid] + count_raw_green[mid] + count_raw_blue[mid]);
+ }
+
+ /* ----- red ----- */
+
+ min_red = -1;
+ val = 0;
+ while ( (val < limit) && (min_red < 254) )
+ {
+ min_red++;
+ val += count_raw_red[min_red];
+ }
+
+ max_red = HIST_WIDTH;
+ val = 0;
+ while ( (val < limit) && (max_red > min_red + 1) )
+ {
+ max_red--;
+ val += count_raw_red[max_red];
+ }
+
+ limit_mid = points_red / 2.0;
+
+ mid_red = 0;
+ val = 0;
+ while ( (val < limit_mid) && (mid_red < max_red - 2) )
+ {
+ mid_red++;
+ val += 10 * log(1 + count_raw_red[mid_red]);
+ }
+
+ /* ----- green ----- */
+
+ min_green = -1;
+ val = 0;
+ while ( (val < limit) && (min_green < 254) )
+ {
+ min_green++;
+ val += count_raw_green[min_green];
+ }
+
+ max_green = HIST_WIDTH;
+ val = 0;
+ while ( (val < limit) && (max_green > min_green + 1) )
+ {
+ max_green--;
+ val += count_raw_green[max_green];
+ }
+
+ limit_mid = points_green / 2.0;
+
+ mid_green = 0;
+ val = 0;
+ while ( (val < limit_mid) && (mid_green < max_green - 2) )
+ {
+ mid_green++;
+ val += 10 * log(1 + count_raw_green[mid_green]);
+ }
+
+ /* ----- blue ----- */
+
+ min_blue = -1;
+ val = 0;
+ while ( (val < limit) && (min_blue < 254) )
+ {
+ min_blue++;
+ val += count_raw_blue[min_blue];
+ }
+
+ max_blue = HIST_WIDTH;
+ val = 0;
+ while ( (val < limit) && (max_blue > min_blue + 1) )
+ {
+ max_blue--;
+ val += count_raw_blue[max_blue];
+ }
+
+ limit_mid = points_blue / 2.0;
+
+ mid_blue = 0;
+ val = 0;
+ while ( (val < limit_mid) && (mid_blue < max_blue - 2) )
+ {
+ mid_blue++;
+ val += 10 * log(1 + count_raw_blue[mid_blue]);
+ }
+
+ if (negative)
+ {
+ xsane.auto_white = (255-min)/2.55;
+ xsane.auto_gray = (255-mid)/2.55;
+ xsane.auto_black = (255-max)/2.55;
+
+ xsane.auto_white_red = (255-min_red)/2.55;
+ xsane.auto_gray_red = (255-mid_red)/2.55;
+ xsane.auto_black_red = (255-max_red)/2.55;
+
+ xsane.auto_white_green = (255-min_green)/2.55;
+ xsane.auto_gray_green = (255-mid_green)/2.55;
+ xsane.auto_black_green = (255-max_green)/2.55;
+
+ xsane.auto_white_blue = (255-min_blue)/2.55;
+ xsane.auto_gray_blue = (255-mid_blue)/2.55;
+ xsane.auto_black_blue = (255-max_blue)/2.55;
+ }
+ else /* positive */
+ {
+ xsane.auto_white = max/2.55;
+ xsane.auto_gray = mid/2.55;
+ xsane.auto_black = min/2.55;
+
+ xsane.auto_white_red = max_red/2.55;
+ xsane.auto_gray_red = mid_red/2.55;
+ xsane.auto_black_red = min_red/2.55;
+
+ xsane.auto_white_green = max_green/2.55;
+ xsane.auto_gray_green = mid_green/2.55;
+ xsane.auto_black_green = min_green/2.55;
+
+ xsane.auto_white_blue = max_blue/2.55;
+ xsane.auto_gray_blue = mid_blue/2.55;
+ xsane.auto_black_blue = min_blue/2.55;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_calculate_histogram(void)
+{
+ SANE_Int *count_raw;
+ SANE_Int *count_raw_red;
+ SANE_Int *count_raw_green;
+ SANE_Int *count_raw_blue;
+ SANE_Int *count_enh;
+ SANE_Int *count_enh_red;
+ SANE_Int *count_enh_green;
+ SANE_Int *count_enh_blue;
+ int i;
+ int maxval_raw;
+ int maxval_enh;
+ int maxval;
+ double scale;
+
+ /* at first reset auto enhancement values */
+
+ xsane.auto_black = 0.0;
+ xsane.auto_gray = 50.0;
+ xsane.auto_white = 100.0;
+
+ xsane.auto_black_red = 0.0;
+ xsane.auto_gray_red = 50.0;
+ xsane.auto_white_red = 100.0;
+
+ xsane.auto_black_green = 0.0;
+ xsane.auto_gray_green = 50.0;
+ xsane.auto_white_green = 100.0;
+
+ xsane.auto_black_blue = 0.0;
+ xsane.auto_gray_blue = 50.0;
+ xsane.auto_white_blue = 100.0;
+
+ if (xsane.preview) /* preview window exists? */
+ {
+ count_raw = calloc(256, sizeof(SANE_Int));
+ count_raw_red = calloc(256, sizeof(SANE_Int));
+ count_raw_green = calloc(256, sizeof(SANE_Int));
+ count_raw_blue = calloc(256, sizeof(SANE_Int));
+ count_enh = calloc(256, sizeof(SANE_Int));
+ count_enh_red = calloc(256, sizeof(SANE_Int));
+ count_enh_green = calloc(256, sizeof(SANE_Int));
+ count_enh_blue = calloc(256, sizeof(SANE_Int));
+
+ preview_calculate_histogram(xsane.preview, count_raw, count_raw_red, count_raw_green, count_raw_blue,
+ count_enh, count_enh_red, count_enh_green, count_enh_blue);
+
+ if (xsane.param.depth > 1)
+ {
+ xsane_calculate_auto_enhancement(xsane.negative, count_raw, count_raw_red, count_raw_green, count_raw_blue);
+ }
+
+ if (xsane.histogram_log) /* logarithmical display */
+ {
+ for (i=0; i<=255; i++)
+ {
+ count_raw[i] = (int) (50*log(1.0 + count_raw[i]));
+ count_raw_red[i] = (int) (50*log(1.0 + count_raw_red[i]));
+ count_raw_green[i] = (int) (50*log(1.0 + count_raw_green[i]));
+ count_raw_blue[i] = (int) (50*log(1.0 + count_raw_blue[i]));
+
+ count_enh[i] = (int) (50*log(1.0 + count_enh[i]));
+ count_enh_red[i] = (int) (50*log(1.0 + count_enh_red[i]));
+ count_enh_green[i] = (int) (50*log(1.0 + count_enh_green[i]));
+ count_enh_blue[i] = (int) (50*log(1.0 + count_enh_blue[i]));
+ }
+ }
+
+ maxval_raw = 0;
+ maxval_enh = 0;
+
+ /* first and last 10 values are not used for calculating maximum value */
+ for (i = 10 ; i < HIST_WIDTH - 10; i++)
+ {
+ if (count_raw[i] > maxval_raw) { maxval_raw = count_raw[i]; }
+ if (count_raw_red[i] > maxval_raw) { maxval_raw = count_raw_red[i]; }
+ if (count_raw_green[i] > maxval_raw) { maxval_raw = count_raw_green[i]; }
+ if (count_raw_blue[i] > maxval_raw) { maxval_raw = count_raw_blue[i]; }
+ if (count_enh[i] > maxval_enh) { maxval_enh = count_enh[i]; }
+ if (count_enh_red[i] > maxval_enh) { maxval_enh = count_enh_red[i]; }
+ if (count_enh_green[i] > maxval_enh) { maxval_enh = count_enh_green[i]; }
+ if (count_enh_blue[i] > maxval_enh) { maxval_enh = count_enh_blue[i]; }
+ }
+ maxval = ((maxval_enh > maxval_raw) ? maxval_enh : maxval_raw);
+ scale = 100.0/maxval;
+
+ if (xsane.histogram_lines)
+ {
+ xsane_draw_histogram_with_lines(&xsane.histogram_raw, xsane.negative,
+ count_raw, count_raw_red, count_raw_green, count_raw_blue,
+ xsane.histogram_red, xsane.histogram_green, xsane.histogram_blue, xsane.histogram_int, scale);
+
+ xsane_draw_histogram_with_lines(&xsane.histogram_enh, 0 /* negative is done by gamma table */,
+ count_enh, count_enh_red, count_enh_green, count_enh_blue,
+ xsane.histogram_red, xsane.histogram_green, xsane.histogram_blue, xsane.histogram_int, scale);
+ }
+ else
+ {
+ xsane_draw_histogram_with_points(&xsane.histogram_raw, xsane.negative,
+ count_raw, count_raw_red, count_raw_green, count_raw_blue,
+ xsane.histogram_red, xsane.histogram_green, xsane.histogram_blue, xsane.histogram_int, scale);
+
+ xsane_draw_histogram_with_points(&xsane.histogram_enh, 0 /*negative is done by gamma table */,
+ count_enh, count_enh_red, count_enh_green, count_enh_blue,
+ xsane.histogram_red, xsane.histogram_green, xsane.histogram_blue, xsane.histogram_int, scale);
+ }
+
+ free(count_enh_blue);
+ free(count_enh_green);
+ free(count_enh_red);
+ free(count_enh);
+ free(count_raw_blue);
+ free(count_raw_green);
+ free(count_raw_red);
+ free(count_raw);
+ }
+ else
+ {
+ xsane_clear_histogram(&xsane.histogram_raw);
+ xsane_clear_histogram(&xsane.histogram_enh);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_update_histogram()
+{
+ if (preferences.show_histogram)
+ {
+ xsane_calculate_histogram();
+ gtk_widget_show(xsane.histogram_dialog);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_histogram_toggle_button_callback(GtkWidget *widget, gpointer data)
+{
+ int *valuep = data;
+
+ *valuep = (GTK_TOGGLE_BUTTON(widget)->active != 0);
+ xsane_update_histogram();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_create_gamma_curve(SANE_Int *gammadata, int negative, double gamma,
+ double brightness, double contrast, int numbers, int maxout)
+{
+ int i;
+ double midin;
+ double val;
+ double m;
+ double b;
+ int maxin = numbers-1;
+
+ if (contrast < -100.0)
+ {
+ contrast = -100.0;
+ }
+
+ midin = (int)(numbers / 2.0);
+
+ m = 1.0 + contrast/100.0;
+ b = (1.0 + brightness/100.0) * midin;
+
+ if (negative)
+ {
+ for (i=0; i <= maxin; i++)
+ {
+ val = ((double) (maxin - i)) - midin;
+ val = val * m + b;
+ xsane_bound_double(&val, 0.0, maxin);
+
+ gammadata[i] = 0.5 + maxout * pow( val/maxin, (1.0/gamma) );
+ }
+ }
+ else /* positive */
+ {
+ for (i=0; i <= maxin; i++)
+ {
+ val = ((double) i) - midin;
+ val = val * m + b;
+ xsane_bound_double(&val, 0.0, maxin);
+
+ gammadata[i] = 0.5 + maxout * pow( val/maxin, (1.0/gamma) );
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_update_gamma(void)
+{
+ if (xsane.preview)
+ {
+ if (!xsane.preview_gamma_data_red)
+ {
+ xsane.preview_gamma_data_red = malloc(256 * sizeof(SANE_Int));
+ xsane.preview_gamma_data_green = malloc(256 * sizeof(SANE_Int));
+ xsane.preview_gamma_data_blue = malloc(256 * sizeof(SANE_Int));
+
+ xsane.histogram_gamma_data_red = malloc(256 * sizeof(SANE_Int));
+ xsane.histogram_gamma_data_green = malloc(256 * sizeof(SANE_Int));
+ xsane.histogram_gamma_data_blue = malloc(256 * sizeof(SANE_Int));
+ }
+
+ xsane_create_gamma_curve(xsane.preview_gamma_data_red, xsane.negative,
+ xsane.gamma * xsane.gamma_red * preferences.preview_gamma * preferences.preview_gamma_red,
+ xsane.brightness + xsane.brightness_red,
+ xsane.contrast + xsane.contrast_red, 256, 255);
+
+ xsane_create_gamma_curve(xsane.preview_gamma_data_green, xsane.negative,
+ xsane.gamma * xsane.gamma_green * preferences.preview_gamma * preferences.preview_gamma_green,
+ xsane.brightness + xsane.brightness_green,
+ xsane.contrast + xsane.contrast_green, 256, 255);
+
+ xsane_create_gamma_curve(xsane.preview_gamma_data_blue, xsane.negative,
+ xsane.gamma * xsane.gamma_blue * preferences.preview_gamma * preferences.preview_gamma_blue,
+ xsane.brightness + xsane.brightness_blue,
+ xsane.contrast + xsane.contrast_blue , 256, 255);
+
+ xsane_create_gamma_curve(xsane.histogram_gamma_data_red, xsane.negative,
+ xsane.gamma * xsane.gamma_red,
+ xsane.brightness + xsane.brightness_red,
+ xsane.contrast + xsane.contrast_red, 256, 255);
+
+ xsane_create_gamma_curve(xsane.histogram_gamma_data_green, xsane.negative,
+ xsane.gamma * xsane.gamma_green,
+ xsane.brightness + xsane.brightness_green,
+ xsane.contrast + xsane.contrast_green, 256, 255);
+
+ xsane_create_gamma_curve(xsane.histogram_gamma_data_blue, xsane.negative,
+ xsane.gamma * xsane.gamma_blue,
+ xsane.brightness + xsane.brightness_blue,
+ xsane.contrast + xsane.contrast_blue , 256, 255);
+
+ preview_gamma_correction(xsane.preview,
+ xsane.preview_gamma_data_red, xsane.preview_gamma_data_green, xsane.preview_gamma_data_blue,
+ xsane.histogram_gamma_data_red, xsane.histogram_gamma_data_green, xsane.histogram_gamma_data_blue);
+
+ }
+ xsane_update_histogram();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_enhancement_update(void)
+{
+ guint sig_changed=0;
+
+ if (xsane.param.depth == 1) /* lineart? no gamma */
+ {
+ return;
+ }
+
+ sig_changed = gtk_signal_lookup("changed", GTK_OBJECT_TYPE(xsane.gamma_widget));
+
+ GTK_ADJUSTMENT(xsane.gamma_widget)->value = xsane.gamma;
+ GTK_ADJUSTMENT(xsane.brightness_widget)->value = xsane.brightness;
+ GTK_ADJUSTMENT(xsane.contrast_widget)->value = xsane.contrast;
+
+ if ( (xsane.xsane_color) && (!xsane.enhancement_rgb_default) )
+ {
+ GTK_ADJUSTMENT(xsane.gamma_red_widget)->value = xsane.gamma_red;
+ GTK_ADJUSTMENT(xsane.gamma_green_widget)->value = xsane.gamma_green;
+ GTK_ADJUSTMENT(xsane.gamma_blue_widget)->value = xsane.gamma_blue;
+
+ GTK_ADJUSTMENT(xsane.brightness_red_widget)->value = xsane.brightness_red;
+ GTK_ADJUSTMENT(xsane.brightness_green_widget)->value = xsane.brightness_green;
+ GTK_ADJUSTMENT(xsane.brightness_blue_widget)->value = xsane.brightness_blue;
+
+ GTK_ADJUSTMENT(xsane.contrast_red_widget)->value = xsane.contrast_red;
+ GTK_ADJUSTMENT(xsane.contrast_green_widget)->value = xsane.contrast_green;
+ GTK_ADJUSTMENT(xsane.contrast_blue_widget)->value = xsane.contrast_blue;
+
+ gtk_signal_emit(xsane.gamma_red_widget, sig_changed);
+ gtk_signal_emit(xsane.gamma_green_widget, sig_changed);
+ gtk_signal_emit(xsane.gamma_blue_widget, sig_changed);
+
+ gtk_signal_emit(xsane.brightness_red_widget, sig_changed);
+ gtk_signal_emit(xsane.brightness_green_widget, sig_changed);
+ gtk_signal_emit(xsane.brightness_blue_widget, sig_changed);
+
+ gtk_signal_emit(xsane.contrast_red_widget, sig_changed);
+ gtk_signal_emit(xsane.contrast_green_widget, sig_changed);
+ gtk_signal_emit(xsane.contrast_blue_widget, sig_changed);
+
+ }
+
+ gtk_signal_emit(xsane.gamma_widget, sig_changed);
+ gtk_signal_emit(xsane.brightness_widget, sig_changed);
+ gtk_signal_emit(xsane.contrast_widget, sig_changed);
+
+ xsane_update_sliders(); /* update histogram slider */
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_gamma_to_histogram(double *min, double *mid, double *max,
+ double contrast, double brightness, double gamma)
+{
+ double m;
+ double b;
+
+ m = 1.0 + contrast/100.0;
+ b = (1.0 + brightness/100.0) * 50.0;
+
+ if (m > 0)
+ {
+ *min = 50.0 - b/m;
+ *mid = (100.0 * pow(0.5, gamma)-b) / m + 50.0;
+ *max = (100.0-b)/m + 50.0;
+ }
+ else
+ {
+ *min = 0.0;
+ *mid = 50.0;
+ *max = 100.0;
+ }
+
+ xsane_bound_double(min, 0.0, 99.0);
+ xsane_bound_double(max, 1.0, 100.0);
+ xsane_bound_double(mid, *min+1, *max-1);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_enhancement_by_gamma(void)
+{
+ double min, mid, max;
+ double contrast, brightness, gamma;
+
+ xsane_gamma_to_histogram(&min, &mid, &max, xsane.contrast, xsane.brightness, xsane.gamma);
+
+ xsane.slider_gray.value[0] = min;
+ xsane.slider_gray.value[1] = mid;
+ xsane.slider_gray.value[2] = max;
+
+
+ /* red */
+ contrast = xsane.contrast + xsane.contrast_red;
+ brightness = xsane.brightness + xsane.brightness_red;
+ gamma = xsane.gamma * xsane.gamma_red;
+
+ if (contrast < -100.0)
+ {
+ contrast = -100.0;
+ }
+
+ xsane_gamma_to_histogram(&min, &mid, &max, contrast, brightness, gamma);
+
+ xsane.slider_red.value[0] = min;
+ xsane.slider_red.value[1] = mid;
+ xsane.slider_red.value[2] = max;
+
+
+ /* green */
+ contrast = xsane.contrast + xsane.contrast_green;
+ brightness = xsane.brightness + xsane.brightness_green;
+ gamma = xsane.gamma * xsane.gamma_green;
+
+ if (contrast < -100.0)
+ {
+ contrast = -100.0;
+ }
+
+ xsane_gamma_to_histogram(&min, &mid, &max, contrast, brightness, gamma);
+
+ xsane.slider_green.value[0] = min;
+ xsane.slider_green.value[1] = mid;
+ xsane.slider_green.value[2] = max;
+
+
+ /* blue */
+ contrast = xsane.contrast + xsane.contrast_blue;
+ brightness = xsane.brightness + xsane.brightness_blue;
+ gamma = xsane.gamma * xsane.gamma_blue;
+
+ if (contrast < -100.0)
+ {
+ contrast = -100.0;
+ }
+
+ xsane_gamma_to_histogram(&min, &mid, &max,
+ xsane.contrast + xsane.contrast_blue,
+ xsane.brightness + xsane.brightness_blue,
+ xsane.gamma * xsane.gamma_blue);
+
+ xsane.slider_blue.value[0] = min;
+ xsane.slider_blue.value[1] = mid;
+ xsane.slider_blue.value[2] = max;
+
+
+ xsane_enhancement_update();
+ xsane_update_gamma();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_enhancement_restore_default()
+{
+ xsane.gamma = 1.0;
+ xsane.gamma_red = 1.0;
+ xsane.gamma_green = 1.0;
+ xsane.gamma_blue = 1.0;
+
+ xsane.brightness = 0.0;
+ xsane.brightness_red = 0.0;
+ xsane.brightness_green = 0.0;
+ xsane.brightness_blue = 0.0;
+
+ xsane.contrast = 0.0;
+ xsane.contrast_red = 0.0;
+ xsane.contrast_green = 0.0;
+ xsane.contrast_blue = 0.0;
+
+ xsane_enhancement_by_gamma();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_enhancement_restore()
+{
+ xsane.gamma = preferences.xsane_gamma;
+ xsane.gamma_red = preferences.xsane_gamma_red;
+ xsane.gamma_green = preferences.xsane_gamma_green;
+ xsane.gamma_blue = preferences.xsane_gamma_blue;
+
+ xsane.brightness = preferences.xsane_brightness;
+ xsane.brightness_red = preferences.xsane_brightness_red;
+ xsane.brightness_green = preferences.xsane_brightness_green;
+ xsane.brightness_blue = preferences.xsane_brightness_blue;
+
+ xsane.contrast = preferences.xsane_contrast;
+ xsane.contrast_red = preferences.xsane_contrast_red;
+ xsane.contrast_green = preferences.xsane_contrast_green;
+ xsane.contrast_blue = preferences.xsane_contrast_blue;
+
+ xsane.enhancement_rgb_default = preferences.xsane_rgb_default;
+ xsane.negative = preferences.xsane_negative;
+
+ xsane_refresh_dialog(dialog);
+ xsane_enhancement_by_gamma();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_enhancement_store()
+{
+ preferences.xsane_gamma = xsane.gamma;
+ preferences.xsane_gamma_red = xsane.gamma_red;
+ preferences.xsane_gamma_green = xsane.gamma_green;
+ preferences.xsane_gamma_blue = xsane.gamma_blue;
+
+ preferences.xsane_brightness = xsane.brightness;
+ preferences.xsane_brightness_red = xsane.brightness_red;
+ preferences.xsane_brightness_green = xsane.brightness_green;
+ preferences.xsane_brightness_blue = xsane.brightness_blue;
+
+ preferences.xsane_contrast = xsane.contrast;
+ preferences.xsane_contrast_red = xsane.contrast_red;
+ preferences.xsane_contrast_green = xsane.contrast_green;
+ preferences.xsane_contrast_blue = xsane.contrast_blue;
+
+ preferences.xsane_rgb_default = xsane.enhancement_rgb_default;
+ preferences.xsane_negative = xsane.negative;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_histogram_to_gamma(XsaneSlider *slider, double *contrast, double *brightness, double *gamma)
+{
+ double mid;
+ double range;
+
+ *contrast = (10000.0 / (slider->value[2] - slider->value[0]) - 100.0);
+ *brightness = - (slider->value[0] - 50.0) * (*contrast + 100.0)/50.0 - 100.0;
+
+ mid = slider->value[1] - slider->value[0];
+ range = slider->value[2] - slider->value[0];
+
+ *gamma = log(mid/range) / log(0.5);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_enhancement_by_histogram(void)
+{
+ double gray_brightness;
+ double gray_contrast;
+ double gray_gamma;
+ double brightness;
+ double contrast;
+ double gamma;
+
+ xsane_histogram_to_gamma(&xsane.slider_gray, &gray_contrast, &gray_brightness, &gray_gamma);
+
+ xsane.gamma = gray_gamma;
+ xsane.brightness = gray_brightness;
+ xsane.contrast = gray_contrast;
+
+ if ( (xsane.xsane_color) && (!xsane.enhancement_rgb_default) ) /* rgb sliders active */
+ {
+ if ((xsane.slider_gray.active == XSANE_SLIDER_ACTIVE) ||
+ (xsane.slider_gray.active == XSANE_SLIDER_INACTIVE)) /* gray slider not moved */
+ {
+ xsane_histogram_to_gamma(&xsane.slider_red, &contrast, &brightness, &gamma);
+
+ xsane.gamma_red = gamma / gray_gamma;
+ xsane.brightness_red = brightness - gray_brightness;
+ xsane.contrast_red = contrast - gray_contrast;
+
+ xsane_histogram_to_gamma(&xsane.slider_green, &contrast, &brightness, &gamma);
+
+ xsane.gamma_green = gamma / gray_gamma;
+ xsane.brightness_green = brightness - gray_brightness;
+ xsane.contrast_green = contrast - gray_contrast;
+
+ xsane_histogram_to_gamma(&xsane.slider_blue, &contrast, &brightness, &gamma);
+
+ xsane.gamma_blue = gamma / gray_gamma;
+ xsane.brightness_blue = brightness - gray_brightness;
+ xsane.contrast_blue = contrast - gray_contrast;
+
+ xsane_enhancement_update();
+ xsane_update_gamma();
+ }
+ else /* gray slider was moved in rgb-mode */
+ {
+ xsane_enhancement_by_gamma();
+ }
+ }
+ else /* rgb sliders not active */
+ {
+ xsane_enhancement_update();
+ xsane_update_gamma();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static gint xsane_histogram_win_delete(GtkWidget *widget, gpointer data)
+{
+ gtk_widget_hide(widget);
+ preferences.show_histogram = FALSE;
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_histogram_widget), preferences.show_histogram);
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_create_histogram_dialog(const char *devicetext)
+{
+ char windowname[255];
+ GtkWidget *xsane_color_hbox;
+ GtkWidget *xsane_histogram_vbox;
+ GdkColor color_black;
+ GdkColor color_red;
+ GdkColor color_green;
+ GdkColor color_blue;
+ GdkColor color_backg;
+ GdkColormap *colormap;
+ GtkStyle *style;
+
+ xsane.histogram_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
+ gtk_window_set_policy(GTK_WINDOW(xsane.histogram_dialog), FALSE, FALSE, FALSE);
+ gtk_widget_set_uposition(xsane.histogram_dialog, XSANE_HISTOGRAM_POS_X, XSANE_HISTOGRAM_POS_Y);
+ gtk_signal_connect(GTK_OBJECT(xsane.histogram_dialog), "delete_event", GTK_SIGNAL_FUNC(xsane_histogram_win_delete), 0);
+ sprintf(windowname, "%s %s", WINDOW_HISTOGRAM, devicetext);
+ gtk_window_set_title(GTK_WINDOW(xsane.histogram_dialog), windowname);
+ xsane_set_window_icon(xsane.histogram_dialog, 0);
+
+ xsane_histogram_vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(xsane_histogram_vbox), 5);
+ gtk_container_add(GTK_CONTAINER(xsane.histogram_dialog), xsane_histogram_vbox);
+ gtk_widget_show(xsane_histogram_vbox);
+
+
+ /* set gc for histogram drawing */
+ gtk_widget_realize(xsane.histogram_dialog); /* realize dialog to get colors and style */
+
+ style = gtk_widget_get_style(xsane.histogram_dialog);
+/*
+ style = gtk_rc_get_style(xsane.histogram_dialog);
+ style = gtk_widget_get_default_style();
+*/
+
+ xsane.gc_trans = style->bg_gc[GTK_STATE_NORMAL];
+ xsane.bg_trans = &style->bg[GTK_STATE_NORMAL];
+
+ colormap = gdk_window_get_colormap(xsane.histogram_dialog->window);
+
+ xsane.gc_black = gdk_gc_new(xsane.histogram_dialog->window);
+ color_black.red = 0;
+ color_black.green = 0;
+ color_black.blue = 0;
+ gdk_color_alloc(colormap, &color_black);
+ gdk_gc_set_foreground(xsane.gc_black, &color_black);
+
+ xsane.gc_red = gdk_gc_new(xsane.histogram_dialog->window);
+ color_red.red = 40000;
+ color_red.green = 10000;
+ color_red.blue = 10000;
+ gdk_color_alloc(colormap, &color_red);
+ gdk_gc_set_foreground(xsane.gc_red, &color_red);
+
+ xsane.gc_green = gdk_gc_new(xsane.histogram_dialog->window);
+ color_green.red = 10000;
+ color_green.green = 40000;
+ color_green.blue = 10000;
+ gdk_color_alloc(colormap, &color_green);
+ gdk_gc_set_foreground(xsane.gc_green, &color_green);
+
+ xsane.gc_blue = gdk_gc_new(xsane.histogram_dialog->window);
+ color_blue.red = 10000;
+ color_blue.green = 10000;
+ color_blue.blue = 40000;
+ gdk_color_alloc(colormap, &color_blue);
+ gdk_gc_set_foreground(xsane.gc_blue, &color_blue);
+
+ xsane.gc_backg = gdk_gc_new(xsane.histogram_dialog->window);
+ color_backg.red = 50000;
+ color_backg.green = 50000;
+ color_backg.blue = 50000;
+ gdk_color_alloc(colormap, &color_backg);
+ gdk_gc_set_foreground(xsane.gc_backg, &color_backg);
+
+
+ /* add histogram images and sliders */
+
+ xsane_create_histogram(xsane_histogram_vbox, FRAME_RAW_IMAGE, 256, 100, &(xsane.histogram_raw));
+
+ xsane_separator_new(xsane_histogram_vbox, 0);
+
+ xsane.slider_gray.r = 1;
+ xsane.slider_gray.g = 1;
+ xsane.slider_gray.b = 1;
+ xsane.slider_gray.active = XSANE_SLIDER_ACTIVE;
+ xsane_create_slider(&xsane.slider_gray);
+ gtk_box_pack_start(GTK_BOX(xsane_histogram_vbox), xsane.slider_gray.preview, FALSE, FALSE, 0);
+ gtk_widget_show(xsane.slider_gray.preview);
+ gtk_widget_realize(xsane.slider_gray.preview);
+
+ xsane_separator_new(xsane_histogram_vbox, 0);
+
+ xsane.slider_red.r = 1;
+ xsane.slider_red.g = 0;
+ xsane.slider_red.b = 0;
+ xsane.slider_red.active = XSANE_SLIDER_ACTIVE;
+ xsane_create_slider(&xsane.slider_red);
+ gtk_box_pack_start(GTK_BOX(xsane_histogram_vbox), xsane.slider_red.preview, FALSE, FALSE, 0);
+ gtk_widget_show(xsane.slider_red.preview);
+ gtk_widget_realize(xsane.slider_red.preview);
+
+ xsane_separator_new(xsane_histogram_vbox, 0);
+
+ xsane.slider_green.r = 0;
+ xsane.slider_green.g = 1;
+ xsane.slider_green.b = 0;
+ xsane.slider_green.active = XSANE_SLIDER_ACTIVE;
+ xsane_create_slider(&xsane.slider_green);
+ gtk_box_pack_start(GTK_BOX(xsane_histogram_vbox), xsane.slider_green.preview, FALSE, FALSE, 0);
+ gtk_widget_show(xsane.slider_green.preview);
+ gtk_widget_realize(xsane.slider_green.preview);
+
+ xsane_separator_new(xsane_histogram_vbox, 0);
+
+ xsane.slider_blue.r = 0;
+ xsane.slider_blue.g = 0;
+ xsane.slider_blue.b = 1;
+ xsane.slider_blue.active = XSANE_SLIDER_ACTIVE;
+ xsane_create_slider(&xsane.slider_blue);
+ gtk_box_pack_start(GTK_BOX(xsane_histogram_vbox), xsane.slider_blue.preview, FALSE, FALSE, 0);
+ gtk_widget_show(xsane.slider_blue.preview);
+ gtk_widget_realize(xsane.slider_blue.preview);
+
+ xsane_draw_slider_level(&xsane.slider_gray);
+ xsane_draw_slider_level(&xsane.slider_red);
+ xsane_draw_slider_level(&xsane.slider_green);
+ xsane_draw_slider_level(&xsane.slider_blue);
+
+ xsane_separator_new(xsane_histogram_vbox, 0);
+
+ xsane_create_histogram(xsane_histogram_vbox, FRAME_ENHANCED_IMAGE, 256, 100, &(xsane.histogram_enh));
+
+ xsane_color_hbox = gtk_hbox_new(TRUE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(xsane_color_hbox), 5);
+ gtk_container_add(GTK_CONTAINER(xsane_histogram_vbox), xsane_color_hbox);
+ gtk_widget_show(xsane_color_hbox);
+
+ xsane_toggle_button_new_with_pixmap(xsane_color_hbox, intensity_xpm, DESC_HIST_INTENSITY,
+ &xsane.histogram_int, xsane_histogram_toggle_button_callback);
+ xsane_toggle_button_new_with_pixmap(xsane_color_hbox, red_xpm, DESC_HIST_RED,
+ &xsane.histogram_red, xsane_histogram_toggle_button_callback);
+ xsane_toggle_button_new_with_pixmap(xsane_color_hbox, green_xpm, DESC_HIST_GREEN,
+ &xsane.histogram_green, xsane_histogram_toggle_button_callback);
+ xsane_toggle_button_new_with_pixmap(xsane_color_hbox, blue_xpm, DESC_HIST_BLUE,
+ &xsane.histogram_blue, xsane_histogram_toggle_button_callback);
+ xsane_toggle_button_new_with_pixmap(xsane_color_hbox, pixel_xpm, DESC_HIST_PIXEL,
+ &xsane.histogram_lines, xsane_histogram_toggle_button_callback);
+ xsane_toggle_button_new_with_pixmap(xsane_color_hbox, log_xpm, DESC_HIST_LOG,
+ &xsane.histogram_log, xsane_histogram_toggle_button_callback);
+
+ gtk_widget_show(xsane_color_hbox);
+
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
diff --git a/frontend/xsane-gamma.h b/frontend/xsane-gamma.h
new file mode 100644
index 0000000..ed70175
--- /dev/null
+++ b/frontend/xsane-gamma.h
@@ -0,0 +1,48 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-gamma.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include <sane/sane.h>
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+extern void xsane_clear_histogram(XsanePixmap *hist);
+extern void xsane_draw_slider_level(XsaneSlider *slider);
+extern void xsane_update_slider(XsaneSlider *slider);
+extern void xsane_update_sliders(void);
+extern void xsane_create_slider(XsaneSlider *slider);
+extern void xsane_create_histogram(GtkWidget *parent, const char *title, int width, int height, XsanePixmap *hist);
+extern void xsane_calculate_histogram(void);
+extern void xsane_update_histogram(void);
+extern void xsane_histogram_toggle_button_callback(GtkWidget *widget, gpointer data);
+extern void xsane_create_gamma_curve(SANE_Int *gammadata, int negative, double gamma,
+ double brightness, double contrast, int numbers, int maxout);
+extern void xsane_update_gamma(void);
+extern void xsane_enhancement_by_gamma(void);
+extern void xsane_enhancement_restore_default(void);
+extern void xsane_enhancement_restore(void);
+extern void xsane_enhancement_store(void);
+extern void xsane_enhancement_by_histogram(void);
+extern void xsane_create_histogram_dialog(const char *devicetext);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
diff --git a/frontend/xsane-icons.c b/frontend/xsane-icons.c
new file mode 100644
index 0000000..3165ab6
--- /dev/null
+++ b/frontend/xsane-icons.c
@@ -0,0 +1,1751 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-icons.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* --------------------------------------------------- */
+
+const char *xsane_window_icon_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 14 16 6 1",
+/* color */
+" none",
+"b c #2020e0",
+"w c #eeeeee",
+"x c #fff098",
+"y c #f0e890",
+"z c #e8d880",
+/* pixel */
+" xxyyyyy ",
+" xyyyyyyyyyyz ",
+"xyyyyzzyyyyyyz",
+"xyxz zyyz",
+"xxy yyz",
+" yyyy yy ",
+" zyyyyz yyy ",
+" zzzz yxz",
+" zbbbbz yyz",
+" x zbbwwbbz yy",
+"xy zbbwwbbz yy",
+"xyy zbbbbz xyy",
+"xyyy zzzz xyyz",
+"xyyyyyyyyyyyz ",
+" zyyyyyyyyz ",
+" zzzzzzz "
+};
+
+/* --------------------------------------------------- */
+
+const char *error_xpm[] =
+{
+"40 40 6 1",
+" c None",
+". c #FE0809",
+"+ c #FDFDFC",
+"@ c #E5D5D6",
+"# c #E1A09F",
+"$ c #FE8081",
+" #@@++++@@# ",
+" #@++++++++++++@# ",
+" #+++@#$......$$@+++# ",
+" #+++#..............#+++# ",
+" #++#..................@++# ",
+" @++$....................$++@ ",
+" @+@........................++@ ",
+" #+@$.........................@+# ",
+" #++...........................$++# ",
+" ++$.....@+...............++....$++ ",
+" #+#.....+++@.............+++@....#+# ",
+" ++.....$+++++...........@++++$....++ ",
+" #+#......#++++@.........+++++#.....#+# ",
+" @+........#+++++.......@++++$.......+@ ",
+" +@.........$++++@.....+++++#........++ ",
+"#+$..........#+++++...@++++$.........$+#",
+"@+$...........$+++++$@++++#..........$+@",
+"@+.............#+++++++++$............+@",
+"++..............$+++++++#.............++",
+"++...............#+++++#..............++",
+"++...............++++++@..............++",
+"++..............@++++++++.............++",
+"@+.............+++++@++++@............+@",
+"@+$...........@++++#.#+++++..........$+@",
+"#+$..........+++++$...$++++@.........$+#",
+" +@.........@++++#.....#+++++........@+ ",
+" @+........+++++$.......$++++@.......+@ ",
+" #+#......@++++#.........#+++++.....#+# ",
+" ++.....#++++#...........$++++$....++ ",
+" @+#.....$++$.............#++$....#+# ",
+" ++$.....#$...............$#....$++ ",
+" #++............................++# ",
+" #++..........................++# ",
+" @++........................++@ ",
+" @++$....................$++@ ",
+" #++#..................#++# ",
+" #+++#..............#+++# ",
+" #+++@$$......$$++++# ",
+" #+++++++++++++@# ",
+" #@@++++@@# "
+};
+
+/* --------------------------------------------------- */
+
+const char *warning_xpm[] =
+{
+"40 40 4 1",
+" c None",
+". c #FFFFFF",
+"+ c #FFFA00",
+"@ c #000000",
+" ",
+" .. ",
+" .. ",
+" .++. ",
+" .++. ",
+" .++++. ",
+" .++++. ",
+" .++++++. ",
+" .++++++. ",
+" .++++++++. ",
+" .++++++++. ",
+" .++++++++++. ",
+" .++++@@++++. ",
+" .++++@@@@++++. ",
+" .+++@@@@@@+++. ",
+" .++++@@@@@@++++. ",
+" .+++@@@@@@@@+++. ",
+" .++++@@@@@@@@++++. ",
+" .++++@@@@@@@@++++. ",
+" .++++++@@@@@@++++++. ",
+" .++++++@@@@@@++++++. ",
+" .+++++++@@@@@@+++++++. ",
+" .+++++++@@@@@@+++++++. ",
+" .+++++++++@@@@+++++++++. ",
+" .+++++++++@@@@+++++++++. ",
+" .++++++++++@@@@++++++++++. ",
+" .+++++++++++@@+++++++++++. ",
+" .++++++++++++++++++++++++++. ",
+" .++++++++++++++++++++++++++. ",
+" .++++++++++++++++++++++++++++. ",
+" .+++++++++++++@@+++++++++++++. ",
+" .+++++++++++++@@@@+++++++++++++. ",
+" .+++++++++++++@@@@+++++++++++++. ",
+" .+++++++++++++++@@+++++++++++++++. ",
+" .++++++++++++++++++++++++++++++++. ",
+" .++++++++++++++++++++++++++++++++++. ",
+" .++++++++++++++++++++++++++++++++++. ",
+" ..++++++++++++++++++++++++++++++++++.. ",
+" ...................................... ",
+"........................................"
+};
+
+/* --------------------------------------------------- */
+
+const char *file_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+"* c #000000",
+". c #707070",
+"- c #e0e0e0",
+" none",
+/* pixels */
+" ",
+" ................ ",
+" .*............*. ",
+" .*............*. ",
+" ................ ",
+" ................ ",
+" ................ ",
+" ................ ",
+" ................ ",
+" ................ ",
+" ................ ",
+" ................ ",
+" ....--------.... ",
+" ....-...----.... ",
+" ....-...----.... ",
+" ....-...----.... ",
+" ....-...----.... ",
+" ............... ",
+" ",
+" "
+};
+
+/* --------------------------------------------------- */
+
+const char *fax_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+"* c #000000",
+". c #ffffff",
+"- c #e0e0e0",
+" none",
+/* pixels */
+" ",
+" ",
+" ",
+" .*********** ",
+" ..*.........* ",
+" ...*..**.*.*.* ",
+" ****.........* ",
+" *............* ",
+" *...*...***..* ",
+" *............* ",
+" *.*.*.**.**..* ",
+" *............* ",
+" *..*....*..*.* ",
+" *............* ",
+" *.**..*....*.* ",
+" *............* ",
+" ************** ",
+" ",
+" ",
+" "
+};
+
+/* --------------------------------------------------- */
+
+const char *faxreceiver_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+"* c #000000",
+"- c #e0e0e0",
+" none",
+/* pixels */
+" ",
+" ********* ",
+" *****-----* ",
+" ****--------* ",
+" ****-----***-* ",
+" ****----*----* ",
+" ***-------**--* ",
+" ***------------* ",
+" **--------------* ",
+" **------------*** ",
+" **-----------* ",
+" **----------* ",
+" *-------*---* ",
+" *--------*** ",
+" *----------* ",
+" *----------* ",
+" *----------* ",
+" *---------* ",
+" *------*** ",
+" *----* ",
+};
+
+/* --------------------------------------------------- */
+
+const char *colormode_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 20 20 7 1",
+/* colors */
+". c #000000",
+"# c #0000ff",
+"a c #00ff00",
+"b c #7f7f7f",
+"d c #ff0000",
+"e c #ffffff",
+" none",
+/* pixels */
+" ",
+" eeeee ",
+" eeeee...... ",
+" eeeee......ddddddd ",
+" eeeee......ddddddd ",
+" eeeee......ddddddd ",
+" eeeee......ddddddd ",
+" eeeeebbbbbbddddddd ",
+" eeeeebbbbbbddddddd ",
+" .....bbbbbbaaaaaaa ",
+" .....bbbbbbaaaaaaa ",
+" .....bbbbbbaaaaaaa ",
+" .....eeeeeeaaaaaaa ",
+" .....eeeeeeaaaaaaa ",
+" .....eeeeee####### ",
+" .....eeeeee####### ",
+" .....eeeeee####### ",
+" eeeeee####### ",
+" ####### ",
+" "
+};
+
+/* --------------------------------------------------- */
+
+const char *Gamma_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 2 1",
+/* colors */
+". c #000000",
+" none",
+/* pixels */
+" ",
+" ",
+" ............. ",
+" ... .. ",
+" .. .. ",
+" .. . ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .... ",
+" ........ ",
+" "
+};
+
+/* --------------------------------------------------- */
+
+const char *Gamma_red_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 2 1",
+/* colors */
+". c #ff0000",
+" none",
+/* pixels */
+" ",
+" ",
+" ",
+" ............. ",
+" ... .. ",
+" .. .. ",
+" .. . ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .... ",
+" ........ ",
+};
+
+/* --------------------------------------------------- */
+
+const char *Gamma_green_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 2 1",
+/* colors */
+". c #00ff00",
+" none",
+/* pixels */
+" ",
+" ",
+" ",
+" ............. ",
+" ... .. ",
+" .. .. ",
+" .. . ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .... ",
+" ........ ",
+};
+
+/* --------------------------------------------------- */
+
+const char *Gamma_blue_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 2 1",
+/* colors */
+". c #0000ff",
+" none",
+/* pixels */
+" ",
+" ",
+" ",
+" ............. ",
+" ... .. ",
+" .. .. ",
+" .. . ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .... ",
+" ........ ",
+};
+
+/* --------------------------------------------------- */
+
+const char *brightness_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ffffff",
+/* pixels */
+" ",
+" ",
+" ",
+" . ",
+" . ",
+" . ",
+" . ... . ",
+" . .aaa. . ",
+" .aaaaa. ",
+" .aaaaaaa. ",
+" .aaaaaaaaa. ",
+" ....aaaaaaaaa.... ",
+" .aaaaaaaaa. ",
+" .aaaaaaa. ",
+" .aaaaa. ",
+" . .aaa. . ",
+" . ... . ",
+" . ",
+" . ",
+" . ",
+};
+
+/* --------------------------------------------------- */
+
+const char *brightness_red_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ff0000",
+/* pixels */
+" ",
+" ",
+" ",
+" . ",
+" . ",
+" . ",
+" . ... . ",
+" . .aaa. . ",
+" .aaaaa. ",
+" .aaaaaaa. ",
+" .aaaaaaaaa. ",
+" ....aaaaaaaaa.... ",
+" .aaaaaaaaa. ",
+" .aaaaaaa. ",
+" .aaaaa. ",
+" . .aaa. . ",
+" . ... . ",
+" . ",
+" . ",
+" . ",
+};
+
+/* --------------------------------------------------- */
+
+const char *brightness_green_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #00ff00",
+/* pixels */
+" ",
+" ",
+" ",
+" . ",
+" . ",
+" . ",
+" . ... . ",
+" . .aaa. . ",
+" .aaaaa. ",
+" .aaaaaaa. ",
+" .aaaaaaaaa. ",
+" ....aaaaaaaaa.... ",
+" .aaaaaaaaa. ",
+" .aaaaaaa. ",
+" .aaaaa. ",
+" . .aaa. . ",
+" . ... . ",
+" . ",
+" . ",
+" . ",
+};
+
+const char *brightness_blue_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #0000ff",
+/* pixels */
+" ",
+" ",
+" ",
+" . ",
+" . ",
+" . ",
+" . ... . ",
+" . .aaa. . ",
+" .aaaaa. ",
+" .aaaaaaa. ",
+" .aaaaaaaaa. ",
+" ....aaaaaaaaa.... ",
+" .aaaaaaaaa. ",
+" .aaaaaaa. ",
+" .aaaaa. ",
+" . .aaa. . ",
+" . ... . ",
+" . ",
+" . ",
+" . ",
+};
+
+/* --------------------------------------------------- */
+
+const char *contrast_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ffffff",
+/* pixels */
+" ",
+" ",
+" ",
+" ..... ",
+" .....aa.. ",
+" ......aaaa.. ",
+" .......aaaaa. ",
+" ........aaaaaa. ",
+" ........aaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" ........aaaaaa. ",
+" ........aaaaaa. ",
+" .......aaaaa. ",
+" ......aaaa.. ",
+" .....aa.. ",
+" ..... ",
+};
+
+/* --------------------------------------------------- */
+
+const char *contrast_red_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ff0000",
+/* pixels */
+" ",
+" ",
+" ",
+" ..... ",
+" .....aa.. ",
+" ......aaaa.. ",
+" .......aaaaa. ",
+" ........aaaaaa. ",
+" ........aaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" ........aaaaaa. ",
+" ........aaaaaa. ",
+" .......aaaaa. ",
+" ......aaaa.. ",
+" .....aa.. ",
+" ..... ",
+};
+
+/* --------------------------------------------------- */
+
+const char *contrast_green_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #00ff00",
+/* pixels */
+" ",
+" ",
+" ",
+" ..... ",
+" .....aa.. ",
+" ......aaaa.. ",
+" .......aaaaa. ",
+" ........aaaaaa. ",
+" ........aaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" ........aaaaaa. ",
+" ........aaaaaa. ",
+" .......aaaaa. ",
+" ......aaaa.. ",
+" .....aa.. ",
+" ..... ",
+};
+
+/* --------------------------------------------------- */
+
+const char *contrast_blue_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #0000ff",
+/* pixels */
+" ",
+" ",
+" ",
+" ..... ",
+" .....aa.. ",
+" ......aaaa.. ",
+" .......aaaaa. ",
+" ........aaaaaa. ",
+" ........aaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" .........aaaaaaa. ",
+" ........aaaaaa. ",
+" ........aaaaaa. ",
+" .......aaaaa. ",
+" ......aaaa.. ",
+" .....aa.. ",
+" ..... ",
+};
+
+/* --------------------------------------------------- */
+
+const char *rgb_default_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 6 1",
+/* colors */
+" none",
+"w c #ffffff",
+"k c #000000",
+"r c #ff0000",
+"g c #00ff00",
+"b c #0000ff",
+/* pixels */
+" ",
+" rr gg kk bb ",
+" rr gg kk bb ",
+" rr gg kk bb ",
+" rr gg kk bb ",
+" rr gg kk bb ",
+" rr gg kk bb ",
+" r g k b ",
+" r g k b ",
+" r g k b ",
+" rg k b ",
+" rg k b ",
+" rgkkb ",
+" kk ",
+" kk ",
+" kk ",
+" kk ",
+" kk ",
+" kk ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *negative_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 16 1",
+/* colors */
+" none",
+"W c #b0b0b0",
+"r c #a01010",
+"g c #20a010",
+"b c #0020a0",
+"c c #10a0a0",
+"m c #a000a0",
+"y c #a0a000",
+"W c #ffffff",
+"R c #ff0000",
+"G c #00ff00",
+"B c #0000ff",
+"C c #00ffff",
+"M c #ff00ff",
+"Y c #ffff00",
+"k c #000000",
+/* pixels */
+" ",
+" YYYYYYYYYYYYYYYYYk ",
+" kkYYYYYYYYYkkCCbkB ",
+" kkkYYYYYYkkkkCCkBB ",
+" YYYYYYYYYYCCCCkRRB ",
+" YYYYYkkYYYYCCkRWWW ",
+" YYYYYYYYYYYYkRRBWW ",
+" YYkkYYYYYYkkWBBBBB ",
+" YkkYYYYYYkkWWWBBBB ",
+" YYYYYYYYYkBBBBBBBB ",
+" MMMMMMMMkGGGGGGGGG ",
+" MMMMMMMkGGGGGGGGGG ",
+" MMMGMMkGGGGGGGgGGG ",
+" MMMCMkGGGGGGGGGgGG ",
+" MBMMkgGGRGGGGGGGGG ",
+" MMMkGGgGGGGGGGGgGG ",
+" MMkGGGGGgGGGGgGGGG ",
+" MkGGGGGGGgGGGGGGgG ",
+" kGGGGGGGGGGGGGGGGG ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *enhance_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 16 1",
+/* colors */
+" none",
+"w c #b0b0b0",
+"r c #a01010",
+"g c #20a010",
+"b c #0020a0",
+"c c #10a0a0",
+"m c #a000a0",
+"y c #a0a000",
+"W c #ffffff",
+"R c #ff0000",
+"G c #00ff00",
+"B c #0000ff",
+"C c #00ffff",
+"M c #ff00ff",
+"Y c #ffff00",
+"k c #000000",
+/* pixels */
+" ",
+" bbbbbbbbbbbbbbbbbk ",
+" wwbbbbbbbbbwwrrbkB ",
+" wwwbbbbbbwwwwrrkBB ",
+" bbbbbbbbbbrrrrkRRB ",
+" bbbbbwwbbbbrrkRWWW ",
+" bbbbbbbbbbbbkRRBWW ",
+" bbwwbbbbbbwkBBBBBB ",
+" bwwbbbbbbbkBWBBBBB ",
+" bbbbbbbbbkBBBBBBBB ",
+" ggggggggkGGGGGGGGG ",
+" gggmgggkGGGGGGGGGG ",
+" gggrggkGGGGGGGgGGG ",
+" gggggkGGGGGGGGGgGG ",
+" gyggkgGGRGGGGGGGGG ",
+" gggkGGgGGGGGGGGgGG ",
+" ggkGGGGGgGGGGgGGGG ",
+" gkGGGGGGGgGGGGGGgG ",
+" kGGGGGGGGGGGGGGGGG ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *store_enhancement_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 5 1",
+/* colors */
+"* c #000000",
+". c #606060",
+"- c #e0e0e0",
+"w c #ffffff",
+" none",
+/* pixels */
+" ",
+" * * ",
+" ** ** ",
+" *** *** ",
+" **** **** ",
+" ** ** ** ** ",
+" ** ** ** ** ",
+" ** ***** ** ",
+" ** *** ** ",
+" ** * ** ",
+" ** ** ",
+" ** ** ",
+" ** ** ",
+" ** ** ",
+" ** ** ",
+" ** ** ",
+" ** ** ",
+" ** ** ",
+" **** **** ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *restore_enhancement_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 5 1",
+/* colors */
+"* c #000000",
+". c #606060",
+"- c #e0e0e0",
+"w c #ffffff",
+" none",
+/* pixels */
+" ",
+" ************** ",
+" **************** ",
+" ** ** ",
+" ** ** ",
+" ** ** ",
+" ** ** ",
+" ** ** ",
+" ** ** ",
+" *************** ",
+" ************* ",
+" ** *** ",
+" ** ** ",
+" ** ** ",
+" ** *** ",
+" ** ** ",
+" ** *** ",
+" ** ** ",
+" **** **** ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *default_enhancement_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+"* c #000000",
+". c #606060",
+"- c #e0e0e0",
+" none",
+/* pixels */
+" ",
+" * ",
+" * ",
+" * ",
+" * ",
+" * ",
+" * * * ",
+" ** * ** ",
+" ** * ** ",
+" ******* * ******** ",
+" ** * ** ",
+" ** * ** ",
+" * * * ",
+" * ",
+" * ",
+" * ",
+" * ",
+" * ",
+" * ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *pipette_white_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ffffff",
+/* pixels */
+" ",
+" ",
+" .... ",
+" ...... ",
+" ...... ",
+" ......... ",
+" ...... ",
+" .aaa.. ",
+" .aaaa.. ",
+" .aaaa. . ",
+" .aaaa. . ",
+" .aaaa. ",
+" .aaaa. ",
+" .aaaa. ",
+" .aaaa. ",
+" .aaaa. ",
+" .aaaa. ",
+" .aaaa. ",
+" .aaa. ",
+" ... ",
+};
+
+/* --------------------------------------------------- */
+
+const char *pipette_gray_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ffffff",
+"X c #808080",
+/* pixels */
+" ",
+" ",
+" .... ",
+" ...... ",
+" ...... ",
+" ......... ",
+" ...... ",
+" .aaa.. ",
+" .aaaa.. ",
+" .aaaa. . ",
+" .XXXX. . ",
+" .XXXX. ",
+" .XXXX. ",
+" .XXXX. ",
+" .XXXX. ",
+" .XXXX. ",
+" .XXXX. ",
+" .XXXX. ",
+" .XXX. ",
+" ... ",
+};
+
+
+const char *pipette_black_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ffffff",
+/* pixels */
+" ",
+" ",
+" .... ",
+" ...... ",
+" ...... ",
+" ......... ",
+" ...... ",
+" .aaa.. ",
+" .aaaa.. ",
+" .aaaa. . ",
+" ...... . ",
+" ...... ",
+" ...... ",
+" ...... ",
+" ...... ",
+" ...... ",
+" ...... ",
+" ...... ",
+" ..... ",
+" ... ",
+};
+
+/* --------------------------------------------------- */
+
+const char *zoom_in_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+" none",
+". c #000000",
+"+ c #c0c0c0",
+"a c #ffffff",
+/* pixels */
+" ",
+" ",
+" ..... ",
+" .+aaaa. ",
+" .++aaaaa. ",
+" .+++aaaaaa. ",
+" .aaaaaaaaa. ",
+" .aaaaaaaaa. ",
+" .aaaaaaaaa. ",
+" .aaaaaaa. ",
+" .aaaaa. ",
+" ....... ",
+" ... ",
+" . ... ",
+" . ... ",
+" ..... ... ",
+" . ... ",
+" . .. ",
+" ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *zoom_not_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+" none",
+". c #000000",
+"+ c #c0c0c0",
+"a c #ffffff",
+/* pixels */
+" ",
+" ",
+" .. ..... .. ",
+" ...+aaaa. .. ",
+" ..+aaaaa. .. ",
+" .+..aaaaaa. .. ",
+" .aa..aaaaa... ",
+" .aaa..aaaa.. ",
+" .aaaa..aa.. ",
+" .aaaa.... ",
+" .aaaa.. ",
+" ....... ",
+" .. ... ",
+" .. ... ",
+" .. ... ",
+" .. ... ",
+" .. ... ",
+" .. .. ",
+" ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *zoom_undo_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+" none",
+". c #000000",
+"+ c #c0c0c0",
+"a c #ffffff",
+/* pixels */
+" ",
+" ",
+" ..... ",
+" .+aaaa. ",
+" .++aaaaa. ",
+" .+++aaaaaa. ",
+" .aaaaaaaaa. ",
+" .aaaaaaaaa. ",
+" .aaaaaaaaa. ",
+" .aaaaaaa. ",
+" .aaaaa. ",
+" ....... ",
+" ... ",
+" ",
+" . . . . .. .. ",
+" . . .. . . . . . ",
+" . . .... . . . . ",
+" . . . .. . . . . ",
+" .. . . .. .. ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *zoom_out_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+" none",
+". c #000000",
+"+ c #c0c0c0",
+"a c #ffffff",
+/* pixels */
+" ",
+" ",
+" ..... ",
+" .+aaaa. ",
+" .++aaaaa. ",
+" .+++aaaaaa. ",
+" .aaaaaaaaa. ",
+" .aaaaaaaaa. ",
+" .aaaaaaaaa. ",
+" .aaaaaaa. ",
+" .aaaaa. ",
+" ....... ",
+" ... ",
+" ... ",
+" ... ",
+" ..... ... ",
+" ... ",
+" .. ",
+" ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *full_preview_area_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+" none",
+". c #000000",
+"+ c #c0c0c0",
+"a c #ffffff",
+/* pixels */
+" ",
+" ................. ",
+" . . ",
+" . . . ",
+" . ... . ",
+" . . . . . ",
+" . . . ",
+" . . . ",
+" . . . . . ",
+" . . . . . ",
+" . ............. . ",
+" . . . . . ",
+" . . . . . ",
+" . . . ",
+" . . . ",
+" . . . . . ",
+" . ... . ",
+" . . . ",
+" ................. ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *printer_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+". c #000000",
+"# c #7f7f7f",
+" none",
+"b c #ffffff",
+/* pixels */
+" ",
+" ",
+" ",
+" ",
+" ......... ",
+" .bbbbbbb.. ",
+" .bbbbbbb.b. ",
+" .bbbbbbb.... ",
+" .bbbbbbbbbb. ",
+" .bbbbbbbbbb. ",
+" .bbbbbbbbbb. ",
+" .bbbbbbbbbb. ",
+" .bbbbbbbbbb... ",
+" ............. .. ",
+" ..###########.. . ",
+" . . . ",
+" . . . ",
+" .bbbbbbbbbbbbb. . ",
+" .#############.. ",
+" .............. ",
+};
+
+/* --------------------------------------------------- */
+
+const char *zoom_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ffffff",
+/* pixels */
+" ",
+" ",
+" .............. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaa....aaaaa. ",
+" .aaaa...aaaaa. ",
+" .aaa....aaaaa. ",
+" .aa...a.aaaaa. ",
+" .aaa.aaaaaaaa. ",
+" .......aaaaaaaaaa. ",
+" .aaaaa.aaaaaaaaaa. ",
+" .aaaaa............ ",
+" .aaaaa. ",
+" .aaaaa. ",
+" .aaaaa. ",
+" ....... ",
+};
+
+/* --------------------------------------------------- */
+
+const char *zoom_x_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ffffff",
+"# c #4040ff",
+/* pixels */
+" ",
+" ",
+" .............. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaa....aaaaa. ",
+" .aaaa...aaaaa. ",
+" .aaa....aaaaa. ",
+" .aa...a.aaaaa. ",
+" .aaa ",
+" .......a ## ## ",
+" .aaaaa.a ## ## ",
+" .aaaaa.. #### ",
+" .aaaaa. ## ",
+" .aaaaa. #### ",
+" .aaaaa. ## ## ",
+" ....... ## ## ",
+};
+
+/* --------------------------------------------------- */
+
+const char *zoom_y_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 4 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ffffff",
+"# c #4040ff",
+/* pixels */
+" ",
+" ",
+" .............. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaaaaaaaaaaa. ",
+" .aaa....aaaaa. ",
+" .aaaa...aaaaa. ",
+" .aaa....aaaaa. ",
+" .aa...a.aaaaa. ",
+" .aaa ",
+" .......a ## ## ",
+" .aaaaa.a ## ## ",
+" .aaaaa.. #### ",
+" .aaaaa. ## ",
+" .aaaaa. ## ",
+" .aaaaa. ## ",
+" ....... ## ",
+};
+
+/* --------------------------------------------------- */
+
+const char *resolution_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 2 1",
+/* colors */
+". c #000000",
+" none",
+/* pixels */
+" ",
+" ",
+" ",
+" ... .. ",
+" ..... .... . ",
+" ....... ...... ... ",
+" ....... .... . ",
+" ....... .. ",
+" ..... ",
+" ... .. . ",
+" .... ... ",
+" .. .... . ",
+" .... .. ",
+" ...... ",
+" .... .. . ",
+" .. .... ",
+" .. . ",
+" .. ",
+" .... .. . ",
+" .. .. ",
+};
+
+/* --------------------------------------------------- */
+
+const char *resolution_x_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+"# c #4040ff",
+" none",
+/* pixels */
+" ",
+" ",
+" ",
+" ... .. ",
+" ..... .... . ",
+" ....... ...... ... ",
+" ....... .... . ",
+" ....... .. ",
+" ..... ",
+" ... .. . ",
+" .... ... ",
+" .. .... . ",
+" .... ",
+" ...... ## ## ",
+" .... ## ## ",
+" .. #### ",
+" ## ",
+" .. #### ",
+" .... ## ## ",
+" .. ## ## ",
+};
+
+/* --------------------------------------------------- */
+
+const char *resolution_y_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+"# c #4040ff",
+" none",
+/* pixels */
+" ",
+" ",
+" ",
+" ... .. ",
+" ..... .... . ",
+" ....... ...... ... ",
+" ....... .... . ",
+" ....... .. ",
+" ..... ",
+" ... .. . ",
+" .... ... ",
+" .. .... . ",
+" .... ",
+" ...... ## ## ",
+" .... ## ## ",
+" .. #### ",
+" ## ",
+" .. ## ",
+" .... ## ",
+" .. ## ",
+};
+
+/* --------------------------------------------------- */
+
+const char *scanner_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ffffff",
+/* pixels */
+" ",
+" ",
+" ",
+" ",
+" ",
+" .. ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" ... ",
+" .... ",
+" ................ ",
+" .aaaa.a.a.a.a.aa. ",
+" .aaaaa.a.a.a.a.aa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" ................. ",
+" .................. ",
+};
+
+/* --------------------------------------------------- */
+
+const char *intensity_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ffffff",
+/* pixels */
+" ",
+" .................. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaa........aaaa. ",
+" .aaaaaaa..aaaaaaa. ",
+" .aaaaaaa..aaaaaaa. ",
+" .aaaaaaa..aaaaaaa. ",
+" .aaaaaaa..aaaaaaa. ",
+" .aaaaaaa..aaaaaaa. ",
+" .aaaaaaa..aaaaaaa. ",
+" .aaaaaaa..aaaaaaa. ",
+" .aaaaaaa..aaaaaaa. ",
+" .aaaaaaa..aaaaaaa. ",
+" .aaaaaaa..aaaaaaa. ",
+" .aaaa........aaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .................. ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *red_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #ff2020",
+/* pixels */
+" ",
+" .................. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aa..........aaaa. ",
+" .aa...........aaa. ",
+" .aa..aaaaaaaa..aa. ",
+" .aa..aaaaaaaa..aa. ",
+" .aa..aaaaaaaa..aa. ",
+" .aa...........aaa. ",
+" .aa..........aaaa. ",
+" .aa..aaaa..aaaaaa. ",
+" .aa..aaaaa..aaaaa. ",
+" .aa..aaaaaa..aaaa. ",
+" .aa..aaaaaaa..aaa. ",
+" .aa..aaaaaaa..aaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .................. ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *green_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #20ff20",
+/* pixels */
+" ",
+" .................. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaaa.......aaaa. ",
+" .aaa...........aa. ",
+" .aaa..aaaaaaa..aa. ",
+" .aaa..aaaaaaaaaaa. ",
+" .aaa..aaaaaaaaaaa. ",
+" .aaa..aaa.....aaa. ",
+" .aaa..aaa......aa. ",
+" .aaa..aaaaaaa..aa. ",
+" .aaa..aaaaaaa..aa. ",
+" .aaa..aaaaaaa..aa. ",
+" .aaaa.........aaa. ",
+" .aaaaa.......aaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .................. ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *blue_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+". c #000000",
+" none",
+"a c #3030ff",
+/* pixels */
+" ",
+" .................. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aa...........aaa. ",
+" .aa............aa. ",
+" .aa..aaaaaaaa..aa. ",
+" .aa..aaaaaaaa..aa. ",
+" .aa..aaaaaaaa..aa. ",
+" .aa...........aaa. ",
+" .aa...........aaa. ",
+" .aa..aaaaaaaa..aa. ",
+" .aa..aaaaaaaa..aa. ",
+" .aa..aaaaaaaa..aa. ",
+" .aa............aa. ",
+" .aa...........aaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .aaaaaaaaaaaaaaaa. ",
+" .................. ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *pixel_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 3 1",
+/* colors */
+"a c #000000",
+" none",
+". c #ffffff",
+/* pixels */
+" ",
+" ",
+" ",
+" a . ",
+" a a . ",
+" a . ",
+" a a a . ",
+" a . ",
+" a . ",
+" . ",
+" . ",
+" . a ",
+" . aaaa ",
+" . aaaa a ",
+" . a aaaaaa ",
+" . a aaaaaaaaa ",
+" . aaaaaaaaaaaa ",
+" ",
+" ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *log_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 2 1",
+/* colors */
+". c #000000",
+" none",
+/* pixels */
+" ",
+" ",
+" ",
+" ",
+" ",
+" . .. ... ",
+" . . . . . ",
+" . . . . ",
+" . . . . ",
+" . . . . ",
+" . . . . ... ",
+" . . . . . ",
+" . . . . . ",
+" . . . . . ",
+" . . . . . ",
+" .... .. ... ",
+" ",
+" ",
+" ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *move_up_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 2 1",
+/* colors */
+". c #000000",
+" none",
+/* pixels */
+" ",
+" ",
+" ",
+" . ",
+" ... ",
+" ... ",
+" . . . ",
+" . . . ",
+" . . . ",
+" . . . ",
+" . ",
+" . ",
+" . ",
+" . ",
+" . ",
+" . ",
+" . ",
+" ",
+" ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const char *move_down_xpm[] =
+{
+/* width height num_colors chars_per_pixel */
+" 20 20 2 1",
+/* colors */
+". c #000000",
+" none",
+/* pixels */
+" ",
+" ",
+" ",
+" . ",
+" . ",
+" . ",
+" . ",
+" . ",
+" . ",
+" . ",
+" . . . ",
+" . . . ",
+" . . . ",
+" . . . ",
+" ... ",
+" ... ",
+" . ",
+" ",
+" ",
+" ",
+};
+
+/* --------------------------------------------------- */
+
+const unsigned char cursor_pipette_white[] =
+{
+ 0x00, 0x70, 0x00, 0xf8, 0x80, 0xff, 0x00, 0xfe, 0x00, 0x7d, 0x80, 0x38,
+ 0x40, 0x18, 0x20, 0x14, 0x10, 0x12, 0x08, 0x01, 0x84, 0x00, 0x44, 0x00,
+ 0x32, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00
+};
+
+/* --------------------------------------------------- */
+
+const unsigned char cursor_pipette_black[] =
+{
+ 0x00, 0x70, 0x00, 0xf8, 0x80, 0xff, 0x00, 0xfe, 0x00, 0x7d, 0x80, 0x38,
+ 0x40, 0x18, 0xe0, 0x17, 0xf0, 0x13, 0xf8, 0x01, 0xfc, 0x00, 0x7c, 0x00,
+ 0x3e, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x00
+};
+
+/* --------------------------------------------------- */
+
+const unsigned char cursor_pipette_gray[] =
+{
+ 0x00, 0x70, 0x00, 0xf8, 0x80, 0xff, 0x00, 0xfe, 0x00, 0x7d, 0x80, 0x38,
+ 0x40, 0x18, 0x20, 0x14, 0x10, 0x12, 0xf8, 0x01, 0xfc, 0x00, 0x7c, 0x00,
+ 0x3e, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x00
+};
+
+/* --------------------------------------------------- */
+
+const unsigned char cursor_pipette_mask[] =
+{
+ 0x00, 0x70, 0x00, 0xf8, 0x80, 0xff, 0x00, 0xfe, 0x00, 0x7f, 0x80, 0x3f,
+ 0xc0, 0x1f, 0xe0, 0x17, 0xf0, 0x13, 0xf8, 0x01, 0xfc, 0x00, 0x7c, 0x00,
+ 0x3e, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x00
+};
+
+/* --------------------------------------------------- */
+
diff --git a/frontend/xsane-icons.h b/frontend/xsane-icons.h
new file mode 100644
index 0000000..538f9ea
--- /dev/null
+++ b/frontend/xsane-icons.h
@@ -0,0 +1,87 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-icons.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ------------------------------------------------------------------------- */
+
+#ifndef XSANE_ICONS_H
+#define XSANE_ICONS_H
+
+#define CURSOR_PIPETTE_WIDTH 16
+#define CURSOR_PIPETTE_HEIGHT 16
+#define CURSOR_PIPETTE_HOT_X 1
+#define CURSOR_PIPETTE_HOT_Y 14
+
+extern const char *xsane_window_icon_xpm[];
+extern const char *error_xpm[];
+extern const char *warning_xpm[];
+extern const char *file_xpm[];
+extern const char *fax_xpm[];
+extern const char *faxreceiver_xpm[];
+extern const char *colormode_xpm[];
+extern const char *Gamma_xpm[];
+extern const char *Gamma_red_xpm[];
+extern const char *Gamma_green_xpm[];
+extern const char *Gamma_blue_xpm[];
+extern const char *brightness_xpm[];
+extern const char *brightness_red_xpm[];
+extern const char *brightness_green_xpm[];
+extern const char *brightness_blue_xpm[];
+extern const char *contrast_xpm[];
+extern const char *contrast_red_xpm[];
+extern const char *contrast_green_xpm[];
+extern const char *contrast_blue_xpm[];
+extern const char *rgb_default_xpm[];
+extern const char *negative_xpm[];
+extern const char *enhance_xpm[];
+extern const char *store_enhancement_xpm[];
+extern const char *restore_enhancement_xpm[];
+extern const char *default_enhancement_xpm[];
+extern const char *pipette_white_xpm[];
+extern const char *pipette_gray_xpm[];
+extern const char *pipette_black_xpm[];
+extern const char *zoom_not_xpm[];
+extern const char *zoom_out_xpm[];
+extern const char *zoom_in_xpm[];
+extern const char *zoom_undo_xpm[];
+extern const char *full_preview_area_xpm[];
+extern const char *printer_xpm[];
+extern const char *zoom_xpm[];
+extern const char *zoom_x_xpm[];
+extern const char *zoom_y_xpm[];
+extern const char *resolution_xpm[];
+extern const char *resolution_x_xpm[];
+extern const char *resolution_y_xpm[];
+extern const char *scanner_xpm[];
+extern const char *intensity_xpm[];
+extern const char *red_xpm[];
+extern const char *green_xpm[];
+extern const char *blue_xpm[];
+extern const char *pixel_xpm[];
+extern const char *log_xpm[];
+extern const char *move_up_xpm[];
+extern const char *move_down_xpm[];
+extern const char cursor_pipette_white[];
+extern const char cursor_pipette_gray[];
+extern const char cursor_pipette_black[];
+extern const char cursor_pipette_mask[];
+
+#endif
diff --git a/frontend/xsane-logo.xpm b/frontend/xsane-logo.xpm
new file mode 100644
index 0000000..cfe0a03
--- /dev/null
+++ b/frontend/xsane-logo.xpm
@@ -0,0 +1,372 @@
+/* XPM */
+static char *xsane_64c[] = {
+/* width height ncolors chars_per_pixel */
+"256 300 65 1",
+/* colors */
+" c #CBDDFC",
+". c #363777",
+"X c #D2E6FC",
+"o c #27241C",
+"O c #B0A47E",
+"+ c #1F1C14",
+"@ c #D9EFFC",
+"# c #E2D9B5",
+"$ c #788DFC",
+"% c #3848FC",
+"& c #978C6B",
+"* c #0A0B54",
+"= c #C8BC94",
+"- c #D6CCA5",
+"; c #C0B48C",
+": c #2028FB",
+"> c #687CFC",
+", c #242777",
+"< c #5468FC",
+"1 c #8097FC",
+"2 c #4E4836",
+"3 c #A2B9FC",
+"4 c #858279",
+"5 c #98ACFC",
+"6 c #E9F7FC",
+"7 c #3D47B3",
+"8 c #B0C7FC",
+"9 c #AAA27F",
+"0 c #F1ECD5",
+"q c #13179A",
+"w c #BED7FC",
+"e c #595785",
+"r c #8FA6FC",
+"t c #5F5842",
+"y c #2F36B5",
+"u c #CFC49C",
+"i c #302C21",
+"p c #BBCDFC",
+"a c #4558FC",
+"s c #867C5E",
+"d c #8C9EFC",
+"f c #DAD2AD",
+"g c #020204",
+"h c #B8AC85",
+"j c #FBFBF5",
+"k c #2B2EA0",
+"l c #1E25B1",
+"z c #AEBEFC",
+"x c #7D7458",
+"c c #C6C2B0",
+"v c #EBE4C7",
+"b none",
+"n c #171CF9",
+"m c #71684F",
+"M c #15140F",
+"N c #5E74FC",
+"B c #3D3829",
+"V c #131863",
+"C c #6F84FC",
+"Z c #474A8C",
+"A c #0D0C0A",
+"S c #8E8464",
+"D c #2B37FB",
+"F c #62669C",
+"G c #A49976",
+/* pixels */
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS&#u##-#f###v##ummbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&&G=f#f#f---ff#f-#-##--#v#v#v-u=-==uh9hu=mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbsS&;u-u-#f#v#vfff-###--f####---###-#fvv#ff#fu=-;OO;u;h;;===9&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbGO-##-=-#f;==#-uff#vv####vv#fff#=vvvf#-f-f-####vff#--u==-uhOhu=;u=uf-u==;&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbGOcvvvfff#--###uuff--uff-#fvv##f#v##-u-f#-##v#--u-###vfu-f-u-#-==uuu==uu;uu---===-=u;&Sbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;;-vvf-##vv##f--f-#--##-#u--uf#f--fv#####-==-ffv##-##u=-###f-uu;uu-u=u-uu;==hu-===u---uu=;OGh;;;Gbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=-##v#f##f##v###fv#ff-f#fu-##-u=u-#fu-f-#f-##fuu----#-u--uu-fu-u=#u=uu=u=hu--u;h=u-u==uu-u=f-u;h===;u;h;;&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbf-vvf##v#fvvv##v#f#v#-v#f#f=##-f###u;=uuuu=u==-##-u-ufu--=u--u=-u=u-u;-u=uuhuu==u--uOh========u-#-u-=-=uu;===;h=hGSbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9-=-vvv##0######v####ff#-#f#vv--=-##f-=uu-=uuhO=-uhhu--uu-===u==-u=uu=;u=u=--#;=9=f--u;;;u;=uhO;-u;hu#--=-u=uu==u=h==h9hhhGbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=u#fv-=##vvv#vfvvvvv-##---u--f--#--=-----==h;uu;hO;;-hG=;-u-u=;u=f--h==;hhO;;=;=-=uh=uu=u=;hh=uhh9;;-h9;=-u#uu;-=ff-==;;hhGhhhhhhbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;fffv#--f####-v-v-f#v####v-uu;u==-u-f#ff-;u=-=h;O;h;==hhO==;;huhu;;=-===--=h;O;h;===ufu-uO==u;hhO;O;;=hOO===;hu;u==u--u--#uh=h;Oh;hh;=;hbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=v---#v#fv##fu#0##f##ff#-f#-uu;;==;---ff-u-=-uuu=uh9h;u;h=;;;G;-==h==;=u;uu=;=Ohh;h=uu---u-=uuuu;=h99;=;h=;;;9;-u=;-u=u-uu-u=uhh;;h=uu=-=;=hh9bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=v#vv###fv#-vff#v###--cfuu--====-h=9;-=uu-fu==uff=h==;hhO;;h=hh;u=-=;=h==;=uh;;;=h;Ghu==u--===u-f=h=u;;;9==h;;;;u=-u;=O==h=u;;;=u;;9;-u=u--=;hh==O9mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc00#v##0#vv#vfu-#--f#--=h-f;;c9;h;O;;h=u==;-u=hh-uu;=;;u;=u;=;9h=h;=uu;h9;uhO;GhOhOh;h=u==;uuuhO-uu;=;hu;;uh=;9O;h;;=u;;G;u;O;9;O;hh=;=u=u;-uuh9uu=hh99OGbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#6v0#-f00##vv#f--=--c-f--Ou-;u=;OOOG;;h=u===;;h=u-=u;OOuu;;=u=;Oh9=;;;=;=9;uh==;hO9Gh;O===;=;=O==-==;O9=u;;=u=;hhO;=h;u;u9;u;==;OO99O=h=uuuuu=h=uf==;O&h;OG9hSbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbhvvv#v0v-f#vvv#vf#-u--uu-f-uu==h;=h;O99OO==h;uu;h==;;=u=hO;=uuuu=u;h;u;;uu-=u==hh;O;9G9Oh;=h===;h==;h=u=OO;;=uu===;h===;uu-====hh;h;O99hO;=;;uu=;=u===u=h9hh;;hO9OSbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-#f#f##vvv##--#0#fu=u--u==u;=f-=h;;O;u=hOhu;h=;h;hOhhOh;==h;Oh;fu=;h=uu==;u;=--;hO=O;u;;Oh=;h=h;;hOhh9h;==;;hh;--u;h=u==;;u;=-f=hh=O;==hO9==h;;;h;O;;9;;=u;;hOh==;9GGOGmbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbfvv###vvf#-vf=-#-f#-u=-u;u;u=cu-=h;;99;u;h;=;;h;;;=hhh=;hhOu;9h===-==h==;=hu;;uuuh;;OOhuhhh=;hh;;;=hhh;=hOh=;9O==;u==h=u;=h==;uuuh;=OOhu;hh=hh;;;=;;hh;=;hh==hO=u;u;h9OhGG&Sbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-0v#fvv0###uffu-f-f-uuuu===huu=-uhu;hhhhh;;h&huu;;=h9;h=;=h9h;h;=u=u=;==;=;Ouu=-uh=;hOhhO;;h&O=-;;=h9;h=;;;OO;h;=u=uu=u==;=h=u=u-hu=O9hhO;h;GOu-;;;;9hh==;;Oh;;;=-=u=;=;h9O&G9G&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-v06#vvv#vf--f=9;u;--=;===uh;huuuu;=-;h;;Ghh9GGOh;uuh;=;;;hhh=O&9hOuu;;====h;hu-uu;;-=hh;9h;9GGOh;=u;;;;;;hO;;h&G;h=u=h;u==h;huuuu;h-=hh=9h;9GG9O==-;;=;;;;O;;;&G;hu-uh;==;hO9OOOGGxbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;f-0v00vv##-uu-;9;uGh=u=u;=;=;;-=u==hh==;=hOh;OOOGG==uu;;h;;OO=O&9=GO=u=uh;=u;;u=;==O;==;uhOO;OOOGG===u;=h;;OO;h&O;G9=u;u;;==;;=====O;==;uhOO=OOO9&;=;u=;h;;9O;;&9;99;u=u=;==;;h;hOG&&&Sbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=##fvvv0v#-##ff---9h;=-hOuuuu=G-#=huOhh;u=uu;hOGhhh9h;h-=hO;u;=;=u99h=uh9=-uu;Guf=;=hO;huu===hO9hhh9h;;u=hOhu;u;=u99h=uhO=uuu=G=#=;=;9;Ouu===;O9Ohh9h=;u=hhhu;u;=uOGh=u;O=-u-u&=f=OhhSG&GG&tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbvf##v#fv###f##f###f;=;-u=h9uu-cu=uf=hh=h=O;h;=;=;9hOhhO;hh=;==u-#f-;;;uu=O9=uuu=uu-uhh=h;O;h;=;=;OhOhOhh;h;===uu#ff;;huu=hG=u=u=u=-uhh=h;hh;h=h;;OOOhhOh;h=;==uu#-#=;huuuh9;-uu=uu--hOh9G&&SS&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbc0v0#0--v#uv#ufu--##-;h;uu-;---uuu=u=u-uu;h=;h9O==;;O;OG=;Ouu;====ff-;h;=uf;u-uuuu=u==-=u;h==hOO==;;O;h&=;Ouu;u=u=-#f;hhuu-=uu-u=-uuu=-=-;h=;hh9===;O;h&;;O=u;====-ff;h;=u-==--uuuuu==-=uh999&&xSsbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbb;vvv0vv0#=-v-#-u=u-uu=;u;=u=-uuu;u-==uu-u-u-uh9;hO;h=h;=;Ghu=uu=;uuuu=;===u=-=uu;=-=uuu--fu-=h9;hO;h;;;;=9Ou=uu=;=u-=u;==;u=uuuu;u-;u=u---uuu;G;hO=h;;;;=OO==uuu;=u-=u;==;u=uuuu;=f;-=u---u==OS&&sSstbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbv00ff#vv--f----u-u====u=;=;==u;;=;uu==u--f--=hh;h==9GO=;OO;;====u=u=====h;;;=u=hu;uu=;ufu#-f=hh;h;=OGO==OO;;====u=u;====;;;==u=hu;uu=;u-u#-#=;h;;;=OGO==h9;=====u=u=====;h;;=u=hu;=-=;u-uf-#;h9hGG9smmsbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbvv0ff-v#--uu-=-u--==;;u==;;===u=;;==-=;-=ff-=uu=;h=9GG;;hhh;=;uuu-==;;uu;;;;u=u==h==-==-=-f-=u-=;h=OGGh=Ohh;=;=uu-==;;uu=;;;=;uu=h==-==u=-f-==uu;h;OGGh=hOhh=;=uu-==;;uu=;=;u==u=h==-==u=-#-u=-=hOO&xss&mtbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbb;60v#v0#u=uf==u=---u;==ff=h;;=Ou--;=;=;;=uuuuu==uu;;9;=;GGO=hh=;u-uu;=;ufu;;;=9uuu==O;;=;=-uuu==u==;Oh=;G9O=hh=;u-uu;=;u-u;;;=Ouu-=;;=;=;u-uuu=====;hh=;GGO=;O=;=--u;===fu;h;;Ouuu==;;;u;=-uuu==uu=h9GOGsxmsmbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbb;vv60v###fu;=u=h==-u=u=u-u;;O;;;;uu-=h;h;=uu-;;;=;h==;Oh;;;Oh=;h==--====-u;;O;;;;-u-OsOhhuuu-;;;=;h==;hh;hhOh=;h==--u===-u=;O;;=;uu-u;;h;=uu-=;;==h=u;OO;;;OO==h;=u-u;==-uu;O;;=;u-f=;;hh=uu-=;;;=O;u;OG9&&Sxxs2bbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbcvvv0v#v#--=;;h==;h;=f-===u;OO;u;-u==u=hh=uuu=;;hO;h=hh;=h;hhhO;=hh;;-u===u;O9;u=uu=hs9hh;uuu==hhO;h=;h;=;=OhOO;=;hh;-u===u;O9h=;uu==u=;O==u=u=;hO;h;=h;=;;hhh9h=;hh=uuu==u;hGh===u==u==O;=u=u;;;9;h;=9h;h9&&Smsxtbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbc00vvv#-##f=;Oh;==O;=;==;uu=Oh;=;uu;;===;;u;u;;u;;;h;=hO;==hO&G9OOG9O9h;=uu=Oh;=;=u=hGxh;;u;==;u;;;h;=hOh==hO9h;=;hh;;=;=uu=hO;===u=;===;;u;u=hu;;;hh=hO;==;O9O;==hh;;==;uu=hO===;-=;=u=;;u===hu=h;hh=hOh=;h&&SSSSmtbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbb=0v00#vv-f-fu;9O;h;;;;hh=u-======u=;=u=;=u=h=u=;u=;=;O==h;=u=OmtmmmtmmtxG;u===;===uhuhxG;u=h=u=hu=;=;O=;;;=u=;G9h;hhh;h;;uf=;u;u;uuh==u;;u=;;u=;u=;=;O;;;h=u=;G9h;h;h;;h;=f==u;u;=u;===;;==;;u=;=u;=;O;;;h===h&&&Ssxmmbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbb;6jj0##v0#fu=;;;;;;OG;==9Ouu=u;u=h==;=u=;==;;=;=;uuu;hO=-uu=;h;OG9G&&GhOSmmh==;u=h==;;;m9==;;=;=;uu-;;9;-uu=;;;=h;;O9;==O9=uuu;u=;;=;;u=;==;h=;=;uu-;=G=-uu=h;;=h;;OGh==O9=uu=;==h==;;uu;===h=;=;uu-;=G;-uu=;hh;G9&smmxmbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbb;000j0vfvff-u=;Oh==OOG;;=OG;;u=u==h==-uu;==hhhh;;=;u-==O=====;hOh==OO9h;=9smmh=-==hu=-u;mO=hhhO;==;=-==h====u;;Oh;=OOGhhuOG;;u=u==hu=--uh==;hhO;;;;=uu=h;u==u;;Oh;uOO9OhuhGh;u=-=uh=;u-u;==;h;O;;;;=uu=;huu;u=;OOh;&&xmmxtbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbb0600600vf=uu-hOhh;h;=;OOh;;;;O-huh;uu=u=u-h;hO;hh===u-u-uh==u;OhOhO;=;OOh;;9&mhhuh;=u==Gs=;;;O;hh===u-uuuh==u;OOO;Oh=;OOO;;hhO=h=;;=u===u-=h;O;;h===u--u-O==u=OOO;O;;=hOO;;;hh=;=;h=uu==uu=h;O;hh===u--u-O====9OOh9OGGSmmmtbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbv0jjjjvvv=fuu=;=O9h;=hh=;;h9;u=;;u;;=-;u==;hOhh;h=uu--uu-;u=uu==O9O;=hh=;;hG;=s&O===;-;OS9h;OOh;h==uu-uuu;====;;OGO;=hh;;hhG;uu;h=;;;u;==;h;OOhhh=uuufuuu=u==u;=h99;=;O=;;;G;u=;hu==;u==u=;;hOh;h==uufuu-===u===hGGh;h&Gsxmttbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbb0j00000#--#ff;huu=hOh=;h;hh===-=;;Ou;h==hh;;uh;&;-===--u;=u-f;h==;;hO=;h;hh=u=u9mm&;;;;9s&h;=OhGh====--=;==-f;h==;;hO=;h;h;;u=u=;=h=;h;=;;;;=hhGhu===--u=u=-f=h=u;;hO;=h;h;;u=uu;=O;;;;u;h;;=hhGOu==u--u===-f=;;==;hOhh9G&smtttbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbj000j0v==#-u;=;;=;u;;=OOh;h;;u=;O9=uOh;GhhhOO;h;=;=;-u=OOfuu;=;huh==;=h9h;O;;u=;&tx9G99tG9h9O;h;===;uu=hO--u;=;h=;;=O=;GO;hhhuu;h&;uhO=GhOhOO;h;==;;uu=O9uu-==;h=;;=h=;GO=hh;=u;;G;u;O;GhOhhO;h;===;uuuh9uuu=;;;=;;=h=;&&&x2B2t2bbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbb00j00#f##fuu;Ohuu;;=u=hhhO=;h==;=G==h==hhO9G;;O=u====;h=u-==;OO=u;;=u=;hh9==h=u;=G9Gmm&x&99Gh;O;==;;;;O==-;;h9G==hO;=;h9OGh;Oh=h=&;=h;;hOO9GO;hh==;=;=O==-==;OG=u;;;-==hhOh=h;u;u9hu;;;;hh99O=h;==;=;=O==-==;h9===h;-=;OOSmmxmmttBbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbb-v0j0vf#fu;uu;Oh;==u=u==;h;u;;u-u====hh=O;GG9Oh==h===;h===huu=OO;==u====hh;=;;=uu;==;&tm&hGGGOO;=O;=;;O;;;O;;;GGOh;;;;h;hOh;hh;;=h;;hOOO9OGGGOO;=h;==;O===h===h9;==uu===;h===;uu-;u==;;hhhOGGOO;u;;=u;;;==;=u=h9;;uuu===;&x&&&&smt2Bbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbv0vvv#--;;h===;;Oh=fu=hh=u===;u;=--;hh;hhu=hOh=;h;h;hhOhOOO;==h;hh=f-=;h====;h=;=--;h9GtG=;hOO;hO;OOO9G9GGG9hhG9G&O;hOG&GO99GGOGO;;9GGOG9;h9GG;hOhh;hhOhhGhh==h;hO;--=;h====;;=;=--;hO=O;==;OO=;h;;;hhOhhGhh;=h;hO;u-==hOx9;hGGsxmt2bbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbv000vvfu=uuh;hu;9h==uu==;=uh=hu==u-=O;;9O;=hh;;h;h;;;=hhh==hhO=;9h===u=;h==h=hu=;uu=O;;&t9=OOO;O9GO9OOG&&GG&&SG&sS&&GGG&S&&S&s&GSG99S&GSSGOGGGh9GOOOhhhOO;;;Oh;;OO===u==h=u;=hu=;uu-hh=OOh=;;h=;hh;;;=hhh;=;hh==OO==;-==h9s9;Oh9Ssmt2ibbbbbbbbbbbbbb",
+"bbbbbbbbbbbbjv00#ffuf==h9;;h;=u=u====;=;huu;-=hu;hOhOh;;h&;uu;==OO;h=;=hOh;h;=========;=O=u;-=h=h99&tSO99sGOhGGGSsSS&SSsmxsmxxsxxxmxxmxmmxxxssmsxxmxxxSSSxS9hG9O9GOOhh;OOO;h;;u=u=====;=h=u=u-h==hOhhO;hhG9=u=;=;Oh;;=;;OO;h;;u=uu;=hx&;h;;OGGmt2Bobbbbbbbbbbbbb",
+"bbbbbbbbbbb#0jjv##-uuhhh=O&O;huu;;====h;hu-uuh=-;h;;9hhGGGOh;=uh;;;;;hhh;hSOhO=u;h==;=hhh=u==OhuhG9&tSGssxsSS&&xxxmmmttttt22t2ttt22t22222ttttttmtttmtmmtmtmsS&9&GGGOOOGhhh&Ghh===;;===h;h=uuu=;-=Oh;9h;OGG9h;=u;;;;h;;O;;;&Ghh=u=h;hGx9;O=u=9&xmtBibbbbbbbbbbbbb",
+"bbbbbbbbbbb-0000v#uu;hh=O&O;GO=u;=h=;u;;u====hh==;=h9h;OO99G==uu;=h;hOh;O&9=&O=u;uh;;=h;==hhhG99OGGx2xSxmmmtmmmmttt222B2BiBBiBBBBBBBBBBBBB2B2BB22222B2t2ttt2xxsSS&S&G&&OOSGhGG;u;=;;==;;=u===hh;u;=;OO=O9O9&;=;u=;h;;OO;;&9hOO;===Gxx9;;;=;hO&xmtBiobbbbbbbbbbbb",
+"bbbbbbbbbbb00vj#f=uu=;=u=GO;;uhhu-uu;G--=huOhh;u=uu;hOGhhh9h;h-;hO;u;=;;=G9;;uhO=u=u;&=uh9hG&&&G&&S2tmtttt22222BBiBBiiiio++oooo+oooio+iiioii+iiBBBBBBBBB2222ttmmmmx&S&&Gh&&O;;;O;-=u=G=f=;=;9;h=uu==;O9OhhOO=;uuhhhu;=h=uOGh===O;h4Oh&=#=;;;&Gsxm2Bibbbbbbbbbbbb",
+"bbbbbbbbbb#v#0###---ufff-;;;uu;O9u=u===uf=Oh=h=O;O;;;;;9hOhhO=h;;;===uff-;;;u=;9G=;;;hhh;9SS&sxmxt2B222BBiiioiooo++++o+++++++++++++++MMM+MMMMoooioiBiBiiiBB2B222ttmxsSG9OGGGh;;9G;u=u;uu-uh;;;h;;hhuh;;OOOOhhO;h;;==uufff=;huu=h&xhuu=u=--hhh9Sst2iiMbbbbbbbbbbb",
+"bbbbbbbbbb-f0v#0#u-==u##u;h;uu-;u--uuuuu=u-u=;h==hOh==;;O;GG=;Ouuh=;==f#-hhO==uh;=;hOOGGSSSxxttt2B++iBio+o+MM+MMMMMMMMMbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbMiBoBB2222ttmms&&ssSOh=h;u-==u=u==-=u;h;=hhG===hh;h&;=O=u;==u=-f#;hh=u-9shuu=u=u==u==O&Sm2io+bbbbbbbbbbb",
+"bbbbbbbbbb6vfv0v#f==ufuu=;u;=u=uuuu;u-==uu-u-uuuh9=Oh;h=;;=;Ghu======-==;;;;;;hhOO9&&Gsxxmmtt2BBioMM+MMMMAMAAAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbB2222tttmxxsSSG9hh=;;=f=u=u---uuu;G;;O;h;;;;=O9==uu=;=u-=u;=u;uhx9uu==f=u;u-uu=hGx2Bo+bbbbbbbbbbb",
+"bbbbbbbbb-##0vv#f-uu=====;;;;=uu;;uhuu=;---#-f=hhhh==OGO==OO;;;===u===;;=hOOGOOO&xSxxmt222BBii+MMMAAAAgAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbB22tttmmmss&O9G;O;===u-u#-#=;O;h;uhGO;=;Oh====uu=u=====;h=hOshh===u===-ufu-h&xmBioMbbbbbbbbbb",
+"bbbbbbbbb#v#0v##f--u=;;u==;h=====;;u=-==u=ff-;uu=;h=GGG;;Ohh;;;uuuu=;hh;hOGGG&ssxmt222Biioo+MAAAAAgAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb22tttmssS&GGhhu=;==-f-=u-u;;;h&GhuhOh;;==u=-==;;u==;;hhsO==;==-u===-#-=hhSm2i+Abbbbbbbbbb",
+"bbbbbbbbbv0#vv###fu;u=--=h=;=O--u;=;;=;=uuuuu==uu=hO;=h9Gh=OO=;=-u=h;h;;O&SsstxttBBio++MMAAgAggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2tmtxs&S&GOO=h=-u-u==uu=;Ohu;GG9;;O=;=--u;=;=fu;h;OmG--;;;;hu;=u=u==hh&x2i+Mbbbbbbbbbb",
+"bbbbbbbbb#v0#####-===u-u;;O;;;;-u-=h=O;=-u-;;;=;h==;Oh;hhOh=;h==u=;hOOOGSstm22BBio+MMAgAgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbtmxs&&S&GG;=u-=;;==h;u;hO;;hOh;=h;=--u===-uu;hhOm9=--u;;;h=uu-=;hhOSxtB+Mbbbbbbbbbb",
+"bbbbbbbb=v##0v-=u=fuu;=;99G===uu==u=hh=uu===;hO;h=h;;=h;hOO9;=hOhh;OGSSst22BBio+MAAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbtxxSSSSOhu=u=;hO;;;=O;=;;hhhOh=;hh=uuu==u=OGhhmhu==u=;O;=-=u;;OGG&smBoMAbbbbbbbbb",
+"bbbbbbbbhfvvvvuuu===;;OxmG;=====;===;;=;u=;u;;;h;=hO;==hOGO;=;9OOGG&sxmtBio+MAAgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbtxsSS9Oh=;u=;;hh=;O;==;OOOh==hh;;====u=hOhO&m;=;====;u;==hu=h;9&StBoMAbbbbbbbbb",
+"bbbbbbbb;ffv#fu==h;hOsx9h====u;===;===;===hu=;;;O;=h;=u=hGO;OhGG&sxmmtBio+MAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbxs&&9;;;uu==;O;;;h=uu;GOh;hhh;;h;=f===GsmtmO==u;;==;;u=h==;hh&Sm2oMAbbbbbbbbb",
+"bbbbbbbb#vvv#-;-=hsxsOh=;u=h=;;=u=;=u;;===;uuu=hOu-uu=;hh;hhh&S&Sx2BBi+MAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbtSSGO;;--#=u9=-uu=;h;=h;;OOh==hG=u=hGs9&mG;;u=;==;;;==;=uu;h&SstB+Abbbbbbbbb",
+"bbbbbbbb#vvv-=Oh9xmsh;==u==;==-uuh==hhhh;=;;u-=;O====u;hOh;hGSsxmtB++AAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbs&h;=uu#-=h;uu;u=hOh;uh9GOhuhGh;Oxs9;Gm9u--;;=;h;O;;;;=u=hGSsti+Abbbbbbbbb",
+"bbbbbbbb#####uxmm&O=h;h-Ouh;uuu==u-h;h9;hh===u--uuh==uhOhOhGGGxtBo+AggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbSh;----#f--O==u=OhOhO;;;hOO;;;9s9O;;Gx;u==uuuh;Ohh;=;;=-uhhmtB+ggbbbbbbbb",
+"bbbbbbbbf=uu-9mGO;OO;u=;;u;==u;u==;hOh;hh=u=-fuu-;u=u=;;OGGGGxt2iMggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbu##vv#--u==u===hOOh=;h=h;;Gh=;;h-h4Gc==u=;;hOh;h;=uu-u=;9stBoAgbbbbbbbb",
+"bbbbbbbbf&sssxx;;;=uuu=;;O=;;==;h=;uO;G;-=uu--===u--;h;=;OG&&m2iMgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbG#vvv#--=-#=;;=;hhO;=h;;h;u;-c;=Ohh;h-;h;;=;h9O=u=uu-u;hG&xB+ggbbbbbbbb",
+"bbbbbbbbf#ccchO==h;=u=;hG=-Oh;GhhhOhhh;===;u-=OOfu-;=;h=h;O&stiMgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#vvvuh---==;;==;uh==GO=h;;=u=;G;u;9;GhOhhOhhh===;=uuhGhhGm2+Agbbbbbbbb",
+"bbbbbbbbv00v#-uh=;h=u;=Gu=h==hhO9G;;h=u;;==;h=u-==;Oh==;h;h&x2iAgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbvv-v#v-u;hO=u;;;u=;hhOh=h;u;u9hu;;=;hh9O9=hhuu;=;=O;=u;9StioAgbbbbbbbb",
+"bbbbbbbbvv0vv-uu-;=u-u;===hh;hhGGOOO==h===;h===hu==9O;===;O&m2+Agbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;vvv##uuu=hO;==uu=u=;h;=;;=u-;===;;O;h99GOO;=;;=u=;h==hh9&mBoggbbbbbbbb",
+"bbbbbbbb00vff##uu=;u==fuhhh;hhu;h9h=;h=h;hhOhOhO==;hhOh=-;Ost2+gbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbvv###;==uuh=hO;--=;h;=u==hu==--=h9=hh;=hOO;=h;;;hhOhhGO9G&tBoAgbbbbbbbb",
+"bbbbbbbbcvvvf##==;===u-=h;;9O==;h;;;hh;;;;hhh;;h;Ou;Gh;==;G&m2oAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0vv###u;h==hO===u==h=u;=;==;uu-;h=9hh=;;h=hhh;;;=hhOh;h9G&mB+Agbbbbbbbb",
+"bbbbbbbb=vvvv#-u;uu=-=hu;hOhOh;;h&;uuh==hO;h=;=hG;;h;;===h9&m2iAgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbf#vvv#f=hh;h;;u==u=u==;=h=u;=-h=;hOhOO=hhGO=-;;=;Ohh;==hG&Sm2oAgbbbbbbbb",
+"bbbbbbbbh#vv##uu;--uuh=-hO;hOhhGGGhh==uh;;;;;hh;;h&O;h=uhOO&x2BMAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh0vvvvf==;;GG;h=u=;;u==;;h=--u=h-u9h=9h;OGG9h==u;;;;;h;OOOGmtB+ggbbbbbbbb",
+"bbbbbbbbbvf##v-uuu===;h==;=hG;;OOOGG==uu;=h;;Oh=O&O;GO==;;GGstBi+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbj00vv#u=u=&9;GO;u==;;==;;;u===h;==;uh99=O9O9Gh=;u==h;;OOhOsmmB+Agbbbbbbbb",
+"bbbbbbbbbvvvf=v#===Ohh;u=uu;hOG;hhG;;h-;hO;=;;;;=GO=h=hh==hOSttBo+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb600v#0ff=uu9G;==;O;-=u=G=f=;==9hOuuu==hOOO;O9O=;uuhhO=h=;;;&mm2+ggbbbbbbbb",
+"bbbbbbbbb0vv#####=h;=h=O;O==;;h9hOhhO=;;;;=uu-ff-h;;uu;OG=;h&st2Bobbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv0vvvvvv##f#;=h=u=hO;-uu;uu-uOh;=h;hhhu;;hh9hOhOO=h;h==uu-u-9St2oggbbbbbbbb",
+"bbbbbbbbb0vvvv#fu-uuu;h==h9O==;hO;GG=;Ouu;====f#-;h;=-u==u=;9&xt2iibbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;00v00vv#f-#f#;;h=--==u-uuu=uu=-=u;h==h;G===hO;;&;=O;-;====---GstBoggbbbbbbbb",
+"bbbbbbbbbcv#vv#f#-fu-u-=hO=Oh;h=;;=;Ghu=u==;u-u==;=====uu==OO9xm22Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#0##000v#----uu;==;u===uu;=f=-=u---uuu;Gh;9=h;;;;uOG=u=u=;;u-;;GSmB+ggbbbbbbbb",
+"bbbbbbbbb;vfvvf-f#-fff;hOhh==OGh=;h9;===u=u===u===;;;==u=huO;9Sxtt2Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#0v#v000vvf-uu=u==;h;;=uuhu;=u=;u-uf-f=;O=h;=hGO;;;Oh;===uu==;==hOStBMggbbbbbbbb",
+"bbbbbbbbbbfv#0#f#-#f-;-uu;h=GGG=;hOh;=;u=u-u=;;u=;;;==;===h=h;Gsxtt2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=v6v#vvvv##f#-==;===;;;u;u==h==-===u-#-uuuu;;;h&Ghuh9h;===u=-u==;;hGstiMggbbbbbbbb",
+"bbbbbbbbbb##f#fff-f=-==uu;hO;=hGGh=hh;=u--=;=;--=;;;=O-u=;;;hO9&smm22bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-6v#f#0v-vf#ff-;=;=fu;h;;h=u-;;;;;u;=uu-=u=u==hhh=;GG9;;9=;=--u;;;=u;&xtiAggbbbbbbbb",
+"bbbbbbbbbb--#-ff#f-=;==;hu=;Oh;hhO;=;h==f-==;u-u;;h;;;;-u-=hh9G9GSmt22bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-v##v##----;==-u=h;;u==;Oh;;;=-fu;;hh==--==;==h;u;hO;;hOh;=O;=--u;;=u=;Gm2iAggbbbbbbbb",
+"bbbbbbbbbbu#uf###--=hhh;h=hh;=h=hOhO;=;h;;-u=;uu;OG;==uu==u=OOhh9&xt22ibbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbOf#v-f#-u=;hGGOG&&G;hhhh;;OGO===u;=u=;9==u;u=hhO;;;uO;=;;;hhOh=;hh=uu==;;OSm2igggbbbbbbbb",
+"bbbbbbbbbbbff##f#u=-;;;h;;;O;==hO9h;==O;=;===uu;Oh;=;=u;;;==;;;Oh&sstt2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh9h=;;9SsxsSsxssS&G&9hOGGOh;hu;;====;=;=uh==h=hO=hO;;=;OOOh==hh;=;===u;&sm2+Aggbbbbbbbb",
+"bbbbbbbbbbb-##-##-=-=;=;h;=h;=u=OGO;h;hh;h;=u-=======u;=u=;;=;hh;9S&xtt2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbxSxtttttttmmmsSGGGG9hO;=h;;=;h;=;=u=;==;;=O;;;huu=;9Oh;;hhh;h;;-=hO&mB+ggbbbbbbbbb",
+"bbbbbbbbbbb-#ffff-u--==hh=-u=;;;=;h;;OG==;Ghu-==;u=;=;;==;;=u;O=hOGG&stt2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbBtt222ttttxssSS&9&9OOh=;h===h;;=;uu-;=9=-uu=;;;;O;;OOO==hG=u=h9SmBMggbbbbbbbbb",
+"bbbbbbbbbbbbu-----u=--=;O===u=;hO;=;O9G;;=OG;=u=u==hu=--u;=;hhhOhh9GG&xt22bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbi22t222mmmsSSSGGO;;Oh;hhhhh;;;=-==Ohu=;u;;Oh==h99OhuOGO;=O9StBMggbbbbbbbbb",
+"bbbbbbbbbbbb--=----u=-u-uuh==uhOOOhhh=;OOh;;hhhuhuh;u=u===-;h;9hhOhOGGSSmm2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2222tmtmmmsSGG9h;;Gh9Oh;;=;=-uu-O==u=OhOhh;;h;OO;h;hOOGxtogggbbbbbbbbb",
+"bbbbbbbbbbbb;u--uf#-ffuuf;==u==;OOO;=h;;;;hO;-=;;u;==u;=u=;;Oh;;O;;;;hGSsmmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2t2tmtmxsss&&GGGGOhO;=uu--uuu;==u=;h9Oh=;h;h;;Gh=;9Sx2ogggbbbbbbbbb",
+"bbbbbbbbbbbbb-h-vf#fffu==---;;==;hhh=;h=h;===u=;;O=;;=u;;;;=OhGh-==;=;9&sxxmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb22ttttxmxss&&&SGh;;=u-u=u;-f=h;u=hhO;=;;;;h=;=;G&mB+gggbbbbbbbbb",
+"bbbbbbbbbbbbb=-#-f-##uOOffu==;;=h==h=hGh;Oh=u=;;G=uOh;GhhhOhhhh=;=huuh&SG&smm2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb22t2ttmmxsS&9OhO=u=hG=--u=;h=;;uh==GO;hh;=uOGmtBMggbbbbbbbbbb",
+"bbbbbbbbbbbbbG#-fffu=u--==;Ohu=;;u==hhhO;=h=u;=G==h==hhO9G;;h;u;==;;9;;;9&xmtmmbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2t2ttxmxS&GGOhG;;-u=;h9=u;;;u=;OhO;=O;uO;smtiAggbbbbbbbbbb",
+"bbbbbbbbbbbbbbu-##-=--=;=u=GO;==uu=u=;h;u;;=uu;===hh;hhGOGOO=uh;=u=O;=;9hO&mmmmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2tttsxxSG&G9h;h;;=O9;===u=uu;h;=;;===O&x2ogggbbbbbbbbbb",
+"bbbbbbbbbbbbbbuuuuu=uh;h==;;;hh=fu=O;=====;u==fu;hh;hhu;OOh=;h;;;hhOh999OOGSsmtmmbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2mtmmxxss&GS9O==O;;O;--=;h;===;h=;;hGmBMgggbbbbbbbbbb",
+"bbbbbbbbbbbbbbbu--u==u=;;hu;G;===uu=h=u;=h==;ufuh;;9O==;h;;;hh;h;;hhO;;hhGh&sxxxmmbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2tmmxxxsSGGGO;;O9;==-u=h=u==O=uOhO&tiMggbbbbbbbbbbb",
+"bbbbbbbbbbbbbbb-==-ufu-;G;;h;===u=====;;h=u;-=h=h;OhOO=hh&;uu;;=hO;h=;=OGOhGG&&sxmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbttmmsS&&&&OOhhu===;u==;;h;u;;;Ss2igggbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbb=uu--=;==h&h;huu;;====;;;ufuu;=-hOh;O;h99GO;=uuh;h;;;hOhhOSG9&G&sxmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbtmxxmSSGs&hh;u=;;===;;h=uu;Osx2+gggbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbu-u-===uOGO;Gh=u;=;;==h======h;=u;uhGh;G9OGG;=u=;=h;hOh;h&GhSG9GSSxmmbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbmmmsSxs&&Gh==;;;==;;;==;h&stBAggbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbb&;u--uuu=GO=huhhuuuuhOu-=;=OhO;u=uuhhO9;hhG;;;-;;Ohu;;;;=GG;O;G&GGSSmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbmxsxm&OOhO=-uu=G=f=;;;&Sm2igggbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbb=-f###vf;;;uu;OOuuu=;uuf;;h=h=h;O==;;hOOO;hO;;;;;==u--f-h;hu=hG&9G&sxx2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbm&sssGOhGG;uuu;=u-uOOG&t2+gggbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbb&--ffvvf=;;u--;uuuuuu=u=uu=uhh==hOh==;hO;9G;;O=u;u==uf#-;hh=uuhhhO&&sxxtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbmxs&9hOh=uuu-=-===OOxtiMgggbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbuuf##f-====u=uuuu=u-==uu--uuu=h9;hO=h=h;;;9h=uu==;uuu==;=;==;=;;hG&GssmmbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbSS&GOh=u==-=u;==;Gs2ogggbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbG---#-fu==;=uu=;uhuu==ufu#-f=hOhh==O9h=;hOh====uu====;u=;;h;==h9h&G&xxmmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbmSG9O9;;=-===u=;&sB+gggbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbb-uuuf-u====;u==;u=-=;-=ff-=uu=;h=GGG;=hOh==;uu=u=;;;u=;hh;=h;hOG9&&smmttt2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbSGG99;=-u===;h&tiAggbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbb&u=##-=u;uh-uu;;=;===u=-=-==uu=hO;=hGGh=OO=;u-u=;=hu-=hhh;G=hh&&Ssxmt22BBBBbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbG9GGO;;-;;=hGm2Mgggbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbb=-#f-u=u=;;---=h=O;=-fu=;==;h==hOh;hhOhu=O==--==;u-u;h9OOG&9G&xmt2BBBioo+oibbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbShhOhhOu==hstiAgggbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbb&=-#=;h=-=uu=uu=hh==u=u;hhhhhuh;;=h=hhOO;;hh;;-u=h;=hGSGG&&smmt2iio+MMAAMMoobbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbSOOO9;=-OOx2+gggbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbb;-u;;u-=u-;;====;====;u;;;h;;hO;==hO9Oh;=Oh;;=;;==OGSGSsxmt2Bio+MAgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=f##vvv00v-uSSbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbSOhO=;;hsmBMgggbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbb=u-u----=====;==;=u;;==;;;h;=h;=u=hGO;h;hh;9;h;=OGGssxmtBBo+MAgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbhc##0v#v#v#v#fffhsbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbSO;h;;9xtiAggbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbh-=f-=-==uu;;=u;h=;=;-uu;;O=-==;;h;hO;;OG;;h&GOO&SxxtBBi+MMAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-#v##vf#vff#f---uOSbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbGhOhhGx2+gggbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbb;-uu=f---u;==hh;;;=;;u-=;O=uu==hhO;=;OGGOhhSs&Ssmt2Bo+MAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9&GG;uuuufuu-GG&4bbbbbbbbbbbbbbbbbbbbbb9#f#-##v0vf#-u==h;=;Sbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9OO9&mBAggbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbOh-;=f-f-u=-;;;G;h;==;-u-uuh;=uhOhOhOOh9G&&&smt22iiMAgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbhhf##v###v---=uuu--uf-#-fh=u=&bbbbbbbbbbbbbbbbbbbh=-f####v#--;==h=;h;h&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9GGStigggbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbb;-=-u#uu-u=hOh;;O==u-f=u-;=====;9GOhhGGGSsmtt2BoMAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&;v#ffv###vf###f-uu--=f###vf-#-uuuu==O9&&bbbbbbbbbbbbbbbb9==u----uuuu=uuuu-u;=h&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&O&stiggbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbhu==--=;===O;G;u=u=--u===u-;h=;hOGGOGSsmm2Bi+AAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;=f#v#vv#f-##f#v#fff#u-;u##f####f-u##u;uu=hhO=;9Gbbbbbbbbbbbbbb9hOh;-=u;ufff---u=;h==9sbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&GS2oggbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbb;-h;uh=;;hh;hh===;--=O9---==hh=O;O&Gstm2BB+MAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=-#uf#-f#vv#f=#v-u#;---uu-u-fffu#v#=;###--==u==uh=;&hhGGbbbbbbbbbbbbb&&GGh;;-ff-##-;9=uu;hOG9xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbSGx2+gbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbh==;=;h9=;h======;h==u==hOO=;OG9Gsxt2Bi+Agggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbu#0v#ff-##f#vfv=fv-##---;=u-uu#----f;--#-fu;;--u==-=;hhGh;9h;xbbbbbbbbbbbmSG&9===uuf;-u#uuhOGhh9GG9bbbbbbbbbbbbbbbbbbbbbbbbbbbbbSm2+gbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbh=;hhhhhh=u;====O;==;==;GGOhOG&SxtBB+MAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbfvvvvv0v###v#vvvvf###-uuu=h=O=;ufuuf--=--fu---h;u-u--u-u;;;=hh;;=O9bbbbbbbbbbbSS9GO;u==u-fuuuuO9h;hhh999bbbbbbbbbbbbbbbbbbbbbbbbbbbxm2+bbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbb;hh-=;hhu=h;;;hhOhOOh;=hOOG&&&xt2ioMgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb###ffv60vffv#vv#-###v#-u=-=;fuh;h-u;===;;h=;Oh=uu;=;hu##-===uu==O;;h=;9&bbbbbbbbbbbS&&9GOO;=O;=-u;;hOhu=OG&GGbbbbbbbbbbbbbbbbbbbbbbbbbbt2bbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbO=====u=;h=h;;hOh;;OhG;GsSsxm2BoMAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc##vv-##vvvv#f#vf#c##cf##;=uh;uu=;;u;h;=;==;hh;=;;;u=h;=uu-uu;u-=uhuuO;=;GG9sbbbbbbbbbb&S&GGG9h==;=uuhO=;;=;hG9OG&mbbbbbbbbbbbbbbbbbbbbbbbm2bbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbsO=;;G=-u;==hh;h;;=O&9GSSxmtBiMAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbcv0f-#v###0vvv#v##--uf#=f#;u==h;O9=;;Ghuu;;=;h;h==uhOh=h;;u=uu=uu===;uu;uu9;9G&&&xbbbbbbbbbbSS&&O;=;Oh=;;=-uuu;;hO9G&&&bbbbbbbbbbbbbbbbbbbbbbmbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbOhGOO;=uuuh;hh;;GGGG&mmm22i+Aggggbbbbbbbbbbbbbbbbbbbbbbbbbbbb##v#f###=fvfvv##f#-f-==-#fu==f;Oh;h;hO9GO;=uuh;hh;=hO;h;G9;;uu===uu==;;u--u;huh&GGS&&tbbbbbbbbbs&GG9Ghh=9O;;uu=;=;;hO9&O99Gbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;GhOhOuuf=;=hhOG9GSmttBi+Mggggbbbbbbbbbbbbbbbbbbbbbbbbbbbcv0#vvf#v#=u#=u##--uu--;uuu==u;;=u;u;9O=9999G;=uu=u;;;Oh;;&9h99=u==h;=uh==uu=u;;;=hh9&sGsmbbbbbbbbbm&&&&hhG9hOO=uu=;===OOOOGG&&mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbmhhhO===f;hOhhOG&Sm22B+Mgggggbbbbbbbbbbbbbbbbbbbbbbbbbbf0v###v#v#v#;=f-fuu-#--=Ou-==u;9h;uuuuhhh9hhOGO=;u;hOh=;=h==GG;;;;h=-=u;9u-=;u=9O;===;9G&ssSsbbbbbbbbbb&G&9h&&;=;=h=-=-=9=-;OO9sSs&sbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbGOO;;hu=;hhhhOO&smBiMAggggbbbbbbbbbbbbbbbbbbbbbbbbbb-fvv#v#v000000--uf#uhhu-u=;u-#;h;;;;h;hhu;;hOOhh;Oh=;hh;=uu-u-hhhu-;O9u-uu;=uf=h;;=h;;;O=O9&Ssxsmbbbbbbbbb9hh=hhhu-uOO=-uu;=u-=99GGSSssbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9;GGu=h==hOGSsxtB+Mggggbbbbbbbbbbbbbbbbbbbbbbbbbhvfuvvfv0vvv#v00v-==--f==u-u--=u=uu;uOh;;hhO;;;;9;O&=;O=u=u===##f;hh=u-=;=-=uuuu=uu;uhh;;;h9hhO&ssstmbbbbbbbbbh;=OOh=ufu=u-=u-=u;==h;G&&&sstbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;;;G;u=;O&sxt2iMAggggbbbbbbbbbbbbbbbbbbbbbbbb;0vvvv-f000v#####f-=uu=u=uuuu==u==;;-u-c==;9hOO;h;hh;;99;==;;h=uu=u;===u==u-u==f=uuu---uuu=9hO9;99G&sSmmbbbbbbbbb9OGOh;uu=u--==-=uuuuuu;hOGxsxmbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&h9h;=;OGst2B+Aggggbbbbbbbbbbbbbbbbbbbbbbbbc#-#vv#fvv#v#v#-f--uu=;;;==u=h=O==hGS;=-=-;OGOhh;9GG;hOGOh;;;==;=;=;=;hhh;=u=huh=u;;=-uf-f=;9h;;=OGG9G&xmxxbbbbbbbbb999Ghhu-u;-;u-===-u---=9&&SSSx2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&O;;Oh&sm2oMAgggbbbbbbbbbbbbbbbbbbbbbbbb;0fu-v0#-fv#f##-#-u;;==;hhh;h;hh9;h=&x9;=u=h;;hO9OGSSG;G&GhhO;;h=;hhO;;hOhh=h;;;h=;u=;u=-f-=uu=;h;O&&O;GssSxmmbbbbbbbbbsG9O=;uu=;u=-u=uu-#-u;=h9&Sstmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbGGhGGSm2oAggggbbbbbbbbbbbbbbbbbbbbbbb-v##vv#--#v-u#--#-uh=O;uh9O9h&;hhG9GG&mSG9OOO9GOOG&S&G&sSSGGSOG9hhhGOGO=h99OOG;=;OhOhh=h;;uu==;uu=hOh=OGG&O&xSxmmbbbbbbbbbs9OG;u-====;u;=uu-=u;=;hG&SSxmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbGO&&sx2oAgggbbbbbbbbbbbbbbbbbbbbbbbc0v00###vf-u-fu;==-=hO9O;O9GS&&&SGGGSxmtmS&GGSSsSsxsSxxxsxxxsSSxS&O9&GSGO9GGSGGGGO;;O9OG9h;u=h;h;;O;=hOO;O9G99GxxmsmbbbbbbbbbsGOO=-fu==;Ouu--==;==hh;G&sSxmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&&sm2oMgggbbbbbbbbbbbbbbbbbbbbbbbv###0vv#vf#u=h9hhG&G&O&&SS&smmxxxssxxmBBtmmmmmtttttttmttmtmttttmmmtmmsxsxxssmmxSS&G&GGG&SGOhO;OGGGO9h;OO;OhhOGG&&smtmtbbbbbbbbb&h;==uuu=h==-;-=hhO;hh=GGG&ssmm2bbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbSmtB+Agbbbbbbbbbbbbbbbbbbbbbbb0vvv#vv#u#--;9&&&&&sxxxsxmxxtttttttt22t2BB22222222222B22B2222B22222222tttttmtttttmmxxmxxsssSs&GSGG&GG&OG&hh;hGGG9O9sxmtt2bbbbbbbbbG;hh=uuu;u=uuh==h;hO=OGhOG&xmmttbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbt2i+gbbbbbbbbbbbbbbbbbbbbbbcv60vvv##f=;;hGsmmxmtttt2t22t2B2BB2BBBBBBiiBiBBBiiBiiiiioiioiBBiiiBBBBBBBBBB2222222t2ttttmttmxmxxxsSsSSS&G&&O;h9&GGGGSxmtt2bbbbbbbbbbOh;==;=u==u=;=u;;;;;h;9;h9Smmmttbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbB2Bbbbbbbbbbbbbbbbbbbbbbb=00v000#f;u=OGsmmmt222BBBBBiiBBiiiiioooooooooo++++o+ooo+o+oioioooioooooooiiiiBBiiBBBBBBBB2B22t22ttttmmxmxms&SG&SGGG&O9&SSSm222bbbbbbbbbbG;;;=-u;=u=;uu-;uG;-==hO9G&xxmttbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbBbbbbbbbbbbbbbbbbbbbbbb#vv0vvvvuO&&smtt2BiBiio++o++MM++++++M++++MMMMM+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb++oiooiiiBiBBBBiBBBBBB222ttttttxxxsxsxS&G&SssSst2BBbbbbbbbbbb;h;==;=;;=;;=-u;Ohu=;uhO&&&&mt22bbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc0fv###uhO&Stt2BBioo++++++MMMM+MAAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbboiooiiBBBBBB22t2t2ttmmtmmxxsSSSxmm22Bbbbbbbbbbb&;==;=h;=;==;=u-uuh====9OG&Sxxmt2bbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv#v#f==h&4xt2Biooo+MMAAAAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbioBBB2B2B222t222tmxxxxxmm222Bbbbbbbbbbb9;;hh=u;=uu---u-u;==u;h9&&&SxmmtBbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#vv--OO9Smt2iio+MMMAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbboB2BB222222ttmmmmmt2BBibbbbbbbbbbO=;hh;u-u----=u;-f=h;u;O9&G&mmtt2bbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbG#v-c;9sxt2iii+AAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbsS&SGSsx222bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbBB2222t2ttttt22iibbbbbbbbbbbOh;hu-u=u-u;O=--u=;h=;h=G9Gmmm22bbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-f=hh9smt2BobbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbG=#v###0v#=;;G&S&msmBbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbi2B2Bt2t22BioobbbbbbbbbbS;h--u-=-;u;u-u;hO=u;;hu;O&Smxtt2bbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-ch9SsmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbGfvvvv0vffvv#-u;;hGGO;OO&s2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbBt22222iiobbbbbbbbbbbhu=u-#u=;uu;===O9;=======9&Ssmtt2bbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcu;u9xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&-ff#fu##vv#f-u-fu;9O;;===O;;hSmibbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbi22BBiibbbbbbbbbbbbh==u==;=;9hh=uh;hO;--=hOhO9&stttBbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-hObbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;fvv##f####-#f;;;;;;;h9;u;uhuu9=;hStibbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbBBBiobbbbbbbbbbbS=;u-==;;==h;=;hO;;=uu;O;;9&sxm22bbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbO##0#####v#uh;=h9G9G&G&99hh;;=u;uu9hG&x2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbiiBbbbbbbbbbbbb;u==u=u-u;O9=;hhu==u;u=;hOG&smttBbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS#000v####-=9O&&mmxmxxmmxxs&&G9=---;;uhS&&tBbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbboBbbbbbbbbbbbb==;uuuuO;h;G9;;=u=;====hhGGGSmtBBbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;#-v000##=h9ssmm222BB22222tmmmsS9Oh=;;;=OOGsmt+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbu-fuu-;;==GGO9O;u=;h;==hhhOGsxt2Bbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbf#f#0vvf=G&SttBBio+iiooiiBB222txmsS&SGh=uu;G&sxtobbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;;ufu-=u=G&;;;;h=-===G=u;O9&tt22Bbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&#v#vv---hSmt2Bio++M+AAAMAAMMMiBBtmtmxS&GOhhh=O&sxtibbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;=-f###f#=;huu=OO;-u=;=uu;GGsx22Bibbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh0v#vf;-;SmtBo+AMMgM*,lqn:D::nql,Voo2tmsmS&9OhO;OGSmxBbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb--#ffvvv=h;u-u===-=uu=u;=;9Gxt2Bibbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;0v0vv#c&mt2oMAgggVy%aa%:nDDD::DD%%ayqVitss&&&9OO;9GSxm2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-f-###ff==;=uu==-uu;-=u===;h&xt2iobbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc0vf#v-;st2oMggg*qn:aaa<a::DDD::D%%aa%nnlVimxmSGO=OGGGSxtBgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbff###---=;;;uuu;=;=u===uu=;O&t2Bibbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=0#--##Om2iMggg*qnnnn%a<<aDnDDDDnD%%a<:nn:D:qomSSGG&Gh;&mmmBbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv##ffff==;;u;u=;;==-===uu-=hsxtBiobbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9vvv0#h&m2iAgggqnnnnnnDa<<<%nDDDD:D%a<a:n:D%:nnV2xS&hO99GGxtmobbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbO-fffv#=;=;;=u-;=;=;u;==uuhh&sm2iobbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&06v##-GmB+ggg*:::nnnnn:a<<N%:D%DD:Daa<ann:%D:nnnqix&&hOOG9&st2Mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbf##vv#-h=;;;u--=;;hh==fu=;9GSm2Bi+bbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#00v#fGxBMgggq::::::nnnn%NNNa::%DDD%a<N%n:D%Dnnnn::,mGGh;;OGSxm2Abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbf##fvf=O;=u=uuuu=;O;=u;-;OG&smt2i+bbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-00vf#OxBMggA:D::n:n:nnnnDNNC<:D%%:D%aNNDn:%%nnnnn::D,m9O9OhO9Sm2Bgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh##v#u;===h-=;====h=;=u;=;O9&mt2iobbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh00vv-Os2MggADDD:::::::nnn:<>C>DDa%DDa<NN:nD%%nnnn:DDD:,m99GhhO&mt2obbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbf0###f-=-u======;u;=u;h==h9&Sm2io+bbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb00#00u&2+gg*lDDDDD::::::nn:aC$C%:a%DDaNC<n:%%:nnn:DDD:::,mhhOu;9smtBAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbvvvv#u#==u;;u=;;u=h;=;;u==hhxxt2iobbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv0vvv-9togggn:DD%DDDD:::::nn%C11a:aaD%<>$%:Da%nnn:DD%DD::n,xh;uhGSxm2ogbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbuvv#v#u=u=u-u=;=;h;;h=;;=u;Osxm2o+Mbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS06jv#GmiAggqn::DD%%DD::D::nn:C1d<Daa%%N$1D:%<:nn:DD%D:::nnn.Sh;=uOSm22MbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbO-f#v--;u=uuu=u=;;OO=h;=;==;O&t2io+bbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0jj00cs2Mggq:nn::DD%%%D:D:D:n:<rrCD<a%aC1C:Daann::%%%D:::nnnn2O;u=;9xtBogbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbhvv#fv#u=uu===u;hO;;O;==u-u;;GmtBoMbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb4jjj0fGmogg*nn:::::D%%%%D:DD:nnar5dD<<%<$5N:a<Dnn:%%%D:::nnn:Dlxhu-=;&m2BMbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbvvv#u-u==u==;;uhOOO=u=uuuu;h&&mBo+bbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbvj0#uuSBAggn:::n:::DD%aa%DDDD:nDdzz%N>a>r3a:<<nnDDa%%D::nn:::::.Guuh=9xtBigbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0v#uf#-h=Oh;hhh;hO;==;=--hGhh&m2o+Abbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0v0v#ht+ggq:nnn:::::D%%aa%DDDDn:$p NC1<dz8%aC%n:Daa%D::nn::DD:DDx===h;&t2iAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#v--vfu=;O;GO9;;huu;=;=h;;=;9SmBi+Abbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-v00v;sBAggn::::::::::D%a<<%D%%D:NX6115C3 zD$C::Da<%D::n::DD:DD%aZh=uuu9xti+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0vv#vfu;uO9OGOOhu;==u=;h=u;h9&mBi+Abbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#v0#=&toggqD:::n:::::D:D%aN<a%aaDaXjp58r jr<5<:%<Na%D:n:DDDDD%<N<<x--ff;stBogbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv00vf=-;h==OOO;=h;;;hhOh;GOOOGmtB+Mbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbsv##v=sBMgADD:D:::::::D:DDaNC><<Na%pj@3Xz6j$53%%N$>%D:::D%DDaN>N>N<7=#-#h9xBiMbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv0j###=;;===h=hhh;h;=;OOhhhO9&s2i+Agbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcvvv#=miggVDDDDDDD::D::DDDDa$r1$$$<5jjpjXj61X1aC51<%::D%%DaNCCC><<a%&ff--hmtiMbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0vv##uuh9u;;G9=u;h=;hhhh=uhG&GstBoMAbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-v0vuStMggn:D%%%%%DD:D::DD%a>5835311jj6j6j68@N$88$aD:%a%aN$11$CNaa%De=--#-&tB+gbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0vjv=uuh=hhOG9hu=u;;O;;;hOhOOsxt2oAgbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbjv0#hs2MggnnnnDD%%a%%DDDDD%aNdp@www56jjjjjX68r @5>a%Naa>d5rd$N<a%%DDk;f##uGmBoAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv#v0##-h9=999OGOu=u=u;;;Oh;=&SxtBoMgbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbttbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbGvv0#;miggV:n:nn::Daa<aa%D%%<>186j66@Xjjjjj6j8@jXrN>1C$5zz5dCNaa%DDD::9vf#-OtBiMbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#vv00vu;hOO;O9G=;=uhOh;;hO;;&xSx2iMgbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbm=h&xxbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;v00#=xigglDDD::n:n:D%<<N<a<<Cr3@jjjjjjjjjjj66j6wr335w@ 851><a%DDDDDD:xv0#uhStB+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0#0##=#u=hhOhO;hOu;;h;uuu-u-hGSx2iAgbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbGf=hGG&stbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#000-9moggn::DDDDDD:::D%N$dd1158@6jjjjjjjjjjjjj6 @@@jj6pdC<a%%%DDDDDD%Z##0#;9t2Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv#vv#-=;u=;Oh;&;;hhu=;u==-#fhGSxtBMgbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbhv##=9&sxtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv00#-Gt+g*n::::DDD%%%DDD:%>3wwwX6jjjjjjjjjjjjjj6jjjjj6pd><a%%%%a%aaaa%7#vv0#;sm2gbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc000vu-;;=h;=;;;h9;uu==;=uuuuh9GxtiMggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;v##fuhGsm2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0j0#=sBMg*::::::::DDD%a<<aaNr 6jjjjjjjjjjjjjjjjjjjjj6pr$N<<<NNN<<<aaaa7=v00vuOstgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbfjjvfuuuu;9h;;;OO;=;=-u======;9&xtBMggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbO0vvv#uh&xmBibbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb000#;xBMg*n::::D:D:DDDDDaN1533p6jjjjjjjjjjjjjjjjjjjjX8z5rd11$C>NN<<aa%Dh0v00#hsxMbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbj000vv-u;GGhu;9h;=;===-=u;h==hO&xtiMggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcvvvvvf;Gxt2ibbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbj00uOmiMgqnn:n:::::DDD%%a<>13X@6jjjjjjjjjjjjjjjjjjjjj66 z51$><<a%DDD:::90v0v-;&&ibbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0j000#==h;hOOG;=Gu;uuu-;=;;-uhGGxtiMggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb9#0###vv-GstBiMbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbj0#uGmBMgqnnnnnnn::n:::DD%<$rw@jjjjjjjjjjjjjjjjjjjjj6Xzd><a%%%DDDDDDDDD9000#-OGSBbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;0v00#f#=;O=hOOh;=h;=ffu;==uuuhGSx2BMggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-###f##f=hSmBoAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb##v=&tiMgqn:n::n:n::::::D%aN15w6jjjjjjjjjjjjjjjjjjjjj@X pzz355rd11$C>>N96v0#=9sxibbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb00v#v--f;;;;;;;Ohh;;h;=u=u=;uuG&SstBAggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbb#0vff##vfh&m2iMgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbff-u&mBAgq:::::DDDDD%%a<>158w66jjjjjjjjjjjjjjjjjjjj@ pzrd$CC>>>NNN<<<<aOv0#v=GStobbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb00vv#-f=O==u;OOOh=u;h;==uu=u=OGO&xtiMggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbO#00v#uf#=OxtiMggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb==-;&xBMglDD%D%D%%a%%%%%%%aNr8@jjjjjjjjjjjjjjjjjjjjXz1<aDDDD:D:::::::::h0v##=&mt+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc600v#f-;=Ou-uhGOh;hhhh;h=;-==;hh&stiAggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbj00vvvvf=;GmB+gggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbuuhhOxB+g*:::::n:nn:::D%N5 6jjjjjjjjjjjjjjjjjjjjjjjj6@85Ca%D::::::::::nh00#v=&tBgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0jj0vv=u#uu;;;=;Oh;hhh==h9uu===O=SxtiAggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbb#06000v#u-=&tiMgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;-O9Gs2oA*nnnnn:DD%<>$53p @66666jjjjjjjjjjjjjjjjjX8w8z5d$CCN<a%DD::nn:nc#0#uhsB+gbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh0v0jv#;=-==-=hO;==hGGG;uOGOuu=;=O&x2oAggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbj00v000f-=G&tiMgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbO;-;9x2iM*::D%%<NC$$1dddr535rr3Xjjjjjjjjjjjjjjj6j6w$NNN><a<a<aaaa%%DD:lu0#v-&tiAgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv0000vvff;==u=OhOh;h=h;Oh;;hOh==OOsx2igggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbc06jvv00v#=GxtiMgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbhuu;=GtiMA%a<<N<N>>C>>>N<<aaN5@jjjjjjjjjjjjjjjjj XjX1aDDD%%%%%%%%%%%%%7##vv=&2+ggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc00j000##fhu=-=;OOOh;;;;Oh;O;=;;h;O&m2ogggbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbXjj0v0j0#-uhStiMgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbOuf-;hxB+A7aa<<<<<<<aa%DDD%Nz6@ 66jjjjjjjjjjjjjjjz5 X5<D::::D%DDDDDDDDev00#=sBAgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0j0j00v#-;f-u;;u;hOO==h;;;;;h==uh9GStB+ggbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbvj0v#j60vf=h&mB+gggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbs=f##=x2oAyaa%%%%%%DDD:::%>3855w86jjjjjjjjj@@jj@jj3<13r>D::nn::DDDDD:D400v-Omoggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbfj0v0jv-;uf--==;==;uh;=9h;;hO;u=h9OGStBMggbbbbbbb",
+"bbbbbbbbbbbbbbbbbb00v0000vfc=;9m2ogggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbx--ffh&2i+VD%%%DDD::n::Da>d1N$51 jjjjjjjjjj88XjXw66z%%>$CaD::nnn::::::f00vuG2Mggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbj0000f#--fu=;O==;;;u;=hOO;;9xm9=OGOGstiAggbbbbbbb",
+"bbbbbbbbbbbbbbbbb60v000vv#fOh9&tBMggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;f##-GmBiADD:D::::n:D%NCC<%>CN36 @@wj6jjjj3r1@j3rw 3%D%N>N%D:nnnnn::7v00v=xBAggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb000jvv-ffu===hG;u=;=u=uhhh;=hGmh;;hh&x2igggbbbbbbb",
+"bbbbbbbbbbbbbbbb0vvvv##-##=h9Sm2ogggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=u#vf=S2iMq::nnnn::%a<N<%D<a%$w83w38@Xjjj63NNr6X>$53d%:D%<<a%::nnnnnxvvv-9togggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0000v#f--O;;=uhhhh=--=;;=;=u=hhmhuuhGsmB+gggbbbbbbb",
+"bbbbbbbbbbbbbbb#00vv#v#--=uh&st2+ggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbs#vuf-GtBo*nnnn::D%a<<%DDa%D<5315r$pp8@6jj3a<a363aNC1dan:Daaa%D::nnnu0vvu&2Mggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-v0vvv#-==;;h;u=hO;;=-u=h=u=;;=;mGu-O&StB+gggbbbbbbb",
+"bbbbbbbbbbbbbbv6vvv#vv#=h;;9&stiAggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-uv#;Om2iAn:n:DDaaaaD:%aDDa$1NC$CNw13pX668%%D<z >DaNC$<:n:%aaa%:::Z000#;xiAggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#j000v#fuuuu;OO;;hhu==u;u===h=;u;G&G;O&xtiAggbbbbbbbb",
+"bbbbbbbbbbbbbb6vvvvvvu==;;&G&xtiAgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-#v-fc42Bo*::D%%a%%::%%::%N1a<N<aN3>138 @8%:D:N33%Da<NC<Dnn:D%%%D:u00v-&togggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=060jvvu--u=O;h=GG;;u====;==;=O=--=9m9Gxm2oAggbbbbbbbb",
+"bbbbbbbbbbbbb0jj00v#f=uu;GGGsm2oAgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS-#-=uhmtBA:D%%%%::DD%::%<$a%aaaDCC<C1rzw3%:DnD>5$DD%a<N<Dnn:DD%%Fvj0v-&2Aggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#fv000#vu==hh=uG9OOO;-=hh===hh===;=httttt2+gggbbbbbbbb",
+"bbbbbbbbbbbbv0jvv###uu=;9SGOsx2oggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh;=--;Gm2oV%%%D:::%D::Da>a%a%%DD$aa<$1r35annnn%Cr<:D%aa<<%nn::Dy=6v0#Otigggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv#fjv0vf==u;;O=;&S;;O=h=-===Ouu;===GOGG&x2oAgggbbbbbbbb",
+"bbbbbbbbbbbb#00vv###ff-f-OhGGs2oAgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbSu-uu;&xm2MlD::nDD::nDaN<D%%%D:%>%%a<>$dda:n:n:a$1a::%%aa<%nnnnsvvvvuS2Aggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb4#vv0##v#u==uuu-uf=;huu=9O=--=h=-f=O;h=9O&xB+gggbbbbbbbbb",
+"bbbbbbbbbbb-00#00v#f-f#vuhOGGstB+gbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=hu-uO&tt2Ann:DD:::D%aa%D%%DD:<<D%%<<>$1<nnnnnD<$CD:DD%%aa%:ny#-0vuGmiAggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#v#vv=vv-ufuuu=uf##;;h=u-=;=-=-u=-;-=;=9GGSmB+gggbbbbbbbbb",
+"bbbbbbbbbbbvfv0jv#----u-=hh99&tBoMbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&-u-uOGxmti*::D:nn:%aa%DDDDD:D<%D%%a<NC$<:nnnnn%NC<:nDD%%%a%:-0v#-=&2Aggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbfv00vvvf=-#fu=;u---u=;==-===uu==-=uu=u--;hGS2iAgggbbbbbbbbb",
+"bbbbbbbbbbb#v00vvvfu=uu=u;hOGSx2iobbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc-uhh;Gsm2oqD:n::D%a%:D%DD::D<DDD%aa<NCNDnnnnn:%>>a:::DD%%%O00#f-9togggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbf0#vvvvfuf-uu-u;u=====;;;=uuu;u;;-;===--uu;&mBogggbbbbbbbbbb",
+"bbbbbbbbbbcv0vv0vfff=;;==;OOG&xtBi+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbx=u--;h&ttt+qnn:D%a%:DDDD:::aa:DD%aa<N>NDnnnnn:DaN<D:::DDD460f##OxBAggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb00--v0vu-f-=uu=-uu=;===;h;=;u==;==uu==uufu;&SmBMgggbbbbbbbbbb",
+"bbbbbbbbbb##v0vvv#-==;--=;;Oh&&xtBBbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&-u=;h9&mt2Mq:D%%%D:DDD::::a%:DDD%%a<<N%nnnnnn:%a<<Dn:::xf##0#OS2Mgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-#v#v##fu#-h-uuu--=;;;fu;;=;;=uu;=;==u=;uu-;h&stoAggbbbbbbbbbbb",
+"bbbbbbbbbb-vv#vv0vuu=ufu=;h;O&&Smt2Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbt9;cu;h9Gmm2MlD%%D::D:D::nDaD:DD%%%aa<<%nnnnnnnD%a<%:nnx0vv##u&t+gggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-06v#v0f-uu-u;;=-fu==u-u-=O;;h;u--=;;hh;;fu;OGStBMgggbbbbbbbbbbb",
+"bbbbbbbbbbc#vvvff---=;-uhOGh;O9&smt22bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbmG===h;GG4mto,%:::DD:::::%%:::DD%%%a<<%:nnnnnnnD%aa%:xvvvvf;&tiAggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbhvvv0vvv##fu=;;=;;;uu=u==u=hGh===u=uu=hhh=-h-h&Sm2oggggbbbbbbbbbbb",
+"bbbbbbbbbb=#vv#ffu--=uu;OOh;;h;GSxmmttbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbxhO9c=;9SxxmBV:::D::::n:%%::DDDD%%aaaa:nnnnnnn:Daa7G000v-;&tiAgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbfvv#0v-vv#-=;h;=uh;;==u==u=hOh;=;uu;=u=uO;;=uOh&xtiMgggbbbbbbbbbbbb",
+"bbbbbbbbbb=#f#--f=u;=-u=;;;=;=O9GSsmmmtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS99Ou-;9s44xtol:::::nnD%Dn::DDD%%%aa%DnnnnnnnnDDFuv#6j#uSmBMgggbbbbbbbbbbbbbbbbbbbbbbbbbbb#0vv0vvv#-##f=Ohh;;h;h;;=;f=====;uu====u;;====hOO&m2ogggbbbbbbbbbbbbb",
+"bbbbbbbbbbb###u#fuh=-u=u;=uh=;OhOGSssxmmbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbB9h;hh==9&GSsmB,::::n:D%::::DDDD%%aaa:nnnnnnnny&vj00v#uOmiAgggbbbbbbbbbbbbbbbbbbbbbbbbbb=v606v0-v0v#fu=u;O;;hhh=;Ohuuu==hfh;uhhu=;;==hh;hGGStiAgggbbbbbbbbbbbbb",
+"bbbbbbbbbbbfu==-#-GO=uu;-=;;=;==;G9Gsssxxtbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2&O;9h9;;h&sx4x2Vn:n:%D::::DDDDD%%%aDnnnnnnqxffv60vv=GtBMgggbbbbbbbbbbbbbbbbbbbbbbbbbb#0vv0j0v-fv#-fu=O=;=;GGO=uO&hu-;=u;;==uuu=;=;h;;9h&sm2ogggbbbbbbbbbbbbbb",
+"bbbbbbbbbbbu-f--u=u;;hh#Ouhhu=uu;;hG&&mSss&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2&OGG99hhO9GG&&x2,n:%:n::::DDDDD%%%Dnnnnk4vvj00v##=StiAgggbbbbbbbbbbbbbbbbbbbbbbbbb##0v00vvv0#vu---=hhhh;;=O;Oh;;hO;=u;=h=;uuu==;h;O&h&Sm2iAgggbbbbbbbbbbbbbb",
+"bbbbbbbbbbb=-f-uu-;;=u;;=uh==u;;=;;O&9G&sSG&&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbB&G&&G9h;hhGGGG&&s2.ln::::DDDD%%%%D:,Z;#0j00jv--hStiAgggbbbbbbbbbbbbbbbbbbbbbbbb;#vv#v00000#vf=-=-uhOOhh;;;hhh;O;==;h-===uu====;OOhO&SxtiMgggbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbb#--f--u==u==hh=h;u==;;;;G9SGO999OOObbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbboxG&SGG=;h=hhhhG=;GGte.kkyy:yyZZeS;#000000v#--OxBoAgggbbbbbbbbbbbbbbbbbbbbbbb;0####v0000vv#f=ff=;=u;hhO==h=;;=;huu=;O;;;===;;;uhGGG&Sm2+ggggbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbb-u;--==-f-;;h;uOh=OhhhOhOhGh;9Gh;O&&hsbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbms&GS99G;hO;=uu;h9;-hG;Gh;;;uf#u#v##000f=u;&mBMggggbbbbbbbbbbbbbbbbbbbbbb;#vv#vfv0vfvvvu;u#f-=;;u===h;=Gh;hh;u-=;O;==O=hOOOhhOO&&&mBoAgggbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbb===u-u=-;=Ghu;=hOOOG9h;h;=;hhh;9O&G9GGsbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbBx&&&S&Gh;h;-;u9;-u-u==uh=ufffvv#v#v--=OGm2oMggggbbbbbbbbbbbbbbbbbbbbb9v-#fvv#0vv###u-=ufu=hh===h;u;=hOO;;;;=;=OO=;=;OOhGO9hOG9&m2iMgggbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbb===#u---u=;uuhh=;O9OGhO;u;;u=;9h9xtmm9S&O&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbotssss49GO==cu=ufu=u-=;uhuu-#####-;O9&m2iMAgggbbbbbbbbbbbbbbbbbbbbb-v#u#u###vv###fu;u-u===OG=u===u=uhhh=;;==u=;=u;;h=OGOG99GG&xtiMgggbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbhu---=ff-uu;hh;hO=OGOO=;hh;;;OOGm&&9hOO99O;=hSbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbimxxsS&S9;;=u==;--=#-;=;--===;h&sxtBi+AggggbbbbbbbbbbbbbbbbbbbOfv#fvv-#-#v#--fu;;==Ohh=uh;hh=--=;;=;===h=uuuu;;O;hO;;9GGGGSmti+ggggbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbf=-=f-=-#uh;;hO=u;hh;;hh=h;;h&xGhh;h=h99h;u==h9&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbimxxmsSxGh;Gh=;;---u==;hGO&sxt2Bi+AAgggbbbbbbbbbbbbbbbbbbb###v##v0v####-uf===;hO;h;h=u;hhh;uuu=O=u==h=-h=-f;h;OOhu;h9OGSxtB+ggggbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbO==--=#-=u;;hOOh=hh9;==h;=h;OmG=uhOh=hh;u;==;=;=h;OG&bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbi2mmtxxxSS&G99O-hhGGGSmmt2BioMAggggbbbbbbbbbbbbbbbbbbc0#0#v##f-v##c-ffu=u==;h;=u;OO=;hhu==u;u===;;;=;=-;=h;Oh99=OO&&&xtB+ggggbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbb;;-#ff=u-hOhhhhhO9&h=u==;hOGxhhh;;;9h==uu=====;;hhu-==G9bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb+2tmtmxxmsGGOOOSmmmt2Bio+MAAAggbbbbbbbbbbbbbbbbbc000v#6vuf#f#--=h;-f--;=O;;;;O;h;GO;;=u===;==;=hu--=u;-=GOOOhOO&sxxmBoAgggbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbb=uu=-=;-uh=hG;=G99OG;=-==hmGhO;=;9G;Oh--=;=u=-Ou;u=;uhO=u9hsbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbAoi2tmmS&sGhssttii++AAgAggbbbbbbbbbbbbbbbbO=#vvvv0v#vv#v#cc#;;hh9;=u-;uh;=hh;=GGOOO;-=h;===h;;===u;h;=h;=GG;&Sxm2Boggggbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbb==uh;h=--fuOOh9;hOGh=;u;;GmO;;h;9&9u9=;u-f=-;=-u=uu;OO=u=-uhO9G9GbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbA+222m&G&stBMAgAgggbbbbbbbbbbbbbbb9f##-0vvvv#--#vvvv-uu==u;O9-;u=;hh=;;O=;&&;hh;;=-===O=u;===OOh;uu-h9GGSSm2BoAgggbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbb9;h=;u;=h=-;hOOOO;;9;u;9&x;=uu-fO;;u-;9;-f-=;u##hu=u=;;=h=-;;9hOhhh9;mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgbbbbbbbbbbbbbbbbbbbb-f;-000v-v00##v#v-##-u#-=h;hhh=Oh==hh=uuuuuf;;huu=GO=u-=h=-f;h=O=O=;;G=hGSxm2B+Agggbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbb;h=G=;==;h=;;OOh9G;;9;;x9==u##-=h;=-u=;--------#u;fhu=u==;u=uhO;hG==O;-=uu;mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc0#-f#00###0#6vv0#0#-vc---u-==uuhOhh&;=h;u====uf#f=hh=--=;=-;-u=-=-=h=hO;;hhO&G&m2B+ggggbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbOu=u;u=OOh;=;=;;h;9h;-hm9=u-u-=;;=uuu=u--uu---uuf#--u-=hh;;u=u=u;;hhuf-=u=---f-=u=uhmbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbs-f#fv#vvv###vvvv#0vvv0vvv#v0v#---hhO;=;=;=;h;O;uu==;uu-u-=;==uu==-uu=-uu==u--==;hGGSSsm2i+ggggbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbb;=-=h9O=;;hOh;;;9h===;s9;=uu=u=;;;=u-=;uh-u==--#f##fuhhuuu=;=uuuh;u-u-f-=u--u-uuu=u-#-uf=-#uu-######u;;---u=u---=u-#f#vvf##v#v#####v0v#v-#vf########=h9=;=hOh;;=hO;u==uu;==u===;;;==uu;u;=u==;u---fu;G&GSxt2oMggggbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbb=O;h==h=O9G;u;Ghu;hu=Gs;=;;==;h=;=;===;u=-==--ff#=u=u-;u;99=-=h=-u=f-u##fu---u==ufu--uuf-#--#f#vv#-ff#u-u;h=v-;u##-v##vvv##v##ff######f#fvf-#####-=uuu;;;GGOu=GO=;;u==-uu;;u==;h;==u=;;==u;;uuuf-uhhOGsxtB+Mggggbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbb9h;;OOhhhOO9O=Ohu==-;s9=;u-==;;uh-u=;;==uu;u=fu-uu-u;h;;;h;hh-=;--f###u-u-#---u-=###u---#f-#f####f##-=uuu==u=#-=###vvvf#f#0v#ff#-###uf---fuuu#-uu=-u;hhhhhOh9=;O===-u-;=;;-u;;=;;=uu;=;;=u===u-;;OhGst2B+Aggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbOG9hh9O;;9Oh;=;h=ufuh===fu=h;;;h=--u=;=Oh=u-uuu;u;=uuhh==Ohh=uu=u-##u---###u=--=-###-u-=uf#v###-##-##uuf#=u=-f#-fvvv####v##-u--=-###u=u;;=u#uuu;u=;=uhh;=OOO;;=h;u-f===u-u-;h;;h;u-u;;;h;;=-=;hOGSxmtiMAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbb&GOOO9;O;;OOhhh;h==u==;u-;O9h===uu-=;;h=uu=-;hh;hh=;=h=hu;hhh;;u=uufu-u-fu=h=f--#f##-u=-f#-#-uuuuuf#-u-uff-uu-----fv-#-##-=h=--ufuf-=;h=u-=f=hhh;h;u;h;h;;hOhh;;h;=-==;=uuOGh===u=uu=hhh==h=h&Ssm2BiMggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbb&G&&OhhhGOOhu=h;;=uu=uu;OOh==;-=;=u=;;==uu;=;;hhh=hO=;=;OOhh=u;h==--u--u;h;uuu#uu-f--=--#f-fuuuu=-u=f-fu;===--uuu-ff-f--;h==u=fu==u=uh===u;==;;;h=;O;==;OOhO==;;;==uu=u=hhh;=;u=h====O;h;;GGSxt2BoAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbSGSGh;O9GOhh;O;;O=;=uu=;;;==u;====;;===u;;u=;=;;;;hhuu;hGh;;;hh=h===fu==u=uu-uuuu-uu-uuf==#-uuuu==;=f#u;O;uuu;==;u==#u====ucu==u=u;;=====;u=;;;;;h;O=u=hGO;;;hhh;;;;f==;;=;=======hh;;OhGsSxtBo+gggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbG&G&GOOGGhhh9;;;O;u-=u;=uhu=h=u;;=-;h===;-====9u-=u;=;=h9;;hO==;O;-u=u==u;uuh=uu==-u;uuu=-uuuuh-f-u===-;O=;;h;==h;--uuu;-h=u;;==;;-=h=u=;uuu==G=u===;;=h9;;hhh=;hhu-u==huh==h;==hh=hGGGSmm2BoAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbb&G&&&&Ohh9&&;==GG;uu;u==h===uu;==;h;;h=;=--=OO;uu=u;;O==;hGGh=uGG;u-=uu=;=uuuu===;;;=;u==-f=hO;-uu-;;O===hO9h=u9G;u-;uu=;===-u====;;=h=;;u-=;9;uu=u;;O;;=hGGO==O&huu;;u=;=====;hhO&&Smm2Bi+Aggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbxGsSSSGG9h9OhO;OhOh-huhhu=-uu==;h;Gh;;;=;u=f;u;;=uhhhO;;h=O;hh=hhh;u;=hhu=-uu===;;9h=h=u;u=f=u;==u=hhh;;;;O;hh=hhO;u;=;hu=-u-===h;9O=;==;u=f;u;==u=hhhh;h;O;hh=hhG;=uh=O=;u==hh9&SmttBB+Aggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbsxxss&GGOG9hOO==;;=uhu;u;;==u;O;;;9;u=ufuu-;;==u=h9Oh;h;;OO;hh===;;-;u=u===uuhOh;;O;==uf-uf=hu=u=h9Ohh;;;hhhhh=u==;-===u===uu;Oh;;9hu=u--u-uh==u=h9Ohh;;;hOhhG==;;h-;;;;;hOOGSmmt2i+Mggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbssmxsG&G9OOhhhu=;hh=O;u=u==;;9h9h=uuu-u-u==--=;==;OOhu;;==;;;h-=uhh=h=u====;uhhOh=u-uf-fu==f-=;=u;OOhu=;==;=;h-=uhh;;;=====huhOOO=uuu---u=h--=;;u;OOh=;h;;h=;hu==hO;OhhhGGSxxtBi+MAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbtSsxSsxG&&Oh==hhO;uhh=9Ohhh;hh9;u;;u-uhOu#-u=h==;==h=hG;hO;=u-;;h;=hhuOh;hhh;;9;u;;u--hh=f--=;==;=uO;;G;hhh=u-=hh;=;huOOhhhhh;9;u;hu--hh=#-u=;;=;==O;;Gh;hh;uu=hOOhO&GSsmm22ioMAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbmxxmmssG&Gh9;&O=h;hOOhGOO;h;u=;;;u;=;u-=;h;=;=h==;;hOh;;h;uh=GO===hhOhGOO;;huu===u;=;=f=;hh=;=;;=;=hOO;;;huh=99u;=hhO;GhO;;huu;==u;=;=f=;;h=;;hh=;;hOO;hhh=h;G&hOG&xmm22io+Aggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbxmxmSsSGG999h;9O;;9GOGhOh=;==u;hh=u==;=GO;u;==uu;hhOu;;=uu;;u=hh;;OGOG;Oh=;;===hh=u====O9;u;=uu==hhh=;;==u=;=u;;h=hGOGhhh=;==u;hh=u====OG;=;==u==hhO;;h;;;OGGGSxmm2Bi+MAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbtmxxxmSGGG9GGGOOG;OG99;=;h;;;hh;hOO;u;Ohhhu-u=h==;===Ouuuuu;;h;;OuhGO9;=;h;=;hh;;Oh;u;hhhhu-uuh==;===hu-uuu;hO;;9=;GO9h=;;;;;hh;;9hh=;OO;h=-u=h;;h=;;Gh;hOGSstm2BBoMAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbtmmxxmsG&s&&&&hhhOOhhOh;hhhh9Oh;h;;uhhhh;uu==h=-==huuOu--h;;hO===;h;;hh=h;hhOhhhh;;=;hhh;uuu;h=u==huuO=--;;;hO;==;h;;hh;h;;hOOhOhh;u;hhO;=u==O;=hhGhOS&&Smt2BioMAAgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2mtxxxxxsSss&OGGGO;hOh;h;hh;==O9h=hO;u==u==;=;;h=u;-u==h;hO9Ouhh9;==h;=;;hh;==hOO=hhhu==u;u;==;;==;uu;uh;hhOOuhh9;=;hh;hhhO;;u;OO;hOh==;=h;hhOGG&&ssstt2ii+AAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2xtmxmtmxxssSsx&hhh;OOGh;;hhhOhGO;;=u==;==;;;Ouuuu;;u;9Ohhhhh9&O=u==;;9;;=;h;hhOO=;u=u=====;;hu-u-=;-;9OhhhOh9&G;u==;;O;;=hOhOhGOh;=;;;hOOG&&xSsmttBBoMAgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2mmtmmtmstxxsxGO;OO;9OOGO;hGGO9O=u;h;;==O=;u=;uhhu=O=;GO=&9GOG;=u=;uh;;Oh;;9GOOO=-;hh===O=;u==u;h==h;;G9=GGGOGh=u=;uhhhOO;;9&GGGO;O&&&&&mmmt22Bi+MMgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb2mtttttmmmmxSS9&G&GO99GOGsG;Ghh;;-;=h;==;==;OO;=uuuOhOO;hOG9;;==hhO=hhO;OSG;9hh;=-;=;h==;==;G9;==uuhOhhhhhG9=;=;hOO=hhGh9Ssh&&G&GGSsmtttBBio+Agggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbtt222tttmtxssxs&G9OOh;G9O;=O&O=u=;9=--O;h;;h;;h;u;h9OhOh;O;u;hh;==uu=fhhhuu;Ghuuu=9=--h;;;=O=;;hu;hGOOOO;OO=;O9h;;=hh=G&&9Gsmmxmm2BBBi+MAgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbt22222tmtmxxssSGO;OG&G9;;hOh;hu;;==u;9uGhh;h;O;;=OGhO&;;Oh=====u##f;;h=-u=;=u=-uuu;-;Ou9hh;hhhh;;OGOO&h;9OhhOh9h;;hSsxxsmt222iBo+MAgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb22222tmttmmxxSSSssSGGG&9hhhO;;;;h=u=;h;hGG9Ohh;h;hO99hu=;;;u=uu=;h;===;;uu=;u==u=u-=;h=;99GO;OhOh9GG&GhhGG&G&&SSmmttt2Biio+MAAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbB2222tt2tmmtmmmxxxS&&&GsG9&G9hhh;==O&&OOOOGOO9hGGh;;;=;Oh;=;=h;hhh===h=O;=hhh=u=uuuhG&h99O&GGGG&S&GSS&Smmxmtt22iiioo+MAAggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbB2t22222t2tttmmmmmxssSSsGG9OOGGG99GG&ss&hOS&hOGhhO=;;OOhhO9OO;O;hOOhh=hO=;;u;;G99O&G&sxsG&mxsxmxmmmtt2BBiio++MAAAgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbBB22222222ttttttmmmmmxSssssSSsxsssxsssGSsGGGOOO&G&GhGG&GG&999&G&GG9&GGhOGG&GGsssxxmmmtmtttt222BBiioo+MMAAgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbBiBBBB222222222ttttmmtmtmmmtmmxtmmxsxxsS&&sSsS&&&ssssxS&G&SssxxsS&sssxxmmmmtttt22B22BBiBBio++MMMAAggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbiiBBBBBBBBB222BB22222tt222t2t2tttttmmmmmmmmmtttmmmmmxtttttmmtmt2t222222BBBBBiooo++MMMMAAAAgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbooBBBBBBBBiiBBBi2B2BBBBB2222B2222222222222t222222B2222B2BBBBBiiooo++M+MMMMMAAAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbo+iiiiiiiiBiioiiiioiiiBBBBBBiiBBBiBBiBBiiiiiiioioo++o+++M++MMAAAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb+++++oioioioooiiooiooooo+o+o+oooo++o+o++++MMAAbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbggggggggggbbbbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbggggggggbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbggggggbbbbbbbbbgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbgggggbbbbbbbbgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbggggggbbbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggggggbbbgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggggbbbbbbbbbbbbbbbggggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggggggggggggggggggggggbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbggggggbbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggbbbbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggggbbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggggbbbbbbbbbggggggbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbggggggbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggggbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbgggbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbgggggbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggbbbbbbbbbbbgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggggbbbbbbbbbbbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbggbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbggggggbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggbbbbbbbbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggggggbbbbbbbbbbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbggbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbggggggbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggbbggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbggggggbbbbbbbbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbgggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbggggggbbbbbbbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggbbbbggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbggggggbbbbbbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbgbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbggggggbbbbbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbgbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbgggggggggbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbbbbbbbggbbbbbbggggbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbgggbbbbbggggggbbbbbbbbggbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbgggggbbbbbbbbbbbggbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbggggggbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbbbgggggggggbbbbbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbbggbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbgggbbbbbbggggggbbbbbbbggbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbggggggggggggggggggbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbgggggggbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbgggggggggbbbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbbggbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbgggbbbbbbbggggggbbbbbbggbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbggggggggggggggggggbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbggggggggbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbbbbbbbgggggggggbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbbbbbggbbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbgggbbbbbbbbgggggggbbbbggbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbgggggbbbbbbbbbbbggbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbgggbggggggbbbbbbbbbbbbbbbgbbbbbbbbbbbbbbbbbbbbbbbbbggggggggbbbbbbbbbbbbbbbgbbbbbbbbbbbbbbbbbbbbbggbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbgbbbbbbbbbbbbbbbbbgggbbbbbbbbbbggggggbbbggbbbbbbbbbbbbbbbbbgbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbgbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbgggbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggggggggggggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbggggggbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbggbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbgggggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbggbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbggggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbggbbbbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbgggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbggbbbbbbb",
+"bbbbbbbbbbbbbbggbbbbbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbbgggbbbbbbb",
+"bbbbbbbbbbbbbggbbbbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggbbbbbbbbbbbbbbbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbbbbbbgggbbbbbbbb",
+"bbbbbbbbbbbbgggbbbbbbbbbggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggbbbbbbbbbggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbbgggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbbbgggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbggggggbbbbbbbbbbbbgggggbbbbbbbb",
+"bbbbbbbbbbggggggbbbbbbbggggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbggggggggbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggggbbbbbbbbbbbbbbbggggggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggggbbbbbbbbbbbbbbbbbggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbgggggggggggggggggggggggbbbbbbbbb",
+"bbbbbbbbbbgggggggbbbbggggggggggggbbbbbbbbbbbbbbbbbbbbbbbbbbgbbbbbbgggggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
+};
diff --git a/frontend/xsane-preferences.c b/frontend/xsane-preferences.c
new file mode 100644
index 0000000..8c13c5d
--- /dev/null
+++ b/frontend/xsane-preferences.c
@@ -0,0 +1,292 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-preferences.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* --------------------------------------------------------------------- */
+
+#include "xsane.h"
+#include "xsane-preferences.h"
+#include "xsane-rc-io.h"
+
+/* --------------------------------------------------------------------- */
+
+#define POFFSET(field) ((char *) &((Preferences *) 0)->field - (char *) 0)
+#define PFIELD(p,offset,type) (*((type *)(((char *)(p)) + (offset))))
+
+#define PRTOFFSET(field) ((char *) &((Preferences_printer_t *) 0)->field - (char *) 0)
+
+/* --------------------------------------------------------------------- */
+
+Preferences preferences =
+ {
+ 0, /* no default filename */
+ 0137, /* image umask (permission mask for -rw-r------) */
+ 0027, /* image umask (permission mask for -rwxr-x----) */
+ 0, /* no fax project */
+ 0, /* no default faxcommand */
+ 0, /* no default fax receiver option */
+ 0, /* no default fax postscript option */
+ 0, /* no default fax normal option */
+ 0, /* no default fax fine option */
+ 0, /* no fax viewer */
+ 210.0, /* fax_width: width of fax paper in mm */
+ 296.98, /* fax_height: height of fax paper in mm */
+ 0.0, /* fax_leftoffset */
+ 0.0, /* fax_bottomoffset */
+ 0, /* no doc viewer */
+ 80.0, /* jpeg_quality */
+ 7.0, /* png_compression */
+ 5, /* tiff_compression_nr */
+ 5, /* tiff_compression_1_nr */
+ 1, /* overwrite_warning */
+ 1, /* increase_filename_counter */
+ 1, /* skip_existing_numbers */
+ 1, /* tooltips enabled */
+ 0, /* (dont) show histogram */
+ 0, /* (dont) show standard options */
+ 0, /* (dont) show advanced options */
+ 0, /* (dont) show resolution list */
+ 10.0, /* length unit */
+ 1, /* main window fixed (1) or scrolled (0) */
+ 1, /* preserve_preview */
+ 0, /* preview_own_cmap */
+ 1.0, /* preview_gamma */
+ 1.0, /* preview_gamma_red */
+ 1.0, /* preview_gamma_green */
+ 1.0, /* preview_gamma_blue */
+ 1.6, /* gamma */
+ 1.0, /* gamma red */
+ 1.0, /* gamma green */
+ 1.0, /* gamma blue */
+ 0.0, /* brightness */
+ 0.0, /* brightness red */
+ 0.0, /* brightness green */
+ 0.0, /* brightness blue */
+ 0.0, /* contrast */
+ 0.0, /* contrast red */
+ 0.0, /* contrast green */
+ 0.0, /* contrast blue */
+ 1, /* rgb default */
+ 0, /* negative */
+ GTK_UPDATE_DISCONTINUOUS, /* update policy for gtk frontend sliders */
+ 0, /* psrotate: rotate in postscript mode (landscape) */
+ 0, /* printernr */
+ 0 /* printerdefinitions */
+ };
+
+/* --------------------------------------------------------------------- */
+
+static struct
+ {
+ SANE_String name;
+ void (*codec) (Wire *w, void *p, long offset);
+ long offset;
+ }
+desc[] =
+ {
+ {"filename", xsane_rc_pref_string, POFFSET(filename)},
+ {"image-umask", xsane_rc_pref_int, POFFSET(image_umask)},
+ {"directory-umask", xsane_rc_pref_int, POFFSET(directory_umask)},
+ {"fax-project", xsane_rc_pref_string, POFFSET(fax_project)},
+ {"fax-command", xsane_rc_pref_string, POFFSET(fax_command)},
+ {"fax-receiver-option", xsane_rc_pref_string, POFFSET(fax_receiver_option)},
+ {"fax-postscript-option", xsane_rc_pref_string, POFFSET(fax_postscript_option)},
+ {"fax-normal-option", xsane_rc_pref_string, POFFSET(fax_normal_option)},
+ {"fax-fine-option", xsane_rc_pref_string, POFFSET(fax_fine_option)},
+ {"fax-viewer", xsane_rc_pref_string, POFFSET(fax_viewer)},
+ {"fax-left-offset", xsane_rc_pref_double, POFFSET(fax_leftoffset)},
+ {"fax-bottom-offset", xsane_rc_pref_double, POFFSET(fax_bottomoffset)},
+ {"fax-width", xsane_rc_pref_double, POFFSET(fax_width)},
+ {"fax-height", xsane_rc_pref_double, POFFSET(fax_height)},
+ {"doc-viewer", xsane_rc_pref_string, POFFSET(doc_viewer)},
+ {"overwrite-warning", xsane_rc_pref_int, POFFSET(overwrite_warning)},
+ {"increase-filename-counter", xsane_rc_pref_int, POFFSET(increase_filename_counter)},
+ {"skip-existing-numbers", xsane_rc_pref_int, POFFSET(skip_existing_numbers)},
+ {"jpeg-quality", xsane_rc_pref_double, POFFSET(jpeg_quality)},
+ {"png-compression", xsane_rc_pref_double, POFFSET(png_compression)},
+ {"tiff-compression_nr", xsane_rc_pref_int, POFFSET(tiff_compression_nr)},
+ {"tiff-compression_1_nr", xsane_rc_pref_int, POFFSET(tiff_compression_1_nr)},
+ {"tool-tips", xsane_rc_pref_int, POFFSET(tooltips_enabled)},
+ {"show-histogram", xsane_rc_pref_int, POFFSET(show_histogram)},
+ {"show-standard-options", xsane_rc_pref_int, POFFSET(show_standard_options)},
+ {"show-advanced-options", xsane_rc_pref_int, POFFSET(show_advanced_options)},
+ {"show-resolution-list", xsane_rc_pref_int, POFFSET(show_resolution_list)},
+ {"length-unit", xsane_rc_pref_double, POFFSET(length_unit)},
+ {"main-window-fixed", xsane_rc_pref_int, POFFSET(main_window_fixed)},
+ {"preserve-preview", xsane_rc_pref_int, POFFSET(preserve_preview)},
+ {"preview-own-cmap", xsane_rc_pref_int, POFFSET(preview_own_cmap)},
+ {"preview-gamma", xsane_rc_pref_double, POFFSET(preview_gamma)},
+ {"preview-gamma-red", xsane_rc_pref_double, POFFSET(preview_gamma_red)},
+ {"preview-gamma-green", xsane_rc_pref_double, POFFSET(preview_gamma_green)},
+ {"preview-gamma-blue", xsane_rc_pref_double, POFFSET(preview_gamma_blue)},
+ {"gamma", xsane_rc_pref_double, POFFSET(xsane_gamma)},
+ {"gamma-red", xsane_rc_pref_double, POFFSET(xsane_gamma_red)},
+ {"gamma-green", xsane_rc_pref_double, POFFSET(xsane_gamma_green)},
+ {"gamma-blue", xsane_rc_pref_double, POFFSET(xsane_gamma_blue)},
+ {"brightness", xsane_rc_pref_double, POFFSET(xsane_brightness)},
+ {"brightness-red", xsane_rc_pref_double, POFFSET(xsane_brightness_red)},
+ {"brightness-green", xsane_rc_pref_double, POFFSET(xsane_brightness_green)},
+ {"brightness-blue", xsane_rc_pref_double, POFFSET(xsane_brightness_blue)},
+ {"contrast", xsane_rc_pref_double, POFFSET(xsane_contrast)},
+ {"contrast-red", xsane_rc_pref_double, POFFSET(xsane_contrast_red)},
+ {"contrast-green", xsane_rc_pref_double, POFFSET(xsane_contrast_green)},
+ {"contrast-blue", xsane_rc_pref_double, POFFSET(xsane_contrast_blue)},
+ {"rgb-default", xsane_rc_pref_int, POFFSET(xsane_rgb_default)},
+ {"negative", xsane_rc_pref_int, POFFSET(xsane_negative)},
+ {"gtk-update-policy", xsane_rc_pref_int, POFFSET(gtk_update_policy)},
+ {"postscript-rotate", xsane_rc_pref_int, POFFSET(psrotate)},
+ {"printernr", xsane_rc_pref_int, POFFSET(printernr)},
+ {"printerdefinitions", xsane_rc_pref_int, POFFSET(printerdefinitions)}
+ };
+
+/* --------------------------------------------------------------------- */
+
+static struct
+ {
+ SANE_String name;
+ void (*codec) (Wire *w, void *p, long offset);
+ long offset;
+ }
+desc_printer[] =
+ {
+ {"printer-name", xsane_rc_pref_string, PRTOFFSET(name)},
+ {"printer-command", xsane_rc_pref_string, PRTOFFSET(command)},
+ {"printer-copy-number-option", xsane_rc_pref_string, PRTOFFSET(copy_number_option)},
+ {"printer-resolution", xsane_rc_pref_int, PRTOFFSET(resolution)},
+ {"printer-width", xsane_rc_pref_double, PRTOFFSET(width)},
+ {"printer-height", xsane_rc_pref_double, PRTOFFSET(height)},
+ {"printer-left-offset", xsane_rc_pref_double, PRTOFFSET(leftoffset)},
+ {"printer-bottom-offset", xsane_rc_pref_double, PRTOFFSET(bottomoffset)},
+ {"printer-gamma", xsane_rc_pref_double, PRTOFFSET(gamma)},
+ {"printer-gamma-red", xsane_rc_pref_double, PRTOFFSET(gamma_red)},
+ {"printer-gamma-green", xsane_rc_pref_double, PRTOFFSET(gamma_green)},
+ {"printer-gamma-blue", xsane_rc_pref_double, PRTOFFSET(gamma_blue)}
+ };
+
+/* --------------------------------------------------------------------- */
+
+void preferences_save(int fd)
+{
+ Wire w;
+ int i, n;
+
+ w.io.fd = fd;
+ w.io.read = read;
+ w.io.write = write;
+ xsane_rc_io_w_init(&w);
+ xsane_rc_io_w_set_dir(&w, WIRE_ENCODE);
+
+ for (i = 0; i < NELEMS(desc); ++i)
+ {
+ xsane_rc_io_w_string(&w, &desc[i].name);
+ (*desc[i].codec) (&w, &preferences, desc[i].offset);
+ }
+
+ n=0;
+
+ while (n < preferences.printerdefinitions)
+ {
+ for (i = 0; i < NELEMS(desc_printer); ++i)
+ {
+ xsane_rc_io_w_string(&w, &desc_printer[i].name);
+ (*desc_printer[i].codec) (&w, preferences.printer[n], desc_printer[i].offset);
+ }
+ n++;
+ }
+
+ xsane_rc_io_w_set_dir(&w, WIRE_DECODE); /* flush it out */
+}
+
+/* --------------------------------------------------------------------- */
+
+void preferences_restore(int fd)
+{
+ SANE_String name;
+ Wire w;
+ int i, n;
+
+ w.io.fd = fd;
+ w.io.read = read;
+ w.io.write = write;
+ xsane_rc_io_w_init(&w);
+ xsane_rc_io_w_set_dir(&w, WIRE_DECODE);
+
+
+ while (1)
+ {
+ xsane_rc_io_w_space(&w, 3);
+ if (w.status)
+ {
+ return;
+ }
+
+ xsane_rc_io_w_string(&w, &name);
+ if (w.status || !name)
+ {
+ return;
+ }
+
+ for (i = 0; i < NELEMS (desc); ++i)
+ {
+ if (strcmp(name, desc[i].name) == 0)
+ {
+ (*desc[i].codec) (&w, &preferences, desc[i].offset);
+ break;
+ }
+ }
+ if (!strcmp(name, "printerdefinitions"))
+ {
+ break;
+ }
+ }
+
+
+ n=0;
+ while (n < preferences.printerdefinitions)
+ {
+ preferences.printer[n] = calloc(sizeof(Preferences_printer_t), 1);
+ for (i = 0; i < NELEMS(desc_printer); ++i)
+ {
+ xsane_rc_io_w_space (&w, 3);
+ if (w.status)
+ {
+ return;
+ }
+
+ xsane_rc_io_w_string(&w, &name);
+ if (w.status || !name)
+ {
+ return;
+ }
+
+ if (strcmp(name, desc_printer[i].name) == 0)
+ {
+ (*desc_printer[i].codec) (&w, preferences.printer[n], desc_printer[i].offset);
+ }
+ else
+ {
+ break;
+ }
+ }
+ n++;
+ }
+
+}
diff --git a/frontend/xsane-preferences.h b/frontend/xsane-preferences.h
new file mode 100644
index 0000000..19482fa
--- /dev/null
+++ b/frontend/xsane-preferences.h
@@ -0,0 +1,122 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-preferences.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifndef xsane_preferences_h
+#define xsane_preferences_h
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include <sane/sane.h>
+#include <gtk/gtk.h>
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+typedef struct
+ {
+ char *name; /* user defined printer name */
+ char *command; /* printercommand */
+ char *copy_number_option; /* option to define number of copies */
+ int resolution; /* printer resolution for copy mode */
+ double width; /* printer width of printable area in mm */
+ double height; /* printer height of printable area in mm */
+ double leftoffset; /* printer left offset in mm */
+ double bottomoffset;/* printer bottom offset in mm */
+ double gamma; /* printer gamma */
+ double gamma_red; /* printer gamma red */
+ double gamma_green; /* printer gamma green */
+ double gamma_blue; /* printer gamma blue */
+ }
+Preferences_printer_t;
+
+typedef struct
+ {
+ char *filename; /* default filename */
+ mode_t image_umask; /* image umask (permisson mask) */
+ mode_t directory_umask; /* directory umask (permisson mask) */
+
+ char *fax_project; /* fax project */
+ char *fax_command; /* faxcommand */
+ char *fax_receiver_option; /* fax receiver option */
+ char *fax_postscript_option; /* fax postscript option */
+ char *fax_normal_option; /* fax normal mode option */
+ char *fax_fine_option; /* fax fine mode option */
+ char *fax_viewer; /* fax viewer */
+ double fax_width; /* width of fax paper in mm */
+ double fax_height; /* height of fax paper in mm */
+ double fax_leftoffset; /* left offset of fax paper in mm */
+ double fax_bottomoffset; /* bottom offset of fax paper in mm */
+
+ char *doc_viewer; /* doc viewer for helpfiles */
+
+ double jpeg_quality; /* quality when saving image as jpeg */
+ double png_compression; /* compression when saving image as pnm */
+ int tiff_compression_nr; /* compression type nr when saving multi bit image as tiff */
+ int tiff_compression_1_nr; /* compression type nr when saving one bit image as tiff */
+ int overwrite_warning; /* warn if file exists */
+ int increase_filename_counter; /* automatically increase counter */
+ int skip_existing_numbers; /* automatically increase counter */
+
+ int tooltips_enabled; /* should tooltips be disabled? */
+ int show_histogram; /* show histogram ? */
+ int show_standard_options; /* show standard options ? */
+ int show_advanced_options; /* show advanced options ? */
+ int show_resolution_list; /* show resolution list instead of slider ? */
+ double length_unit; /* 1.0==mm, 10.0==cm, 25.4==inches, etc. */
+ int main_window_fixed; /* fixed (1) or scrolled (0) main window */
+ int preserve_preview; /* save/restore preview image(s)? */
+ int preview_own_cmap; /* install colormap for preview */
+ double preview_gamma; /* gamma value for previews */
+ double preview_gamma_red; /* red gamma value for previews */
+ double preview_gamma_green; /* green gamma value for previews */
+ double preview_gamma_blue; /* blue gamma value for previews */
+ double xsane_gamma;
+ double xsane_gamma_red;
+ double xsane_gamma_green;
+ double xsane_gamma_blue;
+ double xsane_brightness;
+ double xsane_brightness_red;
+ double xsane_brightness_green;
+ double xsane_brightness_blue;
+ double xsane_contrast;
+ double xsane_contrast_red;
+ double xsane_contrast_green;
+ double xsane_contrast_blue;
+
+ int xsane_rgb_default;
+ int xsane_negative;
+ GtkUpdateType gtk_update_policy;
+
+ int psrotate; /* rotate by 90 degree in postscript mode - landscape */
+ int printernr; /* number of printers */
+ int printerdefinitions;
+ Preferences_printer_t *printer[10];
+ }
+Preferences;
+
+extern Preferences preferences;
+
+extern void preferences_save (int fd);
+extern void preferences_restore (int fd);
+
+#endif /* preferences_h */
diff --git a/frontend/xsane-preview.c b/frontend/xsane-preview.c
new file mode 100644
index 0000000..a75b12a
--- /dev/null
+++ b/frontend/xsane-preview.c
@@ -0,0 +1,3257 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-preview.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/*
+
+ The preview strategy is as follows:
+ -----------------------------------
+
+ 1) The preview is done on the full scan area or a part of it.
+
+ 2) The preview is zoomable so the user can precisely pick
+ the selection area even for small scans on a large scan
+ surface.
+
+ 3) The preview window is resizeable.
+
+ 4) The preview scan resolution depends on preview window size
+ and the selected preview surface (zoom area).
+
+ 5) We let the user/backend pick whether a preview is in color,
+ grayscale, lineart or what not. The only options that the
+ preview may (temporarily) modify are:
+
+ - resolution (set so the preview fills the window)
+ - scan area options (top-left corner, bottom-right corner)
+ - preview option (to let the backend know we're doing a preview)
+ - gamma table is set to default (gamma=1.0)
+
+ 5) The initial size of the scan surface is determined based on the constraints
+ of the four corner coordinates. Missing constraints are replaced
+ by 0/+INF as appropriate (0 for top-left, +INF for bottom-right coords).
+
+ 6) Given the preview window size and the scan surface size, we
+ select the resolution so the acquired preview image just fits
+ in the preview window. The resulting resolution may be out
+ of range in which case we pick the minum/maximum if there is
+ a range or word-list constraint or a default value if there is
+ no such constraint.
+
+ 7) Once a preview image has been acquired, we know the size of the
+ preview image (in pixels). An initial scale factor is chosen
+ so the image fits into the preview window.
+
+*/
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+/* #include <sys/param.h> */
+#include "xsane-back-gtk.h"
+#include "xsane-front-gtk.h"
+#include "xsane-preview.h"
+#include "xsane-preferences.h"
+#include "xsane-gamma.h"
+
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+extern const char *prog_name;
+extern const char *device_text;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* Cut fp conversion routines some slack: */
+#define GROSSLY_DIFFERENT(f1,f2) (fabs ((f1) - (f2)) > 1e-3)
+#define GROSSLY_EQUAL(f1,f2) (fabs ((f1) - (f2)) < 1e-3)
+
+#ifdef __alpha__
+ /* This seems to be necessary for at least some XFree86 3.1.2
+ servers. It's known to be necessary for the XF86_TGA server for
+ Linux/Alpha. Fortunately, it's no great loss so we turn this on
+ by default for now. */
+# define XSERVER_WITH_BUGGY_VISUALS
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#define PRESET_AREA_ITEMS 11
+typedef struct
+{
+ char *name;
+ float width;
+ float height;
+} Preset_area;
+
+static const Preset_area preset_area[] =
+{
+ { "full size", INF, INF },
+ { "DIN A3", 296.98, 420.0 },
+ { "DIN A4", 210.0, 296.98 },
+ { "DIN A4H", 296.98, 210.0 },
+ { "DIN A5", 148.5, 210.0 },
+ { "DIN A5H", 210.0, 148.5 },
+ { "9x13 cm", 90.0, 130.0 },
+ { "13x9 cm", 130.0, 90.0 },
+ { "legal", 215.9, 355.6 },
+ { "letter", 215.9, 279.4 },
+ { "custom", INF, INF }
+};
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static SANE_Int *preview_gamma_data_red = 0;
+static SANE_Int *preview_gamma_data_green = 0;
+static SANE_Int *preview_gamma_data_blue = 0;
+
+static SANE_Int *histogram_gamma_data_red = 0;
+static SANE_Int *histogram_gamma_data_green = 0;
+static SANE_Int *histogram_gamma_data_blue = 0;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* forward declarations */
+static void preview_order_selection(Preview *p);
+static void preview_bound_selection(Preview *p);
+static void preview_draw_rect(Preview *p, GdkWindow *win, GdkGC *gc, float coord[4]);
+static void preview_draw_selection(Preview *p);
+static void preview_update_selection(Preview *p);
+static void preview_establish_selection(Preview *p);
+/* static void preview_update_batch_selection(Preview *p); */
+static void preview_get_scale_device_to_image(Preview *p, float *xscalep, float *yscalep);
+static void preview_get_scale_device_to_preview(Preview *p, float *xscalep, float *yscalep);
+static void preview_get_scale_preview_to_image(Preview *p, float *xscalep, float *yscalep);
+static void preview_paint_image(Preview *p);
+static void preview_display_partial_image(Preview *p);
+static void preview_display_maybe(Preview *p);
+static void preview_display_image(Preview *p);
+static void preview_save_option(Preview *p, int option, SANE_Word *save_loc, int *valid);
+static void preview_restore_option(Preview *p, int option, SANE_Word saved_value, int valid);
+static void preview_set_option_float(Preview *p, int option, float value);
+static void preview_set_option_bool(Preview *p, int option, SANE_Bool value);
+static void preview_set_option_int(Preview *p, int option, SANE_Int value);
+static int preview_increment_image_y(Preview *p);
+static void preview_read_image_data(gpointer data, gint source, GdkInputCondition cond);
+static void preview_scan_done(Preview *p);
+static void preview_scan_start(Preview *p);
+static int preview_make_image_path(Preview *p, size_t filename_size, char *filename, int level);
+static void preview_restore_image(Preview *p);
+static gint preview_expose_handler(GtkWidget *window, GdkEvent *event, gpointer data);
+static gint preview_event_handler(GtkWidget *window, GdkEvent *event, gpointer data);
+static void preview_start_button_clicked(GtkWidget *widget, gpointer data);
+static void preview_cancel_button_clicked(GtkWidget *widget, gpointer data);
+static void preview_area_correct(Preview *p);
+static void preview_save_image(Preview *p);
+static void preview_zoom_not(GtkWidget *window, gpointer data);
+static void preview_zoom_out(GtkWidget *window, gpointer data);
+static void preview_zoom_in(GtkWidget *window, gpointer data);
+static void preview_zoom_undo(GtkWidget *window, gpointer data);
+static void preview_get_color(Preview *p, int x, int y, int *red, int *green, int *blue);
+static void preview_pipette_white(GtkWidget *window, gpointer data);
+static void preview_pipette_gray(GtkWidget *window, gpointer data);
+static void preview_pipette_black(GtkWidget *window, gpointer data);
+static void preview_full_preview_area(GtkWidget *widget, gpointer call_data);
+static void preview_preset_area_callback(GtkWidget *widget, gpointer call_data);
+
+void preview_do_gamma_correction(Preview *p);
+void preview_calculate_histogram(Preview *p,
+ SANE_Int *count_raw, SANE_Int *count_raw_red, SANE_Int *count_raw_green, SANE_Int *count_raw_blue,
+ SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue);
+void preview_gamma_correction(Preview *p,
+ SANE_Int *gamma_red, SANE_Int *gamma_green, SANE_Int *gamma_blue,
+ SANE_Int *gamma_red_hist, SANE_Int *gamma_green_hist, SANE_Int *gamma_blue_hist);
+void preview_area_resize(GtkWidget *widget);
+void preview_update_maximum_output_size(Preview *p);
+void preview_set_maximum_output_size(Preview *p, float width, float height);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_order_selection(Preview *p)
+{
+ float tmp_coordinate;
+
+ p->selection.active = ( (p->selection.coordinate[0] != p->selection.coordinate[2]) &&
+ (p->selection.coordinate[1] != p->selection.coordinate[3]) );
+
+
+ if (p->selection.active)
+ {
+ if (p->selection.coordinate[0] > p->selection.coordinate[2])
+ {
+ tmp_coordinate = p->selection.coordinate[0];
+ p->selection.coordinate[0] = p->selection.coordinate[2];
+ p->selection.coordinate[2] = tmp_coordinate;
+
+ p->selection_xedge = (p->selection_xedge + 2) & 3;
+ }
+
+ if (p->selection.coordinate[1] > p->selection.coordinate[3])
+ {
+ tmp_coordinate = p->selection.coordinate[1];
+ p->selection.coordinate[1] = p->selection.coordinate[3];
+ p->selection.coordinate[3] = tmp_coordinate;
+
+ p->selection_yedge = (p->selection_yedge + 2) & 3;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_bound_selection(Preview *p)
+{
+
+ p->selection.active = ( (p->selection.coordinate[0] != p->selection.coordinate[2]) &&
+ (p->selection.coordinate[1] != p->selection.coordinate[3]) );
+
+
+ if (p->selection.active)
+ {
+ if (p->selection.coordinate[0] < p->scanner_surface[0])
+ {
+ p->selection.coordinate[0] = p->scanner_surface[0];
+ }
+
+ if (p->selection.coordinate[1] < p->scanner_surface[1])
+ {
+ p->selection.coordinate[1] = p->scanner_surface[1];
+ }
+
+ if (p->selection.coordinate[2] > p->scanner_surface[2])
+ {
+ p->selection.coordinate[2] = p->scanner_surface[2];
+ }
+
+ if (p->selection.coordinate[3] > p->scanner_surface[3])
+ {
+ p->selection.coordinate[3] = p->scanner_surface[3];
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_draw_rect(Preview *p, GdkWindow *win, GdkGC *gc, float coordinate[4])
+{
+ float xscale, yscale;
+ float x, y, w, h;
+ gint xi, yi, wi, hi;
+
+ x = coordinate[0];
+ y = coordinate[1];
+ w = coordinate[2] - x;
+ h = coordinate[3] - y;
+
+ if (w < 0)
+ {
+ x = coordinate[2];
+ w = -w;
+ }
+
+ if (h < 0)
+ {
+ y = coordinate[3];
+ h = -h;
+ }
+
+ preview_get_scale_device_to_preview(p, &xscale, &yscale);
+
+ x = x - p->surface[0];
+ y = y - p->surface[1];
+
+ xi = (gint) (x * xscale + 0.5);
+ yi = (gint) (y * yscale + 0.5);
+ wi = (gint) (w * xscale + 0.5);
+ hi = (gint) (h * yscale + 0.5);
+
+ gdk_draw_rectangle(win, gc, FALSE, xi, yi, wi + 1, hi + 1);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_draw_selection(Preview *p)
+{
+ if (!p->gc_selection) /* window isn't mapped yet */
+ {
+ return;
+ }
+
+ while (gtk_events_pending()) /* make sure all drawing actions are finished */
+ {
+ gtk_main_iteration();
+ }
+
+ if (p->previous_selection.active)
+ {
+ preview_draw_rect(p, p->window->window, p->gc_selection, p->previous_selection.coordinate);
+ }
+
+ if (p->selection.active)
+ {
+ preview_draw_rect(p, p->window->window, p->gc_selection, p->selection.coordinate);
+ }
+
+ p->previous_selection = p->selection;
+
+
+ if (!p->gc_selection_maximum) /* window isn't mapped yet */
+ {
+ return;
+ }
+
+ if (p->previous_selection_maximum.active)
+ {
+ preview_draw_rect(p, p->window->window, p->gc_selection_maximum, p->previous_selection_maximum.coordinate);
+ }
+
+ if (p->selection_maximum.active)
+ {
+ preview_draw_rect(p, p->window->window, p->gc_selection_maximum, p->selection_maximum.coordinate);
+ }
+
+ p->previous_selection_maximum = p->selection_maximum;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_update_selection(Preview *p)
+/* draw selection box as defined in backend */
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Status status;
+ SANE_Word val;
+ int i, optnum;
+
+ p->previous_selection = p->selection;
+
+ for (i = 0; i < 4; ++i)
+ {
+ optnum = p->dialog->well_known.coord[i];
+ if (optnum > 0)
+ {
+ opt = sane_get_option_descriptor(p->dialog->dev, optnum);
+ status = sane_control_option(p->dialog->dev, optnum, SANE_ACTION_GET_VALUE, &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ continue;
+ }
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ p->selection.coordinate[i] = SANE_UNFIX(val);
+ }
+ else
+ {
+ p->selection.coordinate[i] = val;
+ }
+ }
+ else /* backend does not use scanarea options */
+ {
+ switch (i)
+ {
+ case 0:
+ case 1:
+ p->selection.coordinate[i] = 0;
+ break;
+
+ case 2:
+ p->selection.coordinate[i] = p->preview_width;
+ break;
+
+ case 3:
+ p->selection.coordinate[i] = p->preview_height;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < 2; ++i)
+ {
+ if (p->selection.coordinate[i + 2] < p->selection.coordinate[i])
+ {
+ p->selection.coordinate[i + 2] = p->selection.coordinate[i];
+ }
+ }
+
+ p->selection.active = ( (p->selection.coordinate[0] != p->selection.coordinate[2]) &&
+ (p->selection.coordinate[1] != p->selection.coordinate[3]) );
+
+ preview_draw_selection(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_establish_selection(Preview *p)
+{
+ /* This routine only shall be called if the preview area really is changed. */
+
+ int i;
+
+ preview_order_selection(p);
+
+ xsane.block_update_param = TRUE; /* do not change parameters each time */
+
+ for (i = 0; i < 4; ++i)
+ {
+ preview_set_option_float(p, p->dialog->well_known.coord[i], p->selection.coordinate[i]);
+ }
+
+ xsane_back_gtk_update_scan_window(p->dialog);
+
+ xsane.block_update_param = FALSE;
+
+ if (p->dialog->param_change_callback)
+ {
+ (*p->dialog->param_change_callback) (p->dialog, p->dialog->param_change_arg);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#if 0
+static void preview_update_batch_selection(Preview *p)
+{
+ Batch_selection *batch_selection;
+
+ if (!p->gc_selection) /* window isn't mapped yet */
+ {
+ return;
+ }
+
+ batch_selection = p->batch_selection;
+
+ while (batch_selection)
+ {
+ preview_draw_rect(p, p->window->window, p->gc_selection, batch_selection->coordinate);
+
+ batch_selection = batch_selection->next;
+ }
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_get_scale_device_to_image(Preview *p, float *xscalep, float *yscalep)
+{
+ float device_width, device_height;
+ float xscale = 1.0;
+ float yscale = 1.0;
+
+ device_width = fabs(p->image_surface[2] - p->image_surface[0]);
+ device_height = fabs(p->image_surface[3] - p->image_surface[1]);
+
+ if ( (device_width >0) && (device_width < INF) )
+ {
+ xscale = p->image_width / device_width;
+ }
+
+ if ( (device_height >0) && (device_height < INF) )
+ {
+ yscale = p->image_height / device_height;
+ }
+
+ if (p->surface_unit == SANE_UNIT_PIXEL)
+ {
+ if (xscale > yscale)
+ {
+ yscale = xscale;
+ }
+ else
+ {
+ xscale = yscale;
+ }
+ }
+
+ *xscalep = xscale;
+ *yscalep = yscale;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_get_scale_device_to_preview(Preview *p, float *xscalep, float *yscalep)
+{
+ float device_width, device_height;
+ float xscale = 1.0;
+ float yscale = 1.0;
+
+ device_width = fabs(p->image_surface[2] - p->image_surface[0]);
+ device_height = fabs(p->image_surface[3] - p->image_surface[1]);
+
+ if ( (device_width >0) && (device_width < INF) )
+ {
+ xscale = p->preview_width / device_width;
+ }
+
+ if ( (device_height >0) && (device_height < INF) )
+ {
+ yscale = p->preview_height / device_height;
+ }
+
+ if (p->surface_unit == SANE_UNIT_PIXEL)
+ {
+ if (xscale > yscale)
+ {
+ yscale = xscale;
+ }
+ else
+ {
+ xscale = yscale;
+ }
+ }
+
+ *xscalep = xscale;
+ *yscalep = yscale;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_get_scale_preview_to_image(Preview *p, float *xscalep, float *yscalep)
+{
+ float xscale = 1.0;
+ float yscale = 1.0;
+
+ if (p->image_width > 0)
+ {
+ xscale = p->image_width / (float) p->preview_width;
+ }
+
+ if (p->image_height > 0)
+ {
+ yscale = p->image_height / (float) p->preview_height;
+ }
+
+ if (p->surface_unit == SANE_UNIT_PIXEL)
+ {
+ if (xscale > yscale)
+ {
+ yscale = xscale;
+ }
+ else
+ {
+ xscale = yscale;
+ }
+ }
+
+ *xscalep = xscale;
+ *yscalep = yscale;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_paint_image(Preview *p)
+{
+ float xscale, yscale, src_x, src_y;
+ int dst_x, dst_y, height, x, y, old_y, src_offset;
+
+ preview_get_scale_preview_to_image(p, &xscale, &yscale);
+
+ memset(p->preview_row, 0x80, 3*p->preview_window_width);
+
+ /* don't draw last line unless it's complete: */
+ height = p->image_y;
+
+ if (p->image_x == 0 && height < p->image_height)
+ {
+ ++height;
+ }
+
+ /* for now, use simple nearest-neighbor interpolation: */
+ src_offset = 0;
+ src_x = src_y = 0.0;
+ old_y = -1;
+
+ for (dst_y = 0; dst_y < p->preview_height; ++dst_y)
+ {
+ y = (int) (src_y + 0.5);
+ if (y >= height)
+ {
+ break;
+ }
+ src_offset = y * 3 * p->image_width;
+
+ if ((p->image_data_enh) && (old_y != y))
+ {
+ old_y = y;
+ for (dst_x = 0; dst_x < p->preview_width; ++dst_x)
+ {
+ x = (int) (src_x + 0.5);
+ if (x >= p->image_width)
+ {
+ break;
+ }
+
+ p->preview_row[3*dst_x + 0] = p->image_data_enh[src_offset + 3*x + 0];
+ p->preview_row[3*dst_x + 1] = p->image_data_enh[src_offset + 3*x + 1];
+ p->preview_row[3*dst_x + 2] = p->image_data_enh[src_offset + 3*x + 2];
+ src_x += xscale;
+ }
+ }
+ gtk_preview_draw_row(GTK_PREVIEW(p->window), p->preview_row, 0, dst_y, p->preview_window_width);
+ src_x = 0.0;
+ src_y += yscale;
+ }
+
+ if (dst_y >= p->preview_height-5)
+ {
+ memset(p->preview_row, 0x80, 3*p->preview_window_width);
+ for (dst_y = p->preview_height-1; dst_y < p->preview_window_height; ++dst_y)
+ {
+ gtk_preview_draw_row(GTK_PREVIEW(p->window), p->preview_row, 0, dst_y, p->preview_window_width);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_display_partial_image(Preview *p)
+{
+ preview_paint_image(p);
+
+ if (GTK_WIDGET_DRAWABLE(p->window))
+ {
+ GtkPreview *preview = GTK_PREVIEW(p->window);
+ int src_x, src_y;
+
+ src_x = (p->window->allocation.width - preview->buffer_width)/2;
+ src_y = (p->window->allocation.height - preview->buffer_height)/2;
+ gtk_preview_put(preview, p->window->window, p->window->style->black_gc, src_x, src_y,
+ 0, 0, p->preview_width, p->preview_height);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_display_maybe(Preview *p)
+{
+ time_t now;
+
+ time(&now);
+
+ if (now > p->image_last_time_updated) /* wait at least one secone */
+ {
+ p->image_last_time_updated = now;
+ preview_display_partial_image(p);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_display_image(Preview *p)
+{
+ /* if image height was unknown and got larger than expected get missing memory */
+ if (p->params.lines <= 0 && p->image_y < p->image_height)
+ {
+ p->image_height = p->image_y;
+ p->image_data_raw = realloc(p->image_data_raw, 3 * p->image_width * p->image_height);
+ p->image_data_enh = realloc(p->image_data_enh, 3 * p->image_width * p->image_height);
+ assert(p->image_data_raw);
+ assert(p->image_data_enh);
+ }
+
+ memcpy(p->image_data_raw, p->image_data_enh, 3 * p->image_width * p->image_height);
+
+ preview_do_gamma_correction(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_save_option(Preview *p, int option, SANE_Word *save_loc, int *valid)
+{
+ SANE_Status status;
+
+ if (option <= 0)
+ {
+ *valid = 0;
+ return;
+ }
+
+ status = sane_control_option(p->dialog->dev, option, SANE_ACTION_GET_VALUE, save_loc, 0);
+ *valid = (status == SANE_STATUS_GOOD);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_restore_option(Preview *p, int option, SANE_Word saved_value, int valid)
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Status status;
+ SANE_Handle dev;
+
+ if (!valid)
+ {
+ return;
+ }
+
+ dev = p->dialog->dev;
+ status = sane_control_option(dev, option, SANE_ACTION_SET_VALUE, &saved_value, 0);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ char buf[256];
+ opt = sane_get_option_descriptor(dev, option);
+ snprintf(buf, sizeof(buf), "%s %s: %s.", ERR_SET_OPTION, opt->name, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_set_option_float(Preview *p, int option, float value)
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Handle dev;
+ SANE_Word word;
+
+ if (option <= 0 || value <= -INF || value >= INF)
+ {
+ return;
+ }
+
+ dev = p->dialog->dev;
+ opt = sane_get_option_descriptor(dev, option);
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ word = SANE_FIX(value) + 0.5;
+ }
+ else
+ {
+ word = value + 0.5;
+ }
+
+ sane_control_option(dev, option, SANE_ACTION_SET_VALUE, &word, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_set_option_bool(Preview *p, int option, SANE_Bool value)
+{
+ SANE_Handle dev;
+
+ if (option <= 0)
+ return;
+
+ dev = p->dialog->dev;
+ sane_control_option(dev, option, SANE_ACTION_SET_VALUE, &value, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_set_option_int(Preview *p, int option, SANE_Int value)
+{
+ SANE_Handle dev;
+
+ if (option <= 0)
+ return;
+
+ dev = p->dialog->dev;
+ sane_control_option(dev, option, SANE_ACTION_SET_VALUE, &value, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int preview_increment_image_y(Preview *p)
+{
+ size_t extra_size, offset;
+ char buf[256];
+
+ p->image_x = 0;
+ ++p->image_y;
+ if (p->params.lines <= 0 && p->image_y >= p->image_height)
+ {
+ offset = 3 * p->image_width*p->image_height;
+ extra_size = 3 * 32 * p->image_width;
+ p->image_height += 32;
+ p->image_data_raw = realloc(p->image_data_raw, offset + extra_size);
+ p->image_data_enh = realloc(p->image_data_enh, offset + extra_size);
+ if ( (!p->image_data_enh) || (!p->image_data_raw) )
+ {
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_ALLOCATE_IMAGE, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ preview_scan_done(p);
+ return -1;
+ }
+ memset(p->image_data_enh + offset, 0xff, extra_size);
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_read_image_data(gpointer data, gint source, GdkInputCondition cond)
+{
+ SANE_Status status;
+ Preview *p = data;
+ u_char buf[8192];
+ SANE_Handle dev;
+ SANE_Int len;
+ int i, j;
+
+ dev = p->dialog->dev;
+ while (1)
+ {
+ status = sane_read(dev, buf, sizeof(buf), &len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (status == SANE_STATUS_EOF)
+ {
+ if (p->params.last_frame) /* got all preview image data */
+ {
+ preview_display_image(p); /* display preview image */
+ preview_save_image(p); /* save preview image */
+ preview_scan_done(p); /* scan is done */
+ return; /* ok, all finished */
+ }
+ else
+ {
+ preview_scan_start(p);
+ break;
+ }
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_DURING_READ, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ }
+ preview_scan_done(p);
+ return;
+ }
+
+ if (!len)
+ {
+ break; /* out of data for now */
+ }
+
+ switch (p->params.format)
+ {
+ case SANE_FRAME_RGB:
+ if (p->params.depth != 8)
+ {
+ goto bad_depth;
+ }
+
+ for (i = 0; i < len; ++i)
+ {
+ p->image_data_enh[p->image_offset++] = buf[i];
+ if (p->image_offset%3 == 0)
+ {
+ if (++p->image_x >= p->image_width && preview_increment_image_y(p) < 0)
+ {
+ return;
+ }
+ }
+ }
+ break;
+
+ case SANE_FRAME_GRAY:
+ switch (p->params.depth)
+ {
+ case 1:
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask = buf[i];
+
+ for (j = 7; j >= 0; --j)
+ {
+ u_char gl = (mask & (1 << j)) ? 0x00 : 0xff;
+ p->image_data_enh[p->image_offset++] = gl;
+ p->image_data_enh[p->image_offset++] = gl;
+ p->image_data_enh[p->image_offset++] = gl;
+ if (++p->image_x >= p->image_width)
+ {
+ if (preview_increment_image_y(p) < 0)
+ {
+ return;
+ }
+ break; /* skip padding bits */
+ }
+ }
+ }
+ break;
+
+ case 8:
+ for (i = 0; i < len; ++i)
+ {
+ u_char gl = buf[i];
+ p->image_data_enh[p->image_offset++] = gl;
+ p->image_data_enh[p->image_offset++] = gl;
+ p->image_data_enh[p->image_offset++] = gl;
+ if (++p->image_x >= p->image_width && preview_increment_image_y(p) < 0)
+ {
+ return;
+ }
+ }
+ break;
+
+ default:
+ goto bad_depth;
+ }
+ break;
+
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ switch (p->params.depth)
+ {
+ case 1:
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask = buf[i];
+
+ for (j = 0; j < 8; ++j)
+ {
+ u_char gl = (mask & 1) ? 0xff : 0x00;
+ mask >>= 1;
+ p->image_data_enh[p->image_offset++] = gl;
+ p->image_offset += 3;
+ if (++p->image_x >= p->image_width && preview_increment_image_y(p) < 0)
+ {
+ return;
+ }
+ }
+ }
+ break;
+
+ case 8:
+ for (i = 0; i < len; ++i)
+ {
+ p->image_data_enh[p->image_offset] = buf[i];
+ p->image_offset += 3;
+ if (++p->image_x >= p->image_width && preview_increment_image_y(p) < 0)
+ {
+ return;
+ }
+ }
+ break;
+
+ default:
+ goto bad_depth;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "preview_read_image_data: %s %d\n", ERR_BAD_FRAME_FORMAT, p->params.format);
+ preview_scan_done(p);
+ return;
+ }
+
+ if (p->input_tag < 0)
+ {
+ preview_display_maybe(p);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ }
+ }
+ preview_display_maybe(p);
+ return;
+
+bad_depth:
+ snprintf(buf, sizeof(buf), "%s %d.", ERR_PREVIEW_BAD_DEPTH, p->params.depth);
+ xsane_back_gtk_error(buf, TRUE);
+ preview_scan_done(p);
+ return;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_scan_done(Preview *p)
+{
+ int i;
+
+ p->scanning = FALSE;
+
+ if (p->input_tag >= 0)
+ {
+ gdk_input_remove(p->input_tag);
+ p->input_tag = -1;
+ }
+
+ sane_cancel(p->dialog->dev);
+
+ xsane.block_update_param = TRUE; /* do not change parameters each time */
+
+ preview_restore_option(p, p->dialog->well_known.dpi, p->saved_dpi, p->saved_dpi_valid);
+ preview_restore_option(p, p->dialog->well_known.dpi_x, p->saved_dpi_x, p->saved_dpi_x_valid);
+ preview_restore_option(p, p->dialog->well_known.dpi_y, p->saved_dpi_y, p->saved_dpi_y_valid);
+
+ for (i = 0; i < 4; ++i)
+ {
+ preview_restore_option(p, p->dialog->well_known.coord[i], p->saved_coord[i], p->saved_coord_valid[i]);
+ }
+
+ preview_restore_option(p, p->dialog->well_known.bit_depth, p->saved_bit_depth, p->saved_bit_depth_valid);
+
+ preview_set_option_bool(p, p->dialog->well_known.preview, SANE_FALSE);
+
+ gtk_widget_set_sensitive(p->cancel, FALSE);
+ xsane_set_sensitivity(TRUE);
+
+ xsane.block_update_param = FALSE;
+
+ preview_update_surface(p, 0); /* if surface was not defined it's necessary to redefine it now */
+
+ preview_update_selection(p);
+ xsane_update_histogram();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int preview_get_memory(Preview *p)
+{
+ char buf[256];
+
+ if (p->image_data_enh)
+ {
+ free(p->image_data_enh);
+ p->image_data_enh = 0;
+ }
+
+ if (p->image_data_raw)
+ {
+ free(p->image_data_raw);
+ p->image_data_raw = 0;
+ }
+
+ if (p->preview_row)
+ {
+ free(p->preview_row);
+ p->preview_row = 0;
+ }
+
+ p->image_data_enh = malloc(3 * p->image_width * (p->image_height));
+ p->image_data_raw = malloc(3 * p->image_width * (p->image_height));
+ p->preview_row = malloc(3 * p->preview_window_width);
+
+ if ( (!p->image_data_raw) || (!p->image_data_enh) || (!p->preview_row) )
+ {
+ if (p->image_data_enh)
+ {
+ free(p->image_data_enh);
+ p->image_data_enh = 0;
+ }
+
+ if (p->image_data_raw)
+ {
+ free(p->image_data_raw);
+ p->image_data_raw = 0;
+ }
+
+ if (p->preview_row)
+ {
+ free(p->preview_row);
+ p->preview_row = 0;
+ }
+
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_ALLOCATE_IMAGE, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+
+ return -1; /* error */
+ }
+
+ memset(p->image_data_enh, 0xff, 3*p->image_width*p->image_height); /* clean memory */
+
+ return 0; /* ok */
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_scan_start(Preview *p)
+{
+ SANE_Handle dev = p->dialog->dev;
+ SANE_Status status;
+ char buf[256];
+ int fd, y, i;
+ int gamma_gray_size = 256; /* set this values to image depth for more than 8bpp input support!!! */
+ int gamma_red_size = 256;
+ int gamma_green_size = 256;
+ int gamma_blue_size = 256;
+ int gamma_gray_max = 255; /* set this to to image depth for more than 8bpp output support */
+ int gamma_red_max = 255;
+ int gamma_green_max = 255;
+ int gamma_blue_max = 255;
+
+ for (i=0; i<4; i++)
+ {
+ p->image_surface[i] = p->surface[i];
+ }
+
+ xsane_clear_histogram(&xsane.histogram_raw);
+ xsane_clear_histogram(&xsane.histogram_enh);
+ gtk_widget_set_sensitive(p->cancel, TRUE);
+ xsane_set_sensitivity(FALSE);
+
+ /* clear old preview: */
+ memset(p->preview_row, 0xff, 3*p->preview_width);
+ for (y = 0; y < p->preview_height; ++y)
+ {
+ gtk_preview_draw_row(GTK_PREVIEW(p->window), p->preview_row, 0, y, p->preview_width);
+ }
+
+ if (p->input_tag >= 0)
+ {
+ gdk_input_remove(p->input_tag);
+ p->input_tag = -1;
+ }
+
+ if (p->dialog->well_known.gamma_vector >0)
+ {
+ const SANE_Option_Descriptor *opt;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector);
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ SANE_Int *gamma_data;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector);
+ gamma_gray_size = opt->size / sizeof(opt->type);
+ gamma_gray_max = opt->constraint.range->max;
+
+ gamma_data = malloc(gamma_gray_size * sizeof(SANE_Int));
+ xsane_create_gamma_curve(gamma_data, 0, 1.0, 0.0, 0.0, gamma_gray_size, gamma_gray_max);
+ xsane_back_gtk_update_vector(p->dialog, p->dialog->well_known.gamma_vector, gamma_data);
+ free(gamma_data);
+ }
+ }
+
+ if (p->dialog->well_known.gamma_vector_r >0)
+ {
+ const SANE_Option_Descriptor *opt;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector_r);
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ SANE_Int *gamma_data_red, *gamma_data_green, *gamma_data_blue;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector_r);
+ gamma_red_size = opt->size / sizeof(opt->type);
+ gamma_red_max = opt->constraint.range->max;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector_g);
+ gamma_green_size = opt->size / sizeof(opt->type);
+ gamma_green_max = opt->constraint.range->max;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector_b);
+ gamma_blue_size = opt->size / sizeof(opt->type);
+ gamma_blue_max = opt->constraint.range->max;
+
+ gamma_data_red = malloc(gamma_red_size * sizeof(SANE_Int));
+ gamma_data_green = malloc(gamma_green_size * sizeof(SANE_Int));
+ gamma_data_blue = malloc(gamma_blue_size * sizeof(SANE_Int));
+
+ xsane_create_gamma_curve(gamma_data_red, 0, 1.0, 0.0, 0.0, gamma_red_size, gamma_red_max);
+ xsane_create_gamma_curve(gamma_data_green, 0, 1.0, 0.0, 0.0, gamma_green_size, gamma_green_max);
+ xsane_create_gamma_curve(gamma_data_blue, 0, 1.0, 0.0, 0.0, gamma_blue_size, gamma_blue_max);
+
+ xsane_back_gtk_update_vector(p->dialog, p->dialog->well_known.gamma_vector_r, gamma_data_red);
+ xsane_back_gtk_update_vector(p->dialog, p->dialog->well_known.gamma_vector_g, gamma_data_green);
+ xsane_back_gtk_update_vector(p->dialog, p->dialog->well_known.gamma_vector_b, gamma_data_blue);
+
+ free(gamma_data_red);
+ free(gamma_data_green);
+ free(gamma_data_blue);
+ }
+ }
+
+ status = sane_start(dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_START_SCANNER, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ preview_scan_done(p);
+ return;
+ }
+
+ status = sane_get_parameters(dev, &p->params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_GET_PARAMS, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ preview_scan_done(p);
+ return;
+ }
+
+ p->image_offset = p->image_x = p->image_y = 0;
+
+ if (p->params.format >= SANE_FRAME_RED && p->params.format <= SANE_FRAME_BLUE)
+ {
+ p->image_offset = p->params.format - SANE_FRAME_RED;
+ }
+
+ if ( (!p->image_data_enh) || (p->params.pixels_per_line != p->image_width)
+ || ( (p->params.lines >= 0) && (p->params.lines != p->image_height) ) )
+ {
+ p->image_width = p->params.pixels_per_line;
+ p->image_height = p->params.lines;
+
+ if (p->image_height < 0)
+ {
+ p->image_height = 32; /* may have to adjust as we go... */
+ }
+
+ if (preview_get_memory(p))
+ {
+ preview_scan_done(p); /* error */
+ return;
+ }
+ }
+
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+/* THIS IS A BIT STRANGE HERE */
+ p->selection.active = FALSE;
+ p->previous_selection_maximum.active = FALSE;
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+ p->scanning = TRUE;
+
+ if (sane_set_io_mode(dev, SANE_TRUE) == SANE_STATUS_GOOD && sane_get_select_fd(dev, &fd) == SANE_STATUS_GOOD)
+ {
+ p->input_tag = gdk_input_add(fd, GDK_INPUT_READ, preview_read_image_data, p);
+ }
+ else
+ {
+ preview_read_image_data(p, -1, GDK_INPUT_READ);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int preview_make_image_path(Preview *p, size_t filename_size, char *filename, int level)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "preview-level-%d-", level);
+ return xsane_back_gtk_make_path(filename_size, filename, 0, 0, buf, p->dialog->dev_name, ".ppm", XSANE_PATH_TMP);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int preview_restore_image_from_file(Preview *p, FILE *in, int min_quality)
+{
+ u_int psurface_type, psurface_unit;
+ int image_width, image_height;
+ int xoffset, yoffset, width, height;
+ int quality;
+ int y;
+ float psurface[4];
+ size_t nread;
+ char *imagep;
+
+ if (!in)
+ {
+ return min_quality;
+ }
+
+ /* See whether there is a saved preview and load it if present: */
+
+ if (fscanf(in, "P6\n# surface: %g %g %g %g %u %u\n%d %d\n255\n",
+ psurface + 0, psurface + 1, psurface + 2, psurface + 3,
+ &psurface_type, &psurface_unit,
+ &image_width, &image_height) != 8)
+ {
+ return min_quality;
+ }
+
+ if ((psurface_type != p->surface_type) || (psurface_unit != p->surface_unit))
+ {
+ return min_quality;
+ }
+
+ xoffset = (p->surface[0] - psurface[0])/(psurface[2] - psurface[0]) * image_width;
+ yoffset = (p->surface[1] - psurface[1])/(psurface[3] - psurface[1]) * image_height;
+ width = (p->surface[2] - p->surface[0])/(psurface[2] - psurface[0]) * image_width;
+ height = (p->surface[3] - p->surface[1])/(psurface[3] - psurface[1]) * image_height;
+ quality = width;
+
+ if ((xoffset < 0) || (yoffset < 0) ||
+ (xoffset+width > image_width) || (yoffset+height > image_height) ||
+ (width == 0) || (height == 0))
+ {
+ return min_quality;
+ }
+
+ if (quality < min_quality)
+ {
+ return min_quality;
+ }
+
+ p->params.depth = 8;
+ p->image_width = width;
+ p->image_height = height;
+
+ if (preview_get_memory(p))
+ {
+ return min_quality; /* error allocating memory */
+ }
+
+ fseek(in, yoffset * 3 * image_width, SEEK_CUR); /* skip unused lines */
+
+ imagep = p->image_data_enh;
+
+ for (y = yoffset; y < yoffset + height; y++)
+ {
+ fseek(in, xoffset * 3, SEEK_CUR); /* skip unused pixel left of area */
+
+ nread = fread(imagep, 3, width, in);
+ imagep += width * 3;
+
+ fseek(in, (image_width - width - xoffset) * 3, SEEK_CUR); /* skip unused pixel right of area */
+ }
+
+ p->image_y = height;
+ p->image_x = width;
+
+ p->image_surface[0] = p->surface[0];
+ p->image_surface[1] = p->surface[1];
+ p->image_surface[2] = p->surface[2];
+ p->image_surface[3] = p->surface[3];
+
+ return quality;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_restore_image(Preview *p)
+{
+ char filename[PATH_MAX];
+ FILE *in;
+ int status;
+ int quality = 0;
+ int level;
+
+ /* See whether there is a saved preview and load it if present: */
+
+ for(level = 2; level >= 0; level--)
+ {
+ status = preview_make_image_path(p, sizeof(filename), filename, level);
+ if (status >= 0)
+ {
+ in = fopen(filename, "r");
+ if (in)
+ {
+ quality = preview_restore_image_from_file(p, in, quality);
+ }
+ }
+ }
+ memcpy(p->image_data_raw, p->image_data_enh, 3 * p->image_width * p->image_height);
+
+/* the following commands may be removed because they are done because a event is emmited */
+ preview_do_gamma_correction(p);
+ xsane_update_histogram();
+ preview_draw_selection(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* This is executed _after_ the gtkpreview's expose routine. */
+static gint preview_expose_handler(GtkWidget *window, GdkEvent *event, gpointer data)
+{
+ Preview *p = data;
+
+ p->previous_selection.active = FALSE; /* ok, old selections are overpainted */
+ p->previous_selection_maximum.active = FALSE;
+ p->selection.active = TRUE; /* ok, old selections are overpainted */
+ p->selection_maximum.active = TRUE;
+ preview_draw_selection(p); /* draw selections again */
+
+ return FALSE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static gint preview_event_handler(GtkWidget *window, GdkEvent *event, gpointer data)
+{
+ Preview *p = data;
+ GdkCursor *cursor;
+ GdkColor color;
+ GdkColormap *colormap;
+ float preview_selection[4];
+ float xscale, yscale;
+ static int event_count = 0;
+ int cursornr;
+
+ event_count++;
+
+ preview_get_scale_device_to_preview(p, &xscale, &yscale);
+
+ preview_selection[0] = xscale * (p->selection.coordinate[0] - p->surface[0]);
+ preview_selection[1] = yscale * (p->selection.coordinate[1] - p->surface[1]);
+ preview_selection[2] = xscale * (p->selection.coordinate[2] - p->surface[0]);
+ preview_selection[3] = yscale * (p->selection.coordinate[3] - p->surface[1]);
+
+ if (event->type == GDK_EXPOSE)
+ {
+ if (!p->gc_selection)
+ {
+ colormap = gdk_window_get_colormap(p->window->window);
+
+ p->gc_selection = gdk_gc_new(p->window->window);
+ gdk_gc_set_function(p->gc_selection, GDK_INVERT);
+ gdk_gc_set_line_attributes(p->gc_selection, 1, GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER);
+
+ p->gc_selection_maximum = gdk_gc_new(p->window->window);
+ gdk_gc_set_function(p->gc_selection_maximum, GDK_XOR);
+ gdk_gc_set_line_attributes(p->gc_selection_maximum, 1, GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER);
+ color.red = 0;
+ color.green = 65535;
+ color.blue = 30000;
+ gdk_color_alloc(colormap, &color);
+ gdk_gc_set_foreground(p->gc_selection_maximum, &color);
+
+ preview_paint_image(p);
+ }
+ else
+ {
+ while (gtk_events_pending()) /* make sure image is updated */
+ {
+ gtk_main_iteration();
+ }
+
+ p->previous_selection.active = FALSE; /* ok, old selections are overpainted */
+ p->previous_selection_maximum.active = FALSE;
+ preview_draw_selection(p); /* draw selections again */
+ }
+ }
+ else if (!p->scanning)
+ {
+ switch (event->type)
+ {
+ case GDK_UNMAP:
+ case GDK_MAP:
+ break;
+
+ case GDK_BUTTON_PRESS:
+ switch (p->mode)
+ {
+ case MODE_PIPETTE_WHITE:
+ {
+ if ( ( (((GdkEventButton *)event)->button == 1) || (((GdkEventButton *)event)->button == 2) ) &&
+ (p->image_data_raw) ) /* left or middle button */
+ {
+ int r,g,b;
+
+ preview_get_color(p, event->button.x, event->button.y, &r, &g, &b);
+
+ xsane.slider_gray.value[2] = sqrt( (r*r+g*g+b*b) / 3)/2.55;
+
+ if ( (!xsane.enhancement_rgb_default) && (((GdkEventButton *)event)->button == 2) ) /* middle button */
+ {
+ xsane.slider_red.value[2] = r/2.55;
+ xsane.slider_green.value[2] = g/2.55;
+ xsane.slider_blue.value[2] = b/2.55;
+ }
+ else
+ {
+ xsane.slider_red.value[2] = xsane.slider_gray.value[2];
+ xsane.slider_green.value[2] = xsane.slider_gray.value[2];
+ xsane.slider_blue.value[2] = xsane.slider_gray.value[2];
+ }
+
+ if (xsane.slider_gray.value[2] < 2)
+ {
+ xsane.slider_gray.value[2] = 2;
+ }
+ if (xsane.slider_gray.value[1] >= xsane.slider_gray.value[2])
+ {
+ xsane.slider_gray.value[1] = xsane.slider_gray.value[2]-1;
+ if (xsane.slider_gray.value[0] >= xsane.slider_gray.value[1])
+ {
+ xsane.slider_gray.value[0] = xsane.slider_gray.value[1]-1;
+ }
+ }
+
+ if (xsane.slider_red.value[2] < 2)
+ {
+ xsane.slider_red.value[2] = 2;
+ }
+ if (xsane.slider_red.value[1] >= xsane.slider_red.value[2])
+ {
+ xsane.slider_red.value[1] = xsane.slider_red.value[2]-1;
+ if (xsane.slider_red.value[0] >= xsane.slider_red.value[1])
+ {
+ xsane.slider_red.value[0] = xsane.slider_red.value[1]-1;
+ }
+ }
+
+ if (xsane.slider_green.value[2] < 2)
+ {
+ xsane.slider_green.value[2] = 2;
+ }
+ if (xsane.slider_green.value[1] >= xsane.slider_green.value[2])
+ {
+ xsane.slider_green.value[1] = xsane.slider_green.value[2]-1;
+ if (xsane.slider_green.value[0] >= xsane.slider_green.value[1])
+ {
+ xsane.slider_green.value[0] = xsane.slider_green.value[1]-1;
+ }
+ }
+
+ if (xsane.slider_blue.value[2] < 2)
+ {
+ xsane.slider_blue.value[2] = 2;
+ }
+ if (xsane.slider_blue.value[1] >= xsane.slider_blue.value[2])
+ {
+ xsane.slider_blue.value[1] = xsane.slider_blue.value[2]-1;
+ if (xsane.slider_blue.value[0] >= xsane.slider_blue.value[1])
+ {
+ xsane.slider_blue.value[0] = xsane.slider_blue.value[1]-1;
+ }
+ }
+
+ xsane_enhancement_by_histogram();
+ }
+
+ p->mode = MODE_NORMAL;
+
+ cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW);
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = XSANE_CURSOR_PREVIEW;
+ }
+ break;
+
+ case MODE_PIPETTE_GRAY:
+ {
+ if ( ( (((GdkEventButton *)event)->button == 1) || (((GdkEventButton *)event)->button == 2) ) &&
+ (p->image_data_raw) ) /* left or middle button */
+ {
+ int r,g,b;
+
+ preview_get_color(p, event->button.x, event->button.y, &r, &g, &b);
+
+ xsane.slider_gray.value[1] = sqrt( (r*r+g*g+b*b) / 3)/2.55;
+
+ if ( (!xsane.enhancement_rgb_default) && (((GdkEventButton *)event)->button == 2) ) /* middle button */
+ {
+ xsane.slider_red.value[1] = r/2.55;
+ xsane.slider_green.value[1] = g/2.55;
+ xsane.slider_blue.value[1] = b/2.55;
+ }
+ else
+ {
+ xsane.slider_red.value[1] = xsane.slider_gray.value[1];
+ xsane.slider_green.value[1] = xsane.slider_gray.value[1];
+ xsane.slider_blue.value[1] = xsane.slider_gray.value[1];
+ }
+
+ if (xsane.slider_gray.value[1] == 0)
+ {
+ xsane.slider_gray.value[1] += 1;
+ }
+ if (xsane.slider_gray.value[1] == 100)
+ {
+ xsane.slider_gray.value[1] -= 1;
+ }
+ if (xsane.slider_gray.value[1] >= xsane.slider_gray.value[2])
+ {
+ xsane.slider_gray.value[2] = xsane.slider_gray.value[1]+1;
+ }
+ if (xsane.slider_gray.value[1] <= xsane.slider_gray.value[0])
+ {
+ xsane.slider_gray.value[0] = xsane.slider_gray.value[1]-1;
+ }
+
+ if (xsane.slider_red.value[1] == 0)
+ {
+ xsane.slider_red.value[1] += 1;
+ }
+ if (xsane.slider_red.value[1] == 100)
+ {
+ xsane.slider_red.value[1] -= 1;
+ }
+ if (xsane.slider_red.value[1] >= xsane.slider_red.value[2])
+ {
+ xsane.slider_red.value[2] = xsane.slider_red.value[1]+1;
+ }
+ if (xsane.slider_red.value[1] <= xsane.slider_red.value[0])
+ {
+ xsane.slider_red.value[0] = xsane.slider_red.value[1]-1;
+ }
+
+ if (xsane.slider_green.value[1] == 0)
+ {
+ xsane.slider_green.value[1] += 1;
+ }
+ if (xsane.slider_green.value[1] == 100)
+ {
+ xsane.slider_green.value[1] -= 1;
+ }
+ if (xsane.slider_green.value[1] >= xsane.slider_green.value[2])
+ {
+ xsane.slider_green.value[2] = xsane.slider_green.value[1]+1;
+ }
+ if (xsane.slider_green.value[1] <= xsane.slider_green.value[0])
+ {
+ xsane.slider_green.value[0] = xsane.slider_green.value[1]-1;
+ }
+
+ if (xsane.slider_blue.value[1] == 0)
+ {
+ xsane.slider_blue.value[1] += 1;
+ }
+ if (xsane.slider_blue.value[1] == 100)
+ {
+ xsane.slider_blue.value[1] -= 1;
+ }
+ if (xsane.slider_blue.value[1] >= xsane.slider_blue.value[2])
+ {
+ xsane.slider_blue.value[2] = xsane.slider_blue.value[1]+1;
+ }
+ if (xsane.slider_blue.value[1] <= xsane.slider_blue.value[0])
+ {
+ xsane.slider_blue.value[0] = xsane.slider_blue.value[1]-1;
+ }
+
+ xsane_enhancement_by_histogram();
+ }
+
+ p->mode = MODE_NORMAL;
+
+ cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW);
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = XSANE_CURSOR_PREVIEW;
+ }
+ break;
+
+ case MODE_PIPETTE_BLACK:
+ {
+ if ( ( (((GdkEventButton *)event)->button == 1) || (((GdkEventButton *)event)->button == 2) ) &&
+ (p->image_data_raw) ) /* left or middle button */
+ {
+ int r,g,b;
+
+ preview_get_color(p, event->button.x, event->button.y, &r, &g, &b);
+
+ xsane.slider_gray.value[0] = sqrt( (r*r+g*g+b*b) / 3)/2.55;
+
+ if ( (!xsane.enhancement_rgb_default) && (((GdkEventButton *)event)->button == 2) ) /* middle button */
+ {
+ xsane.slider_red.value[0] = r/2.55;
+ xsane.slider_green.value[0] = g/2.55;
+ xsane.slider_blue.value[0] = b/2.55;
+ }
+ else
+ {
+ xsane.slider_red.value[0] = xsane.slider_gray.value[0];
+ xsane.slider_green.value[0] = xsane.slider_gray.value[0];
+ xsane.slider_blue.value[0] = xsane.slider_gray.value[0];
+ }
+
+ if (xsane.slider_gray.value[0] > 98)
+ {
+ xsane.slider_gray.value[0] = 98;
+ }
+ if (xsane.slider_gray.value[1] <= xsane.slider_gray.value[0])
+ {
+ xsane.slider_gray.value[1] = xsane.slider_gray.value[0]+1;
+ if (xsane.slider_gray.value[2] <= xsane.slider_gray.value[1])
+ {
+ xsane.slider_gray.value[2] = xsane.slider_gray.value[1]+1;
+ }
+ }
+
+ if (xsane.slider_red.value[0] > 98)
+ {
+ xsane.slider_red.value[0] = 98;
+ }
+ if (xsane.slider_red.value[1] <= xsane.slider_red.value[0])
+ {
+ xsane.slider_red.value[1] = xsane.slider_red.value[0]+1;
+ if (xsane.slider_red.value[2] <= xsane.slider_red.value[1])
+ {
+ xsane.slider_red.value[2] = xsane.slider_red.value[1]+1;
+ }
+ }
+
+ if (xsane.slider_green.value[0] > 98)
+ {
+ xsane.slider_green.value[0] = 98;
+ }
+ if (xsane.slider_green.value[1] <= xsane.slider_green.value[0])
+ {
+ xsane.slider_green.value[1] = xsane.slider_green.value[0]+1;
+ if (xsane.slider_green.value[2] <= xsane.slider_green.value[1])
+ {
+ xsane.slider_green.value[2] = xsane.slider_green.value[1]+1;
+ }
+ }
+
+ if (xsane.slider_blue.value[0] > 98)
+ {
+ xsane.slider_blue.value[0] = 98;
+ }
+ if (xsane.slider_blue.value[1] <= xsane.slider_blue.value[0])
+ {
+ xsane.slider_blue.value[1] = xsane.slider_blue.value[0]+1;
+ if (xsane.slider_blue.value[2] <= xsane.slider_blue.value[1])
+ {
+ xsane.slider_blue.value[2] = xsane.slider_blue.value[1]+1;
+ }
+ }
+
+ xsane_enhancement_by_histogram();
+ }
+
+ p->mode = MODE_NORMAL;
+
+ cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW);
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = XSANE_CURSOR_PREVIEW;
+ }
+ break;
+
+ case MODE_NORMAL:
+ {
+ switch (((GdkEventButton *)event)->button)
+ {
+ case 1: /* left button */
+ p->selection_xedge = -1;
+ if ( (preview_selection[0] - SELECTION_RANGE_OUT < event->button.x) && (event->button.x < preview_selection[0] + SELECTION_RANGE_IN) ) /* left */
+ {
+ p->selection_xedge = 0;
+ }
+ else if ( (preview_selection[2] - SELECTION_RANGE_IN < event->button.x) && (event->button.x < preview_selection[2] + SELECTION_RANGE_OUT) ) /* right */
+ {
+ p->selection_xedge = 2;
+ }
+
+ p->selection_yedge = -1;
+ if ( (preview_selection[1] - SELECTION_RANGE_OUT < event->button.y) && (event->button.y < preview_selection[1] + SELECTION_RANGE_IN) ) /* top */
+ {
+ p->selection_yedge = 1;
+ }
+ else if ( (preview_selection[3] - SELECTION_RANGE_IN < event->button.y) && (event->button.y < preview_selection[3] + SELECTION_RANGE_OUT) ) /* bottom */
+ {
+ p->selection_yedge = 3;
+ }
+
+ if ( (p->selection_xedge != -1) && (p->selection_yedge != -1) ) /* move edge */
+ {
+ p->selection_drag_edge = TRUE;
+ p->selection.coordinate[p->selection_xedge] = p->surface[0] + event->button.x / xscale;
+ p->selection.coordinate[p->selection_yedge] = p->surface[1] + event->button.y / yscale;
+ preview_draw_selection(p);
+ }
+ else /* select new area */
+ {
+ p->selection_xedge = 2;
+ p->selection_yedge = 3;
+ p->selection.coordinate[0] = p->surface[0] + event->button.x / xscale;
+ p->selection.coordinate[1] = p->surface[1] + event->button.y / yscale;
+ p->selection_drag = TRUE;
+
+ cursornr = GDK_CROSS;
+ cursor = gdk_cursor_new(cursornr); /* set curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = cursornr;
+ }
+ break;
+
+ case 2: /* middle button */
+ case 3: /* right button */
+ if ( (preview_selection[0]-SELECTION_RANGE_OUT < event->button.x) &&
+ (preview_selection[2]+SELECTION_RANGE_OUT > event->button.x) &&
+ (preview_selection[1]-SELECTION_RANGE_OUT < event->button.y) &&
+ (preview_selection[3]+SELECTION_RANGE_OUT > event->button.y) )
+ {
+ p->selection_drag = TRUE;
+ p->selection_xpos = event->button.x;
+ p->selection_ypos = event->button.y;
+
+ cursornr = GDK_HAND2;
+ cursor = gdk_cursor_new(cursornr); /* set curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = cursornr;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ switch (((GdkEventButton *)event)->button)
+ {
+ case 1: /* left button */
+ case 2: /* middle button */
+ case 3: /* right button */
+ if (p->selection_drag)
+ {
+ cursornr = XSANE_CURSOR_PREVIEW;
+ cursor = gdk_cursor_new(cursornr); /* set curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = cursornr;
+ }
+
+ if ( (p->selection_drag) || (p->selection_drag_edge) )
+ {
+
+ if (((GdkEventButton *)event)->button == 1) /* left button */
+ {
+ p->selection.coordinate[p->selection_xedge] = p->surface[0] + event->button.x / xscale;
+ p->selection.coordinate[p->selection_yedge] = p->surface[1] + event->button.y / yscale;
+ }
+
+ p->selection_drag_edge = FALSE;
+ p->selection_drag = FALSE;
+
+ preview_order_selection(p);
+ preview_bound_selection(p);
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+ preview_establish_selection(p);
+ }
+ default:
+ break;
+ }
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ switch (((GdkEventMotion *)event)->state)
+ {
+ case 256: /* left button */
+ if (p->selection_drag_edge)
+ {
+ p->selection.active = TRUE;
+ p->selection.coordinate[p->selection_xedge] = p->surface[0] + event->button.x / xscale;
+ p->selection.coordinate[p->selection_yedge] = p->surface[1] + event->button.y / yscale;
+
+ preview_order_selection(p);
+ preview_bound_selection(p);
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+
+ if ((preferences.gtk_update_policy == GTK_UPDATE_CONTINUOUS) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ else if ((preferences.gtk_update_policy == GTK_UPDATE_DELAYED) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ }
+
+ if (p->selection_drag)
+ {
+ p->selection.active = TRUE;
+ p->selection.coordinate[p->selection_xedge] = p->surface[0] + event->motion.x / xscale;
+ p->selection.coordinate[p->selection_yedge] = p->surface[1] + event->motion.y / yscale;
+
+ preview_order_selection(p);
+ preview_bound_selection(p);
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+
+ if ((preferences.gtk_update_policy == GTK_UPDATE_CONTINUOUS) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ else if ((preferences.gtk_update_policy == GTK_UPDATE_DELAYED) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ }
+
+ cursornr = p->cursornr;
+
+ if ( ( (preview_selection[0] - SELECTION_RANGE_OUT < event->button.x) && (event->button.x < preview_selection[0] + SELECTION_RANGE_IN) ) && /* left */
+ ( (preview_selection[1] - SELECTION_RANGE_OUT < event->button.y) && (event->button.y < preview_selection[1] + SELECTION_RANGE_IN) ) ) /* top */
+ {
+ cursornr = GDK_TOP_LEFT_CORNER;
+ }
+ else if ( ( (preview_selection[2] - SELECTION_RANGE_IN < event->button.x) && (event->button.x < preview_selection[2] + SELECTION_RANGE_OUT) ) && /* right */
+ ( (preview_selection[1] - SELECTION_RANGE_OUT < event->button.y) && (event->button.y < preview_selection[1] + SELECTION_RANGE_IN) ) ) /* top */
+ {
+ cursornr = GDK_TOP_RIGHT_CORNER;
+ }
+ else if ( ( (preview_selection[0] - SELECTION_RANGE_OUT < event->button.x) && (event->button.x < preview_selection[0] + SELECTION_RANGE_IN) ) && /* left */
+ ( (preview_selection[3] - SELECTION_RANGE_IN < event->button.y) && (event->button.y < preview_selection[3] + SELECTION_RANGE_OUT) ) ) /* bottom */
+ {
+ cursornr = GDK_BOTTOM_LEFT_CORNER;
+ }
+ else if ( ( (preview_selection[2] - SELECTION_RANGE_IN < event->button.x) && (event->button.x < preview_selection[2] + SELECTION_RANGE_OUT) ) && /* right */
+ ( (preview_selection[3] - SELECTION_RANGE_IN < event->button.y) && (event->button.y < preview_selection[3] + SELECTION_RANGE_OUT) ) ) /* bottom */
+ {
+ cursornr = GDK_BOTTOM_RIGHT_CORNER;
+ }
+
+ if (cursornr != p->cursornr)
+ {
+ cursor = gdk_cursor_new(cursornr); /* set curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = cursornr;
+ }
+ break;
+
+ case 512: /* middle button */
+ case 1024: /* right button */
+ if (p->selection_drag)
+ {
+ int dx, dy;
+
+ dx = p->selection_xpos - event->motion.x;
+ dy = p->selection_ypos - event->motion.y;
+
+ p->selection_xpos = event->motion.x;
+ p->selection_ypos = event->motion.y;
+
+ p->selection.active = TRUE;
+ p->selection.coordinate[0] -= dx / xscale;
+ p->selection.coordinate[1] -= dy / yscale;
+ p->selection.coordinate[2] -= dx / xscale;
+ p->selection.coordinate[3] -= dy / yscale;
+
+ preview_bound_selection(p);
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+
+ if ((preferences.gtk_update_policy == GTK_UPDATE_CONTINUOUS) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ else if ((preferences.gtk_update_policy == GTK_UPDATE_DELAYED) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ }
+ break;
+
+ default:
+ if ( ( (preview_selection[0] - SELECTION_RANGE_OUT < event->button.x) && (event->button.x < preview_selection[0] + SELECTION_RANGE_IN) ) && /* left */
+ ( (preview_selection[1] - SELECTION_RANGE_OUT < event->button.y) && (event->button.y < preview_selection[1] + SELECTION_RANGE_IN) ) ) /* top */
+ {
+ cursornr = GDK_TOP_LEFT_CORNER;
+ }
+ else if ( ( (preview_selection[2] - SELECTION_RANGE_IN < event->button.x) && (event->button.x < preview_selection[2] + SELECTION_RANGE_OUT) ) && /* right */
+ ( (preview_selection[1] - SELECTION_RANGE_OUT < event->button.y) && (event->button.y < preview_selection[1] + SELECTION_RANGE_IN) ) ) /* top */
+ {
+ cursornr = GDK_TOP_RIGHT_CORNER;
+ }
+ else if ( ( (preview_selection[0] - SELECTION_RANGE_OUT < event->button.x) && (event->button.x < preview_selection[0] + SELECTION_RANGE_IN) ) && /* left */
+ ( (preview_selection[3] - SELECTION_RANGE_IN < event->button.y) && (event->button.y < preview_selection[3] + SELECTION_RANGE_OUT) ) ) /* bottom */
+ {
+ cursornr = GDK_BOTTOM_LEFT_CORNER;
+ }
+ else if ( ( (preview_selection[2] - SELECTION_RANGE_IN < event->button.x) && (event->button.x < preview_selection[2] + SELECTION_RANGE_OUT) ) && /* right */
+ ( (preview_selection[3] - SELECTION_RANGE_IN < event->button.y) && (event->button.y < preview_selection[3] + SELECTION_RANGE_OUT) ) ) /* bottom */
+ {
+ cursornr = GDK_BOTTOM_RIGHT_CORNER;
+ }
+ else
+ {
+ cursornr = XSANE_CURSOR_PREVIEW;
+ }
+
+ if ((cursornr != p->cursornr) && (p->cursornr != -1))
+ {
+ cursor = gdk_cursor_new(cursornr); /* set curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = cursornr;
+ }
+ break;
+ }
+ break;
+
+ default:
+#if 0
+ fprintf(stderr, "preview_event_handler: unhandled event type %d\n", event->type);
+#endif
+ break;
+ }
+ }
+
+ while (gtk_events_pending()) /* make sure all selection draw is done now */
+ {
+ gtk_main_iteration();
+ }
+
+ event_count--;
+
+ return FALSE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_start_button_clicked(GtkWidget *widget, gpointer data)
+{
+ preview_scan(data);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_cancel_button_clicked(GtkWidget *widget, gpointer data)
+{
+ preview_scan_done(data);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+Preview *preview_new(GSGDialog *dialog)
+{
+ static int first_time = 1;
+ GtkWidget *table, *frame;
+ GtkSignalFunc signal_func;
+ GtkWidgetClass *class;
+ GtkBox *vbox, *hbox;
+ GdkCursor *cursor;
+ GtkWidget *preset_area_option_menu, *preset_area_menu, *preset_area_item;
+ Preview *p;
+ int i;
+ char buf[256];
+
+ p = malloc(sizeof(*p));
+ if (!p)
+ {
+ return 0;
+ }
+ memset(p, 0, sizeof(*p));
+
+ p->mode = MODE_NORMAL; /* no pipette functions etc */
+ p->dialog = dialog;
+ p->input_tag = -1;
+
+ if (first_time)
+ {
+ first_time = 0;
+ gtk_preview_set_gamma(1.0);
+ gtk_preview_set_install_cmap(preferences.preview_own_cmap);
+ }
+
+ p->preset_width = INF; /* use full scanarea */
+ p->preset_height = INF; /* use full scanarea */
+
+ p->maximum_output_width = INF; /* full output with */
+ p->maximum_output_height = INF; /* full output height */
+
+#ifndef XSERVER_WITH_BUGGY_VISUALS
+ gtk_widget_push_visual(gtk_preview_get_visual());
+#endif
+ gtk_widget_push_colormap(gtk_preview_get_cmap());
+
+ snprintf(buf, sizeof(buf), "%s %s", WINDOW_PREVIEW, device_text);
+ p->top = gtk_dialog_new();
+ gtk_window_set_title(GTK_WINDOW(p->top), buf);
+ vbox = GTK_BOX(GTK_DIALOG(p->top)->vbox);
+ hbox = GTK_BOX(GTK_DIALOG(p->top)->action_area);
+
+ xsane_set_window_icon(p->top, 0);
+
+ /* top hbox for pipette buttons */
+ p->button_box = gtk_hbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(p->button_box), 4);
+ gtk_box_pack_start(GTK_BOX(vbox), p->button_box, FALSE, FALSE, 0);
+
+ /* White, gray and black pipette button */
+ p->pipette_white = xsane_button_new_with_pixmap(p->button_box, pipette_white_xpm, DESC_PIPETTE_WHITE, (GtkSignalFunc) preview_pipette_white, p);
+ p->pipette_gray = xsane_button_new_with_pixmap(p->button_box, pipette_gray_xpm, DESC_PIPETTE_GRAY, (GtkSignalFunc) preview_pipette_gray, p);
+ p->pipette_black = xsane_button_new_with_pixmap(p->button_box, pipette_black_xpm, DESC_PIPETTE_BLACK, (GtkSignalFunc) preview_pipette_black, p);
+
+ /* Zoom not, zoom out and zoom in button */
+ p->zoom_not = xsane_button_new_with_pixmap(p->button_box, zoom_not_xpm, DESC_ZOOM_FULL, (GtkSignalFunc) preview_zoom_not, p);
+ p->zoom_out = xsane_button_new_with_pixmap(p->button_box, zoom_out_xpm, DESC_ZOOM_OUT, (GtkSignalFunc) preview_zoom_out, p);
+ p->zoom_in = xsane_button_new_with_pixmap(p->button_box, zoom_in_xpm, DESC_ZOOM_IN, (GtkSignalFunc) preview_zoom_in, p);
+ p->zoom_undo = xsane_button_new_with_pixmap(p->button_box, zoom_undo_xpm, DESC_ZOOM_UNDO, (GtkSignalFunc) preview_zoom_undo, p);
+
+ gtk_widget_set_sensitive(p->zoom_not, FALSE); /* no zoom at this point, so no zoom not */
+ gtk_widget_set_sensitive(p->zoom_out, FALSE); /* no zoom at this point, so no zoom out */
+ gtk_widget_set_sensitive(p->zoom_undo, FALSE); /* no zoom at this point, so no zoom undo */
+
+
+
+ xsane_button_new_with_pixmap(p->button_box, full_preview_area_xpm, DESC_FULL_PREVIEW_AREA,
+ (GtkSignalFunc) preview_full_preview_area, p);
+
+ /* select maximum scanarea */
+ preset_area_menu = gtk_menu_new();
+
+ for (i = 0; i < PRESET_AREA_ITEMS; ++i)
+ {
+ preset_area_item = gtk_menu_item_new_with_label(preset_area[i].name);
+ gtk_container_add(GTK_CONTAINER(preset_area_menu), preset_area_item);
+ gtk_signal_connect(GTK_OBJECT(preset_area_item), "activate", (GtkSignalFunc) preview_preset_area_callback, p);
+ gtk_object_set_data(GTK_OBJECT(preset_area_item), "Selection", (void *) i);
+
+ gtk_widget_show(preset_area_item);
+ }
+
+ preset_area_option_menu = gtk_option_menu_new();
+ gtk_box_pack_start(GTK_BOX(p->button_box), preset_area_option_menu, FALSE, FALSE, 2);
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(preset_area_option_menu), preset_area_menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(preset_area_option_menu), 0); /* full area */
+/* xsane_back_gtk_set_tooltip(tooltips, preset_area_option_menu, desc); */
+
+ gtk_widget_show(preset_area_option_menu);
+ p->preset_area_option_menu = preset_area_option_menu;
+
+ gtk_widget_show(p->button_box);
+
+
+
+ /* construct the preview area (table with sliders & preview window) */
+ table = gtk_table_new(2, 2, /* homogeneous */ FALSE);
+ gtk_table_set_col_spacing(GTK_TABLE(table), 0, 1);
+ gtk_table_set_row_spacing(GTK_TABLE(table), 0, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(table), 2);
+ gtk_box_pack_start(vbox, table, /* expand */ TRUE, /* fill */ TRUE, /* padding */ 0);
+
+ /* the empty box in the top-left corner */
+ frame = gtk_frame_new(/* label */ 0);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
+ gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+
+ /* the horizontal ruler */
+ p->hruler = gtk_hruler_new();
+ gtk_table_attach(GTK_TABLE(table), p->hruler, 1, 2, 0, 1, GTK_FILL, 0, 0, 0);
+
+ /* the vertical ruler */
+ p->vruler = gtk_vruler_new();
+ gtk_table_attach(GTK_TABLE(table), p->vruler, 0, 1, 1, 2, 0, GTK_FILL, 0, 0);
+
+ /* the preview area */
+
+ p->window = gtk_preview_new(GTK_PREVIEW_COLOR);
+ gtk_preview_set_expand(GTK_PREVIEW(p->window), TRUE);
+ gtk_widget_set_events(p->window,
+ GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+ gtk_signal_connect(GTK_OBJECT(p->window), "event", (GtkSignalFunc) preview_event_handler, p);
+ gtk_signal_connect_after(GTK_OBJECT(p->window), "expose_event", (GtkSignalFunc) preview_expose_handler, p);
+ gtk_signal_connect_after(GTK_OBJECT(p->window), "size_allocate", (GtkSignalFunc) preview_area_resize, 0);
+ gtk_object_set_data(GTK_OBJECT(p->window), "PreviewPointer", p);
+
+ /* Connect the motion-notify events of the preview area with the rulers. Nifty stuff! */
+
+ class = GTK_WIDGET_CLASS(GTK_OBJECT(p->hruler)->klass);
+ signal_func = (GtkSignalFunc) class->motion_notify_event;
+ gtk_signal_connect_object(GTK_OBJECT(p->window), "motion_notify_event", signal_func, GTK_OBJECT(p->hruler));
+
+ class = GTK_WIDGET_CLASS(GTK_OBJECT(p->vruler)->klass);
+ signal_func = (GtkSignalFunc) class->motion_notify_event;
+ gtk_signal_connect_object(GTK_OBJECT(p->window), "motion_notify_event", signal_func, GTK_OBJECT(p->vruler));
+
+ p->viewport = gtk_frame_new(/* label */ 0);
+ gtk_frame_set_shadow_type(GTK_FRAME(p->viewport), GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(p->viewport), p->window);
+
+ gtk_table_attach(GTK_TABLE(table), p->viewport, 1, 2, 1, 2,
+ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
+
+ preview_update_surface(p, 0);
+
+ /* fill in action area: */
+
+ /* Start button */
+ p->start = gtk_button_new_with_label(BUTTON_PREVIEW_ACQUIRE);
+ gtk_signal_connect(GTK_OBJECT(p->start), "clicked", (GtkSignalFunc) preview_start_button_clicked, p);
+ gtk_box_pack_start(GTK_BOX(hbox), p->start, TRUE, TRUE, 0);
+
+ /* Cancel button */
+ p->cancel = gtk_button_new_with_label(BUTTON_PREVIEW_CANCEL);
+ gtk_signal_connect(GTK_OBJECT(p->cancel), "clicked", (GtkSignalFunc) preview_cancel_button_clicked, p);
+ gtk_box_pack_start(GTK_BOX(hbox), p->cancel, TRUE, TRUE, 0);
+ gtk_widget_set_sensitive(p->cancel, FALSE);
+
+ gtk_widget_show(p->cancel);
+ gtk_widget_show(p->start);
+ gtk_widget_show(p->viewport);
+ gtk_widget_show(p->window);
+ gtk_widget_show(p->hruler);
+ gtk_widget_show(p->vruler);
+ gtk_widget_show(frame);
+ gtk_widget_show(table);
+ gtk_widget_show(p->top);
+
+ cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW); /* set default curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = XSANE_CURSOR_PREVIEW;
+
+ gtk_widget_pop_colormap();
+#ifndef XSERVER_WITH_BUGGY_VISUALS
+ gtk_widget_pop_visual();
+#endif
+ return p;
+}
+
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_area_correct(Preview *p)
+{
+ float width, height, max_width, max_height;
+
+ width = p->preview_width;
+ height = p->preview_height;
+ max_width = p->preview_window_width;
+ max_height = p->preview_window_height;
+
+ width = max_width;
+ height = width / p->aspect;
+
+ if (height > max_height)
+ {
+ height = max_height;
+ width = height * p->aspect;
+ }
+
+ p->preview_width = width + 0.5;
+ p->preview_height = height + 0.5;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_update_surface(Preview *p, int surface_changed)
+{
+ float val;
+ float width, height;
+ float max_width, max_height;
+ float preset_width, preset_height;
+ const SANE_Option_Descriptor *opt;
+ int i;
+ SANE_Value_Type type;
+ SANE_Unit unit;
+ double min, max;
+
+ unit = SANE_UNIT_PIXEL;
+ type = SANE_TYPE_INT;
+
+ for (i = 0; i < 4; ++i) /* test if surface (max vals of scanarea) has changed */
+ {
+/* val = (i & 2) ? INF : -INF; */
+ val = (i & 2) ? INF : 0;
+
+ if (p->dialog->well_known.coord[i] > 0)
+ {
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.coord[i]);
+ assert(opt->unit == SANE_UNIT_PIXEL || opt->unit == SANE_UNIT_MM);
+ unit = opt->unit;
+ type = opt->type;
+
+ xsane_get_bounds(opt, &min, &max);
+
+ if (i & 2)
+ {
+ val = max;
+ }
+ else
+ {
+ val = min;
+ }
+ }
+
+ if (p->max_scanner_surface[i] != val)
+ {
+ surface_changed = 2;
+ p->max_scanner_surface[i] = val;
+ }
+ }
+
+ if (surface_changed == 2) /* redefine all surface subparts */
+ {
+ for (i = 0; i < 4; i++)
+ {
+ val = p->max_scanner_surface[i];
+ p->scanner_surface[i] = val;
+ p->surface[i] = val;
+ p->image_surface[i] = val;
+ }
+ }
+
+ max_width = p->max_scanner_surface[xsane_back_gtk_BR_X] - p->max_scanner_surface[xsane_back_gtk_TL_X];
+ max_height = p->max_scanner_surface[xsane_back_gtk_BR_Y] - p->max_scanner_surface[xsane_back_gtk_TL_Y];
+
+ width = p->scanner_surface[xsane_back_gtk_BR_X] - p->scanner_surface[xsane_back_gtk_TL_X];
+ height = p->scanner_surface[xsane_back_gtk_BR_Y] - p->scanner_surface[xsane_back_gtk_TL_Y];
+
+ preset_width = p->preset_width;
+ preset_height = p->preset_height;
+
+ if (preset_width > max_width)
+ {
+ preset_width = max_width;
+ }
+
+ if (preset_height > max_height)
+ {
+ preset_height = max_height;
+ }
+
+ if ( (width != preset_width) || (height != preset_height) )
+ {
+ p->scanner_surface[xsane_back_gtk_TL_X] = p->scanner_surface[xsane_back_gtk_TL_X];
+ p->surface[xsane_back_gtk_TL_X] = p->scanner_surface[xsane_back_gtk_TL_X];
+ p->image_surface[xsane_back_gtk_TL_X] = p->scanner_surface[xsane_back_gtk_TL_X];
+
+ p->scanner_surface[xsane_back_gtk_BR_X] = p->scanner_surface[xsane_back_gtk_TL_X] + preset_width;
+ p->surface[xsane_back_gtk_BR_X] = p->scanner_surface[xsane_back_gtk_TL_X] + preset_width;
+ p->image_surface[xsane_back_gtk_BR_X] = p->scanner_surface[xsane_back_gtk_TL_X] + preset_width;
+
+ p->scanner_surface[xsane_back_gtk_TL_Y] = p->scanner_surface[xsane_back_gtk_TL_Y];
+ p->surface[xsane_back_gtk_TL_Y] = p->scanner_surface[xsane_back_gtk_TL_Y];
+ p->image_surface[xsane_back_gtk_TL_Y] = p->scanner_surface[xsane_back_gtk_TL_Y];
+
+ p->scanner_surface[xsane_back_gtk_BR_Y] = p->scanner_surface[xsane_back_gtk_TL_Y] + preset_height;
+ p->surface[xsane_back_gtk_BR_Y] = p->scanner_surface[xsane_back_gtk_TL_Y] + preset_height;
+ p->image_surface[xsane_back_gtk_BR_Y] = p->scanner_surface[xsane_back_gtk_TL_Y] + preset_height;
+
+ surface_changed = 1;
+ }
+
+ if (p->surface_unit != unit)
+ {
+ surface_changed = 1;
+ p->surface_unit = unit;
+ }
+
+ if (p->surface_unit == SANE_UNIT_MM)
+ {
+ gtk_widget_set_sensitive(p->preset_area_option_menu, TRUE); /* enable preset area */
+ }
+ else
+ {
+ gtk_widget_set_sensitive(p->preset_area_option_menu, FALSE); /* disable preset area */
+ }
+
+ if (p->surface_type != type)
+ {
+ surface_changed = 1;
+ p->surface_type = type;
+ }
+
+ if (surface_changed)
+ {
+ /* guess the initial preview window size: */
+
+ width = p->surface[xsane_back_gtk_BR_X] - p->surface[xsane_back_gtk_TL_X];
+ height = p->surface[xsane_back_gtk_BR_Y] - p->surface[xsane_back_gtk_TL_Y];
+
+ if (p->surface_type == SANE_TYPE_INT)
+ {
+ width += 1.0;
+ height += 1.0;
+ }
+ else
+ {
+ width += SANE_UNFIX(1.0);
+ height += SANE_UNFIX(1.0);
+ }
+
+ assert(width > 0.0 && height > 0.0);
+
+ if (width >= INF || height >= INF)
+ {
+ p->aspect = 1.0;
+ }
+ else
+ {
+ p->aspect = width/height;
+ }
+ }
+ else if ( (p->image_height) && (p->image_width) )
+ {
+ p->aspect = p->image_width/(float) p->image_height;
+ }
+
+ if ( (surface_changed) && (p->preview_window_width == 0) )
+ {
+ p->preview_window_width = 0.5 * gdk_screen_width();
+ p->preview_window_height = 0.5 * gdk_screen_height();
+ }
+
+ preview_area_correct(p);
+
+ if (surface_changed)
+ {
+ gtk_widget_set_usize(GTK_WIDGET(p->window), p->preview_width, p->preview_height);
+ /* preview_area_resize is automatically called by signal handler */
+
+ preview_bound_selection(p); /* make sure selection is not larger than surface */
+ preview_restore_image(p); /* draw selected surface of the image */
+ }
+ else
+ {
+ preview_update_selection(p);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_scan(Preview *p)
+{
+ double min, max, swidth, sheight, width, height, dpi = 0;
+ const SANE_Option_Descriptor *opt;
+ gint gwidth, gheight;
+ int i;
+
+ xsane.block_update_param = TRUE; /* do not change parameters each time */
+
+ preview_save_option(p, p->dialog->well_known.dpi, &p->saved_dpi, &p->saved_dpi_valid);
+ preview_save_option(p, p->dialog->well_known.dpi_x, &p->saved_dpi_x, &p->saved_dpi_x_valid);
+ preview_save_option(p, p->dialog->well_known.dpi_y, &p->saved_dpi_y, &p->saved_dpi_y_valid);
+
+ for (i = 0; i < 4; ++i)
+ {
+ preview_save_option(p, p->dialog->well_known.coord[i], &p->saved_coord[i], p->saved_coord_valid + i);
+ }
+ preview_save_option(p, p->dialog->well_known.bit_depth, &p->saved_bit_depth, &p->saved_bit_depth_valid);
+
+ /* determine dpi, if necessary: */
+
+ if (p->dialog->well_known.dpi > 0)
+ {
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.dpi);
+
+ gwidth = p->preview_width;
+ gheight = p->preview_height;
+
+ height = gheight;
+ width = height * p->aspect;
+
+ if (width > gwidth)
+ {
+ width = gwidth;
+ height = width / p->aspect;
+ }
+
+ swidth = (p->surface[xsane_back_gtk_BR_X] - p->surface[xsane_back_gtk_TL_X]);
+
+ if (swidth < INF)
+ {
+ dpi = MM_PER_INCH * width/swidth;
+ }
+ else
+ {
+ sheight = (p->surface[xsane_back_gtk_BR_Y] - p->surface[xsane_back_gtk_TL_Y]);
+ if (sheight < INF)
+ {
+ dpi = MM_PER_INCH * height/sheight;
+ }
+ else
+ {
+ dpi = 18.0;
+ }
+ }
+
+ xsane_get_bounds(opt, &min, &max);
+
+ if (dpi < min)
+ {
+ dpi = min;
+ }
+
+ if (dpi > max)
+ {
+ dpi = max;
+ }
+
+ xsane_set_resolution(p->dialog->well_known.dpi, dpi); /* set resolution to dpi or next higher value that is available */
+ xsane_set_resolution(p->dialog->well_known.dpi_x, dpi); /* set resolution to dpi or next higher value that is available */
+ xsane_set_resolution(p->dialog->well_known.dpi_y, dpi); /* set resolution to dpi or next higher value that is available */
+ }
+
+ /* set the scan window (necessary since backends may default to non-maximum size): */
+
+ for (i = 0; i < 4; ++i)
+ {
+ preview_set_option_float(p, p->dialog->well_known.coord[i], p->surface[i]);
+ }
+
+ preview_set_option_bool(p, p->dialog->well_known.preview, SANE_TRUE);
+
+ if ( (p->saved_bit_depth > 8) && (p->saved_bit_depth_valid) ) /* don't scan with more than 8bpp */
+ {
+ preview_set_option_int(p, p->dialog->well_known.bit_depth, 8);
+ }
+
+ xsane.block_update_param = FALSE;
+
+ /* OK, all set to go */
+ preview_scan_start(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_save_image_file(Preview *p, FILE *out)
+{
+ if (out)
+ {
+ /* always save it as a PPM image: */
+ fprintf(out, "P6\n# surface: %g %g %g %g %u %u\n%d %d\n255\n",
+ p->surface[0], p->surface[1], p->surface[2], p->surface[3],
+ p->surface_type, p->surface_unit, p->image_width, p->image_height);
+
+ fwrite(p->image_data_raw, 3, p->image_width*p->image_height, out);
+ fclose(out);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_save_image(Preview *p)
+{
+ char filename[PATH_MAX];
+ FILE *out;
+ int status;
+
+ if (!p->image_data_enh)
+ {
+ return;
+ }
+
+ if ( GROSSLY_EQUAL(p->max_scanner_surface[0], p->surface[0]) && /* full device surface */
+ GROSSLY_EQUAL(p->max_scanner_surface[1], p->surface[1]) &&
+ GROSSLY_EQUAL(p->max_scanner_surface[2], p->surface[2]) &&
+ GROSSLY_EQUAL(p->max_scanner_surface[3], p->surface[3]) )
+ {
+ status = preview_make_image_path(p, sizeof(filename), filename, 0);
+ }
+ else if ( GROSSLY_EQUAL(p->scanner_surface[0], p->surface[0]) && /* user defined surface */
+ GROSSLY_EQUAL(p->scanner_surface[1], p->surface[1]) &&
+ GROSSLY_EQUAL(p->scanner_surface[2], p->surface[2]) &&
+ GROSSLY_EQUAL(p->scanner_surface[3], p->surface[3]) )
+ {
+ status = preview_make_image_path(p, sizeof(filename), filename, 1);
+ }
+ else /* zoom area */
+ {
+ status = preview_make_image_path(p, sizeof(filename), filename, 2);
+ }
+
+ if (status >= 0)
+ {
+ /* save preview image */
+ remove(filename); /* remove existing preview */
+ umask(0177); /* creare temporary file with "-rw-------" permissions */
+ out = fopen(filename, "w");
+ umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
+
+ preview_save_image_file(p, out);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_destroy(Preview *p)
+{
+ int level;
+ int status;
+ char filename[PATH_MAX];
+
+ if (p->scanning)
+ {
+ preview_scan_done(p); /* don't save partial window */
+ }
+ else
+ {
+ preview_save_image(p);
+ }
+
+ if (!preferences.preserve_preview)
+ {
+ for(level = 0; level <= 2; level++)
+ {
+ status = preview_make_image_path(p, sizeof(filename), filename, level);
+ if (status >= 0)
+ {
+ remove(filename); /* remove existing preview */
+ }
+ }
+ }
+
+ if (p->image_data_enh)
+ {
+ free(p->image_data_enh);
+ p->image_data_enh = 0;
+ }
+
+ if (p->image_data_raw)
+ {
+ free(p->image_data_raw);
+ p->image_data_raw = 0;
+ }
+
+ if (p->preview_row)
+ {
+ free(p->preview_row);
+ p->preview_row = 0;
+ }
+
+ if (p->gc_selection)
+ {
+ gdk_gc_destroy(p->gc_selection);
+ }
+
+ if (p->gc_selection_maximum)
+ {
+ gdk_gc_destroy(p->gc_selection_maximum);
+ }
+
+ if (p->top)
+ {
+ gtk_widget_destroy(p->top);
+ }
+ free(p);
+
+ p = 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_zoom_not(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ int i;
+
+ for (i=0; i<4; i++)
+ {
+ p->surface[i] = p->scanner_surface[i];
+ }
+
+ preview_update_surface(p, 1);
+ gtk_widget_set_sensitive(p->zoom_not, FALSE); /* forbid unzoom */
+ gtk_widget_set_sensitive(p->zoom_out, FALSE); /* forbid zoom out */
+ gtk_widget_set_sensitive(p->zoom_undo,TRUE); /* allow zoom undo */
+
+ while (gtk_events_pending()) /* make sure all selection draw is done now */
+ {
+ gtk_main_iteration();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_zoom_out(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ int i;
+ float delta_width = (p->surface[2] - p->surface[0]) * 0.2;
+ float delta_height = (p->surface[3] - p->surface[1]) * 0.2;
+
+ for (i=0; i<4; i++)
+ {
+ p->old_surface[i] = p->surface[i];
+ }
+
+ p->surface[0] -= delta_width;
+ p->surface[1] -= delta_height;
+ p->surface[2] += delta_width;
+ p->surface[3] += delta_height;
+
+ if (p->surface[0] < p->scanner_surface[0])
+ {
+ p->surface[0] = p->scanner_surface[0];
+ }
+
+ if (p->surface[1] < p->scanner_surface[1])
+ {
+ p->surface[1] = p->scanner_surface[1];
+ }
+
+ if (p->surface[2] > p->scanner_surface[2])
+ {
+ p->surface[2] = p->scanner_surface[2];
+ }
+
+ if (p->surface[3] > p->scanner_surface[3])
+ {
+ p->surface[3] = p->scanner_surface[3];
+ }
+
+ preview_update_surface(p, 1);
+ gtk_widget_set_sensitive(p->zoom_not, TRUE); /* allow unzoom */
+ gtk_widget_set_sensitive(p->zoom_undo,TRUE); /* allow zoom undo */
+
+ while (gtk_events_pending()) /* make sure all selection draw is done now */
+ {
+ gtk_main_iteration();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_zoom_in(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ const SANE_Option_Descriptor *opt;
+ SANE_Status status;
+ SANE_Word val;
+ int i, optnum;
+
+ for (i=0; i<4; i++)
+ {
+ p->old_surface[i] = p->surface[i];
+
+ optnum = p->dialog->well_known.coord[i];
+ if (optnum > 0)
+ {
+ opt = sane_get_option_descriptor(p->dialog->dev, optnum);
+ status = sane_control_option(p->dialog->dev, optnum, SANE_ACTION_GET_VALUE, &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ continue;
+ }
+
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ p->surface[i] = SANE_UNFIX(val);
+ }
+ else
+ {
+ p->surface[i] = val;
+ }
+ }
+ }
+
+ preview_update_surface(p, 1);
+ gtk_widget_set_sensitive(p->zoom_not, TRUE); /* allow unzoom */
+ gtk_widget_set_sensitive(p->zoom_out, TRUE); /* allow zoom out */
+ gtk_widget_set_sensitive(p->zoom_undo,TRUE); /* allow zoom undo */
+
+ while (gtk_events_pending()) /* make sure all selection draw is done now */
+ {
+ gtk_main_iteration();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_zoom_undo(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ int i;
+
+ for (i=0; i<4; i++)
+ {
+ p->surface[i] = p->old_surface[i];
+ }
+
+ preview_update_surface(p, 1);
+ gtk_widget_set_sensitive(p->zoom_not, TRUE); /* allow unzoom */
+ gtk_widget_set_sensitive(p->zoom_out, TRUE); /* allow zoom out */
+ gtk_widget_set_sensitive(p->zoom_undo, FALSE); /* forbid zoom undo */
+
+ while (gtk_events_pending()) /* make sure all selection draw is done now */
+ {
+ gtk_main_iteration();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_get_color(Preview *p, int x, int y, int *red, int *green, int *blue)
+{
+ int image_x, image_y;
+ float xscale_p2i, yscale_p2i;
+ int offset;
+
+ if (p->image_data_raw)
+ {
+ preview_get_scale_preview_to_image(p, &xscale_p2i, &yscale_p2i);
+
+ image_x = x * xscale_p2i;
+ image_y = y * yscale_p2i;
+
+ offset = 3 * (image_y * p->image_width + image_x);
+
+ if (!xsane.negative) /* positive */
+ {
+ *red = p->image_data_raw[offset ];
+ *green = p->image_data_raw[offset + 1];
+ *blue = p->image_data_raw[offset + 2];
+ }
+ else /* negative */
+ {
+ *red = 255 - p->image_data_raw[offset ];
+ *green = 255 - p->image_data_raw[offset + 1];
+ *blue = 255 - p->image_data_raw[offset + 2];
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_pipette_white(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ GdkCursor *cursor;
+ GdkColor fg;
+ GdkColor bg;
+ GdkPixmap *pixmap;
+ GdkPixmap *mask;
+
+ p->mode = MODE_PIPETTE_WHITE;
+
+ pixmap = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_white, CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+ mask = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_mask, CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+
+ bg.red = 65535;
+ bg.green = 65535;
+ bg.blue = 65535;
+
+ cursor = gdk_cursor_new_from_pixmap(pixmap, mask, &fg, &bg, CURSOR_PIPETTE_HOT_X, CURSOR_PIPETTE_HOT_Y);
+
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = -1;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_pipette_gray(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ GdkCursor *cursor;
+ GdkColor fg;
+ GdkColor bg;
+ GdkPixmap *pixmap;
+ GdkPixmap *mask;
+
+ p->mode = MODE_PIPETTE_GRAY;
+
+ pixmap = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_gray, CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+ mask = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_mask, CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+
+ bg.red = 65535;
+ bg.green = 65535;
+ bg.blue = 65535;
+
+ cursor = gdk_cursor_new_from_pixmap(pixmap, mask, &fg, &bg, CURSOR_PIPETTE_HOT_X, CURSOR_PIPETTE_HOT_Y);
+
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = -1;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_pipette_black(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ GdkCursor *cursor;
+ GdkColor fg;
+ GdkColor bg;
+ GdkPixmap *pixmap;
+ GdkPixmap *mask;
+
+ p->mode = MODE_PIPETTE_BLACK;
+
+ pixmap = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_black, CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+ mask = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_mask , CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+
+ bg.red = 65535;
+ bg.green = 65535;
+ bg.blue = 65535;
+
+ cursor = gdk_cursor_new_from_pixmap(pixmap, mask, &fg, &bg, CURSOR_PIPETTE_HOT_X, CURSOR_PIPETTE_HOT_Y);
+
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = -1;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_full_preview_area(GtkWidget *widget, gpointer call_data)
+{
+ Preview *p = call_data;
+ int i;
+
+ p->selection.active = TRUE;
+
+ for (i=0; i<4; i++)
+ {
+ p->selection.coordinate[i] = p->surface[i];
+ }
+
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+ preview_establish_selection(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_preset_area_callback(GtkWidget *widget, gpointer call_data)
+{
+ Preview *p = call_data;
+ int selection;
+
+ selection = (int) gtk_object_get_data(GTK_OBJECT(widget), "Selection");
+
+ p->preset_width = preset_area[selection].width;
+ p->preset_height = preset_area[selection].height;
+
+ preview_update_surface(p, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_do_gamma_correction(Preview *p)
+{
+ int x,y;
+ int offset;
+
+ if (p->image_data_raw)
+ {
+ if ((p->image_data_raw) && (p->params.depth > 1) && (preview_gamma_data_red))
+ {
+ for (y=0; y < p->image_height; y++)
+ {
+ for (x=0; x < p->image_width; x++)
+ {
+ offset = 3 * (y * p->image_width + x);
+ p->image_data_enh[offset ] = preview_gamma_data_red [p->image_data_raw[offset ]];
+ p->image_data_enh[offset + 1] = preview_gamma_data_green[p->image_data_raw[offset + 1]];
+ p->image_data_enh[offset + 2] = preview_gamma_data_blue [p->image_data_raw[offset + 2]];
+ }
+ }
+ }
+
+ preview_display_partial_image(p);
+
+ p->previous_selection.active = FALSE; /* previous selection is not drawn */
+ p->previous_selection_maximum.active = FALSE;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_calculate_histogram(Preview *p,
+ SANE_Int *count_raw, SANE_Int *count_raw_red, SANE_Int *count_raw_green, SANE_Int *count_raw_blue,
+ SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue)
+{
+ int x, y;
+ int offset;
+ SANE_Int red_raw, green_raw, blue_raw;
+ SANE_Int red, green, blue;
+ SANE_Int min_x, max_x, min_y, max_y;
+ float xscale, yscale;
+
+ preview_get_scale_device_to_image(p, &xscale, &yscale);
+
+ min_x = (p->selection.coordinate[0] - p->surface[0]) * xscale;
+ min_y = (p->selection.coordinate[1] - p->surface[1]) * yscale;
+ max_x = (p->selection.coordinate[2] - p->surface[0]) * xscale;
+ max_y = (p->selection.coordinate[3] - p->surface[1]) * yscale;
+
+ if (min_x < 0)
+ {
+ min_x = 0;
+ }
+
+ if (max_x >= p->image_width)
+ {
+ max_x = p->image_width-1;
+ }
+
+ if (min_y < 0)
+ {
+ min_y = 0;
+ }
+
+ if (max_y >= p->image_height)
+ {
+ max_y = p->image_height-1;
+ }
+
+ if ((p->image_data_raw) && (p->params.depth > 1) && (preview_gamma_data_red))
+ {
+ for (y = min_y; y <= max_y; y++)
+ {
+ for (x = min_x; x <= max_x; x++)
+ {
+ offset = 3 * (y * p->image_width + x);
+ red_raw = p->image_data_raw[offset ];
+ green_raw = p->image_data_raw[offset + 1];
+ blue_raw = p->image_data_raw[offset + 2];
+
+ red = histogram_gamma_data_red [red_raw];
+ green = histogram_gamma_data_green[green_raw];
+ blue = histogram_gamma_data_blue [blue_raw];
+
+/* count_raw [(int) sqrt((red_raw*red_raw + green_raw*green_raw + blue_raw*blue_raw)/3.0)]++; */
+ count_raw [(int) ((red_raw + green_raw + blue_raw)/3)]++;
+ count_raw_red [red_raw]++;
+ count_raw_green[green_raw]++;
+ count_raw_blue [blue_raw]++;
+
+/* count [(int) sqrt((red*red + green*green + blue*blue)/3.0)]++; */
+ count [(int) ((red + green + blue)/3)]++;
+ count_red [red]++;
+ count_green[green]++;
+ count_blue [blue]++;
+ }
+ }
+ }
+ else /* no preview image => all colors = 1 */
+ {
+ int i;
+
+ for (i = 1; i <= 254; i++)
+ {
+ count_raw [i] = 0;
+ count_raw_red [i] = 0;
+ count_raw_green[i] = 0;
+ count_raw_blue [i] = 0;
+
+ count [i] = 0;
+ count_red [i] = 0;
+ count_green[i] = 0;
+ count_blue [i] = 0;
+ }
+
+ count_raw [0] = 10;
+ count_raw_red [0] = 10;
+ count_raw_green[0] = 10;
+ count_raw_blue [0] = 10;
+
+ count [0] = 10;
+ count_red [0] = 10;
+ count_green[0] = 10;
+ count_blue [0] = 10;
+
+ count_raw [255] = 10;
+ count_raw_red [255] = 10;
+ count_raw_green[255] = 10;
+ count_raw_blue [255] = 10;
+
+ count [255] = 10;
+ count_red [255] = 10;
+ count_green[255] = 10;
+ count_blue [255] = 10;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_gamma_correction(Preview *p,
+ SANE_Int *gamma_red, SANE_Int *gamma_green, SANE_Int *gamma_blue,
+ SANE_Int *gamma_red_hist, SANE_Int *gamma_green_hist, SANE_Int *gamma_blue_hist)
+{
+ preview_gamma_data_red = gamma_red;
+ preview_gamma_data_green = gamma_green;
+ preview_gamma_data_blue = gamma_blue;
+
+ histogram_gamma_data_red = gamma_red_hist;
+ histogram_gamma_data_green = gamma_green_hist;
+ histogram_gamma_data_blue = gamma_blue_hist;
+
+ preview_do_gamma_correction(p);
+ preview_draw_selection(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_area_resize(GtkWidget *widget)
+{
+ float min_x, max_x, delta_x;
+ float min_y, max_y, delta_y;
+ float xscale, yscale, f;
+ Preview *p;
+
+ p = gtk_object_get_data(GTK_OBJECT(widget), "PreviewPointer");
+
+ p->preview_window_width = widget->allocation.width;
+ p->preview_window_height = widget->allocation.height;
+
+ p->preview_width = widget->allocation.width;
+ p->preview_height = widget->allocation.height;
+
+ preview_area_correct(p); /* set preview dimensions (with right aspect) that they fit into the window */
+
+ if (p->preview_row) /* make sure preview_row is large enough for one line of the new size */
+ {
+ p->preview_row = realloc(p->preview_row, 3 * p->preview_window_width);
+ }
+ else
+ {
+ p->preview_row = malloc(3 * p->preview_window_width);
+ }
+
+ /* set the ruler ranges: */
+
+ min_x = p->surface[xsane_back_gtk_TL_X];
+ if (min_x <= -INF)
+ {
+ min_x = 0.0;
+ }
+
+ max_x = p->surface[xsane_back_gtk_BR_X];
+ if (max_x >= INF)
+ {
+ max_x = p->image_width - 1;
+ }
+
+ min_y = p->surface[xsane_back_gtk_TL_Y];
+ if (min_y <= -INF)
+ {
+ min_y = 0.0;
+ }
+
+ max_y = p->surface[xsane_back_gtk_BR_Y];
+ if (max_y >= INF)
+ {
+ max_y = p->image_height - 1;
+ }
+
+ /* convert mm to inches if that's what the user wants: */
+
+ if (p->surface_unit == SANE_UNIT_MM)
+ {
+ double factor = 1.0/preferences.length_unit;
+
+ min_x *= factor;
+ max_x *= factor;
+ min_y *= factor;
+ max_y *= factor;
+ }
+
+ preview_get_scale_preview_to_image(p, &xscale, &yscale);
+
+ if (p->image_width > 0)
+ {
+ f = xscale * p->preview_width / p->image_width;
+ }
+ else
+ {
+ f = 1.0;
+ }
+
+ min_x *= f;
+ max_x *= f;
+ delta_x = max_x - min_x;
+
+ gtk_ruler_set_range(GTK_RULER(p->hruler), min_x, min_x + delta_x*p->preview_window_width/p->preview_width,
+ min_x, /* max_size */ 20);
+
+ if (p->image_height > 0)
+ {
+ f = yscale * p->preview_height / p->image_height;
+ }
+ else
+ {
+ f = 1.0;
+ }
+
+ min_y *= f;
+ max_y *= f;
+ delta_y = max_y - min_y;
+
+ gtk_ruler_set_range(GTK_RULER(p->vruler), min_y, min_y + delta_y*p->preview_window_height/p->preview_height,
+ min_y, /* max_size */ 20);
+
+ preview_paint_image(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_update_maximum_output_size(Preview *p)
+{
+ if ( (p->maximum_output_width >= INF) || (p->maximum_output_height >= INF) )
+ {
+ if (p->selection_maximum.active)
+ {
+ p->selection_maximum.active = FALSE;
+ }
+ }
+ else
+ {
+ p->previous_selection_maximum = p->selection_maximum;
+
+ p->selection_maximum.active = TRUE;
+ p->selection_maximum.coordinate[0] = p->selection.coordinate[0];
+ p->selection_maximum.coordinate[1] = p->selection.coordinate[1];
+ p->selection_maximum.coordinate[2] = p->selection.coordinate[0] + p->maximum_output_width;
+ p->selection_maximum.coordinate[3] = p->selection.coordinate[1] + p->maximum_output_height;
+
+ if (p->selection_maximum.coordinate[2] > p->max_scanner_surface[2])
+ {
+ p->selection_maximum.coordinate[2] = p->max_scanner_surface[2];
+ }
+
+ if (p->selection_maximum.coordinate[3] > p->max_scanner_surface[3])
+ {
+ p->selection_maximum.coordinate[3] = p->max_scanner_surface[3];
+ }
+
+ if ( (p->selection.coordinate[0] < p->selection_maximum.coordinate[0]) ||
+ (p->selection.coordinate[1] < p->selection_maximum.coordinate[1]) ||
+ (p->selection.coordinate[2] > p->selection_maximum.coordinate[2]) ||
+ (p->selection.coordinate[3] > p->selection_maximum.coordinate[3]) )
+ {
+ if (p->selection.coordinate[2] > p->selection_maximum.coordinate[2])
+ {
+ p->selection.coordinate[2] = p->selection_maximum.coordinate[2];
+ }
+
+ if (p->selection.coordinate[3] > p->selection_maximum.coordinate[3])
+ {
+ p->selection.coordinate[3] = p->selection_maximum.coordinate[3];
+ }
+ preview_draw_selection(p);
+ preview_establish_selection(p);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_set_maximum_output_size(Preview *p, float width, float height)
+{
+ /* witdh and height in device units */
+
+ p->maximum_output_width = width;
+ p->maximum_output_height = height;
+
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
diff --git a/frontend/xsane-preview.h b/frontend/xsane-preview.h
new file mode 100644
index 0000000..fd452ee
--- /dev/null
+++ b/frontend/xsane-preview.h
@@ -0,0 +1,178 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-preview.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ------------------------------------------------------------------------------------------------------ */
+
+#ifndef xsanepreview_h
+#define xsanepreview_h
+
+/* ------------------------------------------------------------------------------------------------------ */
+
+#include <sys/types.h>
+
+#include <sane/config.h>
+#include <sane/sane.h>
+
+#define SELECTION_RANGE_IN 4
+#define SELECTION_RANGE_OUT 8
+#define XSANE_CURSOR_PREVIEW GDK_LEFT_PTR
+
+/* ------------------------------------------------------------------------------------------------------ */
+
+enum
+{
+ MODE_NORMAL,
+ MODE_PIPETTE_WHITE,
+ MODE_PIPETTE_GRAY,
+ MODE_PIPETTE_BLACK
+};
+
+/* ------------------------------------------------------------------------------------------------------ */
+
+typedef struct Batch_selection
+{
+ float coordinate[4]; /* batch selection coordinate (device coord) */
+ struct Batch_selection *next;
+} Batch_selection;
+
+typedef struct
+{
+ int active;
+ float coordinate[4]; /* selection coordinate (device coord) */
+} Tselection;
+
+/* ------------------------------------------------------------------------------------------------------ */
+
+typedef struct
+{
+ int mode;
+ GSGDialog *dialog; /* the dialog for this preview */
+
+ int cursornr;
+
+ SANE_Value_Type surface_type;
+ SANE_Unit surface_unit;
+ float surface[4]; /* the corners of the selected surface (device coords) */
+ float old_surface[4]; /* the corners of the old selected surface (device coords) */
+ float max_scanner_surface[4]; /* the scanner defined corners of the scanner surface (device coords) */
+ float scanner_surface[4]; /* the user defined corners of the scanner surface (device coords) */
+ float image_surface[4]; /* the corners of the surface (device coords) of the scanned image */
+ float aspect; /* the aspect ratio of the scan surface */
+
+ float preset_width; /* user selected maximum scan width */
+ float preset_height; /* user selected maximum scan height */
+
+ float maximum_output_width; /* maximum output width (photocopy) */
+ float maximum_output_height; /* maximum output height (photocopy) */
+
+ int saved_dpi_valid;
+ int saved_dpi_x_valid;
+ int saved_dpi_y_valid;
+ SANE_Word saved_dpi;
+ SANE_Word saved_dpi_x;
+ SANE_Word saved_dpi_y;
+ int saved_coord_valid[4];
+ SANE_Word saved_coord[4];
+ int saved_custom_gamma_valid;
+ SANE_Word saved_custom_gamma;
+ int saved_bit_depth_valid;
+ SANE_Word saved_bit_depth;
+
+ /* desired/user-selected preview-window size: */
+ int preview_width; /* used with for displaying the preview image */
+ int preview_height; /* used height for displaying the preview image */
+ int preview_window_width; /* width of the preview window */
+ int preview_window_height; /* height of the preview window */
+ u_char *preview_row;
+
+ int scanning;
+ time_t image_last_time_updated;
+ gint input_tag;
+ SANE_Parameters params;
+ int image_offset;
+ int image_x;
+ int image_y;
+ int image_width; /* width of preview image in pixels */
+ int image_height; /* height of preview image in pixel lines */
+ u_char *image_data_raw; /* 3 * image_width * image_height bytes */
+ u_char *image_data_enh; /* 3 * image_width * image_height bytes */
+
+ GdkGC *gc_selection;
+ GdkGC *gc_selection_maximum;
+ int selection_drag;
+ int selection_drag_edge;
+ int selection_xpos;
+ int selection_ypos;
+ int selection_xedge;
+ int selection_yedge;
+
+ Tselection selection; /* selected area to scan */
+ Tselection previous_selection; /* previous ... */
+ Tselection selection_maximum; /* maximum selection size (photocopy) */
+ Tselection previous_selection_maximum; /* previous ... */
+
+ Batch_selection *batch_selection;
+
+ GtkWidget *top; /* top-level widget */
+ GtkWidget *hruler;
+ GtkWidget *vruler;
+ GtkWidget *viewport;
+ GtkWidget *window; /* the preview window */
+ GtkWidget *start; /* the start button */
+ GtkWidget *cancel; /* the cancel button */
+
+ GtkWidget *button_box; /* hbox for the following buttons */
+ GtkWidget *pipette_white; /* pipette white button */
+ GtkWidget *pipette_gray; /* pipette gray button */
+ GtkWidget *pipette_black; /* pipette black button */
+ GtkWidget *zoom_not; /* zoom not button */
+ GtkWidget *zoom_out; /* zoom out button */
+ GtkWidget *zoom_in; /* zoom in button */
+ GtkWidget *zoom_undo; /* zoom undo button */
+ GtkWidget *preset_area_option_menu; /* menu for selection of preview area */
+}
+Preview;
+
+/* ------------------------------------------------------------------------------------------------------ */
+
+extern Preview *preview_new (GSGDialog *dialog); /* Create a new preview based on the info in DIALOG. */
+
+extern void preview_gamma_correction(Preview *p, /* Do gamma correction on preview data */
+ int gamma_red[], int gamma_green[], int gamma_blue[],
+ int gamma_red_hist[], int gamma_green_hist[], int gamma_blue_hist[]);
+
+extern void preview_update_surface(Preview *p, int surface_changed); /* params changed: update preview */
+
+extern void preview_scan(Preview *p); /* Acquire a preview image and display it. */
+
+extern void preview_destroy(Preview *p); /* Destroy a preview. */
+
+extern void preview_calculate_histogram(Preview *p, /* calculate histogram */
+ SANE_Int *count_raw, SANE_Int *count_raw_red, SANE_Int *count_raw_green, SANE_Int *count_raw_blue,
+ SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue);
+
+extern void preview_area_resize(GtkWidget *widget); /* redraw preview rulers */
+void preview_set_maximum_output_size(Preview *p, float width, float height); /* set maximum outut size */
+
+/* ------------------------------------------------------------------------------------------------------ */
+
+#endif /* preview_h */
diff --git a/frontend/xsane-rc-io.c b/frontend/xsane-rc-io.c
new file mode 100644
index 0000000..520e97e
--- /dev/null
+++ b/frontend/xsane-rc-io.c
@@ -0,0 +1,903 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-rc-io.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+#ifdef _AIX
+# include <lalloca.h> /* MUST come first for AIX! */
+#endif
+#include <sane/config.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sane/sane.h>
+#include <sane/config.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#ifdef HAVE_LIBC_H
+# include <libc.h> /* NeXTStep/OpenStep */
+#endif
+
+#include <sane/sane.h>
+#include "xsane-rc-io.h"
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_space(Wire *w, size_t howmuch)
+{
+ size_t nbytes, left_over;
+ int fd = w->io.fd;
+ ssize_t nread, nwritten;
+
+ if (w->buffer.curr + howmuch > w->buffer.end)
+ {
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ nbytes = w->buffer.curr - w->buffer.start;
+ w->buffer.curr = w->buffer.start;
+ while (nbytes > 0)
+ {
+ nwritten = (*w->io.write) (fd, w->buffer.curr, nbytes);
+ if (nwritten < 0)
+ {
+ w->status = errno;
+ return;
+ }
+ w->buffer.curr += nwritten;
+ nbytes -= nwritten;
+ }
+
+ w->buffer.curr = w->buffer.start;
+ w->buffer.end = w->buffer.start + w->buffer.size;
+ break;
+
+ case WIRE_DECODE:
+ left_over = w->buffer.end - w->buffer.curr;
+ if (left_over)
+ {
+ memcpy(w->buffer.start, w->buffer.curr, left_over);
+ }
+ w->buffer.curr = w->buffer.start;
+ w->buffer.end = w->buffer.start + left_over;
+
+ do
+ {
+ nread = (*w->io.read) (fd, w->buffer.end, w->buffer.size - left_over);
+ if (nread <= 0)
+ {
+ if (nread == 0)
+ {
+/* errno = EINVAL; */
+ errno = XSANE_EOF;
+ }
+ w->status = errno;
+ return;
+ }
+ left_over += nread;
+ w->buffer.end += nread;
+ } while (left_over < howmuch);
+ break;
+
+ case WIRE_FREE:
+ break;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_void(Wire *w)
+{
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_array(Wire *w, SANE_Word *len_ptr, void **v, WireCodecFunc w_element, size_t element_size)
+{
+ SANE_Word len;
+ char *val;
+ int i;
+
+ if (w->direction == WIRE_FREE)
+ {
+ free(*v);
+ return;
+ }
+
+ if (w->direction == WIRE_ENCODE)
+ {
+ len = *len_ptr;
+ }
+
+ xsane_rc_io_w_word(w, &len);
+
+ if (w->direction == WIRE_DECODE)
+ {
+ *len_ptr = len;
+ if (len)
+ {
+ *v = malloc(len * element_size);
+
+ if (*v == 0)
+ {
+ /* Malloc failed, so return an error. */
+ w->status = ENOMEM;
+ return;
+ }
+ }
+ else
+ {
+ *v = 0;
+ }
+ }
+
+ val = *v;
+
+ for (i = 0; i < len; ++i)
+ {
+ (*w_element) (w, val);
+ val += element_size;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_ptr(Wire *w, void **v, WireCodecFunc w_value, size_t value_size)
+{
+ SANE_Word is_null;
+
+ if (w->direction == WIRE_FREE)
+ {
+ if (*v)
+ {
+ free(*v);
+ }
+ return;
+ }
+
+ if (w->direction == WIRE_ENCODE)
+ {
+ is_null = (*v == 0);
+ }
+
+ xsane_rc_io_w_word(w, &is_null);
+
+ if (!is_null)
+ {
+ if (w->direction == WIRE_DECODE)
+ {
+ *v = malloc(value_size);
+
+ if (*v == 0)
+ {
+ /* Malloc failed, so return an error. */
+ w->status = ENOMEM;
+ return;
+ }
+ }
+ (*w_value) (w, *v);
+ }
+ else if (w->direction == WIRE_DECODE)
+ {
+ *v = 0;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_status(Wire *w, SANE_Status *v)
+{
+ SANE_Word word = *v;
+
+ xsane_rc_io_w_word(w, &word);
+ if (w->direction == WIRE_DECODE)
+ {
+ *v = word;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_bool(Wire *w, SANE_Bool *v)
+{
+ SANE_Word word = *v;
+
+ xsane_rc_io_w_word(w, &word);
+ if (w->direction == WIRE_DECODE)
+ {
+ *v = word;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_constraint_type(Wire *w, SANE_Constraint_Type *v)
+{
+ SANE_Word word = *v;
+
+ xsane_rc_io_w_word(w, &word);
+ if (w->direction == WIRE_DECODE)
+ *v = word;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_value_type(Wire *w, SANE_Value_Type *v)
+{
+ SANE_Word word = *v;
+
+ xsane_rc_io_w_word(w, &word);
+ if (w->direction == WIRE_DECODE)
+ *v = word;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_unit(Wire *w, SANE_Unit *v)
+{
+ SANE_Word word = *v;
+
+ xsane_rc_io_w_word(w, &word);
+ if (w->direction == WIRE_DECODE)
+ {
+ *v = word;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_action(Wire *w, SANE_Action *v)
+{
+ SANE_Word word = *v;
+
+ xsane_rc_io_w_word(w, &word);
+ if (w->direction == WIRE_DECODE)
+ {
+ *v = word;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_frame(Wire *w, SANE_Frame *v)
+{
+ SANE_Word word = *v;
+
+ xsane_rc_io_w_word(w, &word);
+ if (w->direction == WIRE_DECODE)
+ {
+ *v = word;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_range(Wire *w, SANE_Range *v)
+{
+ xsane_rc_io_w_word(w, &v->min);
+ xsane_rc_io_w_word(w, &v->max);
+ xsane_rc_io_w_word(w, &v->quant);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_device(Wire *w, SANE_Device *v)
+{
+ xsane_rc_io_w_string(w, (SANE_String *) &v->name);
+ xsane_rc_io_w_string(w, (SANE_String *) &v->vendor);
+ xsane_rc_io_w_string(w, (SANE_String *) &v->model);
+ xsane_rc_io_w_string(w, (SANE_String *) &v->type);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_device_ptr(Wire *w, SANE_Device **v)
+{
+ xsane_rc_io_w_ptr(w, (void **) v, (WireCodecFunc) xsane_rc_io_w_device, sizeof (**v));
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_option_descriptor(Wire *w, SANE_Option_Descriptor *v)
+{
+ SANE_Word len;
+
+ xsane_rc_io_w_string(w, (SANE_String *) &v->name);
+ xsane_rc_io_w_string(w, (SANE_String *) &v->title);
+ xsane_rc_io_w_string(w, (SANE_String *) &v->desc);
+ xsane_rc_io_w_value_type(w, &v->type);
+ xsane_rc_io_w_unit(w, &v->unit);
+ xsane_rc_io_w_word(w, &v->size);
+ xsane_rc_io_w_word(w, &v->cap);
+ xsane_rc_io_w_constraint_type(w, &v->constraint_type);
+
+ switch (v->constraint_type)
+ {
+ case SANE_CONSTRAINT_NONE:
+ break;
+
+ case SANE_CONSTRAINT_RANGE:
+ xsane_rc_io_w_ptr(w, (void **) &v->constraint.range, (WireCodecFunc) xsane_rc_io_w_range, sizeof (SANE_Range));
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ if (w->direction == WIRE_ENCODE)
+ len = v->constraint.word_list[0] + 1;
+ xsane_rc_io_w_array(w, &len, (void **) &v->constraint.word_list, w->codec.w_word, sizeof(SANE_Word));
+ break;
+
+ case SANE_CONSTRAINT_STRING_LIST:
+ if (w->direction == WIRE_ENCODE)
+ {
+ for (len = 0; v->constraint.string_list[len]; ++len);
+ ++len; /* send NULL string, too */
+ }
+ xsane_rc_io_w_array(w, &len, (void **) &v->constraint.string_list, w->codec.w_string, sizeof(SANE_String));
+ break;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_option_descriptor_ptr(Wire *w, SANE_Option_Descriptor **v)
+{
+ xsane_rc_io_w_ptr(w, (void **) v, (WireCodecFunc) xsane_rc_io_w_option_descriptor, sizeof (**v));
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_parameters(Wire *w, SANE_Parameters *v)
+{
+ xsane_rc_io_w_frame(w, &v->format);
+ xsane_rc_io_w_bool(w, &v->last_frame);
+ xsane_rc_io_w_word(w, &v->bytes_per_line);
+ xsane_rc_io_w_word(w, &v->pixels_per_line);
+ xsane_rc_io_w_word(w, &v->lines);
+ xsane_rc_io_w_word(w, &v->depth);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_flush(Wire *w)
+{
+ w->status = 0;
+
+ if (w->direction == WIRE_ENCODE)
+ {
+ xsane_rc_io_w_space(w, w->buffer.size + 1);
+ }
+ else if (w->direction == WIRE_DECODE)
+ {
+ w->buffer.curr = w->buffer.end = w->buffer.start;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_set_dir(Wire *w, WireDirection dir)
+{
+ xsane_rc_io_w_flush(w);
+ w->direction = dir;
+ xsane_rc_io_w_flush(w);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_call(Wire *w, SANE_Word procnum, WireCodecFunc w_arg, void *arg, WireCodecFunc w_reply, void *reply)
+{
+ w->status = 0;
+ xsane_rc_io_w_set_dir(w, WIRE_ENCODE);
+
+ xsane_rc_io_w_word(w, &procnum);
+ (*w_arg) (w, arg);
+
+ if (w->status == 0)
+ {
+ xsane_rc_io_w_set_dir(w, WIRE_DECODE);
+ (*w_reply) (w, reply);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_reply(Wire *w, WireCodecFunc w_reply, void *reply)
+{
+ w->status = 0;
+ xsane_rc_io_w_set_dir(w, WIRE_ENCODE);
+ (*w_reply) (w, reply);
+ xsane_rc_io_w_flush(w);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_free(Wire *w, WireCodecFunc w_reply, void *reply)
+{
+ WireDirection saved_dir = w->direction;
+
+ w->direction = WIRE_FREE;
+ (*w_reply) (w, reply);
+ w->direction = saved_dir;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_init(Wire *w)
+{
+ w->status = 0;
+ w->direction = WIRE_ENCODE;
+ w->buffer.size = 8192;
+ w->buffer.start = malloc(w->buffer.size);
+
+ if (w->buffer.start == 0) /* Malloc failed, so return an error. */
+ {
+ w->status = ENOMEM;
+ }
+
+ w->buffer.curr = w->buffer.start;
+ w->buffer.end = w->buffer.start + w->buffer.size;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+static const char *hexdigit = "0123456789abcdef";
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_rc_io_skip_ws(Wire *w)
+{
+ while (1)
+ {
+ xsane_rc_io_w_space(w, 1);
+
+ if (w->status != 0)
+ {
+ return;
+ }
+
+ if (!isspace(*w->buffer.curr))
+ {
+ return;
+ }
+
+ ++w->buffer.curr;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_skip_newline(Wire *w)
+{
+ while (*w->buffer.curr != 10)
+ {
+ xsane_rc_io_w_space(w, 1);
+
+ if (w->status != 0)
+ {
+ return;
+ }
+ ++w->buffer.curr;
+ }
+ ++w->buffer.curr;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+static unsigned xsane_rc_io_get_digit(Wire *w)
+{
+ unsigned digit;
+
+ xsane_rc_io_w_space(w, 1);
+ digit = tolower(*w->buffer.curr++) - '0';
+
+ if (digit > 9)
+ {
+ digit -= 'a' - ('9' + 1);
+ }
+
+ if (digit > 0xf)
+ {
+ w->status = EINVAL;
+ return 0;
+ }
+ return digit;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+static SANE_Byte xsane_rc_io_get_byte(Wire *w)
+{
+ return xsane_rc_io_get_digit(w) << 4 | xsane_rc_io_get_digit(w);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_byte(Wire *w, SANE_Byte *v)
+{
+ SANE_Byte *b = v;
+
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ xsane_rc_io_w_space(w, 3);
+ *w->buffer.curr++ = hexdigit[(*b >> 4) & 0x0f];
+ *w->buffer.curr++ = hexdigit[(*b >> 0) & 0x0f];
+ *w->buffer.curr++ = '\n';
+ break;
+
+ case WIRE_DECODE:
+ xsane_rc_io_skip_ws(w);
+ *b = xsane_rc_io_get_byte(w);
+ break;
+
+ case WIRE_FREE:
+ break;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_char(Wire *w, SANE_Char *v)
+{
+ SANE_Char *c = v;
+
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ xsane_rc_io_w_space(w, 5);
+ *w->buffer.curr++ = '\'';
+
+ if (*c == '\'' || *c == '\\')
+ {
+ *w->buffer.curr++ = '\\';
+ }
+
+ *w->buffer.curr++ = *c;
+ *w->buffer.curr++ = '\'';
+ *w->buffer.curr++ = '\n';
+ break;
+
+ case WIRE_DECODE:
+ xsane_rc_io_w_space(w, 4);
+ if (*w->buffer.curr++ != '\'')
+ {
+ w->status = EINVAL;
+ return;
+ }
+ *c = *w->buffer.curr++;
+
+ if (*c == '\\')
+ {
+ xsane_rc_io_w_space(w, 2);
+ *c = *w->buffer.curr++;
+ }
+
+ if (*w->buffer.curr++ != '\'')
+ {
+ w->status = EINVAL;
+ return;
+ }
+ break;
+
+ case WIRE_FREE:
+ break;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_string(Wire *w, SANE_String *s)
+{
+ size_t len, alloced_len;
+ char * str, ch;
+ int done;
+
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ if (*s)
+ {
+ xsane_rc_io_w_space(w, 1);
+ *w->buffer.curr++ = '"';
+ str = *s;
+ while ((ch = *str++))
+ {
+ xsane_rc_io_w_space(w, 2);
+ if (ch == '"' || ch == '\\')
+ {
+ *w->buffer.curr++ = '\\';
+ }
+ *w->buffer.curr++ = ch;
+ }
+ *w->buffer.curr++ = '"';
+ }
+ else
+ {
+ xsane_rc_io_w_space(w, 5);
+ *w->buffer.curr++ = '(';
+ *w->buffer.curr++ = 'n';
+ *w->buffer.curr++ = 'i';
+ *w->buffer.curr++ = 'l';
+ *w->buffer.curr++ = ')';
+ }
+
+ xsane_rc_io_w_space(w, 1);
+ *w->buffer.curr++ = '\n';
+ break;
+
+ case WIRE_DECODE:
+ xsane_rc_io_skip_ws(w);
+ xsane_rc_io_w_space(w, 1);
+
+ if (w->status != 0)
+ {
+ *s = 0; /* make sure pointer does not point to an invalid address */
+ return;
+ }
+
+ ch = *w->buffer.curr++;
+ if (ch == '"')
+ {
+ alloced_len = len = 0;
+ str = 0;
+ done = 0;
+
+ do
+ {
+ xsane_rc_io_w_space(w, 1);
+
+ if (w->status != 0)
+ {
+ return;
+ }
+
+ ch = *w->buffer.curr++;
+ if (ch == '"')
+ {
+ done = 1;
+ }
+
+ if (ch == '\\')
+ {
+ xsane_rc_io_w_space(w, 1);
+ ch = *w->buffer.curr++;
+ }
+
+ if (len >= alloced_len)
+ {
+ alloced_len += 1024;
+ if (!str)
+ {
+ str = malloc(alloced_len);
+ }
+ else
+ {
+ str = realloc(str, alloced_len);
+ }
+
+ if (str == 0)
+ {
+ /* Malloc failed, so return an error. */
+ w->status = ENOMEM;
+ return;
+ }
+ }
+ str[len++] = ch;
+ }
+ while(!done);
+
+ str[len - 1] = '\0';
+ *s = realloc(str, len);
+
+ if (*s == 0)
+ {
+ /* Malloc failed, so return an error. */
+ w->status = ENOMEM;
+ return;
+ }
+ }
+ else if (ch == '(')
+ {
+ *s = 0; /* make sure pointer does not point to an invalid address */
+ xsane_rc_io_w_space(w, 4);
+ if ( *w->buffer.curr++ != 'n'
+ || *w->buffer.curr++ != 'i'
+ || *w->buffer.curr++ != 'l'
+ || *w->buffer.curr++ != ')')
+ {
+ w->status = EINVAL;
+ return;
+ }
+ }
+ else
+ {
+ w->status = EINVAL;
+ *s = 0; /* make sure pointer does not point to an invalid address */
+ return;
+ }
+ break;
+
+ case WIRE_FREE:
+ if (*s)
+ {
+ free(*s);
+ }
+ break;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_io_w_word(Wire *w, SANE_Word *v)
+{
+ SANE_Word val, *word = v;
+ int i, is_negative = 0;
+ char buf[16];
+
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ val = *word;
+ i = sizeof(buf) - 1;
+
+ if (val < 0)
+ {
+ is_negative = 1;
+ val = -val;
+ }
+
+ do
+ {
+ buf[i--] = '0' + (val % 10);
+ val /= 10;
+ }
+ while (val);
+
+ if (is_negative)
+ {
+ buf[i--] = '-';
+ }
+
+ xsane_rc_io_w_space(w, sizeof(buf) - i);
+ memcpy(w->buffer.curr, buf + i + 1, sizeof(buf) - i - 1);
+ w->buffer.curr += sizeof(buf) - i - 1;
+ *w->buffer.curr++ = '\n';
+ break;
+
+ case WIRE_DECODE:
+ xsane_rc_io_skip_ws(w);
+ val = 0;
+ xsane_rc_io_w_space(w, 1);
+ if (*w->buffer.curr == '-')
+ {
+ is_negative = 1;
+ ++w->buffer.curr;
+ }
+
+ while (1)
+ {
+ xsane_rc_io_w_space(w, 1);
+
+ if (w->status != 0)
+ {
+ return;
+ }
+
+ if (!isdigit (*w->buffer.curr))
+ {
+ break;
+ }
+
+ val = 10*val + (*w->buffer.curr++ - '0');
+ }
+ *word = is_negative ? -val : val;
+ break;
+
+ case WIRE_FREE:
+ break;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+#define PFIELD(p,offset,type) (*((type *)(((char *)(p)) + (offset))))
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_pref_string(Wire *w, void *p, long offset)
+{
+ SANE_String string;
+
+ if (w->direction == WIRE_ENCODE)
+ {
+ string = PFIELD(p, offset, char *);
+ }
+
+ xsane_rc_io_w_string(w, &string);
+
+ if (w->direction == WIRE_DECODE)
+ {
+ if (w->status == 0)
+ {
+ const char **field;
+
+ field = &PFIELD(p, offset, const char *);
+ if (*field)
+ {
+ free((char *) *field);
+ }
+ *field = string ? strdup (string) : 0;
+ }
+ xsane_rc_io_w_free(w, (WireCodecFunc) xsane_rc_io_w_string, &string);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_pref_double(Wire *w, void *p, long offset)
+{
+ SANE_Word word;
+
+ if (w->direction == WIRE_ENCODE)
+ {
+ word = SANE_FIX(PFIELD (p, offset, double));
+ }
+
+ xsane_rc_io_w_word (w, &word);
+
+ if (w->direction == WIRE_DECODE)
+ {
+ if (w->status == 0)
+ {
+ PFIELD(p, offset, double) = SANE_UNFIX (word);
+ }
+ xsane_rc_io_w_free(w, (WireCodecFunc) xsane_rc_io_w_word, &word);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+void xsane_rc_pref_int(Wire *w, void *p, long offset)
+{
+ SANE_Word word;
+
+ if (w->direction == WIRE_ENCODE)
+ {
+ word = PFIELD(p, offset, int);
+ }
+
+ xsane_rc_io_w_word (w, &word);
+
+ if (w->direction == WIRE_DECODE)
+ {
+ if (w->status == 0)
+ {
+ PFIELD(p, offset, int) = word;
+ }
+ xsane_rc_io_w_free(w, (WireCodecFunc) xsane_rc_io_w_word, &word);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------- */
diff --git a/frontend/xsane-rc-io.h b/frontend/xsane-rc-io.h
new file mode 100644
index 0000000..6fd5680
--- /dev/null
+++ b/frontend/xsane-rc-io.h
@@ -0,0 +1,123 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-rc-io.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+#ifndef xsane_rc_io_h
+#define xsane_rc_io_h
+
+#include <sys/types.h>
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#define XSANE_EOF -1
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+typedef enum
+ {
+ WIRE_ENCODE = 0,
+ WIRE_DECODE,
+ WIRE_FREE
+ }
+WireDirection;
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+struct Wire;
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+typedef void (*WireCodecFunc) (struct Wire *w, void *val_ptr);
+typedef ssize_t (*WireReadFunc) (int fd, void * buf, size_t len);
+typedef ssize_t (*WireWriteFunc) (int fd, const void * buf, size_t len);
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+typedef struct Wire
+ {
+ int version; /* protocol version in use */
+ WireDirection direction;
+ int status;
+ struct
+ {
+ WireCodecFunc w_byte;
+ WireCodecFunc w_char;
+ WireCodecFunc w_word;
+ WireCodecFunc w_string;
+ }
+ codec;
+ struct
+ {
+ size_t size;
+ char *curr;
+ char *start;
+ char *end;
+ }
+ buffer;
+ struct
+ {
+ int fd;
+ WireReadFunc read;
+ WireWriteFunc write;
+ }
+ io;
+ }
+Wire;
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+extern void xsane_rc_io_w_init(Wire *w);
+extern void xsane_rc_io_w_space(Wire *w, size_t howmuch);
+extern void xsane_rc_io_w_skip_newline(Wire *w);
+extern void xsane_rc_io_w_void(Wire *w);
+extern void xsane_rc_io_w_byte(Wire *w, SANE_Byte *v);
+extern void xsane_rc_io_w_char(Wire *w, SANE_Char *v);
+extern void xsane_rc_io_w_word(Wire *w, SANE_Word *v);
+extern void xsane_rc_io_w_string(Wire *w, SANE_String *v);
+extern void xsane_rc_io_w_status(Wire *w, SANE_Status *v);
+extern void xsane_rc_io_w_constraint_type(Wire *w, SANE_Constraint_Type *v);
+extern void xsane_rc_io_w_value_type(Wire *w, SANE_Value_Type *v);
+extern void xsane_rc_io_w_unit(Wire *w, SANE_Unit *v);
+extern void xsane_rc_io_w_action(Wire *w, SANE_Action *v);
+extern void xsane_rc_io_w_frame(Wire *w, SANE_Frame *v);
+extern void xsane_rc_io_w_range(Wire *w, SANE_Range *v);
+extern void xsane_rc_io_w_range_ptr(Wire *w, SANE_Range **v);
+extern void xsane_rc_io_w_device(Wire *w, SANE_Device *v);
+extern void xsane_rc_io_w_device_ptr(Wire *w, SANE_Device **v);
+extern void xsane_rc_io_w_option_descriptor(Wire *w, SANE_Option_Descriptor *v);
+extern void xsane_rc_io_w_option_descriptor_ptr(Wire *w, SANE_Option_Descriptor **v);
+extern void xsane_rc_io_w_parameters(Wire *w, SANE_Parameters *v);
+extern void xsane_rc_io_w_array(Wire *w, SANE_Word *len, void **v, WireCodecFunc w_element, size_t element_size);
+extern void xsane_rc_io_w_flush(Wire *w);
+extern void xsane_rc_io_w_set_dir(Wire *w, WireDirection dir);
+extern void xsane_rc_io_w_call(Wire *w, SANE_Word proc_num, WireCodecFunc w_arg, void *arg, WireCodecFunc w_reply, void *reply);
+extern void xsane_rc_io_w_reply(Wire *w, WireCodecFunc w_reply, void *reply);
+extern void xsane_rc_io_w_free(Wire *w, WireCodecFunc w_reply, void *reply);
+
+extern void xsane_rc_pref_string(Wire *w, void *p, long offset);
+extern void xsane_rc_pref_double(Wire *w, void *p, long offset);
+extern void xsane_rc_pref_int(Wire *w, void *p, long offset);
+
+/* ---------------------------------------------------------------------------------------------------------------- */
+
+#endif /* xsane_rc_io_wire_h */
diff --git a/frontend/xsane-save.c b/frontend/xsane-save.c
new file mode 100644
index 0000000..d9ac69a
--- /dev/null
+++ b/frontend/xsane-save.c
@@ -0,0 +1,975 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-save.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+#include "xsane-preview.h"
+#include "xsane-back-gtk.h"
+#include "xsane-front-gtk.h"
+#include "xsane-text.h"
+
+#ifdef HAVE_LIBJPEG
+#include <jpeglib.h>
+#endif
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+#include <png.h>
+#include <zlib.h>
+#endif
+#endif
+
+#ifdef HAVE_LIBTIFF
+#include <tiffio.h>
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int cancel_save;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_cancel_save()
+{
+ cancel_save = 1;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_convert_text_to_filename(char **text)
+{
+ if (text)
+ {
+ char *filename = *text;
+ char buf[256];
+ int buflen=0;
+ int txtlen=0;
+
+ while((filename[txtlen] != 0) && (buflen<253))
+ {
+ switch (filename[txtlen])
+ {
+ case ' ':
+ buf[buflen++] = ':';
+ buf[buflen++] = '_';
+ txtlen++;
+ break;
+
+ case '/':
+ buf[buflen++] = ':';
+ buf[buflen++] = '%';
+ txtlen++;
+ break;
+
+ case '*':
+ buf[buflen++] = ':';
+ buf[buflen++] = '#';
+ txtlen++;
+ break;
+
+ case '?':
+ buf[buflen++] = ':';
+ buf[buflen++] = 'q';
+ txtlen++;
+ break;
+
+ case '\\':
+ buf[buflen++] = ':';
+ buf[buflen++] = '=';
+ txtlen++;
+ break;
+
+ case ';':
+ buf[buflen++] = ':';
+ buf[buflen++] = '!';
+ txtlen++;
+ break;
+
+ case '&':
+ buf[buflen++] = ':';
+ buf[buflen++] = '+';
+ txtlen++;
+ break;
+
+ case '<':
+ buf[buflen++] = ':';
+ buf[buflen++] = 's';
+ txtlen++;
+ break;
+
+ case '>':
+ buf[buflen++] = ':';
+ buf[buflen++] = 'g';
+ txtlen++;
+ break;
+
+ case '|':
+ buf[buflen++] = ':';
+ buf[buflen++] = 'p';
+ txtlen++;
+ break;
+
+ case ':':
+ buf[buflen++] = ':';
+ buf[buflen++] = ':';
+ txtlen++;
+ break;
+
+ default:
+ buf[buflen++] = filename[txtlen++];
+ break;
+ }
+ }
+ buf[buflen] = 0;
+ free(filename);
+ *text = strdup(buf);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_increase_counter_in_filename(char *filename, int skip)
+{
+ char *position_point;
+ char *position_counter;
+ char counter;
+ FILE *testfile;
+
+ while (1)
+ {
+ position_point = strrchr(filename, '.');
+ if (position_point)
+ {
+ position_counter = position_point-1;
+ }
+ else
+ {
+ position_counter = filename + strlen(filename) - 1;
+ }
+
+ if (!( (*position_counter >= '0') && (*position_counter <='9') ))
+ {
+ break; /* no counter found */
+ }
+
+ while ( (position_counter > filename) && (*position_counter >= '0') && (*position_counter <='9') )
+ {
+ counter = ++(*position_counter);
+ if (counter != ':')
+ {
+ break;
+ }
+ *position_counter = '0';
+ position_counter--;
+ }
+
+ if (!( (*position_counter >= '0') && (*position_counter <='9') )) /* overflow */
+ {
+ xsane_back_gtk_warning(WARN_COUNTER_OVERFLOW, FALSE);
+ break; /* last available number ("999") */
+ }
+
+ if (skip) /* test if filename already used */
+ {
+ testfile = fopen(filename, "r");
+ if (testfile) /* filename used: skip */
+ {
+ fclose(testfile);
+ }
+ else
+ {
+ break; /* filename not used, ok */
+ }
+ }
+ else /* do not test if filename already used */
+ {
+ break; /* filename ok */
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_save_ps_create_header(FILE *outfile, int color, int bits, int pixel_width, int pixel_height,
+ int left, int bottom, float width, float height,
+ int paperwidth, int paperheight, int rotate)
+{
+ int degree, position_left, position_bottom, box_left, box_bottom, box_right, box_top;
+
+ if (rotate) /* roatet with 90 degrees - eg for landscape mode */
+ {
+ degree = 90;
+ position_left = left;
+ position_bottom = bottom - paperwidth;
+ box_left = paperwidth - bottom - height * 72.0;
+ box_bottom = left;
+ box_right = (int) (box_left + height * 72.0);
+ box_top = (int) (box_bottom + width * 72.0);
+ }
+ else /* do not rotate, eg for portrait mode */
+ {
+ degree = 0;
+ position_left = left;
+ position_bottom = bottom;
+ box_left = left;
+ box_bottom = bottom;
+ box_right = (int) (box_left + width * 72.0);
+ box_top = (int) (box_bottom + height * 72.0);
+ }
+
+ fprintf(outfile, "%%!PS-Adobe-2.0 EPSF-2.0\n");
+ fprintf(outfile, "%%%%Creator: xsane version %s (sane %d.%d)\n", VERSION,
+ SANE_VERSION_MAJOR(xsane.sane_backend_versioncode),
+ SANE_VERSION_MINOR(xsane.sane_backend_versioncode));
+ fprintf(outfile, "%%%%BoundingBox: %d %d %d %d\n", box_left, box_bottom, box_right, box_top);
+ fprintf(outfile, "%%\n");
+ fprintf(outfile, "/origstate save def\n");
+ fprintf(outfile, "20 dict begin\n");
+
+ if (bits == 1)
+ {
+ fprintf(outfile, "/pix %d string def\n", (pixel_width+7)/8);
+ fprintf(outfile, "/grays %d string def\n", pixel_width);
+ fprintf(outfile, "/npixels 0 def\n");
+ fprintf(outfile, "/rgbindx 0 def\n");
+ }
+ else
+ {
+ fprintf(outfile, "/pix %d string def\n", pixel_width);
+ }
+
+
+ fprintf(outfile, "%d rotate\n", degree);
+ fprintf(outfile, "%d %d translate\n", position_left, position_bottom);
+ fprintf(outfile, "%f %f scale\n", width * 72.0, height * 72.0);
+ fprintf(outfile, "%d %d %d\n", pixel_width, pixel_height, bits);
+ fprintf(outfile, "[%d %d %d %d %d %d]\n", pixel_width, 0, 0, -pixel_height, 0 , pixel_height);
+ fprintf(outfile, "{currentfile pix readhexstring pop}\n");
+
+ if (color)
+ {
+ fprintf(outfile, "false 3 colorimage\n");
+ fprintf(outfile, "\n");
+ }
+ else
+ {
+ fprintf(outfile, "image\n");
+ fprintf(outfile, "\n");
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_save_ps_bw(FILE *outfile, FILE *imagefile, int pixel_width, int pixel_height)
+{
+ int x, y, count;
+ int bytes_per_line = (pixel_width+7)/8;
+
+ cancel_save = 0;
+
+ count = 0;
+ for (y = 0; y < pixel_height; y++)
+ {
+ for (x = 0; x < bytes_per_line; x++)
+ {
+ fprintf(outfile, "%02x", (fgetc(imagefile) ^ 255));
+ if (++count >= 40)
+ {
+ fprintf(outfile, "\n");
+ count = 0;
+ }
+ }
+ fprintf(outfile, "\n");
+ count = 0;
+ xsane_progress_update(xsane.progress, (float)y/pixel_height);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ if (cancel_save)
+ {
+ break;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_save_ps_gray(FILE *outfile, FILE *imagefile, int pixel_width, int pixel_height)
+{
+ int x, y, count;
+
+ cancel_save = 0;
+
+ count = 0;
+ for (y=0; y<pixel_height; y++)
+ {
+ for (x=0; x<pixel_width; x++)
+ {
+ fprintf(outfile, "%02x", fgetc(imagefile));
+ if (++count >=40)
+ {
+ fprintf(outfile, "\n");
+ count = 0;
+ }
+ }
+ fprintf(outfile, "\n");
+ count = 0;
+ xsane_progress_update(xsane.progress, (float)y/pixel_height);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ if (cancel_save)
+ {
+ break;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_save_ps_color(FILE *outfile, FILE *imagefile, int pixel_width, int pixel_height)
+{
+ int x, y, count;
+
+ cancel_save = 0;
+
+ count = 0;
+ for (y=0; y<pixel_height; y++)
+ {
+ for (x=0; x<pixel_width; x++)
+ {
+ fprintf(outfile, "%02x", fgetc(imagefile));
+ fprintf(outfile, "%02x", fgetc(imagefile));
+ fprintf(outfile, "%02x", fgetc(imagefile));
+ if (++count >=10)
+ {
+ fprintf(outfile, "\n");
+ count = 0;
+ }
+ }
+ fprintf(outfile, "\n");
+ count = 0;
+
+ xsane_progress_update(xsane.progress, (float)y/pixel_height);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ if (cancel_save)
+ {
+ break;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_save_ps(FILE *outfile, FILE *imagefile,
+ int color, int bits,
+ int pixel_width, int pixel_height,
+ int left, int bottom,
+ float width, float height,
+ int paperheight, int paperwidth, int rotate)
+{
+ xsane_save_ps_create_header(outfile, color, bits, pixel_width, pixel_height,
+ left, bottom, width, height, paperheight, paperwidth, rotate);
+
+ if (color == 0) /* lineart, halftone, grayscale */
+ {
+ if (bits == 1) /* lineart, halftone */
+ {
+ xsane_save_ps_bw(outfile, imagefile, pixel_width, pixel_height);
+ }
+ else /* grayscale */
+ {
+ xsane_save_ps_gray(outfile, imagefile, pixel_width, pixel_height);
+ }
+ }
+ else /* color */
+ {
+ xsane_save_ps_color(outfile, imagefile, pixel_width, pixel_height);
+ }
+
+ fprintf(outfile, "\n");
+ fprintf(outfile, "showpage\n");
+ fprintf(outfile, "end\n");
+ fprintf(outfile, "origstate restore\n");
+ fprintf(outfile, "\n");
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBJPEG
+void xsane_save_jpeg(FILE *outfile, FILE *imagefile,
+ int color, int bits,
+ int pixel_width, int pixel_height,
+ int quality)
+{
+ char *data;
+ char buf[256];
+ int x,y;
+ int components = 1;
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ JSAMPROW row_pointer[1];
+
+ cancel_save = 0;
+
+ if (color)
+ {
+ components = 3;
+ }
+
+ data = malloc(pixel_width * components);
+
+ if (!data)
+ {
+ snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+ jpeg_stdio_dest(&cinfo, outfile);
+ cinfo.image_width = pixel_width;
+ cinfo.image_height = pixel_height;
+ cinfo.input_components = components;
+ if (color)
+ {
+ cinfo.in_color_space = JCS_RGB;
+ }
+ else
+ {
+ cinfo.in_color_space = JCS_GRAYSCALE;
+ }
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, TRUE);
+ jpeg_start_compress(&cinfo, TRUE);
+
+ for (y=0; y<pixel_height; y++)
+ {
+ xsane_progress_update(xsane.progress, (float)y/pixel_height);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ if (bits == 1)
+ {
+ int byte = 0;
+ int mask = 128;
+
+ for (x = 0; x < pixel_width; x++)
+ {
+
+ if ( (x % 8) == 0)
+ {
+ byte = fgetc(imagefile);
+ mask = 128;
+ }
+
+ if (byte & mask)
+ {
+ data[x] = 0;
+ }
+ else
+ {
+ data[x] = 255;
+ }
+ mask >>= 1;
+ }
+ }
+ else
+ {
+ fread(data, components, pixel_width, imagefile);
+ }
+ row_pointer[0] = data;
+ jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ if (cancel_save)
+ {
+ break;
+ }
+ }
+
+ jpeg_finish_compress(&cinfo);
+ free(data);
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBTIFF
+void xsane_save_tiff(const char *outfilename, FILE *imagefile,
+ int color, int bits,
+ int pixel_width, int pixel_height,
+ int compression, int quality)
+{
+ TIFF *tiffile;
+ char *data;
+ char buf[256];
+ int y, w;
+ int components;
+
+ cancel_save = 0;
+
+ if (color)
+ {
+ components = 3;
+ }
+ else
+ {
+ components = 1;
+ }
+
+ tiffile = TIFFOpen(outfilename, "w");
+ if (!tiffile)
+ {
+ snprintf(buf, sizeof(buf), "%s %s %s\n",ERR_DURING_SAVE, ERR_OPEN_FAILED, outfilename);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ data = malloc(pixel_width * components);
+
+ if (!data)
+ {
+ snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ TIFFSetField(tiffile, TIFFTAG_IMAGEWIDTH, pixel_width);
+ TIFFSetField(tiffile, TIFFTAG_IMAGELENGTH, pixel_height);
+ TIFFSetField(tiffile, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tiffile, TIFFTAG_BITSPERSAMPLE, bits);
+ TIFFSetField(tiffile, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tiffile, TIFFTAG_COMPRESSION, compression);
+ TIFFSetField(tiffile, TIFFTAG_SAMPLESPERPIXEL, components);
+ TIFFSetField(tiffile, TIFFTAG_SOFTWARE, "xsane");
+#if 0
+ TIFFSetField(tiffile, TIFFTAG_DATATIME, "0.0.1900,0:0:00");
+ TIFFSetField(tiffile, TIFFTAG_XRESOLUTION, 100);
+ TIFFSetField(tiffile, TIFFTAG_YRESOLUTION, 100);
+#endif
+
+ if (compression == COMPRESSION_JPEG)
+ {
+ TIFFSetField(tiffile, TIFFTAG_JPEGQUALITY, quality);
+ }
+
+ if (color)
+ {
+ TIFFSetField(tiffile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ }
+ else
+ {
+ if (bits == 1) /* lineart */
+ {
+ TIFFSetField(tiffile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
+ }
+ else /* grayscale */
+ {
+ TIFFSetField(tiffile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
+ }
+ }
+
+ TIFFSetField(tiffile, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiffile, -1));
+
+ w = TIFFScanlineSize(tiffile);
+
+ for (y = 0; y < pixel_height; y++)
+ {
+ xsane_progress_update(xsane.progress, (float) y / pixel_height);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ fread(data, 1, w, imagefile);
+
+ TIFFWriteScanline(tiffile, data, y, 0);
+
+ if (cancel_save)
+ {
+ break;
+ }
+ }
+
+ TIFFClose(tiffile);
+ free(data);
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+void xsane_save_png(FILE *outfile, FILE *imagefile,
+ int color, int bits,
+ int pixel_width, int pixel_height,
+ int compression)
+{
+ png_structp png_ptr;
+ png_infop png_info_ptr;
+ png_bytep row_ptr;
+ png_color_8 sig_bit;
+ char *data;
+ char buf[256];
+ int colortype, components, byte_width;
+ int y;
+
+ cancel_save = 0;
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ if (!png_ptr)
+ {
+ snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBTIFF);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ png_info_ptr = png_create_info_struct(png_ptr);
+ if (!png_info_ptr)
+ {
+ snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBTIFF);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ if (setjmp(png_ptr->jmpbuf))
+ {
+ snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG);
+ xsane_back_gtk_error(buf, TRUE);
+ png_destroy_write_struct(&png_ptr, (png_infopp) 0);
+ return;
+ }
+
+ byte_width = pixel_width;
+
+ if (color == 4) /* RGBA */
+ {
+ components = 4;
+ colortype = PNG_COLOR_TYPE_RGB_ALPHA;
+ }
+ else if (color) /* RGB */
+ {
+ components = 3;
+ colortype = PNG_COLOR_TYPE_RGB;
+ }
+ else /* gray or black/white */
+ {
+ components = 1;
+ colortype = PNG_COLOR_TYPE_GRAY;
+ }
+
+ png_init_io(png_ptr, outfile);
+ png_set_compression_level(png_ptr, compression);
+ png_set_IHDR(png_ptr, png_info_ptr, pixel_width, pixel_height, bits,
+ colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ if (color >=3)
+ {
+ sig_bit.red = bits;
+ sig_bit.green = bits;
+ sig_bit.blue = bits;
+
+ if (color ==4)
+ {
+ sig_bit.alpha = bits;
+ }
+
+ }
+ else
+ {
+ sig_bit.gray = bits;
+
+ if (bits == 1)
+ {
+ byte_width = pixel_width/8;
+ png_set_invert_mono(png_ptr);
+ }
+ }
+
+ png_set_sBIT(png_ptr, png_info_ptr, &sig_bit);
+ png_write_info(png_ptr, png_info_ptr);
+ png_set_shift(png_ptr, &sig_bit);
+
+ data = malloc(pixel_width * components);
+
+ if (!data)
+ {
+ snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
+ xsane_back_gtk_error(buf, TRUE);
+ png_destroy_write_struct(&png_ptr, (png_infopp) 0);
+ return;
+ }
+
+ for (y = 0; y < pixel_height; y++)
+ {
+ xsane_progress_update(xsane.progress, (float) y / pixel_height);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ fread(data, components, byte_width, imagefile);
+
+ row_ptr = data;
+ png_write_rows(png_ptr, &row_ptr, 1);
+ if (cancel_save)
+ {
+ break;
+ }
+ }
+
+ free(data);
+ png_write_end(png_ptr, png_info_ptr);
+ png_destroy_write_struct(&png_ptr, (png_infopp) 0);
+
+}
+#endif
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+void xsane_save_png_16(FILE *outfile, FILE *imagefile,
+ int color, int bits,
+ int pixel_width, int pixel_height,
+ int compression)
+{
+ png_structp png_ptr;
+ png_infop png_info_ptr;
+ png_bytep row_ptr;
+ png_color_8 sig_bit; /* should be 16, but then I get a warning about wrong type */
+ char *data;
+ char buf[256];
+ int colortype, components;
+ int x,y;
+ guint16 val;
+
+ cancel_save = 0;
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ if (!png_ptr)
+ {
+ snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ png_info_ptr = png_create_info_struct(png_ptr);
+ if (!png_info_ptr)
+ {
+ snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ if (setjmp(png_ptr->jmpbuf))
+ {
+ snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG);
+ xsane_back_gtk_error(buf, TRUE);
+ png_destroy_write_struct(&png_ptr, (png_infopp) 0);
+ return;
+ }
+
+ if (color == 4) /* RGBA */
+ {
+ components = 4;
+ colortype = PNG_COLOR_TYPE_RGB_ALPHA;
+ }
+ else if (color) /* RGB */
+ {
+ components = 3;
+ colortype = PNG_COLOR_TYPE_RGB;
+ }
+ else /* gray or black/white */
+ {
+ components = 1;
+ colortype = PNG_COLOR_TYPE_GRAY;
+ }
+
+ png_init_io(png_ptr, outfile);
+ png_set_compression_level(png_ptr, compression);
+ png_set_IHDR(png_ptr, png_info_ptr, pixel_width, pixel_height, 16,
+ colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ sig_bit.red = bits;
+ sig_bit.green = bits;
+ sig_bit.blue = bits;
+ sig_bit.alpha = bits;
+ sig_bit.gray = bits;
+
+ png_set_sBIT(png_ptr, png_info_ptr, &sig_bit);
+ png_write_info(png_ptr, png_info_ptr);
+ png_set_shift(png_ptr, &sig_bit);
+ png_set_packing(png_ptr);
+
+ data = malloc(pixel_width * components * 2);
+
+ if (!data)
+ {
+ snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
+ xsane_back_gtk_error(buf, TRUE);
+ png_destroy_write_struct(&png_ptr, (png_infopp) 0);
+ return;
+ }
+
+ for (y = 0; y < pixel_height; y++)
+ {
+ xsane_progress_update(xsane.progress, (float)y/pixel_height);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ for (x = 0; x < pixel_width * components; x++) /* this must be changed in dependance of endianess */
+ {
+ fread(&val, 2, 1, imagefile); /* get data in machine order */
+ data[x*2+0] = val/256; /* write data in network order (MSB first) */
+ data[x*2+1] = val & 255;
+ }
+
+ row_ptr = data;
+ png_write_rows(png_ptr, &row_ptr, 1);
+ if (cancel_save)
+ {
+ break;
+ }
+ }
+
+ free(data);
+ png_write_end(png_ptr, png_info_ptr);
+ png_destroy_write_struct(&png_ptr, (png_infopp) 0);
+}
+#endif
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_save_pnm_16_gray(FILE *outfile, FILE *imagefile, int bits, int pixel_width, int pixel_height)
+{
+ int x,y;
+ guint16 val;
+ int count = 0;
+
+ cancel_save = 0;
+
+ /* write pgm ascii > 8 bpp */
+ fprintf(outfile, "P2\n# SANE data follows\n%d %d\n65535\n", pixel_width, pixel_height);
+
+ for (y=0; y<pixel_height; y++)
+ {
+ for (x=0; x<pixel_width; x++)
+ {
+ fread(&val, 2, 1, imagefile); /* get data in machine order */
+ fprintf(outfile, "%d ", val);
+
+ if (++count >= 10)
+ {
+ fprintf(outfile, "\n");
+ count = 0;
+ }
+ }
+ fprintf(outfile, "\n");
+ count = 0;
+
+ xsane_progress_update(xsane.progress, (float)y/pixel_height);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ if (cancel_save)
+ {
+ break;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_save_pnm_16_color(FILE *outfile, FILE *imagefile, int bits, int pixel_width, int pixel_height)
+{
+ int x,y;
+ guint16 val;
+ int count = 0;
+
+ cancel_save = 0;
+
+ /* write ppm ascii > 8 bpp */
+ fprintf(outfile, "P3\n# SANE data follows\n%d %d\n65535\n", pixel_width, pixel_height);
+
+ for (y=0; y<pixel_height; y++)
+ {
+ for (x=0; x<pixel_width; x++)
+ {
+ fread(&val, 2, 1, imagefile); /* get data in machine order */
+ fprintf(outfile, "%d ", val);
+
+ fread(&val, 2, 1, imagefile);
+ fprintf(outfile, "%d ", val);
+
+ fread(&val, 2, 1, imagefile);
+ fprintf(outfile, "%d ", val);
+
+ if (++count >= 3)
+ {
+ fprintf(outfile, "\n");
+ count = 0;
+ }
+ }
+ fprintf(outfile, "\n");
+ count = 0;
+
+ xsane_progress_update(xsane.progress, (float)y/pixel_height);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ if (cancel_save)
+ {
+ break;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_save_pnm_16(FILE *outfile, FILE *imagefile, int color, int bits, int pixel_width, int pixel_height)
+{
+ if (color)
+ {
+ xsane_save_pnm_16_color(outfile, imagefile, bits, pixel_width, pixel_height);
+ }
+ else
+ {
+ xsane_save_pnm_16_gray(outfile, imagefile, bits, pixel_width, pixel_height);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
diff --git a/frontend/xsane-save.h b/frontend/xsane-save.h
new file mode 100644
index 0000000..7ac856c
--- /dev/null
+++ b/frontend/xsane-save.h
@@ -0,0 +1,79 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-save.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_cancel_save();
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_convert_text_to_filename(char **filename);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+
+void xsane_increase_counter_in_filename(char *filename, int skip);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_save_ps(FILE *outfile, FILE *imagefile,
+ int color, int bits,
+ int pixel_width, int pixel_height,
+ int left, int bottom,
+ float width, float height,
+ int paperwidth, int paperheight, int landscape);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_save_jpeg(FILE *outfile, FILE *imagefile,
+ int color, int bits,
+ int pixel_width, int pixel_height,
+ int quality);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_save_tiff(const char *outfilename, FILE *imagefile,
+ int color, int bits,
+ int pixel_width, int pixel_height,
+ int compression, int quality);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_save_png(FILE *outfile, FILE *imagefile,
+ int color, int bits,
+ int pixel_width, int pixel_height,
+ int compression);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_save_png_16(FILE *outfile, FILE *imagefile,
+ int color, int bits,
+ int pixel_width, int pixel_height,
+ int compression);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_save_pnm_16(FILE *outfile, FILE *imagefile,
+ int color, int bits,
+ int pixel_width, int pixel_height);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
diff --git a/frontend/xsane-scan.c b/frontend/xsane-scan.c
new file mode 100644
index 0000000..d66ab28
--- /dev/null
+++ b/frontend/xsane-scan.c
@@ -0,0 +1,2369 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-scan.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+#include "xsane-back-gtk.h"
+#include "xsane-front-gtk.h"
+#include "xsane-preferences.h"
+#include "xsane-preview.h"
+#include "xsane-save.h"
+#include "xsane-text.h"
+#include "xsane-gamma.h"
+#include "xsane-setup.h"
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+#include <png.h>
+#include <zlib.h>
+#endif
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+
+#include <libgimp/gimp.h>
+
+static void xsane_gimp_query(void);
+static void xsane_gimp_run(char *name, int nparams, GParam * param, int *nreturn_vals, GParam ** return_vals);
+
+GPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ xsane_gimp_query, /* query_proc */
+ xsane_gimp_run, /* run_proc */
+};
+
+#endif /* HAVE_LIBGIMP_GIMP_H */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* forward declarations: */
+
+static int xsane_generate_dummy_filename();
+#ifdef HAVE_LIBGIMP_GIMP_H
+static int xsane_decode_devname(const char *encoded_devname, int n, char *buf);
+static int xsane_encode_devname(const char *devname, int n, char *buf);
+void null_print_func(gchar *msg);
+static void xsane_gimp_advance(void);
+#endif
+static void xsane_read_image_data(gpointer data, gint source, GdkInputCondition cond);
+static RETSIGTYPE xsane_sigpipe_handler(int signal);
+static int xsane_test_multi_scan(void);
+void xsane_scan_done(SANE_Status status);
+void xsane_cancel(void);
+static void xsane_start_scan(void);
+void xsane_scan_dialog(GtkWidget * widget, gpointer call_data);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_generate_dummy_filename()
+{
+ /* returns TRUE if file is a temporary file */
+
+ if (xsane.dummy_filename)
+ {
+ free(xsane.dummy_filename);
+ }
+
+ if ( (xsane.xsane_mode == XSANE_COPY) || (xsane.xsane_mode == XSANE_FAX) || /* we have to do a conversion */
+ ( (xsane.xsane_mode == XSANE_SCAN) && (xsane.xsane_output_format != XSANE_PNM) &&
+ (xsane.xsane_output_format != XSANE_RAW16) && (xsane.xsane_output_format != XSANE_RGBA) ) )
+ {
+ char filename[PATH_MAX];
+
+ xsane_back_gtk_make_path(sizeof(filename), filename, 0, 0, "conversion-", dialog->dev_name, ".ppm", XSANE_PATH_TMP);
+ xsane.dummy_filename = strdup(filename);
+ return TRUE;
+ }
+ else /* no conversion following, save directly to the selected filename */
+ {
+ xsane.dummy_filename = strdup(xsane.output_filename);
+ return FALSE;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+static int xsane_decode_devname(const char *encoded_devname, int n, char *buf)
+{
+ char *dst, *limit;
+ const char *src;
+ char ch, val;
+
+ limit = buf + n;
+ for (src = encoded_devname, dst = buf; *src; ++dst)
+ {
+ if (dst >= limit)
+ {
+ return -1;
+ }
+
+ ch = *src++;
+ /* don't use the ctype.h macros here since we don't want to allow anything non-ASCII here... */
+ if (ch != '-')
+ {
+ *dst = ch;
+ }
+ else /* decode */
+ {
+ ch = *src++;
+ if (ch == '-')
+ {
+ *dst = ch;
+ }
+ else
+ {
+ if (ch >= 'a' && ch <= 'f')
+ {
+ val = (ch - 'a') + 10;
+ }
+ else
+ {
+ val = (ch - '0');
+ }
+ val <<= 4;
+
+ ch = *src++;
+ if (ch >= 'a' && ch <= 'f')
+ {
+ val |= (ch - 'a') + 10;
+ }
+ else
+ {
+ val |= (ch - '0');
+ }
+
+ *dst = val;
+
+ ++src; /* simply skip terminating '-' for now... */
+ }
+ }
+ }
+
+ if (dst >= limit)
+ {
+ return -1;
+ }
+
+ *dst = '\0';
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_encode_devname(const char *devname, int n, char *buf)
+{
+ static const char hexdigit[] = "0123456789abcdef";
+ char *dst, *limit;
+ const char *src;
+ char ch;
+
+ limit = buf + n;
+ for (src = devname, dst = buf; *src; ++src)
+ {
+ if (dst >= limit)
+ {
+ return -1;
+ }
+
+ ch = *src;
+ /* don't use the ctype.h macros here since we don't want to allow anything non-ASCII here... */
+ if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
+ {
+ *dst++ = ch;
+ }
+ else /* encode */
+ {
+ if (dst + 4 >= limit)
+ {
+ return -1;
+ }
+
+ *dst++ = '-';
+ if (ch == '-')
+ {
+ *dst++ = '-';
+ }
+ else
+ {
+ *dst++ = hexdigit[(ch >> 4) & 0x0f];
+ *dst++ = hexdigit[(ch >> 0) & 0x0f];
+ *dst++ = '-';
+ }
+ }
+ }
+
+ if (dst >= limit)
+ {
+ return -1;
+ }
+
+ *dst = '\0';
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_gimp_query(void)
+{
+ static GParamDef args[] =
+ {
+ {PARAM_INT32, "run_mode", "Interactive, non-interactive"},
+ };
+ static GParamDef *return_vals = NULL;
+ static int nargs = sizeof(args) / sizeof(args[0]);
+ static int nreturn_vals = 0;
+ char mpath[1024];
+ char name[1024];
+ size_t len;
+ int i, j;
+
+ snprintf(name, sizeof(name), "%s", prog_name);
+#ifdef GIMP_CHECK_VERSION
+# if GIMP_CHECK_VERSION(1,1,9)
+ snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_DIALOG);
+# else
+ snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_DIALOG_OLD);
+# endif
+#else
+ snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_DIALOG_OLD);
+#endif
+ gimp_install_procedure(name,
+ XSANE_GIMP_INSTALL_BLURB,
+ XSANE_GIMP_INSTALL_HELP,
+ XSANE_AUTHOR,
+ XSANE_COPYRIGHT,
+ XSANE_DATE,
+ mpath,
+ 0, /* "RGB, GRAY", */
+ PROC_EXTENSION,
+ nargs, nreturn_vals,
+ args, return_vals);
+
+ sane_init(&xsane.sane_backend_versioncode, (void *) xsane_authorization_callback);
+ if (SANE_VERSION_MAJOR(xsane.sane_backend_versioncode) != SANE_V_MAJOR)
+ {
+ fprintf(stderr, "\n\n"
+ "%s %s:\n"
+ " %s\n"
+ " %s %d\n"
+ " %s %d\n"
+ "%s\n\n",
+ prog_name, ERR_ERROR,
+ ERR_MAJOR_VERSION_NR_CONFLICT,
+ ERR_XSANE_MAJOR_VERSION, SANE_V_MAJOR,
+ ERR_BACKEND_MAJOR_VERSION, SANE_VERSION_MAJOR(xsane.sane_backend_versioncode),
+ ERR_PROGRAM_ABORTED);
+ return;
+ }
+
+ sane_get_devices(&devlist, SANE_FALSE);
+
+ for (i = 0; devlist[i]; ++i)
+ {
+ snprintf(name, sizeof(name), "%s-", prog_name);
+ if (xsane_encode_devname(devlist[i]->name, sizeof(name) - 6, name + 6) < 0)
+ {
+ continue; /* name too long... */
+ }
+
+#ifdef GIMP_CHECK_VERSION
+# if GIMP_CHECK_VERSION(1,1,9)
+ snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU);
+# else
+ snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_OLD);
+# endif
+#else
+ snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_OLD);
+#endif
+ len = strlen(mpath);
+ for (j = 0; devlist[i]->name[j]; ++j)
+ {
+ if (devlist[i]->name[j] == '/')
+ mpath[len++] = '\'';
+ else
+ mpath[len++] = devlist[i]->name[j];
+ }
+ mpath[len++] = '\0';
+
+ gimp_install_procedure(name,
+ XSANE_GIMP_INSTALL_BLURB,
+ XSANE_GIMP_INSTALL_HELP,
+ XSANE_AUTHOR,
+ XSANE_COPYRIGHT,
+ XSANE_DATE,
+ mpath,
+ "RGB, GRAY",
+ PROC_EXTENSION,
+ nargs, nreturn_vals,
+ args, return_vals);
+ }
+ sane_exit();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_gimp_run(char *name, int nparams, GParam * param, int *nreturn_vals, GParam ** return_vals)
+{
+ static GParam values[2];
+ GRunModeType run_mode;
+ char devname[1024];
+ char *args[2];
+ int nargs;
+
+ run_mode = param[0].data.d_int32;
+ xsane.mode = XSANE_GIMP_EXTENSION;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = PARAM_STATUS;
+ values[0].data.d_status = STATUS_CALLING_ERROR;
+
+ nargs = 0;
+ args[nargs++] = "xsane";
+
+ seldev = -1;
+ if (strncmp(name, "xsane-", 6) == 0)
+ {
+ if (xsane_decode_devname(name + 6, sizeof(devname), devname) < 0)
+ {
+ return; /* name too long */
+ }
+ args[nargs++] = devname;
+ }
+
+ switch (run_mode)
+ {
+ case RUN_INTERACTIVE:
+ xsane_interface(nargs, args);
+ values[0].data.d_status = STATUS_SUCCESS;
+ break;
+
+ case RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ break;
+
+ case RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void null_print_func(gchar *msg)
+{
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_gimp_advance(void)
+{
+ if (++xsane.x >= xsane.param.pixels_per_line)
+ {
+ int tile_height = gimp_tile_height();
+
+ xsane.x = 0;
+ ++xsane.y;
+ if (xsane.y % tile_height == 0)
+ {
+ gimp_pixel_rgn_set_rect(&xsane.region, xsane.tile, 0, xsane.y - tile_height, xsane.param.pixels_per_line, tile_height);
+ if (xsane.param.format >= SANE_FRAME_RED && xsane.param.format <= SANE_FRAME_BLUE)
+ {
+ int height;
+
+ xsane.tile_offset %= 3;
+
+ if (!xsane.first_frame) /* get the data for the existing tile: */
+ {
+ height = tile_height;
+
+ if (xsane.y + height >= xsane.param.lines)
+ {
+ height = xsane.param.lines - xsane.y;
+ }
+
+ gimp_pixel_rgn_get_rect(&xsane.region, xsane.tile, 0, xsane.y, xsane.param.pixels_per_line, height);
+ }
+ }
+ else
+ {
+ xsane.tile_offset = 0;
+ }
+ }
+ }
+}
+
+#endif /* HAVE_LIBGIMP_GIMP_H */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_read_image_data(gpointer data, gint source, GdkInputCondition cond)
+{
+ SANE_Handle dev = xsane_back_gtk_dialog_get_device (dialog);
+ SANE_Status status;
+ SANE_Int len;
+ int i;
+ char buf[255];
+
+ if ( (xsane.param.depth == 1) || (xsane.param.depth == 8) )
+ {
+ static unsigned char buf8[32768];
+
+ while (1)
+ {
+ status = sane_read(dev, (SANE_Byte *) buf8, sizeof(buf8), &len);
+ if (status == SANE_STATUS_EOF)
+ {
+ if (!xsane.param.last_frame)
+ {
+ xsane_start_scan();
+ break; /* leave while loop */
+ }
+
+ xsane_scan_done(SANE_STATUS_EOF); /* image complete, stop scanning */
+ return;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ xsane_scan_done(status); /* status = return of sane_read */
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_DURING_READ, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ if (!len)
+ {
+ break; /* out of data for now, leave while loop */
+ }
+
+ xsane.bytes_read += len;
+ xsane_progress_update(xsane.progress, xsane.bytes_read / (gfloat) xsane.num_bytes);
+
+ if (xsane.input_tag < 0)
+ {
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ }
+
+ switch (xsane.param.format)
+ {
+ case SANE_FRAME_GRAY:
+ if (xsane.mode == XSANE_STANDALONE)
+ {
+ int i;
+ char val;
+
+ if ((!xsane.scanner_gamma_gray) && (xsane.param.depth > 1))
+ {
+ for (i=0; i < len; ++i)
+ {
+ val = xsane.gamma_data[(int) buf8[i]];
+ fwrite(&val, 1, 1, xsane.out);
+ }
+ }
+ else
+ {
+ fwrite(buf8, 1, len, xsane.out);
+ }
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else /* GIMP MODE GRAY 8 bit */
+ {
+ switch (xsane.param.depth)
+ {
+ case 1:
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask;
+ int j;
+
+ mask = buf8[i];
+ for (j = 7; j >= 0; --j)
+ {
+ u_char gl = (mask & (1 << j)) ? 0x00 : 0xff;
+ xsane.tile[xsane.tile_offset++] = gl;
+ xsane_gimp_advance();
+ if (xsane.x == 0)
+ {
+ break;
+ }
+ }
+ }
+ break;
+
+ case 8:
+ if (!xsane.scanner_gamma_gray)
+ {
+ for (i = 0; i < len; ++i)
+ {
+ xsane.tile[xsane.tile_offset++] = xsane.gamma_data[(int) buf8[i]];
+ xsane_gimp_advance();
+ }
+ }
+ else
+ {
+ for (i = 0; i < len; ++i)
+ {
+ xsane.tile[xsane.tile_offset++] = buf8[i];
+ xsane_gimp_advance();
+ }
+ }
+ break;
+ }
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+ break;
+
+ case SANE_FRAME_RGB:
+ if (xsane.mode == XSANE_STANDALONE)
+ {
+ int i;
+ char val;
+
+ if (!xsane.scanner_gamma_color) /* gamma correction by xsane */
+ {
+ for (i=0; i < len; ++i)
+ {
+ if (dialog->pixelcolor == 0)
+ {
+ val = xsane.gamma_data_red[(int) buf8[i]];
+ dialog->pixelcolor++;
+ }
+ else if (dialog->pixelcolor == 1)
+ {
+ val = xsane.gamma_data_green[(int) buf8[i]];
+ dialog->pixelcolor++;
+ }
+ else
+ {
+ val = xsane.gamma_data_blue[(int) buf8[i]];
+ dialog->pixelcolor = 0;
+ }
+ fwrite(&val, 1, 1, xsane.out);
+ }
+ }
+ else /* gamma correction has been done by scanner */
+ {
+ fwrite(buf8, 1, len, xsane.out);
+ }
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else /* GIMP MODE RGB 8 bit */
+ {
+ switch (xsane.param.depth)
+ {
+ case 1:
+ if (xsane.param.format == SANE_FRAME_RGB)
+ {
+ goto bad_depth;
+ }
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask;
+ int j;
+
+ mask = buf8[i];
+ for (j = 0; j < 8; ++j)
+ {
+ u_char gl = (mask & 1) ? 0xff : 0x00;
+ mask >>= 1;
+ xsane.tile[xsane.tile_offset++] = gl;
+ xsane_gimp_advance();
+ if (xsane.x == 0)
+ break;
+ }
+ }
+ break;
+
+ case 8:
+ if (!xsane.scanner_gamma_color) /* gamma correction by xsane */
+ {
+ for (i = 0; i < len; ++i)
+ {
+ if (xsane.tile_offset % 3 == 0)
+ {
+ xsane.tile[xsane.tile_offset++] = xsane.gamma_data_red[(int) buf8[i]];
+ }
+ else if (xsane.tile_offset % 3 == 1)
+ {
+ xsane.tile[xsane.tile_offset++] = xsane.gamma_data_green[(int) buf8[i]];
+ }
+ else
+ {
+ xsane.tile[xsane.tile_offset++] = xsane.gamma_data_blue[(int) buf8[i]];
+ }
+
+ if (xsane.tile_offset % 3 == 0)
+ {
+ xsane_gimp_advance();
+ }
+ }
+ }
+ else /* gamma correction by scanner */
+ {
+ for (i = 0; i < len; ++i)
+ {
+ xsane.tile[xsane.tile_offset++] = buf8[i];
+ if (xsane.tile_offset % 3 == 0)
+ {
+ xsane_gimp_advance();
+ }
+ }
+ }
+ break;
+
+ default:
+ goto bad_depth;
+ break;
+ }
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+ break;
+
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ if (xsane.mode == XSANE_STANDALONE)
+ {
+ if (!xsane.scanner_gamma_color) /* gamma correction by xsane */
+ {
+ char val;
+ SANE_Int *gamma;
+
+ if (xsane.param.format == SANE_FRAME_RED)
+ {
+ gamma = xsane.gamma_data_red;
+ }
+ else if (xsane.param.format == SANE_FRAME_GREEN)
+ {
+ gamma = xsane.gamma_data_green;
+ }
+ else
+ {
+ gamma = xsane.gamma_data_blue;
+ }
+
+ for (i = 0; i < len; ++i)
+ {
+ val = gamma[(int) buf8[i]];
+ fwrite(&val, 1, 1, xsane.out);
+ fseek(xsane.out, 2, SEEK_CUR);
+ }
+ }
+ else /* gamma correction by scanner */
+ {
+ for (i = 0; i < len; ++i)
+ {
+ fwrite(&buf8[i], 1, 1, xsane.out);
+ fseek(xsane.out, 2, SEEK_CUR);
+ }
+ }
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else /* GIMP MODE RED, GREEN, BLUE (3PASS) 8 bit */
+ {
+ switch (xsane.param.depth)
+ {
+ case 1:
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask;
+ int j;
+
+ mask = buf8[i];
+ for (j = 0; j < 8; ++j)
+ {
+ u_char gl = (mask & 1) ? 0xff : 0x00;
+ mask >>= 1;
+ xsane.tile[xsane.tile_offset] = gl;
+ xsane.tile_offset += 3;
+ xsane_gimp_advance();
+ if (xsane.x == 0)
+ {
+ break;
+ }
+ }
+ }
+ break;
+
+ case 8:
+ if (!xsane.scanner_gamma_color) /* gamma correction by xsane */
+ {
+ SANE_Int *gamma;
+
+ if (xsane.param.format == SANE_FRAME_RED)
+ {
+ gamma = xsane.gamma_data_red;
+ }
+ else if (xsane.param.format == SANE_FRAME_GREEN)
+ {
+ gamma = xsane.gamma_data_green;
+ }
+ else
+ {
+ gamma = xsane.gamma_data_blue;
+ }
+
+ for (i = 0; i < len; ++i)
+ {
+ xsane.tile[xsane.tile_offset] = gamma[(int) buf8[i]];
+ xsane.tile_offset += 3;
+ xsane_gimp_advance();
+ }
+ }
+ else /* gamma correction by scanner */
+ {
+ for (i = 0; i < len; ++i)
+ {
+ xsane.tile[xsane.tile_offset] = buf8[i];
+ xsane.tile_offset += 3;
+ xsane_gimp_advance();
+ }
+ }
+ break;
+
+ default:
+ goto bad_depth;
+ break;
+ }
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+ break;
+
+#ifdef SUPPORT_RGBA
+ case SANE_FRAME_RGBA: /* Scanning including Infrared channel */
+ if (xsane.mode == XSANE_STANDALONE)
+ {
+ int i;
+ char val;
+
+ if (!xsane.scanner_gamma_color) /* gamma correction by xsane */
+ {
+ for (i=0; i < len; ++i)
+ {
+ if (dialog->pixelcolor == 0)
+ {
+ val = xsane.gamma_data_red[(int) buf8[i]];
+ dialog->pixelcolor++;
+ }
+ else if (dialog->pixelcolor == 1)
+ {
+ val = xsane.gamma_data_green[(int) buf8[i]];
+ dialog->pixelcolor++;
+ }
+ else if (dialog->pixelcolor == 2)
+ {
+ val = xsane.gamma_data_blue[(int) buf8[i]];
+ dialog->pixelcolor++;
+ }
+ else
+ {
+ val = buf8[i]; /* no gamma table for infrared channel */
+ dialog->pixelcolor = 0;
+ }
+ fwrite(&val, 1, 1, xsane.out);
+ }
+ }
+ else /* gamma correction has been done by scanner */
+ {
+ fwrite(buf8, 1, len, xsane.out);
+ }
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else /* GIMP MODE RGBA 8 bit */
+ {
+ int i;
+
+
+ switch (xsane.param.depth)
+ {
+ case 8:
+ if (!xsane.scanner_gamma_color) /* gamma correction by xsane */
+ {
+ for (i=0; i < len; ++i)
+ {
+ if (xsane.tile_offset % 4 == 0)
+ {
+ xsane.tile[xsane.tile_offset++] = xsane.gamma_data_red[(int) buf8[i]];
+ }
+ else if (xsane.tile_offset % 4 == 1)
+ {
+ xsane.tile[xsane.tile_offset++] = xsane.gamma_data_green[(int) buf8[i]];
+ }
+ else if (xsane.tile_offset % 4 == 2)
+ {
+ xsane.tile[xsane.tile_offset++] = xsane.gamma_data_blue[(int) buf8[i]];
+ }
+ else
+ {
+ xsane.tile[xsane.tile_offset++] = buf8[i]; /* no gamma table for infrared channel */
+ }
+
+ if (xsane.tile_offset % 4 == 0)
+ {
+ xsane_gimp_advance();
+ }
+ }
+ }
+ else /* gamma correction has been done by scanner */
+ {
+ for (i = 0; i < len; ++i)
+ {
+ xsane.tile[xsane.tile_offset++] = buf8[i];
+ if (xsane.tile_offset % 4 == 0)
+ {
+ xsane_gimp_advance();
+ }
+ }
+ }
+ break;
+
+ default:
+ goto bad_depth;
+ break;
+ }
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+ break;
+#endif
+
+ default:
+ xsane_scan_done(-1); /* -1 = error */
+ fprintf(stderr, "xsane_read_image_data: %s %d\n", ERR_BAD_FRAME_FORMAT, xsane.param.format);
+ return;
+ break;
+ }
+ }
+ }
+ else if ( xsane.param.depth == 16 )
+ {
+ static guint16 buf16[32768];
+ char buf[255];
+ char last = 0;
+ int offset = 0;
+
+ while (1)
+ {
+ if (offset) /* if we have had an odd number of bytes */
+ {
+ buf16[0] = last;
+ status = sane_read(dev, (SANE_Byte *) (buf16 + 1), sizeof(buf16) - 1, &len);
+ if (len)
+ {
+ len++;
+ }
+ }
+ else /* last read we had an even number of bytes */
+ {
+ status = sane_read(dev, (SANE_Byte *) buf16, sizeof(buf16), &len);
+ }
+
+ if (len % 2) /* odd number of bytes */
+ {
+ len--;
+ last = buf16[len];
+ offset = 1;
+ }
+ else /* even number of bytes */
+ {
+ offset = 0;
+ }
+
+ if (status == SANE_STATUS_EOF)
+ {
+ if (!xsane.param.last_frame)
+ {
+ xsane_start_scan();
+ break; /* leave while loop */
+ }
+
+ xsane_scan_done(SANE_STATUS_EOF); /* image complete, stop scanning */
+ return;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ xsane_scan_done(status); /* status = return of sane_read */
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_DURING_READ, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ if (!len) /* nothing read */
+ {
+ break; /* out of data for now, leave while loop */
+ }
+
+ xsane.bytes_read += len;
+ xsane_progress_update(xsane.progress, xsane.bytes_read / (gfloat) xsane.num_bytes);
+
+ if (xsane.input_tag < 0)
+ {
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ }
+
+ switch (xsane.param.format)
+ {
+ case SANE_FRAME_GRAY:
+ if (xsane.mode == XSANE_STANDALONE)
+ {
+ int i;
+ guint16 val;
+
+ if (!xsane.scanner_gamma_gray) /* gamma correction by xsane */
+ {
+ for (i=0; i < len/2; ++i)
+ {
+ val = xsane.gamma_data[buf16[i]];
+ fwrite(&val, 2, 1, xsane.out);
+ }
+ }
+ else /* gamma correction by scanner */
+ {
+ fwrite(buf16, 2, len/2, xsane.out);
+ }
+ }
+ break;
+
+ case SANE_FRAME_RGB:
+ if (xsane.mode == XSANE_STANDALONE)
+ {
+ int i;
+ guint16 val;
+
+ if (!xsane.scanner_gamma_color) /* gamma correction by xsane */
+ {
+ for (i=0; i < len/2; ++i)
+ {
+ if (dialog->pixelcolor == 0)
+ {
+ val = xsane.gamma_data_red[buf16[i]];
+ dialog->pixelcolor++;
+ }
+ else if (dialog->pixelcolor == 1)
+ {
+ val = xsane.gamma_data_green[buf16[i]];
+ dialog->pixelcolor++;
+ }
+ else
+ {
+ val = xsane.gamma_data_blue[buf16[i]];
+ dialog->pixelcolor = 0;
+ }
+ fwrite(&val, 2, 1, xsane.out);
+ }
+ }
+ else /* gamma correction by scanner */
+ {
+ fwrite(buf16, 2, len/2, xsane.out);
+ }
+ }
+ break;
+
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ if (xsane.mode == XSANE_STANDALONE)
+ {
+ for (i = 0; i < len/2; ++i)
+ {
+ fwrite(buf16 + i*2, 2, 1, xsane.out);
+ fseek(xsane.out, 4, SEEK_CUR);
+ }
+ }
+ break;
+
+#ifdef SUPPORT_RGBA
+ case SANE_FRAME_RGBA:
+ if (xsane.mode == XSANE_STANDALONE)
+ {
+ int i;
+ guint16 val;
+
+ if (!xsane.scanner_gamma_color)
+ {
+ for (i=0; i < len/2; ++i)
+ {
+ if (dialog->pixelcolor == 0)
+ {
+ val = xsane.gamma_data_red[buf16[i]];
+ dialog->pixelcolor++;
+ }
+ else if (dialog->pixelcolor == 1)
+ {
+ val = xsane.gamma_data_green[buf16[i]];
+ dialog->pixelcolor++;
+ }
+ else if (dialog->pixelcolor == 2)
+ {
+ val = xsane.gamma_data_blue[buf16[i]];
+ dialog->pixelcolor++;
+ }
+ else
+ {
+ val = buf16[i]; /* no gamma table for infrared channel */
+ dialog->pixelcolor = 0;
+ }
+ fwrite(&val, 2, 1, xsane.out);
+ }
+ }
+ else
+ {
+ fwrite(buf16, 2, len/2, xsane.out);
+ }
+ }
+ break;
+#endif
+
+ default:
+ xsane_scan_done(-1); /* -1 = error */
+ fprintf(stderr, "xsane_read_image_data: %s %d\n", ERR_BAD_FRAME_FORMAT, xsane.param.format);
+ return;
+ break;
+ }
+ }
+ }
+ else
+ {
+ xsane_scan_done(-1); /* -1 = error */
+ snprintf(buf, sizeof(buf), "%s %d.", ERR_BAD_DEPTH, xsane.param.depth);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ return;
+
+ /* ---------------------- */
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+bad_depth:
+
+ xsane_scan_done(-1); /* -1 = error */
+ snprintf(buf, sizeof(buf), "%s %d.", ERR_BAD_DEPTH, xsane.param.depth);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+#endif
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static RETSIGTYPE xsane_sigpipe_handler(int signal)
+/* this is to catch a broken pipe while writing to printercommand */
+{
+ xsane_cancel_save(0);
+ xsane.broken_pipe = 1;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_test_multi_scan(void)
+{
+ char *set;
+ SANE_Status status;
+ const SANE_Option_Descriptor *opt;
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.scansource);
+ if (opt)
+ {
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ if (opt->constraint_type == SANE_CONSTRAINT_STRING_LIST)
+ {
+ set = malloc(opt->size);
+ status = sane_control_option(dialog->dev, dialog->well_known.scansource, SANE_ACTION_GET_VALUE, set, 0);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ if (!strcmp(set, SANE_NAME_DOCUMENT_FEEDER))
+ {
+ return TRUE;
+ }
+ }
+ free(set);
+ }
+ }
+ }
+
+#if 0 /* this is planned for the next sane-standard */
+ if (xsane.param.bitfield & XSANE_PARAM_STATUS_MORE_IMAGES)
+ {
+ return TRUE;
+ }
+#endif
+
+ return FALSE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_scan_done(SANE_Status status)
+{
+ if (xsane.input_tag >= 0)
+ {
+ gdk_input_remove(xsane.input_tag);
+ xsane.input_tag = -1;
+ }
+
+ if (xsane.progress) /* remove progressbar */
+ {
+ xsane_progress_free(xsane.progress);
+ xsane.progress = 0;
+ }
+
+ while(gtk_events_pending()) /* let gtk remove the progress bar and update everything that needs it */
+ {
+ gtk_main_iteration();
+ }
+
+
+ /* we have to free the gamma tables if we used software gamma correction */
+
+ if (xsane.gamma_data)
+ {
+ free(xsane.gamma_data);
+ xsane.gamma_data = 0;
+ }
+
+ if (xsane.gamma_data_red)
+ {
+ free(xsane.gamma_data_red);
+ free(xsane.gamma_data_green);
+ free(xsane.gamma_data_blue);
+
+ xsane.gamma_data_red = 0;
+ xsane.gamma_data_green = 0;
+ xsane.gamma_data_blue = 0;
+ }
+
+ if (xsane.out) /* close file - this is dummy_file but if there is no conversion it is the wanted file */
+ {
+ fclose(xsane.out);
+ xsane.out = 0;
+ }
+
+ if ( (status == SANE_STATUS_GOOD) || (status == SANE_STATUS_EOF) ) /* no error, do conversion etc. */
+ {
+ if (xsane.mode == XSANE_STANDALONE)
+ {
+ if ( (xsane.xsane_mode == XSANE_SCAN) && (xsane.xsane_output_format != XSANE_PNM) &&
+ (xsane.xsane_output_format != XSANE_RAW16) && (xsane.xsane_output_format != XSANE_RGBA) )
+ {
+ FILE *outfile;
+ FILE *infile;
+ char buf[256];
+
+ /* open progressbar */
+ snprintf(buf, sizeof(buf), PROGRESS_SAVING);
+ xsane.progress = xsane_progress_new(PROGRESS_CONVERTING_DATA, buf, (GtkSignalFunc) xsane_cancel_save, 0);
+ xsane_progress_update(xsane.progress, 0);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ infile = fopen(xsane.dummy_filename, "r");
+ if (infile != 0)
+ {
+ fseek(infile, xsane.header_size, SEEK_SET);
+
+#ifdef HAVE_LIBTIFF
+ if (xsane.xsane_output_format == XSANE_TIFF) /* routines that want to have filename for saving */
+ {
+ if (xsane.param.depth != 1)
+ {
+ remove(xsane.output_filename);
+ umask(preferences.image_umask); /* define image file permissions */
+ xsane_save_tiff(xsane.output_filename, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line,
+ xsane.param.lines, preferences.tiff_compression_nr, preferences.jpeg_quality);
+ umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
+ }
+ else
+ {
+ remove(xsane.output_filename);
+ umask(preferences.image_umask); /* define image file permissions */
+ xsane_save_tiff(xsane.output_filename, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line,
+ xsane.param.lines, preferences.tiff_compression_1_nr, preferences.jpeg_quality);
+ umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
+ }
+ }
+ else /* routines that want to have filedescriptor for saving */
+#endif
+ {
+ remove(xsane.output_filename);
+ umask(preferences.image_umask); /* define image file permissions */
+ outfile = fopen(xsane.output_filename, "w");
+ umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
+
+ if (outfile != 0)
+ {
+ switch(xsane.xsane_output_format)
+ {
+#ifdef HAVE_LIBJPEG
+ case XSANE_JPEG:
+ xsane_save_jpeg(outfile, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line,
+ xsane.param.lines, preferences.jpeg_quality);
+ break;
+#endif
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+ case XSANE_PNG:
+ if (xsane.param.depth <= 8)
+ {
+ xsane_save_png(outfile, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line,
+ xsane.param.lines, preferences.png_compression);
+ }
+ else
+ {
+ xsane_save_png_16(outfile, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line,
+ xsane.param.lines, preferences.png_compression);
+ }
+ break;
+#endif
+#endif
+
+ case XSANE_PNM16:
+ xsane_save_pnm_16(outfile, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line,
+ xsane.param.lines);
+ break;
+
+ case XSANE_PS: /* save postscript, use original size */
+ {
+ float imagewidth = xsane.param.pixels_per_line/xsane.resolution_x; /* width in inch */
+ float imageheight = xsane.param.lines/xsane.resolution_y; /* height in inch */
+
+ if (preferences.psrotate) /* rotate: landscape */
+ {
+ xsane_save_ps(outfile, infile,
+ xsane.xsane_color /* gray, color */,
+ xsane.param.depth /* bits */,
+ xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */
+ (preferences.printer[preferences.printernr]->bottomoffset +
+ preferences.printer[preferences.printernr]->height) * 36.0/MM_PER_INCH - imagewidth * 36.0, /* left edge */
+ (preferences.printer[preferences.printernr]->leftoffset +
+ preferences.printer[preferences.printernr]->width) * 36.0/MM_PER_INCH - imageheight * 36.0, /* bottom edge */
+ imagewidth, imageheight,
+ (preferences.printer[preferences.printernr]->leftoffset +
+ preferences.printer[preferences.printernr]->width ) * 72.0/MM_PER_INCH, /* paperwidth */
+ (preferences.printer[preferences.printernr]->bottomoffset +
+ preferences.printer[preferences.printernr]->height) * 72.0/MM_PER_INCH, /* paperheight */
+ 1 /* landscape */);
+ }
+ else /* do not rotate: portrait */
+ {
+ xsane_save_ps(outfile, infile,
+ xsane.xsane_color /* gray, color */,
+ xsane.param.depth /* bits */,
+ xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */
+ (preferences.printer[preferences.printernr]->leftoffset +
+ preferences.printer[preferences.printernr]->width) * 36.0/MM_PER_INCH - imagewidth * 36.0,
+ (preferences.printer[preferences.printernr]->bottomoffset +
+ preferences.printer[preferences.printernr]->height) * 36.0/MM_PER_INCH - imageheight * 36.0,
+ imagewidth, imageheight,
+ (preferences.printer[preferences.printernr]->leftoffset +
+ preferences.printer[preferences.printernr]->width ) * 72.0/MM_PER_INCH, /* paperwidth */
+ (preferences.printer[preferences.printernr]->bottomoffset +
+ preferences.printer[preferences.printernr]->height) * 72.0/MM_PER_INCH, /* paperheight */
+ 0 /* portrait */);
+ }
+ }
+ break;
+
+
+ default:
+ snprintf(buf, sizeof(buf),"%s", ERR_UNKNOWN_SAVING_FORMAT);
+ xsane_back_gtk_error(buf, TRUE);
+ break;
+ }
+ fclose(outfile);
+ }
+ else
+ {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.output_filename, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ }
+ }
+ fclose(infile);
+ remove(xsane.dummy_filename);
+ }
+ else
+ {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.output_filename, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ }
+ xsane_progress_free(xsane.progress);
+ xsane.progress = 0;
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ }
+ else if (xsane.xsane_mode == XSANE_COPY)
+ {
+ FILE *outfile;
+ FILE *infile;
+ char buf[256];
+
+ xsane_update_int(xsane.copy_number_entry, &xsane.copy_number); /* get number of copies */
+ if (xsane.copy_number < 1)
+ {
+ xsane.copy_number = 1;
+ }
+
+ /* open progressbar */
+ snprintf(buf, sizeof(buf), PROGRESS_CONVERTING_PS);
+ xsane.progress = xsane_progress_new(PROGRESS_CONVERTING_DATA, buf, (GtkSignalFunc) xsane_cancel_save, 0);
+ xsane_progress_update(xsane.progress, 0);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ xsane.broken_pipe = 0;
+ infile = fopen(xsane.dummy_filename, "r");
+
+ snprintf(buf, sizeof(buf), "%s %s%d", preferences.printer[preferences.printernr]->command,
+ preferences.printer[preferences.printernr]->copy_number_option,
+ xsane.copy_number);
+ outfile = popen(buf, "w");
+/* outfile = popen(preferences.printer[preferences.printernr]->command, "w"); */
+ if ((outfile != 0) && (infile != 0)) /* copy mode, use zoom size */
+ {
+ struct SIGACTION act;
+ float imagewidth = xsane.param.pixels_per_line/(float)preferences.printer[preferences.printernr]->resolution; /* width in inch */
+ float imageheight = xsane.param.lines/(float)preferences.printer[preferences.printernr]->resolution; /* height in inch */
+
+ memset (&act, 0, sizeof (act)); /* define broken pipe handler */
+ act.sa_handler = xsane_sigpipe_handler;
+ sigaction (SIGPIPE, &act, 0);
+
+
+ fseek(infile, xsane.header_size, SEEK_SET);
+
+ if (preferences.psrotate) /* rotate: landscape */
+ {
+ xsane_save_ps(outfile, infile,
+ xsane.xsane_color /* gray, color */,
+ xsane.param.depth /* bits */,
+ xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */
+ (preferences.printer[preferences.printernr]->bottomoffset +
+ preferences.printer[preferences.printernr]->height) * 36.0/MM_PER_INCH - imagewidth * 36.0, /* left edge */
+ (preferences.printer[preferences.printernr]->leftoffset +
+ preferences.printer[preferences.printernr]->width) * 36.0/MM_PER_INCH - imageheight * 36.0, /* bottom edge */
+ imagewidth, imageheight,
+ (preferences.printer[preferences.printernr]->leftoffset +
+ preferences.printer[preferences.printernr]->width ) * 72.0/MM_PER_INCH, /* paperwidth */
+ (preferences.printer[preferences.printernr]->bottomoffset +
+ preferences.printer[preferences.printernr]->height) * 72.0/MM_PER_INCH, /* paperheight */
+ 1 /* landscape */);
+ }
+ else /* do not rotate: portrait */
+ {
+ xsane_save_ps(outfile, infile,
+ xsane.xsane_color /* gray, color */,
+ xsane.param.depth /* bits */,
+ xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */
+ (preferences.printer[preferences.printernr]->leftoffset +
+ preferences.printer[preferences.printernr]->width) * 36.0/MM_PER_INCH - imagewidth * 36.0, /* left edge */
+ (preferences.printer[preferences.printernr]->bottomoffset +
+ preferences.printer[preferences.printernr]->height) * 36.0/MM_PER_INCH - imageheight * 36.0, /* bottom edge */
+ imagewidth, imageheight,
+ (preferences.printer[preferences.printernr]->leftoffset +
+ preferences.printer[preferences.printernr]->width ) * 72.0/MM_PER_INCH, /* paperwidth */
+ (preferences.printer[preferences.printernr]->bottomoffset +
+ preferences.printer[preferences.printernr]->height) * 72.0/MM_PER_INCH, /* paperheight */
+ 0 /* portrait */);
+ }
+ }
+ else
+ {
+ char buf[256];
+
+ if (!infile)
+ {
+ snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.output_filename, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ }
+ else if (!outfile)
+ {
+ xsane_back_gtk_error(ERR_FAILED_PRINTER_PIPE, TRUE);
+ }
+ }
+
+ if (xsane.broken_pipe)
+ {
+ snprintf(buf, sizeof(buf), "%s \"%s\"", ERR_FAILED_EXEC_PRINTER_CMD, preferences.printer[preferences.printernr]->command);
+ xsane_back_gtk_error(buf, TRUE);
+ }
+
+ xsane_progress_free(xsane.progress);
+ xsane.progress = 0;
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ if (infile)
+ {
+ fclose(infile);
+ remove(xsane.dummy_filename);
+ }
+
+ if (outfile)
+ {
+ pclose(outfile);
+ }
+ }
+ else if (xsane.xsane_mode == XSANE_FAX)
+ {
+ FILE *outfile;
+ FILE *infile;
+ char buf[256];
+
+ /* open progressbar */
+ snprintf(buf, sizeof(buf), PROGRESS_SAVING_FAX);
+ xsane.progress = xsane_progress_new(PROGRESS_CONVERTING_DATA, buf, (GtkSignalFunc) xsane_cancel_save, 0);
+ xsane_progress_update(xsane.progress, 0);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ infile = fopen(xsane.dummy_filename, "r");
+ if (infile != 0)
+ {
+ fseek(infile, xsane.header_size, SEEK_SET);
+
+ umask(preferences.image_umask); /* define image file permissions */
+ outfile = fopen(xsane.fax_filename, "w");
+ umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
+ if (outfile != 0)
+ {
+ float imagewidth, imageheight;
+
+ imagewidth = xsane.param.pixels_per_line/xsane.resolution_x; /* width in inch */
+ imageheight = xsane.param.lines/xsane.resolution_y; /* height in inch */
+
+/* disabled ( 0 * ...) in the moment */
+ if (0 * preferences.psrotate) /* rotate: landscape */
+ {
+ xsane_save_ps(outfile, infile,
+ xsane.xsane_color /* gray, color */,
+ xsane.param.depth /* bits */,
+ xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */
+ (preferences.fax_bottomoffset + preferences.fax_height) * 36.0/MM_PER_INCH - imagewidth * 36.0, /* left edge */
+ (preferences.fax_leftoffset + preferences.fax_width) * 36.0/MM_PER_INCH - imageheight * 36.0, /* bottom edge */
+ imagewidth, imageheight,
+ (preferences.fax_leftoffset + preferences.fax_width ) * 72.0/MM_PER_INCH, /* paperwidth */
+ (preferences.fax_bottomoffset + preferences.fax_height) * 72.0/MM_PER_INCH, /* paperheight */
+ 1 /* landscape */);
+ }
+ else /* do not rotate: portrait */
+ {
+ xsane_save_ps(outfile, infile,
+ xsane.xsane_color /* gray, color */,
+ xsane.param.depth /* bits */,
+ xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */
+ (preferences.fax_leftoffset + preferences.fax_width) * 36.0/MM_PER_INCH - imagewidth * 36.0,
+ (preferences.fax_bottomoffset + preferences.fax_height) * 36.0/MM_PER_INCH - imageheight * 36.0,
+ imagewidth, imageheight,
+ (preferences.fax_leftoffset + preferences.fax_width ) * 72.0/MM_PER_INCH, /* paperwidth */
+ (preferences.fax_bottomoffset + preferences.fax_height) * 72.0/MM_PER_INCH, /* paperheight */
+ 0 /* portrait */);
+ }
+ fclose(outfile);
+ }
+ else
+ {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.fax_filename, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ }
+
+ fclose(infile);
+ remove(xsane.dummy_filename);
+ }
+ else
+ {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.fax_filename, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ }
+ xsane_progress_free(xsane.progress);
+ xsane.progress = 0;
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ }
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else
+ {
+ int remaining;
+
+ /* GIMP mode */
+ if (xsane.y > xsane.param.lines)
+ {
+ xsane.y = xsane.param.lines;
+ }
+
+ remaining = xsane.y % gimp_tile_height();
+ if (remaining)
+ {
+ gimp_pixel_rgn_set_rect(&xsane.region, xsane.tile, 0, xsane.y - remaining, xsane.param.pixels_per_line, remaining);
+ }
+ gimp_drawable_flush(xsane.drawable);
+ gimp_display_new(xsane.image_ID);
+ gimp_drawable_detach(xsane.drawable);
+ free(xsane.tile);
+ xsane.tile = 0;
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+
+ xsane.header_size = 0;
+
+ if ( (preferences.increase_filename_counter) && (xsane.xsane_mode == XSANE_SCAN) && (xsane.mode == XSANE_STANDALONE) )
+ {
+ xsane_increase_counter_in_filename(preferences.filename, preferences.skip_existing_numbers);
+ gtk_entry_set_text(GTK_ENTRY(xsane.outputfilename_entry), (char *) preferences.filename);
+ }
+ else if (xsane.xsane_mode == XSANE_FAX)
+ {
+ GtkWidget *list_item;
+ char *page;
+ char *extension;
+
+ page = strdup(strrchr(xsane.fax_filename,'/')+1);
+ extension = strrchr(page, '.');
+ if (extension)
+ {
+ *extension = 0;
+ }
+ list_item = gtk_list_item_new_with_label(page);
+ gtk_object_set_data(GTK_OBJECT(list_item), "list_item_data", strdup(page));
+ gtk_container_add(GTK_CONTAINER(xsane.fax_list), list_item);
+ gtk_widget_show(list_item);
+
+ xsane_increase_counter_in_filename(xsane.fax_filename, preferences.skip_existing_numbers);
+ xsane_fax_project_save();
+ free(page);
+ }
+ }
+ else /* an error occured, remove the dummy_file */
+ {
+ if (xsane.dummy_filename) /* remove corrupt file */
+ {
+ remove(xsane.dummy_filename);
+ }
+ }
+
+ free(xsane.dummy_filename); /* no dummy_filename, needed if an error occurs */
+ xsane.dummy_filename = 0;
+
+ if (xsane.output_filename)
+ {
+ free(xsane.output_filename);
+ xsane.output_filename = 0;
+ }
+
+ if ( ( (status == SANE_STATUS_GOOD) || (status == SANE_STATUS_EOF) ) && (xsane_test_multi_scan()) )
+ {
+ /* multi scan (eg ADF): scan again */
+ /* stopped when: */
+ /* a) xsane_test_multi_scan returns false */
+ /* b) sane_start returns SANE_STATUS_NO_DOCS */
+ /* c) an error occurs */
+
+ gtk_signal_emit_by_name(xsane.start_button, "clicked"); /* press START button */
+ }
+ else /* last scan: update histogram */
+ {
+ xsane_set_sensitivity(TRUE); /* reactivate buttons etc */
+ sane_cancel(xsane_back_gtk_dialog_get_device(dialog)); /* stop scanning */
+ xsane_update_histogram();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_cancel(void)
+{
+ sane_cancel(xsane_back_gtk_dialog_get_device(dialog));
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_start_scan(void)
+{
+ SANE_Status status;
+ SANE_Handle dev = xsane_back_gtk_dialog_get_device(dialog);
+ const char *frame_type = 0;
+ char buf[256];
+ int fd;
+
+ xsane_clear_histogram(&xsane.histogram_raw);
+ xsane_clear_histogram(&xsane.histogram_enh);
+ xsane_set_sensitivity(FALSE);
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+ if (xsane.mode == XSANE_GIMP_EXTENSION && xsane.tile)
+ {
+ int height, remaining;
+
+ /* write the last tile of the frame to the GIMP region: */
+
+ if (xsane.y > xsane.param.lines) /* sanity check */
+ {
+ xsane.y = xsane.param.lines;
+ }
+
+ remaining = xsane.y % gimp_tile_height();
+ if (remaining)
+ {
+ gimp_pixel_rgn_set_rect(&xsane.region, xsane.tile, 0, xsane.y - remaining, xsane.param.pixels_per_line, remaining);
+ }
+
+ /* initialize the tile with the first tile of the GIMP region: */
+
+ height = gimp_tile_height();
+ if (height >= xsane.param.lines)
+ {
+ height = xsane.param.lines;
+ }
+ gimp_pixel_rgn_get_rect(&xsane.region, xsane.tile, 0, 0, xsane.param.pixels_per_line, height);
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+
+ xsane.x = xsane.y = 0;
+
+ status = sane_start(dev);
+
+ if (status == SANE_STATUS_NO_DOCS) /* ADF out of docs */
+ {
+ xsane_scan_done(status); /* ok, stop multi image scan */
+ return;
+ }
+ else if (status != SANE_STATUS_GOOD) /* error */
+ {
+ xsane_scan_done(status);
+ snprintf(buf, sizeof(buf), "%s %s", ERR_FAILED_START_SCANNER, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ status = sane_get_parameters(dev, &xsane.param);
+ if (status != SANE_STATUS_GOOD)
+ {
+ xsane_scan_done(status);
+ snprintf(buf, sizeof(buf), "%s %s", ERR_FAILED_GET_PARAMS, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ xsane.num_bytes = xsane.param.lines * xsane.param.bytes_per_line;
+ xsane.bytes_read = 0;
+
+ switch (xsane.param.format)
+ {
+ case SANE_FRAME_RGB: frame_type = "RGB"; break;
+ case SANE_FRAME_RED: frame_type = "red"; break;
+ case SANE_FRAME_GREEN: frame_type = "green"; break;
+ case SANE_FRAME_BLUE: frame_type = "blue"; break;
+ case SANE_FRAME_GRAY: frame_type = "gray"; break;
+#ifdef SUPPORT_RGBA
+ case SANE_FRAME_RGBA: frame_type = "RGBA"; break;
+#endif
+ default: frame_type = "unknown"; break;
+ }
+
+ if (xsane.mode == XSANE_STANDALONE)
+ { /* We are running in standalone mode */
+ if (xsane_generate_dummy_filename()) /* create filename the scanned data is saved to */
+ {
+ /* temporary file */
+ umask(0177); /* creare temporary file with "-rw-------" permissions */
+ }
+ else
+ {
+ /* no temporary file */
+ umask(preferences.image_umask); /* define image file permissions */
+ }
+
+ if (!xsane.header_size) /* first pass of multi pass scan */
+ {
+ remove(xsane.dummy_filename); /* remove existing file */
+ xsane.out = fopen(xsane.dummy_filename, "w");
+ umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
+
+ if (!xsane.out) /* error while opening the dummy_file for writing */
+ {
+ xsane_scan_done(-1); /* -1 = error */
+ snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.output_filename, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ switch (xsane.param.format)
+ {
+ case SANE_FRAME_RGB:
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ switch (xsane.param.depth)
+ {
+ case 8: /* color 8 bit mode, write ppm header */
+ fprintf(xsane.out, "P6\n# SANE data follows\n%d %d\n255\n", xsane.param.pixels_per_line, xsane.param.lines);
+ break;
+
+ default: /* color, but not 8 bit mode, write as raw data because this is not defined in pnm */
+ fprintf(xsane.out, "SANE_RGB_RAW\n%d %d\n65535\n", xsane.param.pixels_per_line, xsane.param.lines);
+ break;
+ }
+ break;
+
+ case SANE_FRAME_GRAY:
+ switch (xsane.param.depth)
+ {
+ case 1: /* 1 bit lineart mode, write pbm header */
+ fprintf(xsane.out, "P4\n# SANE data follows\n%d %d\n", xsane.param.pixels_per_line, xsane.param.lines);
+ break;
+
+ case 8: /* 8 bit grayscale mode, write pgm header */
+ fprintf(xsane.out, "P5\n# SANE data follows\n%d %d\n255\n", xsane.param.pixels_per_line, xsane.param.lines);
+ break;
+
+ default: /* grayscale mode but not 1 or 8 bit, write as raw data because this is not defined in pnm */
+ fprintf(xsane.out, "SANE_GRAYSCALE_RAW\n%d %d\n65535\n", xsane.param.pixels_per_line, xsane.param.lines);
+ break;
+ }
+ break;
+
+#ifdef SUPPORT_RGBA
+ case SANE_FRAME_RGBA:
+ switch (xsane.param.depth)
+ {
+ case 8: /* 8 bit RGBA mode */
+ fprintf(xsane.out, "SANE_RGBA\n%d %d\n255\n", xsane.param.pixels_per_line, xsane.param.lines);
+ break;
+
+ default: /* 16 bit RGBA mode */
+ fprintf(xsane.out, "SANE_RGBA\n%d %d\n65535\n", xsane.param.pixels_per_line, xsane.param.lines);
+ break;
+ }
+ break;
+#endif
+
+ default:
+ /* unknown file format, do not write header */
+ break;
+ }
+ fflush(xsane.out);
+ xsane.header_size = ftell(xsane.out);
+ }
+
+ if (xsane.param.format >= SANE_FRAME_RED && xsane.param.format <= SANE_FRAME_BLUE)
+ {
+ fseek(xsane.out, xsane.header_size + xsane.param.format - SANE_FRAME_RED, SEEK_SET);
+ }
+
+ if (xsane.xsane_mode == XSANE_SCAN)
+ {
+ snprintf(buf, sizeof(buf), PROGRESS_RECEIVING_SCAN, _(frame_type), xsane.output_filename);
+ }
+ else if (xsane.xsane_mode == XSANE_COPY)
+ {
+ snprintf(buf, sizeof(buf), PROGRESS_RECEIVING_COPY, _(frame_type));
+ }
+ else if (xsane.xsane_mode == XSANE_FAX)
+ {
+ snprintf(buf, sizeof(buf), PROGRESS_RECEIVING_FAX, _(frame_type));
+ }
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else
+ {
+ size_t tile_size;
+
+ /* We are running under the GIMP */
+
+ xsane.tile_offset = 0;
+ tile_size = xsane.param.pixels_per_line * gimp_tile_height();
+
+ switch(xsane.param.format)
+ {
+ case SANE_FRAME_RGB:
+ case SANE_FRAME_RED:
+ case SANE_FRAME_BLUE:
+ case SANE_FRAME_GREEN:
+ tile_size *= 3; /* 24 bits/pixel RGB */
+ break;
+#ifdef SUPPORT_RGBA
+ case SANE_FRAME_RGBA:
+ tile_size *= 4; /* 32 bits/pixel RGBA */
+ break;
+#endif
+ default:
+ break;
+ }
+
+ if (xsane.tile)
+ {
+ xsane.first_frame = 0;
+ }
+ else
+ {
+ GImageType image_type = RGB;
+ GDrawableType drawable_type = RGB_IMAGE;
+ gint32 layer_ID;
+
+ if (xsane.param.format == SANE_FRAME_GRAY)
+ {
+ image_type = GRAY;
+ drawable_type = GRAY_IMAGE;
+ }
+#ifdef SUPPORT_RGBA
+ else if (xsane.param.format == SANE_FRAME_RGBA)
+ {
+ image_type = RGB;
+ drawable_type = RGBA_IMAGE; /* interpret infrared as alpha */
+ }
+#endif
+
+
+ xsane.image_ID = gimp_image_new(xsane.param.pixels_per_line, xsane.param.lines, image_type);
+
+/* the following is supported since gimp-1.1.? */
+#ifdef GIMP_HAVE_RESOLUTION_INFO
+ if (xsane.resolution_x > 0)
+ {
+ gimp_image_set_resolution(xsane.image_ID, xsane.resolution_x ,xsane.resolution_y);
+ }
+/* gimp_image_set_unit(xsane.image_ID, unit?); */
+#endif
+
+ layer_ID = gimp_layer_new(xsane.image_ID, "Background",
+ xsane.param.pixels_per_line,
+ xsane.param.lines,
+ drawable_type, 100, NORMAL_MODE);
+ gimp_image_add_layer(xsane.image_ID, layer_ID, 0);
+
+ xsane.drawable = gimp_drawable_get(layer_ID);
+ gimp_pixel_rgn_init(&xsane.region, xsane.drawable, 0, 0,
+ xsane.drawable->width,
+ xsane.drawable->height, TRUE, FALSE);
+ xsane.tile = g_new(guchar, tile_size);
+ xsane.first_frame = 1;
+ }
+
+ if (xsane.param.format >= SANE_FRAME_RED && xsane.param.format <= SANE_FRAME_BLUE)
+ {
+ xsane.tile_offset = xsane.param.format - SANE_FRAME_RED;
+ }
+
+ snprintf(buf, sizeof(buf), PROGRESS_RECEIVING_GIMP, _(frame_type));
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+
+ dialog->pixelcolor = 0;
+
+ if (xsane.progress)
+ {
+ xsane_progress_free(xsane.progress);
+ }
+ xsane.progress = xsane_progress_new(PROGRESS_SCANNING, buf, (GtkSignalFunc) xsane_cancel, 0);
+
+ xsane.input_tag = -1;
+
+ if (sane_set_io_mode(dev, SANE_TRUE) == SANE_STATUS_GOOD && sane_get_select_fd(dev, &fd) == SANE_STATUS_GOOD)
+ {
+ xsane.input_tag = gdk_input_add(fd, GDK_INPUT_READ, xsane_read_image_data, 0);
+ }
+ else
+ {
+ xsane_read_image_data(0, -1, GDK_INPUT_READ);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* Invoked when the scan button is pressed */
+/* or by scan_done if automatic document feeder is selected */
+void xsane_scan_dialog(GtkWidget * widget, gpointer call_data)
+{
+ char buf[256];
+
+ sane_get_parameters(dialog->dev, &xsane.param); /* update xsane.param */
+
+ if (xsane.output_filename)
+ {
+ free(xsane.output_filename);
+ xsane.output_filename = 0;
+ }
+
+ if (xsane.filetype)
+ {
+ char buffer[256];
+
+ snprintf(buffer, sizeof(buffer), "%s%s", preferences.filename, xsane.filetype);
+ xsane.output_filename = strdup(buffer);
+ }
+ else
+ {
+ xsane.output_filename = strdup(preferences.filename);
+ }
+
+ if (xsane.mode == XSANE_STANDALONE) /* We are running in standalone mode */
+ {
+ char *extension;
+
+ if ( (xsane.xsane_mode == XSANE_SCAN) && (preferences.overwrite_warning) ) /* test if filename already used */
+ {
+ FILE *testfile;
+
+ testfile = fopen(xsane.output_filename, "r");
+ if (testfile) /* filename used: skip */
+ {
+ char buf[256];
+
+ fclose(testfile);
+ snprintf(buf, sizeof(buf), "File %s already exists\n", xsane.output_filename);
+ if (xsane_back_gtk_decision(ERR_HEADER_WARNING, (gchar **) warning_xpm, buf, BUTTON_OVERWRITE, BUTTON_CANCEL, TRUE /* wait */) == FALSE)
+ {
+ return;
+ }
+ }
+ }
+
+
+ extension = strrchr(xsane.output_filename, '.');
+ if (extension)
+ {
+ extension++; /* skip "." */
+ }
+
+ xsane.xsane_output_format = XSANE_UNKNOWN;
+
+ if (xsane.param.depth <= 8)
+ {
+ if (extension)
+ {
+ if ( (!strcasecmp(extension, "pnm")) || (!strcasecmp(extension, "ppm")) ||
+ (!strcasecmp(extension, "pgm")) || (!strcasecmp(extension, "pbm")) )
+ {
+ xsane.xsane_output_format = XSANE_PNM;
+ }
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+ else if (!strcasecmp(extension, "png"))
+ {
+ xsane.xsane_output_format = XSANE_PNG;
+ }
+#endif
+#endif
+#ifdef HAVE_LIBJPEG
+ else if ( (!strcasecmp(extension, "jpg")) || (!strcasecmp(extension, "jpeg")) )
+ {
+ xsane.xsane_output_format = XSANE_JPEG;
+ }
+#endif
+ else if (!strcasecmp(extension, "ps"))
+ {
+ xsane.xsane_output_format = XSANE_PS;
+ }
+#ifdef HAVE_LIBTIFF
+ else if ( (!strcasecmp(extension, "tif")) || (!strcasecmp(extension, "tiff")) )
+ {
+ xsane.xsane_output_format = XSANE_TIFF;
+ }
+#endif
+#ifdef SUPPORT_RGBA
+ else if (!strcasecmp(extension, "rgba"))
+ {
+ xsane.xsane_output_format = XSANE_RGBA;
+ }
+#endif
+ }
+ }
+ else /* depth >8 bpp */
+ {
+ if (extension)
+ {
+ if (!strcasecmp(extension, "raw"))
+ {
+ xsane.xsane_output_format = XSANE_RAW16;
+ }
+ else if ( (!strcasecmp(extension, "pnm")) || (!strcasecmp(extension, "ppm")) ||
+ (!strcasecmp(extension, "pgm")) || (!strcasecmp(extension, "pbm")) )
+ {
+ xsane.xsane_output_format = XSANE_PNM16;
+ }
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+ else if (!strcasecmp(extension, "png"))
+ {
+ xsane.xsane_output_format = XSANE_PNG;
+ }
+#endif
+#endif
+#ifdef SUPPORT_RGBA
+ else if (!strcasecmp(extension, "rgba"))
+ {
+ xsane.xsane_output_format = XSANE_RGBA;
+ }
+#endif
+ }
+ }
+
+ if (xsane.xsane_mode == XSANE_SCAN)
+ {
+ if (xsane.xsane_output_format == XSANE_UNKNOWN)
+ {
+ if (extension)
+ {
+ snprintf(buf, sizeof(buf), "Unsupported %d-bit output format: %s", xsane.param.depth, extension);
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), "%s", ERR_NO_OUTPUT_FORMAT);
+ }
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+#ifdef SUPPORT_RGBA
+ else if ((xsane.xsane_output_format == XSANE_RGBA) && (xsane.param.format != SANE_FRAME_RGBA))
+ {
+ snprintf(buf, sizeof(buf), "No RGBA data format !!!"); /* user selected output format RGBA, scanner uses other format */
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+#endif
+ }
+#ifdef SUPPORT_RGBA
+ else if (xsane.param.format == SANE_FRAME_RGBA) /* no scanmode but format=rgba */
+ {
+ snprintf(buf, sizeof(buf), "Special format RGBA only supported in scan mode !!!");
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+#endif
+
+#ifdef SUPPORT_RGBA
+ if (xsane.param.format == SANE_FRAME_RGBA)
+ {
+ if ( (xsane.xsane_output_format != XSANE_RGBA) && (xsane.xsane_output_format != XSANE_PNG) )
+ {
+ snprintf(buf, sizeof(buf), "Image data of type SANE_FRAME_RGBA\ncan only be saved in rgba or png format");
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+ }
+#endif
+
+ if (xsane.xsane_mode == XSANE_FAX)
+ {
+ mkdir(preferences.fax_project, 7*64 + 0*8 + 0);
+ }
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else /* We are running in gimp mode */
+ {
+ if ((xsane.param.depth != 1) && (xsane.param.depth != 8)) /* not support bit depth ? */
+ {
+ snprintf(buf, sizeof(buf), "%s %d.", ERR_GIMP_BAD_DEPTH, xsane.param.depth);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+ }
+#endif
+
+ if (xsane.dummy_filename) /* no dummy filename defined - necessary if an error occurs */
+ {
+ free(xsane.dummy_filename);
+ xsane.dummy_filename = 0;
+ }
+
+ if (xsane.param.depth > 1) /* if depth > 1 use gamma correction */
+ {
+ int size;
+ int gamma_gray_size, gamma_red_size, gamma_green_size, gamma_blue_size;
+ int gamma_gray_max, gamma_red_max, gamma_green_max, gamma_blue_max;
+ const SANE_Option_Descriptor *opt;
+
+ size = (int) pow(2, xsane.param.depth);
+ gamma_gray_size = size;
+ gamma_red_size = size;
+ gamma_green_size = size;
+ gamma_blue_size = size;
+
+ size--;
+ gamma_gray_max = size;
+ gamma_red_max = size;
+ gamma_green_max = size;
+ gamma_blue_max = size;
+
+ if (xsane.scanner_gamma_gray) /* gamma table for gray available */
+ {
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector);
+ gamma_gray_size = opt->size / sizeof(opt->type);
+ gamma_gray_max = opt->constraint.range->max;
+ }
+
+ if (xsane.scanner_gamma_color) /* gamma table for red, green and blue available */
+ {
+ double gamma_red, gamma_green, gamma_blue;
+
+ /* ok, scanner color gamma function is supported, so we do all conversions about that */
+ /* we do not need any gamma tables while scanning, so we can free them after sending */
+ /* the data to the scanner */
+
+ /* if also gray gamma function is supported, set this to 1.0 to get the right colors */
+ if (xsane.scanner_gamma_gray)
+ {
+ xsane.gamma_data = malloc(gamma_gray_size * sizeof(SANE_Int));
+ xsane_create_gamma_curve(xsane.gamma_data, 0, 1.0, 0.0, 0.0, gamma_gray_size, gamma_gray_max);
+ xsane_back_gtk_update_vector(dialog, dialog->well_known.gamma_vector, xsane.gamma_data);
+ free(xsane.gamma_data);
+ xsane.gamma_data = 0;
+ }
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector_r);
+ gamma_red_size = opt->size / sizeof(opt->type);
+ gamma_red_max = opt->constraint.range->max;
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector_g);
+ gamma_green_size = opt->size / sizeof(opt->type);
+ gamma_green_max = opt->constraint.range->max;
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector_b);
+ gamma_blue_size = opt->size / sizeof(opt->type);
+ gamma_blue_max = opt->constraint.range->max;
+
+ xsane.gamma_data_red = malloc(gamma_red_size * sizeof(SANE_Int));
+ xsane.gamma_data_green = malloc(gamma_green_size * sizeof(SANE_Int));
+ xsane.gamma_data_blue = malloc(gamma_blue_size * sizeof(SANE_Int));
+
+ if (xsane.xsane_mode == XSANE_COPY)
+ {
+ gamma_red = xsane.gamma * xsane.gamma_red * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_red;
+ gamma_green = xsane.gamma * xsane.gamma_green * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_green;
+ gamma_blue = xsane.gamma * xsane.gamma_blue * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_blue;
+ }
+ else
+ {
+ gamma_red = xsane.gamma * xsane.gamma_red;
+ gamma_green = xsane.gamma * xsane.gamma_green;
+ gamma_blue = xsane.gamma * xsane.gamma_blue;
+ }
+
+ xsane_create_gamma_curve(xsane.gamma_data_red, xsane.negative,
+ gamma_red,
+ xsane.brightness + xsane.brightness_red,
+ xsane.contrast + xsane.contrast_red, gamma_red_size, gamma_red_max);
+
+ xsane_create_gamma_curve(xsane.gamma_data_green, xsane.negative,
+ gamma_green,
+ xsane.brightness + xsane.brightness_green,
+ xsane.contrast + xsane.contrast_green, gamma_green_size, gamma_green_max);
+
+ xsane_create_gamma_curve(xsane.gamma_data_blue, xsane.negative,
+ gamma_blue,
+ xsane.brightness + xsane.brightness_blue,
+ xsane.contrast + xsane.contrast_blue , gamma_blue_size, gamma_blue_max);
+
+ xsane_back_gtk_update_vector(dialog, dialog->well_known.gamma_vector_r, xsane.gamma_data_red);
+ xsane_back_gtk_update_vector(dialog, dialog->well_known.gamma_vector_g, xsane.gamma_data_green);
+ xsane_back_gtk_update_vector(dialog, dialog->well_known.gamma_vector_b, xsane.gamma_data_blue);
+
+ free(xsane.gamma_data_red);
+ free(xsane.gamma_data_green);
+ free(xsane.gamma_data_blue);
+
+ xsane.gamma_data_red = 0;
+ xsane.gamma_data_green = 0;
+ xsane.gamma_data_blue = 0;
+ }
+ else if (xsane.scanner_gamma_gray) /* only scanner gray gamma function available */
+ {
+ double gamma;
+ /* ok, the scanner only supports gray gamma function */
+ /* if we are doing a grayscale scan everyting is ok, */
+ /* for a color scan the software has to do the gamma correction set by the component slider */
+
+ if (xsane.xsane_mode == XSANE_COPY)
+ {
+ gamma = xsane.gamma * preferences.printer[preferences.printernr]->gamma;
+ }
+ else
+ {
+ gamma = xsane.gamma;
+ }
+
+ xsane.gamma_data = malloc(gamma_gray_size * sizeof(SANE_Int));
+ xsane_create_gamma_curve(xsane.gamma_data, xsane.negative,
+ gamma, xsane.brightness, xsane.contrast,
+ gamma_gray_size, gamma_gray_max);
+
+ xsane_back_gtk_update_vector(dialog, dialog->well_known.gamma_vector, xsane.gamma_data);
+ free(xsane.gamma_data);
+ xsane.gamma_data = 0;
+
+ if (xsane.xsane_color) /* ok, we are doing a colorscan */
+ {
+ /* we have to create color gamma table for software conversion */
+ /* but we only have to use color slider values, because gray slider value */
+ /* is used by scanner gray gamma */
+
+ double gamma_red, gamma_green, gamma_blue;
+
+ xsane.gamma_data_red = malloc(gamma_red_size * sizeof(SANE_Int));
+ xsane.gamma_data_green = malloc(gamma_green_size * sizeof(SANE_Int));
+ xsane.gamma_data_blue = malloc(gamma_blue_size * sizeof(SANE_Int));
+
+ if (xsane.xsane_mode == XSANE_COPY)
+ {
+ gamma_red = xsane.gamma_red * preferences.printer[preferences.printernr]->gamma_red;
+ gamma_green = xsane.gamma_green * preferences.printer[preferences.printernr]->gamma_green;
+ gamma_blue = xsane.gamma_blue * preferences.printer[preferences.printernr]->gamma_blue;
+ }
+ else
+ {
+ gamma_red = xsane.gamma_red;
+ gamma_green = xsane.gamma_green;
+ gamma_blue = xsane.gamma_blue;
+ }
+
+ xsane_create_gamma_curve(xsane.gamma_data_red, 0,
+ gamma_red, xsane.brightness_red, xsane.contrast_red,
+ gamma_red_size, gamma_red_max);
+
+ xsane_create_gamma_curve(xsane.gamma_data_green, 0,
+ gamma_green, xsane.brightness_green, xsane.contrast_green,
+ gamma_green_size, gamma_green_max);
+
+ xsane_create_gamma_curve(xsane.gamma_data_blue, 0,
+ gamma_blue, xsane.brightness_blue, xsane.contrast_blue,
+ gamma_blue_size, gamma_blue_max);
+
+ /* gamma tables are freed after scan */
+ }
+
+ }
+ else /* scanner does not support any gamma correction */
+ {
+ /* ok, we have to do it on our own */
+
+ if (xsane.xsane_color == 0) /* no color scan */
+ {
+ double gamma;
+
+ if (xsane.xsane_mode == XSANE_COPY)
+ {
+ gamma = xsane.gamma * preferences.printer[preferences.printernr]->gamma;
+ }
+ else
+ {
+ gamma = xsane.gamma;
+ }
+
+ xsane.gamma_data = malloc(gamma_gray_size * sizeof(SANE_Int));
+ xsane_create_gamma_curve(xsane.gamma_data, xsane.negative,
+ gamma, xsane.brightness, xsane.contrast,
+ gamma_gray_size, gamma_gray_max);
+
+ /* gamma table is freed after scan */
+ }
+ else /* color scan */
+ {
+ double gamma_red, gamma_green, gamma_blue;
+ /* ok, we have to combin gray and color slider values */
+
+ xsane.gamma_data_red = malloc(gamma_red_size * sizeof(SANE_Int));
+ xsane.gamma_data_green = malloc(gamma_green_size * sizeof(SANE_Int));
+ xsane.gamma_data_blue = malloc(gamma_blue_size * sizeof(SANE_Int));
+
+ if (xsane.xsane_mode == XSANE_COPY)
+ {
+ gamma_red = xsane.gamma * xsane.gamma_red * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_red;
+ gamma_green = xsane.gamma * xsane.gamma_green * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_green;
+ gamma_blue = xsane.gamma * xsane.gamma_blue * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_blue;
+ }
+ else
+ {
+ gamma_red = xsane.gamma * xsane.gamma_red;
+ gamma_green = xsane.gamma * xsane.gamma_green;
+ gamma_blue = xsane.gamma * xsane.gamma_blue;
+ }
+
+ xsane_create_gamma_curve(xsane.gamma_data_red, xsane.negative,
+ gamma_red,
+ xsane.brightness + xsane.brightness_red,
+ xsane.contrast + xsane.contrast_red, gamma_red_size, gamma_red_max);
+
+ xsane_create_gamma_curve(xsane.gamma_data_green, xsane.negative,
+ gamma_green,
+ xsane.brightness + xsane.brightness_green,
+ xsane.contrast + xsane.contrast_green, gamma_green_size, gamma_green_max);
+
+ xsane_create_gamma_curve(xsane.gamma_data_blue, xsane.negative,
+ gamma_blue,
+ xsane.brightness + xsane.brightness_blue,
+ xsane.contrast + xsane.contrast_blue , gamma_blue_size, gamma_blue_max);
+
+ /* gamma tables are freed after scan */
+ }
+
+ }
+ }
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ xsane_start_scan();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
diff --git a/frontend/xsane-scan.h b/frontend/xsane-scan.h
new file mode 100644
index 0000000..75c5ff8
--- /dev/null
+++ b/frontend/xsane-scan.h
@@ -0,0 +1,35 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-scan.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include "sane/config.h"
+#include <sane/sane.h>
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+extern void null_print_func(gchar *msg);
+extern void xsane_scan_done(SANE_Status status);
+extern void xsane_cancel(void);
+extern void xsane_scan_dialog(GtkWidget * widget, gpointer call_data);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
diff --git a/frontend/xsane-setup.c b/frontend/xsane-setup.c
new file mode 100644
index 0000000..637ea91
--- /dev/null
+++ b/frontend/xsane-setup.c
@@ -0,0 +1,1565 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-setup.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+#include "xsane-back-gtk.h"
+#include "xsane-front-gtk.h"
+#include "xsane-preferences.h"
+#include "xsane-preview.h"
+#include "xsane-save.h"
+#include "xsane-text.h"
+#include "xsane-gamma.h"
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+#include <png.h>
+#include <zlib.h>
+#endif
+#endif
+
+#ifdef HAVE_LIBTIFF
+#include <tiff.h>
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#define XSANE_GTK_NAME_IMAGE_PERMISSIONS "gtk_toggle_button_image_permissions"
+#define XSANE_GTK_NAME_DIRECTORY_PERMISSIONS "gtk_toggle_button_directory_permissions"
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+struct XsaneSetup xsane_setup;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* forward declarations: */
+
+void xsane_new_printer(void);
+void xsane_update_int(GtkWidget *widget, int *val);
+
+static void xsane_update_bool(GtkWidget *widget, int *val);
+static void xsane_update_scale(GtkWidget *widget, double *val);
+static void xsane_update_double(GtkWidget *widget, double *val);
+static void xsane_setup_printer_update(void);
+static void xsane_setup_printer_callback(GtkWidget *widget, gpointer data);
+static void xsane_setup_printer_menu_build(GtkWidget *option_menu);
+static void xsane_setup_printer_apply_changes(GtkWidget *widget, gpointer data);
+static void xsane_setup_printer_new(GtkWidget *widget, gpointer data);
+static void xsane_setup_printer_delete(GtkWidget *widget, gpointer data);
+static void xsane_setup_display_apply_changes(GtkWidget *widget, gpointer data);
+static void xsane_setup_saving_apply_changes(GtkWidget *widget, gpointer data);
+static void xsane_setup_fax_apply_changes(GtkWidget *widget, gpointer data);
+static void xsane_setup_options_ok_callback(GtkWidget *widget, gpointer data);
+
+void xsane_setup_dialog(GtkWidget *widget, gpointer data);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_new_printer(void)
+{
+ preferences.printernr = preferences.printerdefinitions++;
+
+ preferences.printer[preferences.printernr] = calloc(sizeof(Preferences_printer_t), 1);
+
+ preferences.printer[preferences.printernr]->name = strdup(PRINTERNAME);
+ preferences.printer[preferences.printernr]->command = strdup(PRINTERCOMMAND);
+ preferences.printer[preferences.printernr]->copy_number_option = strdup(PRINTERCOPYNUMBEROPTION);
+ preferences.printer[preferences.printernr]->resolution = 300;
+ preferences.printer[preferences.printernr]->width = 203.2;
+ preferences.printer[preferences.printernr]->height = 294.6;
+ preferences.printer[preferences.printernr]->leftoffset = 3.5;
+ preferences.printer[preferences.printernr]->bottomoffset = 3.5;
+ preferences.printer[preferences.printernr]->gamma = 1.0;
+ preferences.printer[preferences.printernr]->gamma_red = 1.0;
+ preferences.printer[preferences.printernr]->gamma_green = 1.0;
+ preferences.printer[preferences.printernr]->gamma_blue = 1.0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_update_int(GtkWidget *widget, int *val)
+{
+ char *start, *end;
+ int v;
+
+ start = gtk_entry_get_text(GTK_ENTRY(widget));
+ if (!start)
+ return;
+
+ v = (int) strtol(start, &end, 10);
+ if (end > start && v > 0)
+ {
+ *val = v;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_update_bool(GtkWidget *widget, int *val)
+{
+ *val = (GTK_TOGGLE_BUTTON(widget)->active != 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_update_scale(GtkWidget *widget, double *val)
+{
+ *val = GTK_ADJUSTMENT(widget)->value;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_update_double(GtkWidget *widget, double *val)
+{
+ char *start, *end;
+ double v;
+
+ start = gtk_entry_get_text(GTK_ENTRY(widget));
+ if (!start)
+ return;
+
+ v = strtod(start, &end);
+ if (end > start && v > 0.0)
+ {
+ *val = v;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_setup_printer_update()
+{
+ char buf[256];
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_name_entry),
+ (char *) preferences.printer[preferences.printernr]->name);
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_command_entry),
+ (char *) preferences.printer[preferences.printernr]->command);
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_copy_number_option_entry),
+ (char *) preferences.printer[preferences.printernr]->copy_number_option);
+
+ snprintf(buf, sizeof(buf), "%d", preferences.printer[preferences.printernr]->resolution);
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_resolution_entry), buf);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.printer[preferences.printernr]->width);
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_width_entry), buf);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.printer[preferences.printernr]->height);
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_height_entry), buf);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.printer[preferences.printernr]->leftoffset);
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_leftoffset_entry), buf);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.printer[preferences.printernr]->bottomoffset);
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_bottomoffset_entry), buf);
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.printer[preferences.printernr]->gamma);
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_gamma_entry), buf);
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.printer[preferences.printernr]->gamma_red);
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_gamma_red_entry), buf);
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.printer[preferences.printernr]->gamma_green);
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_gamma_green_entry), buf);
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.printer[preferences.printernr]->gamma_blue);
+ gtk_entry_set_text(GTK_ENTRY(xsane_setup.printer_gamma_blue_entry), buf);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_setup_printer_callback(GtkWidget *widget, gpointer data)
+{
+ preferences.printernr = (int) data;
+ xsane_setup_printer_update();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_setup_printer_menu_build(GtkWidget *option_menu)
+{
+ GtkWidget *printer_menu, *printer_item;
+ int i;
+
+ printer_menu = gtk_menu_new();
+
+ for (i=0; i < preferences.printerdefinitions; i++)
+ {
+ printer_item = gtk_menu_item_new_with_label(preferences.printer[i]->name);
+ gtk_container_add(GTK_CONTAINER(printer_menu), printer_item);
+ gtk_signal_connect(GTK_OBJECT(printer_item), "activate", (GtkSignalFunc) xsane_setup_printer_callback, (void *) i);
+ gtk_widget_show(printer_item);
+ }
+
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), printer_menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), preferences.printernr);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_setup_printer_apply_changes(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *option_menu = (GtkWidget *) data;
+
+ if (preferences.printer[preferences.printernr]->name)
+ {
+ free((void *) preferences.printer[preferences.printernr]->name);
+ }
+ preferences.printer[preferences.printernr]->name = strdup(gtk_entry_get_text(GTK_ENTRY(xsane_setup.printer_name_entry)));
+
+ if (preferences.printer[preferences.printernr]->command)
+ {
+ free((void *) preferences.printer[preferences.printernr]->command);
+ }
+ preferences.printer[preferences.printernr]->command = strdup(gtk_entry_get_text(GTK_ENTRY(xsane_setup.printer_command_entry)));
+
+ if (preferences.printer[preferences.printernr]->copy_number_option)
+ {
+ free((void *) preferences.printer[preferences.printernr]->copy_number_option);
+ }
+ preferences.printer[preferences.printernr]->copy_number_option =
+ strdup(gtk_entry_get_text(GTK_ENTRY(xsane_setup.printer_copy_number_option_entry)));
+
+ xsane_update_int(xsane_setup.printer_resolution_entry, &preferences.printer[preferences.printernr]->resolution);
+
+ xsane_update_double(xsane_setup.printer_width_entry, &preferences.printer[preferences.printernr]->width);
+ xsane_update_double(xsane_setup.printer_height_entry, &preferences.printer[preferences.printernr]->height);
+ xsane_update_double(xsane_setup.printer_leftoffset_entry, &preferences.printer[preferences.printernr]->leftoffset);
+ xsane_update_double(xsane_setup.printer_bottomoffset_entry, &preferences.printer[preferences.printernr]->bottomoffset);
+
+ xsane_update_double(xsane_setup.printer_gamma_entry, &preferences.printer[preferences.printernr]->gamma);
+ xsane_update_double(xsane_setup.printer_gamma_red_entry, &preferences.printer[preferences.printernr]->gamma_red);
+ xsane_update_double(xsane_setup.printer_gamma_green_entry, &preferences.printer[preferences.printernr]->gamma_green);
+ xsane_update_double(xsane_setup.printer_gamma_blue_entry, &preferences.printer[preferences.printernr]->gamma_blue);
+
+ if (option_menu)
+ {
+ xsane_setup_printer_menu_build(option_menu);
+ }
+
+ xsane_define_maximum_output_size();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_setup_printer_new(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *option_menu = (GtkWidget *) data;
+
+ xsane_new_printer();
+ xsane_setup_printer_update();
+
+ xsane_setup_printer_menu_build(option_menu);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_setup_printer_delete(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *option_menu = (GtkWidget *) data;
+ int i;
+
+ preferences.printerdefinitions--;
+
+ i = preferences.printernr;
+ while (i < preferences.printerdefinitions)
+ {
+ memcpy(preferences.printer[i], preferences.printer[i+1], sizeof(Preferences_printer_t));
+ i++;
+ }
+
+ if (preferences.printernr >= preferences.printerdefinitions)
+ {
+ preferences.printernr--;
+ }
+
+ if (preferences.printerdefinitions == 0)
+ {
+ xsane_new_printer();
+ preferences.printernr = 0;
+ }
+
+ xsane_setup_printer_update();
+
+ xsane_setup_printer_menu_build(option_menu);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBTIFF
+static void xsane_setup_tiff_compression_callback(GtkWidget *widget, gpointer data)
+{
+ xsane_setup.tiff_compression_nr = (int) data;
+}
+
+/* -------------------------------------- */
+
+static void xsane_setup_tiff_compression_1_callback(GtkWidget *widget, gpointer data)
+{
+ xsane_setup.tiff_compression_1_nr = (int) data;
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_setup_display_apply_changes(GtkWidget *widget, gpointer data)
+{
+ xsane_update_bool(xsane_setup.main_window_fixed_button, &preferences.main_window_fixed);
+ xsane_update_bool(xsane_setup.preview_preserve_button, &preferences.preserve_preview);
+ xsane_update_bool(xsane_setup.preview_own_cmap_button, &preferences.preview_own_cmap);
+
+ xsane_update_double(xsane_setup.preview_gamma_entry, &preferences.preview_gamma);
+ xsane_update_double(xsane_setup.preview_gamma_red_entry, &preferences.preview_gamma_red);
+ xsane_update_double(xsane_setup.preview_gamma_green_entry, &preferences.preview_gamma_green);
+ xsane_update_double(xsane_setup.preview_gamma_blue_entry, &preferences.preview_gamma_blue);
+
+ preferences.doc_viewer = strdup(gtk_entry_get_text(GTK_ENTRY(xsane_setup.doc_viewer_entry)));
+
+ xsane_update_gamma();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_setup_saving_apply_changes(GtkWidget *widget, gpointer data)
+{
+#ifdef HAVE_LIBJPEG
+ xsane_update_scale(xsane_setup.jpeg_image_quality_scale, &preferences.jpeg_quality);
+#else
+#ifdef HAVE_LIBTIFF
+ xsane_update_scale(xsane_setup.jpeg_image_quality_scale, &preferences.jpeg_quality);
+#endif
+#endif
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+ xsane_update_scale(xsane_setup.pnm_image_compression_scale, &preferences.png_compression);
+#endif
+#endif
+
+#ifdef HAVE_LIBTIFF
+ preferences.tiff_compression_nr = xsane_setup.tiff_compression_nr;
+ preferences.tiff_compression_1_nr = xsane_setup.tiff_compression_1_nr;
+#endif
+
+ xsane_update_bool(xsane_setup.overwrite_warning_button, &preferences.overwrite_warning);
+ xsane_update_bool(xsane_setup.increase_filename_counter_button, &preferences.increase_filename_counter);
+ xsane_update_bool(xsane_setup.skip_existing_numbers_button, &preferences.skip_existing_numbers);
+ preferences.image_umask = 0777 - xsane_setup.image_permissions;
+ preferences.directory_umask = 0777 - xsane_setup.directory_permissions;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_setup_fax_apply_changes(GtkWidget *widget, gpointer data)
+{
+ if (preferences.fax_command)
+ {
+ free((void *) preferences.fax_command);
+ }
+ preferences.fax_command = strdup(gtk_entry_get_text(GTK_ENTRY(xsane_setup.fax_command_entry)));
+ preferences.fax_receiver_option = strdup(gtk_entry_get_text(GTK_ENTRY(xsane_setup.fax_receiver_option_entry)));
+ preferences.fax_postscript_option = strdup(gtk_entry_get_text(GTK_ENTRY(xsane_setup.fax_postscript_option_entry)));
+ preferences.fax_normal_option = strdup(gtk_entry_get_text(GTK_ENTRY(xsane_setup.fax_normal_option_entry)));
+ preferences.fax_fine_option = strdup(gtk_entry_get_text(GTK_ENTRY(xsane_setup.fax_fine_option_entry)));
+ preferences.fax_viewer = strdup(gtk_entry_get_text(GTK_ENTRY(xsane_setup.fax_viewer_entry)));
+
+ xsane_update_double(xsane_setup.fax_leftoffset_entry, &preferences.fax_leftoffset);
+ xsane_update_double(xsane_setup.fax_bottomoffset_entry, &preferences.fax_bottomoffset);
+ xsane_update_double(xsane_setup.fax_width_entry, &preferences.fax_width);
+ xsane_update_double(xsane_setup.fax_height_entry, &preferences.fax_height);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_setup_options_ok_callback(GtkWidget *widget, gpointer data)
+{
+ xsane_setup_printer_apply_changes(0, 0);
+ xsane_setup_display_apply_changes(0, 0);
+ xsane_setup_saving_apply_changes(0, 0);
+ xsane_setup_fax_apply_changes(0, 0);
+
+ xsane_pref_save();
+
+ gtk_widget_destroy((GtkWidget *)data); /* => xsane_destroy_setup_dialog_callback */
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_destroy_setup_dialog_callback(GtkWidget *widget, gpointer data)
+{
+ xsane_set_sensitivity(TRUE);
+ xsane_back_gtk_refresh_dialog(dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_close_setup_dialog_callback(GtkWidget *widget, gpointer data)
+{
+ gtk_widget_destroy((GtkWidget *)data); /* => xsane_destroy_setup_dialog_callback */
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_permission_toggled(GtkWidget *widget, gpointer data)
+{
+ int mask = (int) data;
+ int *permission = 0;
+ gchar *name = gtk_widget_get_name(widget);
+
+ if (!strcmp(name, XSANE_GTK_NAME_IMAGE_PERMISSIONS))
+ {
+ permission = &xsane_setup.image_permissions;
+ }
+ else if (!strcmp(name, XSANE_GTK_NAME_DIRECTORY_PERMISSIONS))
+ {
+ permission = &xsane_setup.directory_permissions;
+ }
+
+ if (permission)
+ {
+ if (GTK_TOGGLE_BUTTON(widget)->active) /* set bit */
+ {
+ *permission = *permission | mask;
+ }
+ else /* erase bit */
+ {
+ *permission = *permission & (0777-mask);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_permission_box(GtkWidget *parent, gchar *name, gchar *description, int *permission,
+ int header, int x_sensitivity, int user_sensitivity)
+{
+ GtkWidget *hbox, *button, *label, *hspace;
+
+
+ if (header)
+ {
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new("user");
+ gtk_widget_set_usize(label, 75, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ label = gtk_label_new("group");
+ gtk_widget_set_usize(label, 75, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ label = gtk_label_new("all");
+ gtk_widget_set_usize(label, 75, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ gtk_widget_show(hbox);
+ }
+
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 2);
+
+ button = gtk_toggle_button_new_with_label("r");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *permission & 256 );
+ gtk_widget_set_usize(button, 21, 0);
+ gtk_widget_set_name(button, name);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_permission_toggled, (void *) 256);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
+ gtk_widget_show(button);
+ gtk_widget_set_sensitive(button, user_sensitivity);
+
+ button = gtk_toggle_button_new_with_label("w");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *permission & 128 );
+ gtk_widget_set_usize(button, 21, 0);
+ gtk_widget_set_name(button, name);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_permission_toggled, (void *) 128);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
+ gtk_widget_show(button);
+ gtk_widget_set_sensitive(button, user_sensitivity);
+
+ button = gtk_toggle_button_new_with_label("x");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *permission & 64 );
+ gtk_widget_set_usize(button, 21, 0);
+ gtk_widget_set_name(button, name);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_permission_toggled, (void *) 64);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
+ gtk_widget_show(button);
+ gtk_widget_set_sensitive(button, x_sensitivity & user_sensitivity);
+
+
+
+ hspace = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), hspace, FALSE, FALSE, 6);
+ gtk_widget_show(hspace);
+
+
+
+ button = gtk_toggle_button_new_with_label("r");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *permission & 32 );
+ gtk_widget_set_usize(button, 21, 0);
+ gtk_widget_set_name(button, name);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_permission_toggled, (void *) 32);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
+ gtk_widget_show(button);
+
+ button = gtk_toggle_button_new_with_label("w");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *permission & 16 );
+ gtk_widget_set_usize(button, 21, 0);
+ gtk_widget_set_name(button, name);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_permission_toggled, (void *) 16);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
+ gtk_widget_show(button);
+
+ button = gtk_toggle_button_new_with_label("x");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *permission & 8 );
+ gtk_widget_set_usize(button, 21, 0);
+ gtk_widget_set_name(button, name);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_permission_toggled, (void *) 8);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
+ gtk_widget_show(button);
+ gtk_widget_set_sensitive(button, x_sensitivity);
+
+
+
+ hspace = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), hspace, FALSE, FALSE, 6);
+ gtk_widget_show(hspace);
+
+
+
+ button = gtk_toggle_button_new_with_label("r");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *permission & 4 );
+ gtk_widget_set_usize(button, 21, 0);
+ gtk_widget_set_name(button, name);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_permission_toggled, (void *) 4);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
+ gtk_widget_show(button);
+
+ button = gtk_toggle_button_new_with_label("w");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *permission & 2 );
+ gtk_widget_set_usize(button, 21, 0);
+ gtk_widget_set_name(button, name);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_permission_toggled, (void *) 2);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
+ gtk_widget_show(button);
+
+ button = gtk_toggle_button_new_with_label("x");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *permission & 1 );
+ gtk_widget_set_usize(button, 21, 0);
+ gtk_widget_set_name(button, name);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_permission_toggled, (void *) 1);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
+ gtk_widget_show(button);
+ gtk_widget_set_sensitive(button, x_sensitivity);
+
+
+
+ hspace = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), hspace, FALSE, FALSE, 5);
+ gtk_widget_show(hspace);
+
+
+
+ label = gtk_label_new(description);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ gtk_widget_show(hbox);
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_setup_dialog(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *setup_dialog, *setup_vbox, *vbox, *hbox, *button, *label, *text, *frame, *notebook;
+ GtkWidget *printer_option_menu;
+ char buf[64];
+
+#ifdef HAVE_LIBTIFF
+ GtkWidget *tiff_compression_option_menu, *tiff_compression_menu, *tiff_compression_item;
+ int i, select = 1;
+
+ typedef struct tiff_compression_t
+ {
+ char *name;
+ int number;
+ } tiff_compression;
+
+#define TIFF_COMPRESSION_NUMBER 3
+#define TIFF_COMPRESSION1_NUMBER 6
+
+ tiff_compression tiff_compression_strings[TIFF_COMPRESSION_NUMBER];
+ tiff_compression tiff_compression1_strings[TIFF_COMPRESSION1_NUMBER];
+
+ tiff_compression_strings[0].name = MENU_ITEM_TIFF_COMP_NONE;
+ tiff_compression_strings[0].number = COMPRESSION_NONE;
+ tiff_compression_strings[1].name = MENU_ITEM_TIFF_COMP_JPEG;
+ tiff_compression_strings[1].number = COMPRESSION_JPEG;
+ tiff_compression_strings[2].name = MENU_ITEM_TIFF_COMP_PACKBITS;
+ tiff_compression_strings[2].number = COMPRESSION_PACKBITS;
+
+ tiff_compression1_strings[0].name = MENU_ITEM_TIFF_COMP_NONE;
+ tiff_compression1_strings[0].number = COMPRESSION_NONE;
+ tiff_compression1_strings[1].name = MENU_ITEM_TIFF_COMP_CCITTRLE;
+ tiff_compression1_strings[1].number = COMPRESSION_CCITTRLE;
+ tiff_compression1_strings[2].name = MENU_ITEM_TIFF_COMP_CCITFAX3;
+ tiff_compression1_strings[2].number = COMPRESSION_CCITTFAX3;
+ tiff_compression1_strings[3].name = MENU_ITEM_TIFF_COMP_CCITFAX4;
+ tiff_compression1_strings[3].number = COMPRESSION_CCITTFAX4;
+ tiff_compression1_strings[4].name = MENU_ITEM_TIFF_COMP_JPEG;
+ tiff_compression1_strings[4].number = COMPRESSION_JPEG;
+ tiff_compression1_strings[5].name = MENU_ITEM_TIFF_COMP_PACKBITS;
+ tiff_compression1_strings[5].number = COMPRESSION_PACKBITS;
+
+#endif /* HAVE_LIBTIFF */
+
+ xsane_set_sensitivity(FALSE);
+
+ setup_dialog = gtk_dialog_new();
+ snprintf(buf, sizeof(buf), "%s %s", prog_name, WINDOW_SETUP);
+ gtk_window_set_title(GTK_WINDOW(setup_dialog), buf);
+ gtk_signal_connect(GTK_OBJECT(setup_dialog), "destroy", (GtkSignalFunc) xsane_destroy_setup_dialog_callback, setup_dialog);
+ xsane_set_window_icon(setup_dialog, 0);
+
+ setup_vbox = GTK_DIALOG(setup_dialog)->vbox;
+
+ notebook = gtk_notebook_new();
+ gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
+ gtk_box_pack_start(GTK_BOX(setup_vbox), notebook, FALSE, FALSE, 0);
+ gtk_widget_show(notebook);
+
+
+
+
+ /* Printer options notebook page */
+
+ setup_vbox = gtk_vbox_new(FALSE, 5);
+
+ label = gtk_label_new(NOTEBOOK_COPY_OPTIONS);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), setup_vbox, label);
+ gtk_widget_show(setup_vbox);
+
+ frame = gtk_frame_new(0);
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start(GTK_BOX(setup_vbox), frame, TRUE, TRUE, 0); /* sizeable framehight */
+ gtk_widget_show(frame);
+
+ vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_widget_show(vbox);
+
+
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_SEL);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ printer_option_menu = gtk_option_menu_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, printer_option_menu, DESC_PRINTER_SETUP);
+ gtk_box_pack_end(GTK_BOX(hbox), printer_option_menu, FALSE, FALSE, 2);
+ gtk_widget_show(printer_option_menu);
+ gtk_widget_show(hbox);
+
+ xsane_setup_printer_menu_build(printer_option_menu);
+
+ /* printername : */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_NAME);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PRINTER_NAME);
+ gtk_widget_set_usize(text, 250, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.printer[preferences.printernr]->name);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_name_entry = text;
+
+ /* printcommand : */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_CMD);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PRINTER_COMMAND);
+ gtk_widget_set_usize(text, 250, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.printer[preferences.printernr]->command);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_command_entry = text;
+
+ /* copy number option : */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_COPY_NR_OPT);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_COPY_NUMBER_OPTION);
+ gtk_widget_set_usize(text, 250, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.printer[preferences.printernr]->copy_number_option);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_copy_number_option_entry = text;
+
+ /* printerresolution : */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_RES);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PRINTER_RESOLUTION);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%d", preferences.printer[preferences.printernr]->resolution);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_resolution_entry = text;
+
+
+ xsane_separator_new(vbox, 2);
+
+
+ /* printer width: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_WIDTH);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PRINTER_WIDTH);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.printer[preferences.printernr]->width);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_width_entry = text;
+
+ /* printer height: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_HEIGHT);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PRINTER_HEIGHT);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.printer[preferences.printernr]->height);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_height_entry = text;
+
+ /* printer left offset : */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_LEFT);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PRINTER_LEFTOFFSET);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.printer[preferences.printernr]->leftoffset);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_leftoffset_entry = text;
+
+ /* printer bottom offset : */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_BOTTOM);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PRINTER_BOTTOMOFFSET);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.printer[preferences.printernr]->bottomoffset);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_bottomoffset_entry = text;
+
+
+ xsane_separator_new(vbox, 2);
+
+
+ /* printer gamma: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_GAMMA);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PRINTER_GAMMA);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.printer[preferences.printernr]->gamma);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_gamma_entry = text;
+
+ /* printer gamma red: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_GAMMA_RED);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PRINTER_GAMMA_RED);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.printer[preferences.printernr]->gamma_red);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_gamma_red_entry = text;
+
+ /* printer gamma green: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_GAMMA_GREEN);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PRINTER_GAMMA_GREEN);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.printer[preferences.printernr]->gamma_green);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_gamma_green_entry = text;
+
+ /* printer gamma blue: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_PRINTER_GAMMA_BLUE);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PRINTER_GAMMA_BLUE);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.printer[preferences.printernr]->gamma_blue);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.printer_gamma_blue_entry = text;
+
+
+ xsane_separator_new(vbox, 4);
+
+ /* "apply" "add printer" "delete printer" */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ button = gtk_button_new_with_label(BUTTON_APPLY);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_setup_printer_apply_changes, printer_option_menu);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label(BUTTON_ADD_PRINTER);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_setup_printer_new, printer_option_menu);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label(BUTTON_DELETE_PRINTER);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_setup_printer_delete, printer_option_menu);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ gtk_widget_show(hbox);
+
+
+
+
+ /* Saving options notebook page */
+
+ setup_vbox = gtk_vbox_new(FALSE, 5);
+
+ label = gtk_label_new(NOTEBOOK_SAVING_OPTIONS);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), setup_vbox, label);
+ gtk_widget_show(setup_vbox);
+
+ frame = gtk_frame_new(0);
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start(GTK_BOX(setup_vbox), frame, TRUE, TRUE, 0); /* sizeable framehight */
+ gtk_widget_show(frame);
+
+ vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_widget_show(vbox);
+
+ xsane_setup.image_permissions = 0777-preferences.image_umask;
+ xsane_permission_box(vbox, XSANE_GTK_NAME_IMAGE_PERMISSIONS, "Image-file permissions", &xsane_setup.image_permissions,
+ TRUE /* header */, FALSE /* x sens */, FALSE /* user sens */);
+
+ xsane_setup.directory_permissions = 0777-preferences.directory_umask;
+ xsane_permission_box(vbox, XSANE_GTK_NAME_DIRECTORY_PERMISSIONS, "Directory permissions", &xsane_setup.directory_permissions,
+ FALSE /* header */, TRUE /* x sens */, FALSE /* user sens */);
+
+ xsane_separator_new(vbox, 4);
+
+
+ /* overwrite warning */
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+ button = gtk_check_button_new_with_label(RADIO_BUTTON_OVERWRITE_WARNING);
+ xsane_back_gtk_set_tooltip(dialog->tooltips, button, DESC_OVERWRITE_WARNING);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), preferences.overwrite_warning);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 2);
+ gtk_widget_show(button);
+ gtk_widget_show(hbox);
+ xsane_setup.overwrite_warning_button = button;
+
+ /* increase filename counter */
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+ button = gtk_check_button_new_with_label(RADIO_BUTTON_INCREASE_COUNTER);
+ xsane_back_gtk_set_tooltip(dialog->tooltips, button, DESC_INCREASE_COUNTER);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), preferences.increase_filename_counter);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 2);
+ gtk_widget_show(button);
+ gtk_widget_show(hbox);
+ xsane_setup.increase_filename_counter_button = button;
+
+ /* increase filename counter */
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+ button = gtk_check_button_new_with_label(RADIO_BUTTON_SKIP_EXISTING_NRS);
+ xsane_back_gtk_set_tooltip(dialog->tooltips, button, DESC_SKIP_EXISTING);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), preferences.skip_existing_numbers);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 2);
+ gtk_widget_show(button);
+ gtk_widget_show(hbox);
+ xsane_setup.skip_existing_numbers_button = button;
+
+#ifdef HAVE_LIBJPEG
+ xsane_separator_new(vbox, 4);
+#else
+#ifdef HAVE_LIBTIFF
+ xsane_separator_new(vbox, 4);
+#else
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+ xsane_separator_new(vbox, 4);
+#endif
+#endif
+#endif
+#endif
+
+#ifdef HAVE_LIBJPEG
+ xsane_scale_new(GTK_BOX(vbox), TEXT_SETUP_JPEG_QUALITY, DESC_JPEG_QUALITY, 0.0, 100.0, 1.0, 1.0, 0.0, 0,
+ &preferences.jpeg_quality, (GtkObject **) &xsane_setup.jpeg_image_quality_scale, 0, TRUE);
+#else
+#ifdef HAVE_LIBTIFF
+ xsane_scale_new(GTK_BOX(vbox), TEXT_SETUP_JPEG_QUALITY, DESC_JPEG_QUALITY, 0.0, 100.0, 1.0, 1.0, 0.0, 0,
+ &preferences.jpeg_quality, (GtkObject **) &xsane_setup.jpeg_image_quality_scale, 0, TRUE);
+#endif
+#endif
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+ xsane_scale_new(GTK_BOX(vbox), TEXT_SETUP_PNG_COMPRESSION, DESC_PNG_COMPRESSION, 0.0, Z_BEST_COMPRESSION, 1.0, 1.0, 0.0, 0,
+ &preferences.png_compression, (GtkObject **) &xsane_setup.pnm_image_compression_scale, 0, TRUE);
+#endif
+#endif
+
+#ifdef HAVE_LIBTIFF
+ /* TIFF MULTI BIT IMAGES COMPRESSION */
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new(TEXT_SETUP_TIFF_COMPRESSION);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ tiff_compression_option_menu = gtk_option_menu_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, tiff_compression_option_menu, DESC_TIFF_COMPRESSION);
+ gtk_box_pack_end(GTK_BOX(hbox), tiff_compression_option_menu, FALSE, FALSE, 2);
+ gtk_widget_show(tiff_compression_option_menu);
+ gtk_widget_show(hbox);
+
+ tiff_compression_menu = gtk_menu_new();
+
+ for (i=1; i <= TIFF_COMPRESSION_NUMBER; i++)
+ {
+ tiff_compression_item = gtk_menu_item_new_with_label(tiff_compression_strings[i-1].name);
+ gtk_container_add(GTK_CONTAINER(tiff_compression_menu), tiff_compression_item);
+ gtk_signal_connect(GTK_OBJECT(tiff_compression_item), "activate",
+ (GtkSignalFunc) xsane_setup_tiff_compression_callback, (void *) tiff_compression_strings[i-1].number);
+ gtk_widget_show(tiff_compression_item);
+ if (tiff_compression_strings[i-1].number == preferences.tiff_compression_nr)
+ {
+ select = i-1;
+ }
+ }
+
+
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(tiff_compression_option_menu), tiff_compression_menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(tiff_compression_option_menu), select);
+ xsane_setup.tiff_compression_nr = preferences.tiff_compression_nr;
+
+
+ /* TIFF ONE BIT IMAGES COMPRESSION */
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new(TEXT_SETUP_TIFF_COMPRESSION_1);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ tiff_compression_option_menu = gtk_option_menu_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, tiff_compression_option_menu, DESC_TIFF_COMPRESSION_1);
+ gtk_box_pack_end(GTK_BOX(hbox), tiff_compression_option_menu, FALSE, FALSE, 2);
+ gtk_widget_show(tiff_compression_option_menu);
+ gtk_widget_show(hbox);
+
+ tiff_compression_menu = gtk_menu_new();
+
+ for (i=1; i <= TIFF_COMPRESSION1_NUMBER; i++)
+ {
+ tiff_compression_item = gtk_menu_item_new_with_label(tiff_compression1_strings[i-1].name);
+ gtk_container_add(GTK_CONTAINER(tiff_compression_menu), tiff_compression_item);
+ gtk_signal_connect(GTK_OBJECT(tiff_compression_item), "activate",
+ (GtkSignalFunc) xsane_setup_tiff_compression_1_callback, (void *) tiff_compression1_strings[i-1].number);
+ gtk_widget_show(tiff_compression_item);
+ if (tiff_compression1_strings[i-1].number == preferences.tiff_compression_1_nr)
+ {
+ select = i-1;
+ }
+ }
+
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(tiff_compression_option_menu), tiff_compression_menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(tiff_compression_option_menu), select);
+
+ xsane_setup.tiff_compression_1_nr = preferences.tiff_compression_1_nr;
+
+#endif
+
+ xsane_separator_new(vbox, 4);
+
+
+ /* apply button */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ button = gtk_button_new_with_label(BUTTON_APPLY);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_setup_saving_apply_changes, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ gtk_widget_show(hbox);
+
+
+
+
+
+ /* Display options notebook page */
+
+ setup_vbox = gtk_vbox_new(FALSE, 5);
+
+ label = gtk_label_new(NOTEBOOK_DISPLAY_OPTIONS);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), setup_vbox, label);
+ gtk_widget_show(setup_vbox);
+
+ frame = gtk_frame_new(0);
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start(GTK_BOX(setup_vbox), frame, TRUE, TRUE, 0); /* sizeable framehight */
+ gtk_widget_show(frame);
+
+ vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_widget_show(vbox);
+
+ /* main window fixed: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+ button = gtk_check_button_new_with_label(RADIO_BUTTON_WINDOW_FIXED);
+ xsane_back_gtk_set_tooltip(dialog->tooltips, button, DESC_MAIN_WINDOW_FIXED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), preferences.main_window_fixed);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 2);
+ gtk_widget_show(button);
+ gtk_widget_show(hbox);
+ xsane_setup.main_window_fixed_button = button;
+
+
+ /* preserve preview image: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+ button = gtk_check_button_new_with_label(RADIO_BUTTON_PRESERVE_PRVIEW);
+ xsane_back_gtk_set_tooltip(dialog->tooltips, button, DESC_PREVIEW_PRESERVE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), preferences.preserve_preview);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 2);
+ gtk_widget_show(button);
+ gtk_widget_show(hbox);
+ xsane_setup.preview_preserve_button = button;
+
+
+ /* private colormap: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+ button = gtk_check_button_new_with_label(RADIO_BUTTON_PRIVATE_COLORMAP);
+ xsane_back_gtk_set_tooltip(dialog->tooltips, button, DESC_PREVIEW_COLORMAP);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), preferences.preview_own_cmap);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 2);
+ gtk_widget_show(button);
+ gtk_widget_show(hbox);
+ xsane_setup.preview_own_cmap_button = button;
+
+
+ xsane_separator_new(vbox, 2);
+
+
+ /* preview gamma correction value: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+ gtk_widget_show(hbox);
+
+ label = gtk_label_new(TEXT_SETUP_PREVIEW_GAMMA);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.preview_gamma);
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PREVIEW_GAMMA);
+ gtk_widget_set_usize(text, 50, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ xsane_setup.preview_gamma_entry = text;
+
+ /* red preview gamma correction value: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+ gtk_widget_show(hbox);
+
+ label = gtk_label_new(TEXT_SETUP_PREVIEW_GAMMA_RED);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.preview_gamma_red);
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PREVIEW_GAMMA_RED);
+ gtk_widget_set_usize(text, 50, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ xsane_setup.preview_gamma_red_entry = text;
+
+ /* green preview gamma correction value: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+ gtk_widget_show(hbox);
+
+ label = gtk_label_new(TEXT_SETUP_PREVIEW_GAMMA_GREEN);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.preview_gamma_green);
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PREVIEW_GAMMA_GREEN);
+ gtk_widget_set_usize(text, 50, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ xsane_setup.preview_gamma_green_entry = text;
+
+ /* blue preview gamma correction value: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+ gtk_widget_show(hbox);
+
+ label = gtk_label_new(TEXT_SETUP_PREVIEW_GAMMA_BLUE);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ snprintf(buf, sizeof(buf), "%1.2f", preferences.preview_gamma_blue);
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_PREVIEW_GAMMA_BLUE);
+ gtk_widget_set_usize(text, 50, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ xsane_setup.preview_gamma_blue_entry = text;
+
+
+ xsane_separator_new(vbox, 2);
+
+
+ /* docviewer */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_HELPFILE_VIEWER);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_DOC_VIEWER);
+ gtk_widget_set_usize(text, 250, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.doc_viewer);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.doc_viewer_entry = text;
+
+
+ xsane_separator_new(vbox, 4);
+
+
+ /* apply button */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ button = gtk_button_new_with_label(BUTTON_APPLY);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_setup_display_apply_changes, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ gtk_widget_show(hbox);
+
+
+
+
+ /* Fax options notebook page */
+
+ setup_vbox = gtk_vbox_new(FALSE, 5);
+
+ label = gtk_label_new(NOTEBOOK_FAX_OPTIONS);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), setup_vbox, label);
+ gtk_widget_show(setup_vbox);
+
+ frame = gtk_frame_new(0);
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start(GTK_BOX(setup_vbox), frame, TRUE, TRUE, 0); /* sizeable framehight */
+ gtk_widget_show(frame);
+
+ vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_widget_show(vbox);
+
+ /* faxcommand : */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_FAX_COMMAND);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAX_COMMAND);
+ gtk_widget_set_usize(text, 250, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.fax_command);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.fax_command_entry = text;
+
+
+ /* fax receiver option: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_FAX_RECEIVER_OPTION);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAX_RECEIVER_OPT);
+ gtk_widget_set_usize(text, 50, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.fax_receiver_option);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.fax_receiver_option_entry = text;
+
+
+/* fax postscript option: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_FAX_POSTSCRIPT_OPT);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAX_POSTSCRIPT_OPT);
+ gtk_widget_set_usize(text, 50, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.fax_postscript_option);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.fax_postscript_option_entry = text;
+
+
+ /* fax normal mode option : */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_FAX_NORMAL_MODE_OPT);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAX_NORMAL_OPT);
+ gtk_widget_set_usize(text, 50, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.fax_normal_option);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.fax_normal_option_entry = text;
+
+
+ /* fax fine mode option : */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_FAX_FINE_MODE_OPT);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAX_FINE_OPT);
+ gtk_widget_set_usize(text, 50, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.fax_fine_option);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.fax_fine_option_entry = text;
+
+
+ xsane_separator_new(vbox, 2);
+
+
+ /* faxviewer */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_FAX_VIEWER);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAX_VIEWER);
+ gtk_widget_set_usize(text, 250, 0);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.fax_viewer);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.fax_viewer_entry = text;
+
+
+ xsane_separator_new(vbox, 4);
+
+ /* fax width: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_FAX_WIDTH);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAX_WIDTH);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.fax_width);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.fax_width_entry = text;
+
+ /* fax height: */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_FAX_HEIGHT);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAX_HEIGHT);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.fax_height);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.fax_height_entry = text;
+
+ /* fax left offset : */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_FAX_LEFT);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAX_LEFTOFFSET);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.fax_leftoffset);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.fax_leftoffset_entry = text;
+
+ /* fax bottom offset : */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ label = gtk_label_new(TEXT_SETUP_FAX_BOTTOM);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAX_BOTTOMOFFSET);
+ gtk_widget_set_usize(text, 50, 0);
+ snprintf(buf, sizeof(buf), "%3.2f", preferences.fax_bottomoffset);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), text, FALSE, FALSE, 2);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+ xsane_setup.fax_bottomoffset_entry = text;
+
+ xsane_separator_new(vbox, 4);
+
+ /* apply button */
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ button = gtk_button_new_with_label(BUTTON_APPLY);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_setup_fax_apply_changes, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ gtk_widget_show(hbox);
+
+
+
+
+ /* fill in action area: */
+ hbox = GTK_DIALOG(setup_dialog)->action_area;
+
+ button = gtk_button_new_with_label(BUTTON_OK);
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_setup_options_ok_callback, setup_dialog);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_grab_default(button);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label(BUTTON_CANCEL);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_close_setup_dialog_callback, setup_dialog);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ gtk_widget_show(setup_dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
diff --git a/frontend/xsane-setup.h b/frontend/xsane-setup.h
new file mode 100644
index 0000000..411faa8
--- /dev/null
+++ b/frontend/xsane-setup.h
@@ -0,0 +1,33 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-setup.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include <sane/sane.h>
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+extern void xsane_new_printer(void);
+extern void xsane_update_int(GtkWidget *widget, int *val);
+extern void xsane_setup_dialog(GtkWidget *widget, gpointer data);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
diff --git a/frontend/xsane-style.rc b/frontend/xsane-style.rc
new file mode 100644
index 0000000..de6662d
--- /dev/null
+++ b/frontend/xsane-style.rc
@@ -0,0 +1,34 @@
+# style <name> [= <name>]
+# {
+# <option>
+# }
+#
+# widget <widget_set> style <style_name>
+# widget_class <widget_class_set> style <style_name>
+# accelerator <widget_name> <accelerator>
+
+style "progressbar"
+{
+ bg[PRELIGHT] = { 22500, 53280, 22500 } # green
+}
+
+style "curve"
+{
+ fg[NORMAL] = { 58000, 0, 0 } # red
+}
+
+style "font"
+{
+ font = "-*-helvetica-medium-r-*-*-10-*-*-*-*-*-*-*"
+}
+
+style "fixed font"
+{
+ font = "-misc-fixed-medium-r-semicondensed--13-*-*-*-*-*-*-*"
+}
+
+widget "*GtkCurve" style "curve"
+widget "*GtkProgressBar" style "progressbar"
+# widget "*" style "font"
+widget "*GtkRadioButton*" style "fixed font"
+
diff --git a/frontend/xsane-text.h b/frontend/xsane-text.h
new file mode 100644
index 0000000..5cc3367
--- /dev/null
+++ b/frontend/xsane-text.h
@@ -0,0 +1,417 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-text.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ------------------------------------------------------------------------ */
+
+#ifndef XSANE_TEXT_H
+#define XSANE_TEXT_H
+
+#define XSANE_STRSTATUS(status) _(sane_strstatus(status))
+#define _BGT(text) dgettext(xsane.backend, text)
+
+#define WINDOW_ABOUT _("About")
+#define WINDOW_AUTHORIZE _("authorization")
+#define WINDOW_INFO _("info")
+#define WINDOW_BATCH_SCAN _("batch scan")
+#define WINDOW_FAX_PROJECT _("fax project")
+#define WINDOW_FAX_RENAME _("rename fax page")
+#define WINDOW_SETUP _("setup")
+#define WINDOW_HISTOGRAM _("Histogram")
+#define WINDOW_STANDARD_OPTIONS _("Standard options")
+#define WINDOW_ADVANCED_OPTIONS _("Advanced options")
+#define WINDOW_DEVICE_SELECTION _("device selection")
+#define WINDOW_PREVIEW _("Preview")
+#define WINDOW_OUTPUT_FILENAME _("output filename")
+#define WINDOW_SAVE_SETTINGS _("save device settings")
+#define WINDOW_LOAD_SETTINGS _("load device settings")
+
+#define MENU_FILE _("File")
+#define MENU_PREFERENCES _("Preferences")
+#define MENU_VIEW _("View")
+#define MENU_HELP _("Help")
+
+#define MENU_ITEM_ABOUT _("About")
+#define MENU_ITEM_INFO _("Info")
+#define MENU_ITEM_EXIT _("Exit")
+
+#define FRAME_RAW_IMAGE _("Raw image")
+#define FRAME_ENHANCED_IMAGE _("Enhanced image")
+
+#define BUTTON_START _("Start")
+#define BUTTON_OK _("Ok")
+#define BUTTON_APPLY _("Apply")
+#define BUTTON_CANCEL _("Cancel")
+#define BUTTON_CLOSE _("Close")
+#define BUTTON_OVERWRITE _("Overwrite")
+#define BUTTON_ADD_AREA _("Add area")
+#define BUTTON_DELETE _("Delete")
+#define BUTTON_SHOW _("Show")
+#define BUTTON_RENAME _("Rename")
+#define BUTTON_CREATE_PROJECT _("Create project")
+#define BUTTON_SEND_PROJECT _("Send project")
+#define BUTTON_DELETE_PROJECT _("Delete project")
+#define BUTTON_ADD_PRINTER _("Add printer")
+#define BUTTON_DELETE_PRINTER _("Delete printer")
+#define BUTTON_PREVIEW_ACQUIRE _("Acquire Preview")
+#define BUTTON_PREVIEW_CANCEL _("Cancel Preview")
+
+#define RADIO_BUTTON_FINE_MODE _("Fine mode")
+#define RADIO_BUTTON_OVERWRITE_WARNING _("Overwrite warning")
+#define RADIO_BUTTON_INCREASE_COUNTER _("Increase filename counter")
+#define RADIO_BUTTON_SKIP_EXISTING_NRS _("Skip existing numbers")
+#define RADIO_BUTTON_WINDOW_FIXED _("Main window size fixed")
+#define RADIO_BUTTON_PRESERVE_PRVIEW _("Preserve preview image")
+#define RADIO_BUTTON_PRIVATE_COLORMAP _("Use private colormap")
+
+#define TEXT_AVAILABLE_DEVICES _("Available devices:")
+#define TEXT_XSANE_OPTIONS _("XSane options")
+#define TEXT_XSANE_MODE _("XSane mode")
+#define TEXT_SCANNER_BACKEND _("Scanner and backend:")
+#define TEXT_VENDOR _("Vendor:")
+#define TEXT_MODEL _("Model:")
+#define TEXT_TYPE _("Type:")
+#define TEXT_DEVICE _("Device:")
+#define TEXT_LOADED_BACKEND _("Loaded backend:")
+#define TEXT_SANE_VERSION _("Sane version:")
+#define TEXT_RECENT_VALUES _("Recent values:")
+#define TEXT_GAMMA_CORR_BY _("Gamma correction by:")
+#define TEXT_SCANNER _("scanner")
+#define TEXT_SOFTWARE_XSANE _("software (xsane)")
+#define TEXT_NONE _("none")
+#define TEXT_GAMMA_INPUT_DEPTH _("Gamma input depth:")
+#define TEXT_GAMMA_OUTPUT_DEPTH _("Gamma output depth:")
+#define TEXT_SCANNER_OUTPUT_DEPTH _("Scanner output depth:")
+#define TEXT_OUTPUT_FORMATS _("XSane output formats:")
+#define TEXT_8BIT_FORMATS _("8 bit output formats:")
+#define TEXT_16BIT_FORMATS _("16 bit output formats:")
+#define TEXT_AUTHORIZATION_REQ _("Authorization required for")
+#define TEXT_USERNAME _("Username :")
+#define TEXT_PASSWORD _("Password :")
+#define TEXT_INVALID_PARAMS _("Invalid parameters.")
+#define TEXT_VERSION _("version:")
+#define TEXT_PACKAGE _("package")
+#define TEXT_WITH_GIMP_SUPPORT _("with GIMP support")
+#define TEXT_WITHOUT_GIMP_SUPPORT _("without GIMP support")
+#define TEXT_GIMP_VERSION _("compiled with GIMP-")
+#define TEXT_UNKNOWN _("unknown")
+#define TEXT_EMAIL _("Email:")
+#define TEXT_FILE _("File:")
+
+#define TEXT_INFO_BOX _("0x0: 0KB")
+
+#define TEXT_SETUP_PRINTER_SEL _("Printer selection:")
+#define TEXT_SETUP_PRINTER_NAME _("Name:")
+#define TEXT_SETUP_PRINTER_CMD _("Command:")
+#define TEXT_SETUP_COPY_NR_OPT _("Copy number option:")
+#define TEXT_SETUP_PRINTER_RES _("Resolution (dpi):")
+#define TEXT_SETUP_PRINTER_WIDTH _("Width [mm]:")
+#define TEXT_SETUP_PRINTER_HEIGHT _("Height [mm]:")
+#define TEXT_SETUP_PRINTER_LEFT _("Left offset [mm]:")
+#define TEXT_SETUP_PRINTER_BOTTOM _("Bottom offset [mm]:")
+#define TEXT_SETUP_PRINTER_GAMMA _("Printer gamma value:")
+#define TEXT_SETUP_PRINTER_GAMMA_RED _("Printer gamma red:")
+#define TEXT_SETUP_PRINTER_GAMMA_GREEN _("Printer gamma green:")
+#define TEXT_SETUP_PRINTER_GAMMA_BLUE _("Printer gamma blue:")
+#define TEXT_SETUP_JPEG_QUALITY _("JPEG image quality")
+#define TEXT_SETUP_PNG_COMPRESSION _("PNG image compression")
+#define TEXT_SETUP_TIFF_COMPRESSION _("TIFF multi bit image compression")
+#define TEXT_SETUP_TIFF_COMPRESSION_1 _("TIFF lineart image compression")
+#define TEXT_SETUP_PREVIEW_GAMMA _("Preview gamma:")
+#define TEXT_SETUP_PREVIEW_GAMMA_RED _("Preview gamma red:")
+#define TEXT_SETUP_PREVIEW_GAMMA_GREEN _("Preview gamma green:")
+#define TEXT_SETUP_PREVIEW_GAMMA_BLUE _("Preview gamma blue:")
+#define TEXT_SETUP_HELPFILE_VIEWER _("Helpfile viewer (HTML):")
+#define TEXT_SETUP_FAX_COMMAND _("Command:")
+#define TEXT_SETUP_FAX_RECEIVER_OPTION _("Receiver option:")
+#define TEXT_SETUP_FAX_POSTSCRIPT_OPT _("Postscriptfile option:")
+#define TEXT_SETUP_FAX_NORMAL_MODE_OPT _("Normal mode option:")
+#define TEXT_SETUP_FAX_FINE_MODE_OPT _("Fine mode option:")
+#define TEXT_SETUP_FAX_VIEWER _("Viewer (Postscript):")
+#define TEXT_SETUP_FAX_WIDTH _("Width [mm]:")
+#define TEXT_SETUP_FAX_HEIGHT _("Height [mm]:")
+#define TEXT_SETUP_FAX_LEFT _("Left offset [mm]:")
+#define TEXT_SETUP_FAX_BOTTOM _("Bottom offset [mm]:")
+
+#define NOTEBOOK_COPY_OPTIONS _("Copy options")
+#define NOTEBOOK_SAVING_OPTIONS _("Saving options")
+#define NOTEBOOK_DISPLAY_OPTIONS _("Display options")
+#define NOTEBOOK_FAX_OPTIONS _("Fax options")
+
+#define MENU_ITEM_SCAN _("Scan")
+#define MENU_ITEM_COPY _("Copy")
+#define MENU_ITEM_FAX _("Fax")
+
+#define MENU_ITEM_SHOW_TOOLTIPS _("Show tooltips")
+#define MENU_ITEM_SHOW_PREVIEW _("Show preview")
+#define MENU_ITEM_SHOW_HISTOGRAM _("Show histogram")
+#define MENU_ITEM_SHOW_STANDARDOPTIONS _("Show standard options")
+#define MENU_ITEM_SHOW_ADVANCEDOPTIONS _("Show advanced options")
+
+#define MENU_ITEM_SETUP _("Setup")
+#define MENU_ITEM_LENGTH_UNIT _("Length unit")
+#define SUBMENU_ITEM_LENGTH_MILLIMETERS _("millimeters")
+#define SUBMENU_ITEM_LENGTH_CENTIMETERS _("centimeters")
+#define SUBMENU_ITEM_LENGTH_INCHES _("inches")
+#define MENU_ITEM_UPDATE_POLICY _("Update policy")
+#define SUBMENU_ITEM_POLICY_CONTINUOUS _("continuous")
+#define SUBMENU_ITEM_POLICY_DISCONTINU _("discontinuous")
+#define SUBMENU_ITEM_POLICY_DELAYED _("delayed")
+#define MENU_ITEM_SHOW_RESOLUTIONLIST _("Show resolution list")
+#define MENU_ITEM_PAGE_ROTATE _("Rotate postscript")
+#define MENU_ITEM_SAVE_DEVICE_SETTINGS _("Save device settings")
+#define MENU_ITEM_LOAD_DEVICE_SETTINGS _("Load device settings")
+
+#define MENU_ITEM_XSANE_DOC _("Xsane doc")
+#define MENU_ITEM_BACKEND_DOC _("Backend doc")
+#define MENU_ITEM_AVAILABLE_BACKENDS _("Available backends")
+#define MENU_ITEM_SCANTIPS _("Scantips")
+#define MENU_ITEM_PROBLEMS _("Problems?")
+
+#define MENU_ITEM_TIFF_COMP_NONE _("no compression")
+#define MENU_ITEM_TIFF_COMP_CCITTRLE _("CCITT 1D Huffman compression")
+#define MENU_ITEM_TIFF_COMP_CCITFAX3 _("CCITT Group 3 Fax compression")
+#define MENU_ITEM_TIFF_COMP_CCITFAX4 _("CCITT Group 4 Fax compression")
+#define MENU_ITEM_TIFF_COMP_JPEG _("JPEG DCT compression")
+#define MENU_ITEM_TIFF_COMP_PACKBITS _("pack bits")
+
+#define MENU_ITEM_FILETYPE_JPEG _(".jpeg")
+#define MENU_ITEM_FILETYPE_PNG _(".png")
+#define MENU_ITEM_FILETYPE_PNM _(".pnm")
+#define MENU_ITEM_FILETYPE_PS _(".ps")
+#define MENU_ITEM_FILETYPE_RAW _(".raw")
+#define MENU_ITEM_FILETYPE_TIFF _(".tiff")
+#define MENU_ITEM_FILETYPE_BY_EXT _("by ext")
+
+#define PROGRESS_SAVING _("Saving image")
+#define PROGRESS_SAVING_FAX _("Saving fax image")
+#define PROGRESS_CONVERTING_DATA _("Converting data....")
+#define PROGRESS_CONVERTING_PS _("Converting to postscript")
+#define PROGRESS_SCANNING _("Scanning")
+#define PROGRESS_RECEIVING_SCAN _("Receiving %s data for `%s'...")
+#define PROGRESS_RECEIVING_COPY _("Receiving %s data for photocopy ...")
+#define PROGRESS_RECEIVING_FAX _("Receiving %s data for fax ...")
+#define PROGRESS_RECEIVING_GIMP _("Receiving %s data for GIMP...")
+
+
+#define DESC_XSANE_MODE _("Use XSane for SCANning, photoCOPYing, FAXing...")
+
+#define DESC_BROWSE_FILENAME _("Browse for image filename")
+#define DESC_FILENAME _("Filename for scanned image")
+#define DESC_FILETYPE _("Filename extension and type of image format")
+#define DESC_FAXPROJECT _("Enter name of fax project")
+#define DESC_FAXPAGENAME _("Enter new name for faxpage")
+#define DESC_FAXRECEIVER _("Enter receiver phone number or address")
+
+#define DESC_PRINTER_SELECT _("Select printer definition")
+
+#define DESC_RESOLUTION _("Set scan resolution")
+#define DESC_RESOLUTION_X _("Set scan resolution for x direction")
+#define DESC_RESOLUTION_Y _("Set scan resolution for y direction")
+#define DESC_ZOOM _("Set zoomfactor")
+#define DESC_ZOOM_X _("Set zoomfactor for x direction")
+#define DESC_ZOOM_Y _("Set zoomfactor for y direction")
+#define DESC_COPY_NUMBER _("Set number of copies")
+
+#define DESC_NEGATIVE _("Negative: Invert colors for scanning negatives\n" \
+ "e.g. swap black and white")
+
+#define DESC_GAMMA _("Set gamma value")
+#define DESC_GAMMA_R _("Set gamma value for red component")
+#define DESC_GAMMA_G _("Set gamma value for green component")
+#define DESC_GAMMA_B _("Set gamma value for blue component")
+
+#define DESC_BRIGHTNESS _("Set brightness")
+#define DESC_BRIGHTNESS_R _("Set brightness for red component")
+#define DESC_BRIGHTNESS_G _("Set brightness for green component")
+#define DESC_BRIGHTNESS_B _("Set brightness for blue component")
+
+#define DESC_CONTRAST _("Set contrast")
+#define DESC_CONTRAST_R _("Set contrast for red component")
+#define DESC_CONTRAST_G _("Set contrast for green component")
+#define DESC_CONTRAST_B _("Set contrast for blue component")
+
+#define DESC_RGB_DEFAULT _("RGB default: Set enhancement values for red, green and blue to default values:\n" \
+ " gamma = 1.0\n" \
+ " brightness = 0\n" \
+ " contrast = 0")
+
+#define DESC_ENH_AUTO _("Autoadjust gamma, brightness and contrast in dependance of selected area")
+#define DESC_ENH_DEFAULT _("Set default enhancement values:\n" \
+ "gamma = 1.0\n" \
+ "brightness = 0\n" \
+ "contrast = 0")
+#define DESC_ENH_RESTORE _("Restore enhancement values from preferences")
+#define DESC_ENH_STORE _("Store active enhancement values to preferences")
+
+#define DESC_HIST_INTENSITY _("Show histogram of intensity/gray")
+#define DESC_HIST_RED _("Show histogram of red component")
+#define DESC_HIST_GREEN _("Show histogram of green component")
+#define DESC_HIST_BLUE _("Show histogram of blue component")
+#define DESC_HIST_PIXEL _("Display histogram with lines instead of pixels")
+#define DESC_HIST_LOG _("Show logarithm of pixelcount")
+
+#define DESC_PRINTER_SETUP _("Select definition to change")
+#define DESC_PRINTER_NAME _("Define a name for the selection of this definition")
+#define DESC_PRINTER_COMMAND _("Enter command to be executed in copy mode (e.g. \"lpr -\")")
+#define DESC_COPY_NUMBER_OPTION _("Enter option for copy numbers")
+#define DESC_PRINTER_RESOLUTION _("Resolution with which images are printed and saved in postscript")
+#define DESC_PRINTER_WIDTH _("Width of printable area in mm")
+#define DESC_PRINTER_HEIGHT _("Height of printable area in mm")
+#define DESC_PRINTER_LEFTOFFSET _("Left offset from the edge of the paper to the printable area in mm")
+#define DESC_PRINTER_BOTTOMOFFSET _("Bottom offset from the edge of the paper to the printable area in mm")
+#define DESC_PRINTER_GAMMA _("Additional gamma value for photocopy")
+#define DESC_PRINTER_GAMMA_RED _("Additional gamma value for red component for photocopy")
+#define DESC_PRINTER_GAMMA_GREEN _("Additional gamma value for green component for photocopy")
+#define DESC_PRINTER_GAMMA_BLUE _("Additional gamma value for blue component for photocopy")
+#define DESC_JPEG_QUALITY _("Quality in percent if image is saved as jpeg or tiff with jpeg compression")
+#define DESC_PNG_COMPRESSION _("Compression if image is saved as png")
+#define DESC_TIFF_COMPRESSION _("Compression type if multi bit image is saved as tiff")
+#define DESC_TIFF_COMPRESSION_1 _("Compression type if lineart image is saved as tiff")
+#define DESC_OVERWRITE_WARNING _("Warn before overwriting an existing file")
+#define DESC_INCREASE_COUNTER _("If the filename is of the form \"name-001.ext\" " \
+ "(where the number of digits is free) " \
+ "the number is increased after a scan is finished")
+#define DESC_SKIP_EXISTING _("If filename counter is automatically increased, used numbers are skipped")
+#define DESC_MAIN_WINDOW_FIXED _("Use fixed main window size or scrolled, resizable main window")
+#define DESC_PREVIEW_PRESERVE _("Preserve preview image for next program start")
+#define DESC_PREVIEW_COLORMAP _("Use an own colormap for preview if display depth is 8 bpp")
+#define DESC_PREVIEW_GAMMA _("Set gamma correction value for preview image")
+#define DESC_PREVIEW_GAMMA_RED _("Set gamma correction value for red component of preview image")
+#define DESC_PREVIEW_GAMMA_GREEN _("Set gamma correction value for green component of preview image")
+#define DESC_PREVIEW_GAMMA_BLUE _("Set gamma correction value for blue component of preview image")
+#define DESC_DOC_VIEWER _("Enter command to be executed to display helpfiles, must be a html-viewer!")
+
+#define DESC_FAX_COMMAND _("Enter command to be executed in fax mode")
+#define DESC_FAX_RECEIVER_OPT _("Enter option to specify receiver")
+#define DESC_FAX_POSTSCRIPT_OPT _("Enter option to specify postscript files following")
+#define DESC_FAX_NORMAL_OPT _("Enter option to specify normal mode (low resolution)")
+#define DESC_FAX_FINE_OPT _("Enter option to specify fine mode (high resolution)")
+#define DESC_FAX_VIEWER _("Enter command to be executed to view a fax")
+#define DESC_FAX_FINE_MODE _("Use high vertical resolution (196 lpi instead of 98 lpi)")
+#define DESC_FAX_WIDTH _("Width of printable area in mm")
+#define DESC_FAX_HEIGHT _("Height of printable area in mm")
+#define DESC_FAX_LEFTOFFSET _("Left offset from the edge of the paper to the printable area in mm")
+#define DESC_FAX_BOTTOMOFFSET _("Bottom offset from the edge of the paper to the printable area in mm")
+
+#define DESC_PIPETTE_WHITE _("Pick white point")
+#define DESC_PIPETTE_GRAY _("Pick gray point")
+#define DESC_PIPETTE_BLACK _("Pick black point")
+
+#define DESC_ZOOM_FULL _("Use full scanarea")
+#define DESC_ZOOM_OUT _("Zoom 20% out")
+#define DESC_ZOOM_IN _("Zoom into selected area")
+#define DESC_ZOOM_UNDO _("Undo last zoom")
+
+#define DESC_FULL_PREVIEW_AREA _("Select visible area")
+
+
+#define ERR_HOME_DIR _("Failed to determine home directory:")
+#define ERR_FILENAME_TOO_LONG _("Filename too long")
+#define ERR_SET_OPTION _("Failed to set value of option")
+#define ERR_GET_OPTION _("Failed to obtain value of option")
+#define ERR_OPTION_COUNT _("Error obtaining option count")
+#define ERR_DEVICE_OPEN_FAILED _("Failed to open device")
+#define ERR_NO_DEVICES _("no devices available")
+#define ERR_DURING_READ _("Error during read:")
+#define ERR_DURING_SAVE _("Error during save:")
+#define ERR_BAD_DEPTH _("Can't handle depth")
+#define ERR_GIMP_BAD_DEPTH _("GIMP can't handle depth")
+#define ERR_UNKNOWN_SAVING_FORMAT _("Unknown file format for saving")
+#define ERR_OPEN_FAILED _("Failed to open")
+#define ERR_FAILED_PRINTER_PIPE _("Failed to open pipe for executing printercommand")
+#define ERR_FAILED_EXEC_PRINTER_CMD _("Failed to execute printercommand:")
+#define ERR_FAILED_START_SCANNER _("Failed to start scanner:")
+#define ERR_FAILED_GET_PARAMS _("Failed to get parameters:")
+#define ERR_NO_OUTPUT_FORMAT _("No output format given")
+#define ERR_NO_MEM _("out of memory")
+#define ERR_LIBTIFF _("LIBTIFF reports error")
+#define ERR_LIBPNG _("LIBPNG reports error")
+#define ERR_UNKNOWN_TYPE _("unknown type")
+#define ERR_UNKNOWN_CONSTRAINT_TYPE _("unknown constraint type")
+#define ERR_FAILD_EXEC_DOC_VIEWER _("Failed to execute documentation viewer:")
+#define ERR_FAILD_EXEC_FAX_VIEWER _("Failed to execute fax viewer:")
+#define ERR_FAILED_EXEC_FAX_CMD _("Failed to execute faxcommand:")
+#define ERR_BAD_FRAME_FORMAT _("bad frame format")
+#define ERR_FAILED_SET_RESOLUTION _("unable to set resolution")
+
+#define ERR_ERROR _("error")
+#define ERR_MAJOR_VERSION_NR_CONFLICT _("Sane major version number mismatch!")
+#define ERR_XSANE_MAJOR_VERSION _("xsane major version =")
+#define ERR_BACKEND_MAJOR_VERSION _("backend major version =")
+#define ERR_PROGRAM_ABORTED _("*** PROGRAM ABORTED ***")
+
+#define ERR_FAILED_ALLOCATE_IMAGE _("Failed to allocate image memory:")
+#define ERR_PREVIEW_BAD_DEPTH _("Preview cannot handle bit depth")
+#define ERR_GIMP_SUPPORT_MISSING _("GIMP support missing")
+
+#define WARN_COUNTER_OVERFLOW _("Filename counter overflow")
+#define WARN_NO_VALUE_CONSTRAINT _("warning: option has no value constraint")
+
+#define ERR_BUTTON_OK _("Ok")
+#define ERR_HEADER_ERROR _("Error")
+#define ERR_HEADER_WARNING _("Warning")
+
+#define ERR_FAILED_CREATE_FILE _("Failed to create file:")
+#define ERR_LOAD_DEVICE_SETTINGS _("Error while loading device settings:")
+#define ERR_NO_DRC_FILE _("is not a device-rc-file !!!")
+#define ERR_NETSCAPE_EXECUTE_FAIL _("Failed to execute netscape!")
+#define ERR_SENDFAX_RECEIVER_MISSING _("Send fax: no receiver defined")
+
+#define ERR_CREATED_FOR_DEVICE _("has been created for device")
+#define ERR_USED_FOR_DEVICE _("you want to use it for device")
+#define ERR_MAY_CAUSE_PROBLEMS _("this may cause problems!")
+
+#define TEXT_USAGE _("Usage:")
+#define TEXT_USAGE_OPTIONS _("[OPTION]... [DEVICE]")
+#define TEXT_HELP _(\
+"Start up graphical user interface to access SANE (Scanner Access Now Easy) devices.\n\
+\n\
+-h, --help display this help message and exit\n\
+-v, --version print version information\n\
+\n\
+-d, --device-settings file load device settings from file (without \".drc\")\n\
+\n\
+-s, --scan start with scan-mode active\n\
+-c, --copy start with copy-mode active\n\
+-f, --fax start with fax-mode active\n\
+-n, --no-mode-selection disable menu for xsane mode selection\n\
+\n\
+-F, --Fixed fixed main window size (overwrite preferences value)\n\
+-R, --Resizeable resizable, scrolled main window (overwrite preferences value)\n\
+\n\
+--display X11-display redirect output to X11-display\n\
+--no-xshm do not use shared memory images\n\
+--sync request a synchronous connection with the X11 server\
+")
+
+/* strings for gimp plugin */
+
+#define XSANE_GIMP_INSTALL_BLURB _("Front-end to the SANE interface")
+#define XSANE_GIMP_INSTALL_HELP _("This function provides access to scanners and other image acquisition devices through the SANE (Scanner Access Now Easy) interface.")
+
+/* Menu path must not be translated, this is done by the gimp. Only translate the text behind the last "/" */
+#define XSANE_GIMP_MENU_DIALOG _("<Toolbox>/File/Acquire/XSane: Device dialog...")
+#define XSANE_GIMP_MENU _("<Toolbox>/File/Acquire/XSane: ")
+#define XSANE_GIMP_MENU_DIALOG_OLD _("<Toolbox>/Xtns/XSane/Device dialog...")
+#define XSANE_GIMP_MENU_OLD _("<Toolbox>/Xtns/XSane/")
+
+#endif
diff --git a/frontend/xsane.c b/frontend/xsane.c
new file mode 100644
index 0000000..6302168
--- /dev/null
+++ b/frontend/xsane.c
@@ -0,0 +1,4794 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+#include "xsane-back-gtk.h"
+#include "xsane-front-gtk.h"
+#include "xsane-preview.h"
+#include "xsane-save.h"
+#include "xsane-gamma.h"
+#include "xsane-setup.h"
+#include "xsane-scan.h"
+#include "xsane-rc-io.h"
+#include "xsane-device-preferences.h"
+#include "xsane-preferences.h"
+#include "xsane-icons.h"
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+#include <png.h>
+#include <zlib.h>
+#endif
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+struct option long_options[] =
+{
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'v'},
+ {"device-settings", required_argument, 0, 'd'},
+ {"scan", no_argument, 0, 's'},
+ {"copy", no_argument, 0, 'c'},
+ {"no-mode-selection", no_argument, 0, 'n'},
+ {"fax", no_argument, 0, 'f'},
+ {"Fixed", no_argument, 0, 'F'},
+ {"Resizeable", no_argument, 0, 'R'},
+ {0, }
+};
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+int xsane_back_gtk_message_dialog_active = 0;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+const char *prog_name = 0;
+const char *device_text = 0;
+GtkWidget *choose_device_dialog = 0;
+GSGDialog *dialog = 0;
+const SANE_Device **devlist = 0;
+gint seldev = -1; /* The selected device */
+gint ndevs; /* The number of available devices */
+struct Xsane xsane;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+int xsane_scanmode_number[] = { XSANE_SCAN, XSANE_COPY, XSANE_FAX };
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#define XSANE_GTK_NAME_RESOLUTION "GtkMenuResolution"
+#define XSANE_GTK_NAME_X_RESOLUTION "GtkMenuXResolution"
+#define XSANE_GTK_NAME_Y_RESOLUTION "GtkMenuYResolution"
+
+#define XSANE_GTK_NAME_ZOOM "GtkMenuZoom"
+#define XSANE_GTK_NAME_X_ZOOM "GtkMenuXZoom"
+#define XSANE_GTK_NAME_Y_ZOOM "GtkMenuYZoom"
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* forward declarations: */
+
+static int xsane_option_defined(char *string);
+static int xsane_parse_options(char *options, char *argv[]);
+static void xsane_update_param(GSGDialog *dialog, void *arg);
+static void xsane_zoom_update(GtkAdjustment *adj_data, double *val);
+static void xsane_resolution_scale_update(GtkAdjustment *adj_data, double *val);
+static void xsane_gamma_changed(GtkAdjustment *adj_data, double *val);
+static void xsane_modus_callback(GtkWidget *xsane_parent, int *num);
+static void xsane_filetype_callback(GtkWidget *widget, gpointer data);
+static void xsane_outputfilename_changed_callback(GtkWidget *widget, gpointer data);
+static void xsane_browse_filename_callback(GtkWidget *widget, gpointer data);
+static void xsane_outputfilename_new(GtkWidget *vbox);
+static void xsane_faxreceiver_changed_callback(GtkWidget *widget, gpointer data);
+static void xsane_faxproject_changed_callback(GtkWidget *widget, gpointer data);
+static void xsane_fax_fine_mode_callback(GtkWidget * widget);
+static void xsane_enhancement_rgb_default_callback(GtkWidget * widget);
+static void xsane_enhancement_negative_callback(GtkWidget * widget);
+static void xsane_auto_enhancement_callback(GtkWidget * widget);
+static void xsane_show_standard_options_callback(GtkWidget * widget);
+static void xsane_show_advanced_options_callback(GtkWidget * widget);
+static void xsane_show_histogram_callback(GtkWidget * widget);
+static void xsane_printer_callback(GtkWidget *widget, gpointer data);
+static void xsane_update_preview(GSGDialog *dialog, void *arg);
+void xsane_pref_save(void);
+static void xsane_pref_restore(void);
+static void xsane_quit(void);
+static void xsane_exit(void);
+static gint xsane_standard_option_win_delete(GtkWidget *widget, gpointer data);
+static gint xsane_advanced_option_win_delete(GtkWidget *widget, gpointer data);
+static gint xsane_scan_win_delete(GtkWidget *w, gpointer data);
+static gint xsane_preview_window_destroyed(GtkWidget *widget, gpointer call_data);
+static void xsane_show_preview_callback(GtkWidget * widget, gpointer call_data);
+static GtkWidget *xsane_files_build_menu(void);
+static void xsane_set_pref_unit_callback(GtkWidget *widget, gpointer data);
+static void xsane_set_update_policy_callback(GtkWidget *widget, gpointer data);
+static gint xsane_close_info_callback(GtkWidget *widget, gpointer data);
+static void xsane_info_dialog(GtkWidget *widget, gpointer data);
+static void xsane_about_dialog(GtkWidget *widget, gpointer data);
+static SANE_Status xsane_get_area_value(int option, float *val, SANE_Int *unit);
+#ifdef XSANE_TEST
+static void xsane_batch_scan_delete_callback(GtkWidget *widget, gpointer list);
+static void xsane_batch_scan_add_callback(GtkWidget *widget, gpointer list);
+static void xsane_batch_scan_dialog(GtkWidget *widget, gpointer data);
+#endif
+static void xsane_fax_dialog(void);
+static void xsane_fax_dialog_close(void);
+static void xsane_fax_project_delete(void);
+void xsane_fax_project_save(void);
+static void xsane_fax_project_load(void);
+static void xsane_fax_project_create(void);
+static void xsane_pref_toggle_tooltips(GtkWidget *widget, gpointer data);
+static void xsane_show_doc(GtkWidget *widget, gpointer data);
+static void xsane_fax_entrys_swap(GtkWidget *list_item_1, GtkWidget *list_item_2);
+static void xsane_fax_entry_move_up_callback(GtkWidget *widget, gpointer list);
+static void xsane_fax_entry_move_down_callback(GtkWidget *widget, gpointer list);
+static void xsane_fax_entry_rename_callback(GtkWidget *widget, gpointer list);
+static void xsane_fax_entry_delete_callback(GtkWidget *widget, gpointer list);
+static void xsane_fax_show_callback(GtkWidget *widget, gpointer data);
+static void xsane_fax_send(void);
+static GtkWidget *xsane_view_build_menu(void);
+static GtkWidget *xsane_pref_build_menu(void);
+static GtkWidget *xsane_help_build_menu(void);
+static void xsane_device_dialog(void);
+static void xsane_choose_dialog_ok_callback(void);
+static void xsane_select_device_by_key_callback(GtkWidget * widget, gpointer data);
+static void xsane_select_device_by_mouse_callback(GtkWidget * widget, GdkEventButton *event, gpointer data);
+static gint32 xsane_choose_device(void);
+static void xsane_usage(void);
+static void xsane_init(int argc, char **argv);
+void xsane_interface(int argc, char **argv);
+int main(int argc, char ** argv);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_option_defined(char *string)
+{
+ if (string)
+ {
+ while (*string == ' ') /* skip spaces */
+ {
+ string++;
+ }
+ if (*string != 0)
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_parse_options(char *options, char *argv[])
+{
+ int optpos = 0;
+ int bufpos = 0;
+ int arg = 0;
+ char buf[256];
+
+ while (options[optpos] != 0)
+ {
+ switch(options[optpos])
+ {
+ case ' ':
+ buf[bufpos] = 0;
+ argv[arg++] = strdup(buf);
+ bufpos = 0;
+ optpos++;
+ break;
+
+ case '\"':
+ optpos++; /* skip " */
+ while ((options[optpos] != 0) && (options[optpos] != '\"'))
+ {
+ buf[bufpos++] = options[optpos++];
+ }
+ optpos++; /* skip " */
+ break;
+
+ default:
+ buf[bufpos++] = options[optpos++];
+ break;
+ }
+ }
+ buf[bufpos] = 0;
+ argv[arg++] = strdup(buf);
+ return arg;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* Update the info line with the latest size information and update histogram. */
+static void xsane_update_param(GSGDialog *dialog, void *arg)
+{
+ SANE_Parameters params;
+ gchar buf[200];
+
+ if (!xsane.info_label)
+ {
+ return;
+ }
+
+ if (xsane.block_update_param) /* if we change more than one value, we only want to update all once */
+ {
+ return;
+ }
+
+ if (sane_get_parameters(xsane_back_gtk_dialog_get_device(dialog), &params) == SANE_STATUS_GOOD)
+ {
+ float size = params.bytes_per_line * params.lines;
+ const char *unit = "B";
+
+ if (params.format >= SANE_FRAME_RED && params.format <= SANE_FRAME_BLUE)
+ {
+ size *= 3.0;
+ }
+
+ if (size >= 1024.0 * 1024.0)
+ {
+ size /= 1024.0 * 1024.0;
+ unit = "MB";
+ }
+ else if (size >= 1024.0)
+ {
+ size /= 1024.0;
+ unit = "KB";
+ }
+ snprintf(buf, sizeof(buf), "(%d x %d): %5.1f %s", params.pixels_per_line, params.lines, size, unit);
+
+ if (params.format == SANE_FRAME_GRAY)
+ {
+ xsane.xsane_color = 0;
+ }
+#ifdef SUPPORT_RGBA
+ else if (params.format == SANE_FRAME_RGBA)
+ {
+ xsane.xsane_color = 4;
+ }
+#endif
+ else /* RGB */
+ {
+ xsane.xsane_color = 3;
+ }
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), TEXT_INVALID_PARAMS);
+ }
+
+ gtk_label_set(GTK_LABEL(xsane.info_label), buf);
+
+
+ if (xsane.preview)
+ {
+ preview_update_surface(xsane.preview, 0);
+ }
+
+ xsane_update_histogram();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_gamma_changed(GtkAdjustment *adj_data, double *val)
+{
+ *val = adj_data->value;
+ xsane_enhancement_by_gamma();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_modus_callback(GtkWidget *xsane_parent, int *num)
+{
+
+ if (xsane.filetype) /* add extension to filename */
+ {
+ char buffer[256];
+
+ snprintf(buffer, sizeof(buffer), "%s%s", preferences.filename, xsane.filetype);
+ free(preferences.filename);
+ free(xsane.filetype);
+ xsane.filetype = 0;
+ preferences.filename = strdup(buffer);
+ }
+
+ xsane.xsane_mode = *num;
+ xsane_refresh_dialog(dialog);
+
+ if (xsane.xsane_mode != XSANE_FAX)
+ {
+ xsane_fax_dialog_close();
+ gtk_widget_set_sensitive(GTK_WIDGET(xsane.start_button), TRUE);
+ }
+
+ xsane_define_maximum_output_size();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_filetype_callback(GtkWidget *widget, gpointer data)
+{
+ if (data)
+ {
+ char *extension, *filename;
+
+ extension = strrchr(preferences.filename, '.');
+
+ if ((extension) && (extension != preferences.filename))
+ {
+ if ( (!strcasecmp(extension, ".pnm")) || (!strcasecmp(extension, ".raw"))
+ || (!strcasecmp(extension, ".png")) || (!strcasecmp(extension, ".ps"))
+ || (!strcasecmp(extension, ".rgba"))
+ || (!strcasecmp(extension, ".tiff")) || (!strcasecmp(extension, ".tif"))
+ || (!strcasecmp(extension, ".jpg")) || (!strcasecmp(extension, ".jpeg"))
+ ) /* remove filetype extension */
+ {
+ filename = preferences.filename;
+ *extension = 0; /* remove extension */
+ preferences.filename = strdup(filename); /* filename without extension */
+ free(filename); /* free unused memory */
+ }
+ }
+ }
+ else if (xsane.filetype)
+ {
+ char buffer[256];
+
+ snprintf(buffer, sizeof(buffer), "%s%s", preferences.filename, xsane.filetype);
+ free(preferences.filename);
+ free(xsane.filetype);
+ xsane.filetype = 0;
+ preferences.filename = strdup(buffer);
+ }
+
+ if (data)
+ {
+ xsane.filetype = strdup((char *) data); /* set extension for filename */
+ }
+
+ gtk_entry_set_text(GTK_ENTRY(xsane.outputfilename_entry), preferences.filename);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_outputfilename_changed_callback(GtkWidget *widget, gpointer data)
+{
+ if (preferences.filename)
+ {
+ free((void *) preferences.filename);
+ }
+ preferences.filename = strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_browse_filename_callback(GtkWidget *widget, gpointer data)
+{
+ char filename[1024];
+ char windowname[256];
+
+ xsane_set_sensitivity(FALSE);
+
+ if (xsane.filetype) /* set filetype to "by ext." */
+ {
+ char buffer[256];
+
+ snprintf(buffer, sizeof(buffer), "%s%s", preferences.filename, xsane.filetype);
+ free(preferences.filename);
+ free(xsane.filetype);
+ xsane.filetype = 0;
+ preferences.filename = strdup(buffer);
+ }
+
+ if (preferences.filename) /* make sure a correct filename is defined */
+ {
+ strncpy(filename, preferences.filename, sizeof(filename));
+ filename[sizeof(filename) - 1] = '\0';
+ }
+ else /* no filename given, take standard filename */
+ {
+ strcpy(filename, OUTFILENAME);
+ }
+
+ snprintf(windowname, sizeof(windowname), "%s %s %s", prog_name, WINDOW_OUTPUT_FILENAME, device_text);
+
+ umask(preferences.directory_umask); /* define new file permissions */
+ xsane_back_gtk_get_filename(windowname, filename, sizeof(filename), filename, TRUE);
+ umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
+
+ gtk_entry_set_text(GTK_ENTRY(xsane.outputfilename_entry), filename);
+
+ if (preferences.filename)
+ {
+ free((void *) preferences.filename);
+ }
+
+ xsane_set_sensitivity(TRUE);
+
+ preferences.filename = strdup(filename);
+
+ gtk_option_menu_set_history(GTK_OPTION_MENU(xsane.filetype_option_menu), 0); /* set menu to "by ext" */
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_outputfilename_new(GtkWidget *vbox)
+{
+ GtkWidget *hbox;
+ GtkWidget *text;
+ GtkWidget *button;
+ GtkWidget *xsane_filetype_menu, *xsane_filetype_item;
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+ button = xsane_button_new_with_pixmap(hbox, file_xpm, DESC_BROWSE_FILENAME, (GtkSignalFunc) xsane_browse_filename_callback, 0);
+
+ text = gtk_entry_new_with_max_length(255);
+ gtk_widget_set_usize(text, 80, 0); /* set minimum size */
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FILENAME);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.filename);
+ gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 4);
+ gtk_signal_connect(GTK_OBJECT(text), "changed", (GtkSignalFunc) xsane_outputfilename_changed_callback, 0);
+
+ xsane.outputfilename_entry = text;
+
+ xsane_filetype_menu = gtk_menu_new();
+
+ xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_BY_EXT);
+ gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
+ gtk_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate",
+ (GtkSignalFunc) xsane_filetype_callback, NULL);
+ gtk_widget_show(xsane_filetype_item);
+
+#ifdef HAVE_LIBJPEG
+ xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_JPEG);
+ gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
+ gtk_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate",
+ (GtkSignalFunc) xsane_filetype_callback, MENU_ITEM_FILETYPE_JPEG);
+ gtk_widget_show(xsane_filetype_item);
+#endif
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+ xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_PNG);
+ gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
+ gtk_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate",
+ (GtkSignalFunc) xsane_filetype_callback, MENU_ITEM_FILETYPE_PNG);
+ gtk_widget_show(xsane_filetype_item);
+#endif
+#endif
+
+ xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_PNM);
+ gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
+ gtk_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate",
+ (GtkSignalFunc) xsane_filetype_callback, MENU_ITEM_FILETYPE_PNM);
+ gtk_widget_show(xsane_filetype_item);
+
+ xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_PS);
+ gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
+ gtk_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate",
+ (GtkSignalFunc) xsane_filetype_callback, MENU_ITEM_FILETYPE_PS);
+ gtk_widget_show(xsane_filetype_item);
+
+ xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_RAW);
+ gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
+ gtk_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate",
+ (GtkSignalFunc) xsane_filetype_callback, MENU_ITEM_FILETYPE_RAW);
+ gtk_widget_show(xsane_filetype_item);
+
+#ifdef HAVE_LIBTIFF
+ xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_TIFF);
+ gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
+ gtk_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate",
+ (GtkSignalFunc) xsane_filetype_callback, MENU_ITEM_FILETYPE_TIFF);
+ gtk_widget_show(xsane_filetype_item);
+#endif
+
+ xsane.filetype_option_menu = gtk_option_menu_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, xsane.filetype_option_menu, DESC_FILETYPE);
+ gtk_box_pack_end(GTK_BOX(hbox), xsane.filetype_option_menu, FALSE, FALSE, 2);
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(xsane.filetype_option_menu), xsane_filetype_menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(xsane.filetype_option_menu), 0);
+ gtk_widget_show(xsane.filetype_option_menu);
+
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_faxreceiver_changed_callback(GtkWidget *widget, gpointer data)
+{
+ if (xsane.fax_receiver)
+ {
+ free((void *) xsane.fax_receiver);
+ }
+ xsane.fax_receiver = strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
+
+ xsane_fax_project_save();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_faxproject_changed_callback(GtkWidget *widget, gpointer data)
+{
+ if (preferences.fax_project)
+ {
+ free((void *) preferences.fax_project);
+ }
+ preferences.fax_project = strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
+
+ xsane_fax_project_load();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_fine_mode_callback(GtkWidget * widget)
+{
+ xsane.fax_fine_mode = (GTK_TOGGLE_BUTTON(widget)->active != 0);
+
+ if (xsane.fax_fine_mode)
+ {
+ xsane.resolution = 196;
+ xsane.resolution_x = 98;
+ xsane.resolution_y = 196;
+ }
+ else
+ {
+ xsane.resolution = 98;
+ xsane.resolution_x = 98;
+ xsane.resolution_y = 98;
+ }
+
+ xsane_set_all_resolutions();
+
+ xsane_update_param(dialog, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_enhancement_rgb_default_callback(GtkWidget * widget)
+{
+ xsane.enhancement_rgb_default = (GTK_TOGGLE_BUTTON(widget)->active != 0);
+
+ if (xsane.enhancement_rgb_default)
+ {
+ xsane.gamma_red = 1.0;
+ xsane.gamma_green = 1.0;
+ xsane.gamma_blue = 1.0;
+
+ xsane.brightness_red = 0.0;
+ xsane.brightness_green = 0.0;
+ xsane.brightness_blue = 0.0;
+
+ xsane.contrast_red = 0.0;
+ xsane.contrast_green = 0.0;
+ xsane.contrast_blue = 0.0;
+
+ xsane.slider_red.value[0] = 0.0;
+ xsane.slider_red.value[1] = 50.0;
+ xsane.slider_red.value[2] = 100.0;
+
+ xsane.slider_green.value[0] = 0.0;
+ xsane.slider_green.value[1] = 50.0;
+ xsane.slider_green.value[2] = 100.0;
+
+ xsane.slider_blue.value[0] = 0.0;
+ xsane.slider_blue.value[1] = 50.0;
+ xsane.slider_blue.value[2] = 100.0;
+ }
+ else
+ {
+ xsane.slider_red.value[0] = xsane.slider_gray.value[0];
+ xsane.slider_red.value[1] = xsane.slider_gray.value[1];
+ xsane.slider_red.value[2] = xsane.slider_gray.value[2];
+
+ xsane.slider_green.value[0] = xsane.slider_gray.value[0];
+ xsane.slider_green.value[1] = xsane.slider_gray.value[1];
+ xsane.slider_green.value[2] = xsane.slider_gray.value[2];
+
+ xsane.slider_blue.value[0] = xsane.slider_gray.value[0];
+ xsane.slider_blue.value[1] = xsane.slider_gray.value[1];
+ xsane.slider_blue.value[2] = xsane.slider_gray.value[2];
+ }
+
+ xsane_update_sliders();
+ xsane_update_gamma();
+ xsane_refresh_dialog(dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_enhancement_negative_callback(GtkWidget * widget)
+{
+ double v0;
+
+ if (xsane.negative != (GTK_TOGGLE_BUTTON(widget)->active != 0));
+ {
+ v0 = xsane.slider_gray.value[0];
+ xsane.slider_gray.value[0] = 100.0 - xsane.slider_gray.value[2];
+ xsane.slider_gray.value[1] = 100.0 - xsane.slider_gray.value[1];
+ xsane.slider_gray.value[2] = 100.0 - v0;
+
+ if (!xsane.enhancement_rgb_default)
+ {
+ v0 = xsane.slider_red.value[0];
+ xsane.slider_red.value[0] = 100.0 - xsane.slider_red.value[2];
+ xsane.slider_red.value[1] = 100.0 - xsane.slider_red.value[1];
+ xsane.slider_red.value[2] = 100.0 - v0;
+
+ v0 = xsane.slider_green.value[0];
+ xsane.slider_green.value[0] = 100.0 - xsane.slider_green.value[2];
+ xsane.slider_green.value[1] = 100.0 - xsane.slider_green.value[1];
+ xsane.slider_green.value[2] = 100.0 - v0;
+
+ v0 = xsane.slider_blue.value[0];
+ xsane.slider_blue.value[0] = 100.0 - xsane.slider_blue.value[2];
+ xsane.slider_blue.value[1] = 100.0 - xsane.slider_blue.value[1];
+ xsane.slider_blue.value[2] = 100.0 - v0;
+ }
+ }
+
+ xsane.negative = (GTK_TOGGLE_BUTTON(widget)->active != 0);
+
+ xsane_update_sliders();
+ xsane_enhancement_by_histogram();
+ xsane_update_gamma();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_auto_enhancement_callback(GtkWidget * widget)
+{
+ xsane_calculate_histogram();
+
+ xsane.slider_gray.value[0] = xsane.auto_black;
+ xsane.slider_gray.value[1] = xsane.auto_gray;
+ xsane.slider_gray.value[2] = xsane.auto_white;
+
+ if (xsane.enhancement_rgb_default) /* set same values for color components */
+ {
+ xsane.slider_red.value[0] = xsane.auto_black;
+ xsane.slider_red.value[1] = xsane.auto_gray;
+ xsane.slider_red.value[2] = xsane.auto_white;
+
+ xsane.slider_green.value[0] = xsane.auto_black;
+ xsane.slider_green.value[1] = xsane.auto_gray;
+ xsane.slider_green.value[2] = xsane.auto_white;
+
+ xsane.slider_blue.value[0] = xsane.auto_black;
+ xsane.slider_blue.value[1] = xsane.auto_gray;
+ xsane.slider_blue.value[2] = xsane.auto_white;
+ }
+ else /* set different values for each color component */
+ {
+ xsane.slider_red.value[0] = xsane.auto_black_red;
+ xsane.slider_red.value[1] = xsane.auto_gray_red;
+ xsane.slider_red.value[2] = xsane.auto_white_red;
+
+ xsane.slider_green.value[0] = xsane.auto_black_green;
+ xsane.slider_green.value[1] = xsane.auto_gray_green;
+ xsane.slider_green.value[2] = xsane.auto_white_green;
+
+ xsane.slider_blue.value[0] = xsane.auto_black_blue;
+ xsane.slider_blue.value[1] = xsane.auto_gray_blue;
+ xsane.slider_blue.value[2] = xsane.auto_white_blue;
+ }
+
+ xsane_enhancement_by_histogram();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_show_standard_options_callback(GtkWidget * widget)
+{
+ preferences.show_standard_options = (GTK_CHECK_MENU_ITEM(widget)->active != 0);
+ if (preferences.show_standard_options)
+ {
+ gtk_widget_show(xsane.standard_options_shell);
+ }
+ else
+ {
+ gtk_widget_hide(xsane.standard_options_shell);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_show_advanced_options_callback(GtkWidget * widget)
+{
+ preferences.show_advanced_options = (GTK_CHECK_MENU_ITEM(widget)->active != 0);
+ if (preferences.show_advanced_options)
+ {
+ gtk_widget_show(xsane.advanced_options_shell);
+ }
+ else
+ {
+ gtk_widget_hide(xsane.advanced_options_shell);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_show_resolution_list_callback(GtkWidget *widget)
+{
+ preferences.show_resolution_list = (GTK_CHECK_MENU_ITEM(widget)->active != 0);
+ xsane_refresh_dialog(0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_page_rotate_callback(GtkWidget *widget)
+{
+ preferences.psrotate = (GTK_CHECK_MENU_ITEM(widget)->active != 0);
+ xsane_define_maximum_output_size();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_show_histogram_callback(GtkWidget * widget)
+{
+ preferences.show_histogram = (GTK_CHECK_MENU_ITEM(widget)->active != 0);
+ if (preferences.show_histogram)
+ {
+ xsane_update_histogram();
+ gtk_widget_show(xsane.histogram_dialog);
+ }
+ else
+ {
+ gtk_widget_hide(xsane.histogram_dialog);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_printer_callback(GtkWidget *widget, gpointer data)
+{
+ preferences.printernr = (int) data;
+ xsane_back_gtk_refresh_dialog(dialog);
+ xsane_define_maximum_output_size();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_resolution_scale_update(GtkAdjustment *adj_data, double *val)
+{
+#if 1
+/* gtk does not make sure that the value is quantisized correct */
+ float diff, old, new, quant;
+
+ quant = adj_data->step_increment;
+
+ if (quant != 0)
+ {
+ new = adj_data->value;
+ old = *val;
+ diff = quant*((int) ((new - old)/quant));
+
+ *val = old + diff;
+ adj_data->value = *val;
+ }
+#else
+ *val = adj_data->value;
+#endif
+
+ xsane_set_all_resolutions();
+
+ xsane_update_param(dialog, 0);
+ xsane.zoom = xsane.resolution / preferences.printer[preferences.printernr]->resolution;
+ xsane.zoom_x = xsane.resolution_x / preferences.printer[preferences.printernr]->resolution;
+ xsane.zoom_y = xsane.resolution_y / preferences.printer[preferences.printernr]->resolution;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_resolution_list_callback(GtkWidget *widget, gpointer data)
+{
+ GSGMenuItem *menu_item = data;
+ GSGDialogElement *elem = menu_item->elem;
+ GSGDialog *dialog = elem->dialog;
+ SANE_Word val;
+ gchar *name = gtk_widget_get_name(widget->parent);
+
+ sscanf(menu_item->label, "%d", &val);
+
+ if (!strcmp(name, XSANE_GTK_NAME_RESOLUTION))
+ {
+ xsane.resolution = val;
+ xsane.resolution_x = val;
+ xsane.resolution_y = val;
+
+ xsane_set_resolution(dialog->well_known.dpi, xsane.resolution);
+ xsane_set_resolution(dialog->well_known.dpi_x, xsane.resolution_x);
+ xsane_set_resolution(dialog->well_known.dpi_y, xsane.resolution_y);
+
+ xsane.zoom = xsane.resolution / preferences.printer[preferences.printernr]->resolution;
+ xsane.zoom_x = xsane.resolution_x / preferences.printer[preferences.printernr]->resolution;
+ xsane.zoom_y = xsane.resolution_y / preferences.printer[preferences.printernr]->resolution;
+ }
+ else if (!strcmp(name, XSANE_GTK_NAME_X_RESOLUTION))
+ {
+ xsane.resolution = val;
+ xsane.resolution_x = val;
+ xsane_set_resolution(dialog->well_known.dpi_x, xsane.resolution_x);
+ xsane.zoom = xsane.resolution / preferences.printer[preferences.printernr]->resolution;
+ }
+ else if (!strcmp(name, XSANE_GTK_NAME_Y_RESOLUTION))
+ {
+ xsane.resolution_y = val;
+ xsane_set_resolution(dialog->well_known.dpi_y, xsane.resolution_y);
+ xsane.zoom = xsane.resolution / preferences.printer[preferences.printernr]->resolution;
+ }
+
+ xsane_update_param(dialog, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_resolution_widget_new(GtkWidget *parent, int well_known_option, double *resolution, const char *image_xpm[],
+ const gchar *desc, const gchar *widget_name)
+{
+ GtkObject *resolution_widget;
+ const SANE_Option_Descriptor *opt;
+
+ opt = sane_get_option_descriptor(dialog->dev, well_known_option);
+
+ if (!opt)
+ {
+ return -1; /* options does not exist */
+ }
+ else
+ {
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ {
+ SANE_Word quant=0;
+ SANE_Word min=0;
+ SANE_Word max=0;
+ SANE_Word val=0;
+
+ gtk_widget_set_sensitive(xsane.show_resolution_list_widget, TRUE);
+ sane_control_option(dialog->dev, well_known_option, SANE_ACTION_GET_VALUE, &val, 0);
+
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ min = opt->constraint.range->min;
+ max = opt->constraint.range->max;
+ quant = opt->constraint.range->quant;
+ break;
+
+ case SANE_TYPE_FIXED:
+ min = SANE_UNFIX(opt->constraint.range->min);
+ max = SANE_UNFIX(opt->constraint.range->max);
+ quant = SANE_UNFIX(opt->constraint.range->quant);
+ val = SANE_UNFIX(val);
+ break;
+
+ default:
+ fprintf(stderr, "zoom_scale_update: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ }
+
+ if (quant == 0)
+ {
+ quant = 1;
+ }
+
+ if (!(*resolution)) /* no prefered value */
+ {
+ *resolution = val; /* set backend predefined value */
+ }
+
+ if (!preferences.show_resolution_list) /* user wants slider */
+ {
+ xsane_scale_new_with_pixmap(GTK_BOX(parent), image_xpm, desc,
+ min, max, quant, quant, 0.0, 0, resolution, &resolution_widget,
+ well_known_option, xsane_resolution_scale_update, SANE_OPTION_IS_SETTABLE(opt->cap));
+ }
+ else /* user wants list instead of slider */
+ {
+ SANE_Int max_items = 20;
+ char **str_list;
+ char str[16];
+ int i;
+ int j = 0;
+ SANE_Word wanted_res;
+ SANE_Word val = max;
+ int res = max;
+ double mul;
+
+ sane_control_option(dialog->dev, well_known_option, SANE_ACTION_GET_VALUE, &wanted_res, 0);
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ wanted_res = (int) SANE_UNFIX(wanted_res);
+ }
+
+ if (*resolution) /* prefered value */
+ {
+ wanted_res = *resolution; /* set frontend prefered value */
+ }
+
+ str_list = malloc((max_items + 1) * sizeof(str_list[0]));
+
+ sprintf(str, "%d", max);
+ str_list[j++] = strdup(str);
+
+ i=9;
+ while ((j < max_items) && (res > 50) && (res > min) && (i > 0))
+ {
+ mul = ((double) i) / (i+1);
+ res = (int) (max * mul);
+ if (res/mul == max)
+ {
+ sprintf(str, "%d", res);
+ str_list[j++] = strdup(str);
+ if (res >= wanted_res)
+ {
+ val = res;
+ }
+ }
+ i--;
+ }
+
+ i = 3;
+ while ((j < max_items) && (res > 50) && (res > min))
+ {
+ mul = 1.0/i;
+ res = max * mul;
+ if (res/mul == max)
+ {
+ sprintf(str, "%d", res);
+ str_list[j++] = strdup(str);
+ if (res >= wanted_res)
+ {
+ val = res;
+ }
+ }
+ i++;
+ }
+
+ str_list[j] = 0;
+ sprintf(str, "%d", (int) val);
+
+ xsane_option_menu_new_with_pixmap(GTK_BOX(parent), image_xpm, desc, str_list, str, &resolution_widget, well_known_option,
+ xsane_resolution_list_callback, SANE_OPTION_IS_SETTABLE(opt->cap), widget_name);
+
+ free(str_list);
+ *resolution = val;
+ xsane_set_resolution(well_known_option, *resolution);
+ }
+ }
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ {
+ /* use a "list-selection" widget */
+ SANE_Int items;
+ char **str_list;
+ char str[16];
+ int j;
+ SANE_Word val=0;
+
+ gtk_widget_set_sensitive(xsane.show_resolution_list_widget, FALSE);
+
+ items = opt->constraint.word_list[0];
+ str_list = malloc((items + 1) * sizeof(str_list[0]));
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ for (j = 0; j < items; ++j)
+ {
+ sprintf(str, "%d", opt->constraint.word_list[j + 1]);
+ str_list[j] = strdup(str);
+ }
+ str_list[j] = 0;
+ sane_control_option(dialog->dev, well_known_option, SANE_ACTION_GET_VALUE, &val, 0);
+ sprintf(str, "%d", (int) val);
+ break;
+
+ case SANE_TYPE_FIXED:
+ for (j = 0; j < items; ++j)
+ {
+ sprintf(str, "%d", (int) SANE_UNFIX(opt->constraint.word_list[j + 1]));
+ str_list[j] = strdup(str);
+ }
+ str_list[j] = 0;
+ sane_control_option(dialog->dev, well_known_option, SANE_ACTION_GET_VALUE, &val, 0);
+ sprintf(str, "%d", (int) SANE_UNFIX(val));
+ break;
+
+ default:
+ fprintf(stderr, "resolution_word_list_creation: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ }
+
+
+ xsane_option_menu_new_with_pixmap(GTK_BOX(parent), image_xpm, desc,
+ str_list, str, &resolution_widget, well_known_option,
+ xsane_resolution_list_callback, SANE_OPTION_IS_SETTABLE(opt->cap), widget_name);
+ free(str_list);
+ }
+ break;
+
+ default:
+ break;
+ } /* constraint type */
+
+ return 0; /* everything is ok */
+
+ } /* if resolution option active */
+
+ return 1; /* not active */
+
+ } /* if (opt) */
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_zoom_update(GtkAdjustment *adj_data, double *val)
+{
+ *val=adj_data->value;
+
+ /* update all resolutions */
+ xsane.resolution = xsane.zoom * preferences.printer[preferences.printernr]->resolution;
+ xsane.resolution_x = xsane.zoom_x * preferences.printer[preferences.printernr]->resolution;
+ xsane.resolution_y = xsane.zoom_y * preferences.printer[preferences.printernr]->resolution;
+
+ xsane_set_all_resolutions();
+
+ xsane_update_param(dialog, 0);
+
+ xsane_define_maximum_output_size();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_zoom_widget_new(GtkWidget *parent, int well_known_option, double *zoom, double resolution,
+ const char *image_xpm[], const gchar *desc)
+{
+ const SANE_Option_Descriptor *opt;
+ double output_resolution = preferences.printer[preferences.printernr]->resolution;
+
+ opt = sane_get_option_descriptor(dialog->dev, well_known_option);
+ if (!opt)
+ {
+ return -1; /* option not available */
+ }
+ else
+ {
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ double min = 0.0;
+ double max = 0.0;
+ SANE_Word val = 0.0;
+
+ sane_control_option(dialog->dev, well_known_option, SANE_ACTION_GET_VALUE, &val, 0);
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ min = ((double) opt->constraint.range->min) / output_resolution;
+ max = ((double) opt->constraint.range->max) / output_resolution;
+ break;
+
+ case SANE_TYPE_FIXED:
+ min = SANE_UNFIX(opt->constraint.range->min) / output_resolution;
+ max = SANE_UNFIX(opt->constraint.range->max) / output_resolution;
+ val = SANE_UNFIX(val);
+ break;
+
+ default:
+ fprintf(stderr, "zoom_scale_update: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ }
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ xsane_get_bounds(opt, &min, &max);
+ min = min / output_resolution;
+ max = max / output_resolution;
+ break;
+
+ default:
+ fprintf(stderr, "zoom_scale_update: %s %d\n", ERR_UNKNOWN_CONSTRAINT_TYPE, opt->constraint_type);
+ }
+
+ if (resolution == 0) /* no prefered value */
+ {
+ resolution = val; /* set backend predefined value */
+ }
+
+ *zoom = resolution / output_resolution;
+
+ xsane_scale_new_with_pixmap(GTK_BOX(parent), image_xpm, desc, min, max, 0.01, 0.01, 0.1, 2,
+ zoom, &xsane.zoom_widget, well_known_option, xsane_zoom_update,
+ SANE_OPTION_IS_SETTABLE(opt->cap));
+
+ return 0; /* everything is ok */
+ }
+ return 1; /* option not active */
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+GtkWidget *xsane_update_xsane_callback()
+{
+ /* creates the XSane option window */
+
+ GtkWidget *xsane_vbox, *xsane_hbox;
+ GtkWidget *xsane_modus_menu;
+ GtkWidget *xsane_modus_item;
+ GtkWidget *xsane_modus_option_menu;
+ GtkWidget *xsane_vbox_xsane_modus;
+ GtkWidget *xsane_hbox_xsane_modus;
+ GtkWidget *xsane_label;
+ GtkWidget *xsane_text;
+ GtkWidget *xsane_hbox_xsane_enhancement;
+ GtkWidget *xsane_frame;
+ GtkWidget *xsane_button;
+ gchar buf[200];
+
+ /* xsane main options */
+
+ xsane_hbox = gtk_hbox_new(FALSE, 2);
+ gtk_widget_show(xsane_hbox);
+ xsane_vbox = gtk_vbox_new(/* homogeneous */ FALSE, 0);
+ gtk_widget_show(xsane_vbox);
+/* gtk_box_pack_start(GTK_BOX(xsane_hbox), xsane_vbox, FALSE, FALSE, 0); */ /* make scales fixed */
+ gtk_box_pack_start(GTK_BOX(xsane_hbox), xsane_vbox, TRUE, TRUE, 0); /* make scales sizeable */
+
+ /* XSane Frame */
+
+ xsane_frame = gtk_frame_new(TEXT_XSANE_OPTIONS);
+ gtk_container_set_border_width(GTK_CONTAINER(xsane_frame), 4);
+ gtk_frame_set_shadow_type(GTK_FRAME(xsane_frame), GTK_SHADOW_ETCHED_IN);
+/* gtk_box_pack_start(GTK_BOX(xsane_vbox), xsane_frame, FALSE, FALSE, 0); */ /* fixed frameheight */
+ gtk_box_pack_start(GTK_BOX(xsane_vbox), xsane_frame, TRUE, TRUE, 0); /* sizeable framehight */
+ gtk_widget_show(xsane_frame);
+
+/* xsane_vbox_xsane_modus = gtk_vbox_new(FALSE, 5); */
+ xsane_vbox_xsane_modus = gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(xsane_frame), xsane_vbox_xsane_modus);
+ gtk_widget_show(xsane_vbox_xsane_modus);
+
+/* scan copy fax selection */
+
+ if ( (xsane.mode == XSANE_STANDALONE) && (xsane.mode_selection) ) /* display xsane mode selection menu */
+ {
+ xsane_hbox_xsane_modus = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(xsane_hbox_xsane_modus), 2);
+ gtk_box_pack_start(GTK_BOX(xsane_vbox_xsane_modus), xsane_hbox_xsane_modus, FALSE, FALSE, 0);
+
+ xsane_label = gtk_label_new(TEXT_XSANE_MODE);
+ gtk_box_pack_start(GTK_BOX(xsane_hbox_xsane_modus), xsane_label, FALSE, FALSE, 2);
+ gtk_widget_show(xsane_label);
+
+ xsane_modus_menu = gtk_menu_new();
+
+ xsane_modus_item = gtk_menu_item_new_with_label(MENU_ITEM_SCAN);
+ gtk_widget_set_usize(xsane_modus_item, 60, 0);
+ gtk_container_add(GTK_CONTAINER(xsane_modus_menu), xsane_modus_item);
+ gtk_signal_connect(GTK_OBJECT(xsane_modus_item), "activate",
+ (GtkSignalFunc) xsane_modus_callback, &xsane_scanmode_number[XSANE_SCAN]);
+ gtk_widget_show(xsane_modus_item);
+
+ xsane_modus_item = gtk_menu_item_new_with_label(MENU_ITEM_COPY);
+ gtk_container_add(GTK_CONTAINER(xsane_modus_menu), xsane_modus_item);
+ gtk_signal_connect(GTK_OBJECT(xsane_modus_item), "activate",
+ (GtkSignalFunc) xsane_modus_callback, &xsane_scanmode_number[XSANE_COPY]);
+ gtk_widget_show(xsane_modus_item);
+
+ xsane_modus_item = gtk_menu_item_new_with_label(MENU_ITEM_FAX);
+ gtk_container_add(GTK_CONTAINER(xsane_modus_menu), xsane_modus_item);
+ gtk_signal_connect(GTK_OBJECT(xsane_modus_item), "activate",
+ (GtkSignalFunc) xsane_modus_callback, &xsane_scanmode_number[XSANE_FAX]);
+ gtk_widget_show(xsane_modus_item);
+
+ xsane_modus_option_menu = gtk_option_menu_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, xsane_modus_option_menu, DESC_XSANE_MODE);
+ gtk_box_pack_end(GTK_BOX(xsane_hbox_xsane_modus), xsane_modus_option_menu, FALSE, FALSE, 2);
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(xsane_modus_option_menu), xsane_modus_menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(xsane_modus_option_menu), xsane.xsane_mode);
+ gtk_widget_show(xsane_modus_option_menu);
+ gtk_widget_show(xsane_hbox_xsane_modus);
+
+ dialog->xsanemode_widget = xsane_modus_option_menu;
+ }
+
+ {
+ GtkWidget *pixmapwidget;
+ GdkBitmap *mask;
+ GdkPixmap *pixmap;
+ GtkWidget *hbox;
+ const SANE_Option_Descriptor *opt;
+
+
+ /* colormode */
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.scanmode);
+ if (opt)
+ {
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ gtk_box_pack_start(GTK_BOX(xsane_vbox_xsane_modus), hbox, FALSE, FALSE, 2);
+
+ pixmap = gdk_pixmap_create_from_xpm_d(xsane.histogram_dialog->window, &mask, xsane.bg_trans, (gchar **) colormode_xpm);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_box_pack_start(GTK_BOX(hbox), pixmapwidget, FALSE, FALSE, 2);
+ gdk_pixmap_unref(pixmap);
+ gtk_widget_show(pixmapwidget);
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_STRING_LIST:
+ {
+ char *set;
+ SANE_Status status;
+
+ /* use a "list-selection" widget */
+ set = malloc(opt->size);
+ status = sane_control_option(dialog->dev, dialog->well_known.scanmode, SANE_ACTION_GET_VALUE, set, 0);
+
+ xsane_option_menu_new(hbox, (char **) opt->constraint.string_list, set, dialog->well_known.scanmode,
+ _BGT(opt->desc), 0, SANE_OPTION_IS_SETTABLE(opt->cap), 0);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "scanmode_selection: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ }
+ gtk_widget_show(hbox);
+ }
+ }
+
+
+ /* input selection */
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.scansource);
+ if (opt)
+ {
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ gtk_box_pack_start(GTK_BOX(xsane_vbox_xsane_modus), hbox, FALSE, FALSE, 2);
+
+ pixmap = gdk_pixmap_create_from_xpm_d(xsane.histogram_dialog->window, &mask, xsane.bg_trans, (gchar **) scanner_xpm);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_box_pack_start(GTK_BOX(hbox), pixmapwidget, FALSE, FALSE, 2);
+ gdk_pixmap_unref(pixmap);
+ gtk_widget_show(pixmapwidget);
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_STRING_LIST:
+ {
+ char *set;
+ SANE_Status status;
+
+ /* use a "list-selection" widget */
+ set = malloc(opt->size);
+ status = sane_control_option(dialog->dev, dialog->well_known.scansource, SANE_ACTION_GET_VALUE, set, 0);
+
+ xsane_option_menu_new(hbox, (char **) opt->constraint.string_list, set, dialog->well_known.scansource,
+ _BGT(opt->desc), 0, SANE_OPTION_IS_SETTABLE(opt->cap), 0);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "scansource_selection: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ }
+ gtk_widget_show(hbox);
+ }
+ }
+
+ }
+
+ if (xsane.xsane_mode == XSANE_SCAN)
+ {
+ xsane.copy_number_entry = 0;
+
+ if (xsane.mode == XSANE_STANDALONE)
+ {
+ xsane_outputfilename_new(xsane_vbox_xsane_modus);
+ }
+
+ /* resolution selection */
+ if (!xsane_resolution_widget_new(xsane_vbox_xsane_modus, dialog->well_known.dpi_x, &xsane.resolution_x, resolution_x_xpm,
+ DESC_RESOLUTION_X, XSANE_GTK_NAME_X_RESOLUTION)) /* draw x resolution widget if possible */
+ {
+ xsane_resolution_widget_new(xsane_vbox_xsane_modus, dialog->well_known.dpi_y, &xsane.resolution_y, resolution_y_xpm,
+ DESC_RESOLUTION_Y, XSANE_GTK_NAME_Y_RESOLUTION); /* ok, also draw y resolution widget */
+ }
+ else /* no x resolution, so lets draw common resolution widget */
+ {
+ xsane_resolution_widget_new(xsane_vbox_xsane_modus, dialog->well_known.dpi, &xsane.resolution, resolution_xpm,
+ DESC_RESOLUTION, XSANE_GTK_NAME_RESOLUTION);
+ }
+ }
+ else if (xsane.xsane_mode == XSANE_COPY)
+ {
+ GtkWidget *pixmapwidget, *hbox, *xsane_printer_option_menu, *xsane_printer_menu, *xsane_printer_item;
+ GdkBitmap *mask;
+ GdkPixmap *pixmap;
+ int i;
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ gtk_box_pack_start(GTK_BOX(xsane_vbox_xsane_modus), hbox, FALSE, FALSE, 2);
+
+ pixmap = gdk_pixmap_create_from_xpm_d(xsane.histogram_dialog->window, &mask, xsane.bg_trans, (gchar **) printer_xpm);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_box_pack_start(GTK_BOX(hbox), pixmapwidget, FALSE, FALSE, 2);
+ gdk_pixmap_unref(pixmap);
+ gtk_widget_show(pixmapwidget);
+
+ xsane_printer_menu = gtk_menu_new();
+
+ for (i=0; i < preferences.printerdefinitions; i++)
+ {
+ xsane_printer_item = gtk_menu_item_new_with_label(preferences.printer[i]->name);
+ gtk_container_add(GTK_CONTAINER(xsane_printer_menu), xsane_printer_item);
+ gtk_signal_connect(GTK_OBJECT(xsane_printer_item), "activate", (GtkSignalFunc) xsane_printer_callback, (void *) i);
+ gtk_widget_show(xsane_printer_item);
+ }
+
+ xsane_printer_option_menu = gtk_option_menu_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, xsane_printer_option_menu, DESC_PRINTER_SELECT);
+ gtk_box_pack_end(GTK_BOX(hbox), xsane_printer_option_menu, FALSE, FALSE, 2);
+ gtk_widget_show(xsane_printer_option_menu);
+ gtk_widget_show(hbox);
+
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(xsane_printer_option_menu), xsane_printer_menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(xsane_printer_option_menu), preferences.printernr);
+
+ /* number of copies */
+ xsane_text = gtk_entry_new();
+ xsane_back_gtk_set_tooltip(dialog->tooltips, xsane_text, DESC_COPY_NUMBER);
+ gtk_widget_set_usize(xsane_text, 25, 0);
+ snprintf(buf, sizeof(buf), "%d", xsane.copy_number);
+ gtk_entry_set_text(GTK_ENTRY(xsane_text), (char *) buf);
+ gtk_box_pack_end(GTK_BOX(hbox), xsane_text, FALSE, FALSE, 10);
+ gtk_widget_show(xsane_text);
+ gtk_widget_show(hbox);
+ xsane.copy_number_entry = xsane_text;
+
+ /* zoom selection */
+ if (!xsane_zoom_widget_new(xsane_vbox_xsane_modus, dialog->well_known.dpi_x, &xsane.zoom_x,
+ xsane.resolution_x, zoom_x_xpm, DESC_ZOOM_X))
+ {
+ xsane_zoom_widget_new(xsane_vbox_xsane_modus, dialog->well_known.dpi_y, &xsane.zoom_y,
+ xsane.resolution_y, zoom_y_xpm, DESC_ZOOM_Y);
+ }
+ else
+ {
+ xsane_zoom_widget_new(xsane_vbox_xsane_modus, dialog->well_known.dpi, &xsane.zoom,
+ xsane.resolution, zoom_xpm, DESC_ZOOM);
+ }
+ }
+ else /* XSANE_FAX */
+ {
+ const SANE_Option_Descriptor *opt;
+
+ xsane.copy_number_entry = 0;
+ xsane.resolution = 98;
+ xsane.resolution_x = 98;
+ xsane.resolution_y = 98;
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.dpi);
+ if (!opt)
+ {
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.dpi_x);
+ }
+
+ if (opt)
+ {
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ xsane_button = gtk_check_button_new_with_label(RADIO_BUTTON_FINE_MODE);
+ xsane_back_gtk_set_tooltip(dialog->tooltips, xsane_button, DESC_FAX_FINE_MODE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(xsane_button), xsane.fax_fine_mode);
+ gtk_box_pack_start(GTK_BOX(xsane_vbox_xsane_modus), xsane_button, FALSE, FALSE, 2);
+ gtk_widget_show(xsane_button);
+ gtk_signal_connect(GTK_OBJECT(xsane_button), "clicked", (GtkSignalFunc) xsane_fax_fine_mode_callback, 0);
+
+ if (xsane.fax_fine_mode)
+ {
+ xsane.resolution = 196;
+ xsane.resolution_x = 98;
+ xsane.resolution_y = 196;
+ }
+
+ xsane_set_all_resolutions();
+ }
+ }
+ xsane_fax_dialog();
+ }
+
+ /* test if scanner gamma table is selected */
+
+ xsane.scanner_gamma_gray = FALSE;
+ if (dialog->well_known.gamma_vector >0)
+ {
+ const SANE_Option_Descriptor *opt;
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector);
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ xsane.scanner_gamma_gray = TRUE;
+ }
+ }
+
+ xsane.scanner_gamma_color = FALSE;
+ if (dialog->well_known.gamma_vector_r >0)
+ {
+ const SANE_Option_Descriptor *opt;
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector_r);
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ xsane.scanner_gamma_color = TRUE;
+ }
+ }
+
+
+
+ /* XSane Frame Enhancement */
+
+ sane_get_parameters(dialog->dev, &xsane.param); /* update xsane.param */
+
+ if (xsane.param.depth == 1)
+ {
+ return(xsane_hbox);
+ }
+
+ xsane.slider_gray.active = XSANE_SLIDER_ACTIVE; /* mark slider active */
+
+ if ( (xsane.xsane_color) && (!xsane.enhancement_rgb_default) )
+ {
+ xsane_separator_new(xsane_vbox_xsane_modus, 2);
+ }
+
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), Gamma_xpm, DESC_GAMMA,
+ XSANE_GAMMA_MIN, XSANE_GAMMA_MAX, 0.01, 0.01, 0.0, 2,
+ &xsane.gamma, &xsane.gamma_widget, 0, xsane_gamma_changed, TRUE);
+ if ( (xsane.xsane_color) && (!xsane.enhancement_rgb_default) )
+ {
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), Gamma_red_xpm, DESC_GAMMA_R,
+ XSANE_GAMMA_MIN, XSANE_GAMMA_MAX, 0.01, 0.01, 0.0, 2,
+ &xsane.gamma_red , &xsane.gamma_red_widget, 0, xsane_gamma_changed, TRUE);
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), Gamma_green_xpm, DESC_GAMMA_G,
+ XSANE_GAMMA_MIN, XSANE_GAMMA_MAX, 0.01, 0.01, 0.0, 2,
+ &xsane.gamma_green, &xsane.gamma_green_widget, 0, xsane_gamma_changed, TRUE);
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), Gamma_blue_xpm, DESC_GAMMA_B,
+ XSANE_GAMMA_MIN, XSANE_GAMMA_MAX, 0.01, 0.01, 0.0, 2,
+ &xsane.gamma_blue , &xsane.gamma_blue_widget, 0, xsane_gamma_changed, TRUE);
+
+ xsane_separator_new(xsane_vbox_xsane_modus, 2);
+ }
+
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), brightness_xpm, DESC_BRIGHTNESS,
+ XSANE_BRIGHTNESS_MIN, XSANE_BRIGHTNESS_MAX, 1.0, 1.0, 0.0, 0,
+ &xsane.brightness, &xsane.brightness_widget, 0, xsane_gamma_changed, TRUE);
+ if ( (xsane.xsane_color) && (!xsane.enhancement_rgb_default) )
+ {
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), brightness_red_xpm, DESC_BRIGHTNESS_R,
+ XSANE_BRIGHTNESS_MIN, XSANE_BRIGHTNESS_MAX, 1.0, 1.0, 0.0, 0,
+ &xsane.brightness_red , &xsane.brightness_red_widget, 0, xsane_gamma_changed, TRUE);
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), brightness_green_xpm, DESC_BRIGHTNESS_G,
+ XSANE_BRIGHTNESS_MIN, XSANE_BRIGHTNESS_MAX, 1.0, 1.0, 0.0, 0,
+ &xsane.brightness_green, &xsane.brightness_green_widget, 0, xsane_gamma_changed, TRUE);
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), brightness_blue_xpm, DESC_BRIGHTNESS_B,
+ XSANE_BRIGHTNESS_MIN, XSANE_BRIGHTNESS_MAX, 1.0, 1.0, 0.0, 0,
+ &xsane.brightness_blue, &xsane.brightness_blue_widget, 0, xsane_gamma_changed, TRUE);
+
+ xsane_separator_new(xsane_vbox_xsane_modus, 2);
+ }
+
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), contrast_xpm, DESC_CONTRAST,
+ XSANE_CONTRAST_GRAY_MIN, XSANE_CONTRAST_MAX, 1.0, 1.0, 0.0, 0,
+ &xsane.contrast, &xsane.contrast_widget, 0, xsane_gamma_changed, TRUE);
+ if ( (xsane.xsane_color) && (!xsane.enhancement_rgb_default) )
+ {
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), contrast_red_xpm, DESC_CONTRAST_R,
+ XSANE_CONTRAST_MIN, XSANE_CONTRAST_MAX, 1.0, 1.0, 0.0, 0,
+ &xsane.contrast_red , &xsane.contrast_red_widget, 0, xsane_gamma_changed, TRUE);
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), contrast_green_xpm, DESC_CONTRAST_G,
+ XSANE_CONTRAST_MIN, XSANE_CONTRAST_MAX, 1.0, 1.0, 0.0, 0,
+ &xsane.contrast_green, &xsane.contrast_green_widget, 0, xsane_gamma_changed, TRUE);
+ xsane_scale_new_with_pixmap(GTK_BOX(xsane_vbox_xsane_modus), contrast_blue_xpm, DESC_CONTRAST_B,
+ XSANE_CONTRAST_MIN, XSANE_CONTRAST_MAX, 1.0, 1.0, 0.0, 0,
+ &xsane.contrast_blue, &xsane.contrast_blue_widget, 0, xsane_gamma_changed, TRUE);
+ }
+
+ xsane_separator_new(xsane_vbox_xsane_modus, 2);
+
+ /* create lower button box (rgb default, negative ,... */
+ xsane_hbox_xsane_enhancement = gtk_hbox_new(TRUE, 4);
+ gtk_container_set_border_width(GTK_CONTAINER(xsane_hbox_xsane_enhancement), 4);
+ gtk_box_pack_start(GTK_BOX(xsane_vbox_xsane_modus), xsane_hbox_xsane_enhancement, FALSE, FALSE, 0);
+ gtk_widget_show(xsane_hbox_xsane_enhancement);
+
+ if (xsane.xsane_color)
+ {
+ xsane_toggle_button_new_with_pixmap(xsane_hbox_xsane_enhancement, rgb_default_xpm, DESC_RGB_DEFAULT,
+ &xsane.enhancement_rgb_default, xsane_enhancement_rgb_default_callback);
+ }
+
+ xsane_toggle_button_new_with_pixmap(xsane_hbox_xsane_enhancement, negative_xpm, DESC_NEGATIVE,
+ &xsane.negative, xsane_enhancement_negative_callback);
+
+ xsane_button_new_with_pixmap(xsane_hbox_xsane_enhancement, enhance_xpm, DESC_ENH_AUTO,
+ xsane_auto_enhancement_callback, 0);
+
+ xsane_button_new_with_pixmap(xsane_hbox_xsane_enhancement, default_enhancement_xpm, DESC_ENH_DEFAULT,
+ xsane_enhancement_restore_default, 0);
+
+ xsane_button_new_with_pixmap(xsane_hbox_xsane_enhancement, restore_enhancement_xpm, DESC_ENH_RESTORE,
+ xsane_enhancement_restore, 0);
+
+ xsane_button_new_with_pixmap(xsane_hbox_xsane_enhancement, store_enhancement_xpm, DESC_ENH_STORE,
+ xsane_enhancement_store, 0);
+
+ xsane_update_histogram();
+
+ return(xsane_hbox);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_update_preview(GSGDialog *dialog, void *arg)
+{
+ if (xsane.preview)
+ {
+ preview_update_surface(xsane.preview, 0);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_pref_save(void)
+{
+ char filename[PATH_MAX];
+ int fd;
+
+ /* first save xsane-specific preferences: */
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, "xsane", 0, ".rc", XSANE_PATH_LOCAL_SANE);
+ umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_CREATE_FILE, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+ preferences_save(fd);
+ close(fd);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_pref_restore(void)
+{
+ char filename[PATH_MAX];
+ int fd;
+
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, "xsane", 0, ".rc", XSANE_PATH_LOCAL_SANE);
+ fd = open(filename, O_RDONLY);
+
+ if (fd >= 0)
+ {
+ preferences_restore(fd);
+ close(fd);
+ }
+ else /* no local sane file, look for system file */
+ {
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, "xsane", 0, ".rc", XSANE_PATH_SYSTEM);
+ fd = open(filename, O_RDONLY);
+
+ if (fd >= 0)
+ {
+ preferences_restore(fd);
+ close(fd);
+ }
+ }
+
+ if (!preferences.filename)
+ {
+ preferences.filename = strdup(OUTFILENAME);
+ }
+
+ if (preferences.printerdefinitions == 0)
+ {
+ xsane_new_printer();
+ }
+
+ if (!preferences.fax_project)
+ {
+ preferences.fax_project = strdup(FAXPROJECT);
+ }
+
+ if (!preferences.fax_command)
+ {
+ preferences.fax_command = strdup(FAXCOMMAND);
+ }
+
+ if (!preferences.fax_receiver_option)
+ {
+ preferences.fax_receiver_option = strdup(FAXRECEIVEROPT);
+ }
+
+ if (!preferences.fax_postscript_option)
+ {
+ preferences.fax_postscript_option = strdup(FAXPOSTSCRIPTOPT);
+ }
+
+ if (!preferences.fax_normal_option)
+ {
+ preferences.fax_normal_option = strdup(FAXNORMALOPT);
+ }
+
+ if (!preferences.fax_fine_option)
+ {
+ preferences.fax_fine_option = strdup(FAXFINEOPT);
+ }
+
+ if (!preferences.fax_viewer)
+ {
+ preferences.fax_viewer = strdup(FAXVIEWER);
+ }
+
+ if (!preferences.doc_viewer)
+ {
+ preferences.doc_viewer = strdup(DOCVIEWER);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_quit(void)
+{
+ if (xsane.preview)
+ {
+ Preview *preview = xsane.preview;
+ xsane.preview = 0;
+ preview_destroy(preview);
+ }
+
+ while (xsane_back_gtk_message_dialog_active)
+ {
+ gtk_main_iteration();
+ }
+
+ if (dialog && xsane_back_gtk_dialog_get_device(dialog))
+ {
+ sane_close(xsane_back_gtk_dialog_get_device(dialog));
+ }
+
+ sane_exit();
+ gtk_main_quit();
+
+ if (xsane.preview_gamma_data_red)
+ {
+ free(xsane.preview_gamma_data_red);
+ free(xsane.preview_gamma_data_green);
+ free(xsane.preview_gamma_data_blue);
+
+ xsane.preview_gamma_data_red = 0;
+ xsane.preview_gamma_data_green = 0;
+ xsane.preview_gamma_data_blue = 0;
+ }
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+ if (xsane.mode == XSANE_GIMP_EXTENSION)
+ {
+ gimp_quit();
+ }
+#endif
+ exit(0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_exit(void) /* this is called when xsane exits before gtk_main is called */
+{
+ while (xsane_back_gtk_message_dialog_active)
+ {
+ gtk_main_iteration();
+ }
+
+ sane_exit();
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+ if (xsane.mode == XSANE_GIMP_EXTENSION)
+ {
+ gimp_quit();
+ }
+#endif
+ exit(0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static gint xsane_standard_option_win_delete(GtkWidget *widget, gpointer data)
+{
+ gtk_widget_hide(widget);
+ preferences.show_standard_options = FALSE;
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_standard_options_widget), preferences.show_standard_options);
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static gint xsane_advanced_option_win_delete(GtkWidget *widget, gpointer data)
+{
+ gtk_widget_hide(widget);
+ preferences.show_advanced_options = FALSE;
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_advanced_options_widget), preferences.show_advanced_options);
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* Invoked when window manager's "delete" (or "close") function is invoked. */
+static gint xsane_scan_win_delete(GtkWidget *w, gpointer data)
+{
+ xsane_scan_done(-1); /* stop scanner when still scanning */
+
+ if (xsane.filetype) /* add extension to filename */
+ {
+ char buffer[256];
+
+ snprintf(buffer, sizeof(buffer), "%s%s", preferences.filename, xsane.filetype);
+ free(preferences.filename);
+ free(xsane.filetype);
+ xsane.filetype = 0;
+ preferences.filename = strdup(buffer);
+ }
+
+ xsane_pref_save();
+ xsane_quit();
+ return FALSE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static gint xsane_preview_window_destroyed(GtkWidget *widget, gpointer call_data)
+{
+ gtk_widget_hide(widget);
+ xsane.show_preview = FALSE;
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_preview_widget), FALSE);
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_show_preview_callback(GtkWidget * widget, gpointer call_data)
+{
+ if (GTK_CHECK_MENU_ITEM(widget)->active)
+ {
+ gtk_widget_show(xsane.preview->top);
+ xsane.show_preview = TRUE;
+ }
+ else
+ {
+ gtk_widget_hide(xsane.preview->top);
+ xsane.show_preview = FALSE;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static GtkWidget *xsane_files_build_menu(void)
+{
+ GtkWidget *menu, *item;
+
+ menu = gtk_menu_new();
+
+ item = gtk_menu_item_new();
+ gtk_container_add(GTK_CONTAINER(menu), item);
+ gtk_widget_show(item);
+
+
+ /* XSane about dialog */
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_ABOUT);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_about_dialog, 0);
+ gtk_widget_show(item);
+
+
+ /* XSane info dialog */
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_INFO);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_info_dialog, 0);
+ gtk_widget_show(item);
+
+
+ /* Exit */
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_EXIT);
+ gtk_container_add(GTK_CONTAINER(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_scan_win_delete, 0);
+ gtk_widget_show(item);
+
+ return menu;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_set_pref_unit_callback(GtkWidget *widget, gpointer data)
+{
+ const char *unit = data;
+ double unit_conversion_factor = 1.0;
+
+ gtk_signal_handler_block_by_func(GTK_OBJECT(xsane.length_unit_mm), (GtkSignalFunc) xsane_set_pref_unit_callback, "mm");
+ gtk_signal_handler_block_by_func(GTK_OBJECT(xsane.length_unit_cm), (GtkSignalFunc) xsane_set_pref_unit_callback, "cm");
+ gtk_signal_handler_block_by_func(GTK_OBJECT(xsane.length_unit_in), (GtkSignalFunc) xsane_set_pref_unit_callback, "in");
+
+ if (strcmp(unit, "mm") == 0)
+ {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.length_unit_mm), TRUE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.length_unit_cm), FALSE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.length_unit_in), FALSE);
+ }
+ else if (strcmp(unit, "cm") == 0)
+ {
+ unit_conversion_factor = 10.0;
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.length_unit_mm), FALSE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.length_unit_cm), TRUE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.length_unit_in), FALSE);
+ }
+ else if (strcmp(unit, "in") == 0)
+ {
+ unit_conversion_factor = 25.4;
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.length_unit_mm), FALSE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.length_unit_cm), FALSE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.length_unit_in), TRUE);
+ }
+
+ gtk_signal_handler_unblock_by_func(GTK_OBJECT(xsane.length_unit_mm), (GtkSignalFunc) xsane_set_pref_unit_callback, "mm");
+ gtk_signal_handler_unblock_by_func(GTK_OBJECT(xsane.length_unit_cm), (GtkSignalFunc) xsane_set_pref_unit_callback, "cm");
+ gtk_signal_handler_unblock_by_func(GTK_OBJECT(xsane.length_unit_in), (GtkSignalFunc) xsane_set_pref_unit_callback, "in");
+
+ preferences.length_unit = unit_conversion_factor;
+
+ xsane_refresh_dialog(dialog);
+ if (xsane.preview)
+ {
+ preview_area_resize(xsane.preview->window);
+ }
+
+ xsane_pref_save();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_set_update_policy_callback(GtkWidget *widget, gpointer data)
+{
+ GtkUpdateType policy = (GtkUpdateType) data;
+
+ gtk_signal_handler_block_by_func(GTK_OBJECT(xsane.update_policy_continu), (GtkSignalFunc) xsane_set_update_policy_callback,
+ (void *) GTK_UPDATE_CONTINUOUS);
+ gtk_signal_handler_block_by_func(GTK_OBJECT(xsane.update_policy_discont), (GtkSignalFunc) xsane_set_update_policy_callback,
+ (void *) GTK_UPDATE_DISCONTINUOUS);
+ gtk_signal_handler_block_by_func(GTK_OBJECT(xsane.update_policy_delayed), (GtkSignalFunc) xsane_set_update_policy_callback,
+ (void *) GTK_UPDATE_DELAYED);
+
+ if (policy == GTK_UPDATE_CONTINUOUS)
+ {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.update_policy_continu), TRUE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.update_policy_discont), FALSE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.update_policy_delayed), FALSE);
+ }
+ else if (policy == GTK_UPDATE_DISCONTINUOUS)
+ {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.update_policy_continu), FALSE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.update_policy_discont), TRUE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.update_policy_delayed), FALSE);
+ }
+ else if (policy == GTK_UPDATE_DELAYED)
+ {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.update_policy_continu), FALSE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.update_policy_discont), FALSE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.update_policy_delayed), TRUE);
+ }
+
+ gtk_signal_handler_unblock_by_func(GTK_OBJECT(xsane.update_policy_continu), (GtkSignalFunc) xsane_set_update_policy_callback,
+ (void *) GTK_UPDATE_CONTINUOUS);
+ gtk_signal_handler_unblock_by_func(GTK_OBJECT(xsane.update_policy_discont), (GtkSignalFunc) xsane_set_update_policy_callback,
+ (void *) GTK_UPDATE_DISCONTINUOUS);
+ gtk_signal_handler_unblock_by_func(GTK_OBJECT(xsane.update_policy_delayed), (GtkSignalFunc) xsane_set_update_policy_callback,
+ (void *) GTK_UPDATE_DELAYED);
+
+ preferences.gtk_update_policy = policy;
+ xsane_pref_save();
+
+ xsane_back_gtk_refresh_dialog(dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static gint xsane_close_info_callback(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *dialog = data;
+
+ gtk_widget_destroy(dialog);
+
+ xsane_set_sensitivity(TRUE);
+
+ xsane_update_histogram();
+
+ return FALSE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_info_dialog(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *info_dialog, *vbox, *button, *label, *frame, *framebox, *hbox, *table;
+ char buf[256];
+ char *bufptr;
+
+ sane_get_parameters(dialog->dev, &xsane.param); /* update xsane.param */
+
+ info_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
+ gtk_window_set_position(GTK_WINDOW(info_dialog), GTK_WIN_POS_CENTER);
+ gtk_window_set_policy(GTK_WINDOW(info_dialog), FALSE, FALSE, FALSE);
+ gtk_signal_connect(GTK_OBJECT(info_dialog), "destroy", GTK_SIGNAL_FUNC(xsane_close_info_callback), info_dialog);
+ snprintf(buf, sizeof(buf), "%s %s %s", prog_name, WINDOW_INFO, device_text);
+ gtk_window_set_title(GTK_WINDOW(info_dialog), buf);
+
+ xsane_set_window_icon(info_dialog, 0);
+
+ vbox = gtk_vbox_new(/* not homogeneous */ FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(info_dialog), vbox);
+ gtk_widget_show(vbox);
+
+ frame = gtk_frame_new(TEXT_SCANNER_BACKEND);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+ gtk_widget_show(frame);
+
+ framebox = gtk_vbox_new(/* not homogeneous */ FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), framebox);
+ gtk_widget_show(framebox);
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, TRUE, 5);
+ gtk_widget_show(hbox);
+
+ table = gtk_table_new(6, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 5);
+ gtk_widget_show(table);
+
+ snprintf(buf, sizeof(buf), TEXT_VENDOR);
+ label = xsane_info_table_text_new(table, buf, 0, 0);
+ snprintf(buf, sizeof(buf), "%s", devlist[seldev]->vendor);
+ label = xsane_info_table_text_new(table, buf, 1, 0);
+
+ snprintf(buf, sizeof(buf), TEXT_MODEL);
+ label = xsane_info_table_text_new(table, buf, 0, 1);
+ snprintf(buf, sizeof(buf), "%s", devlist[seldev]->model);
+ label = xsane_info_table_text_new(table, buf, 1, 1);
+
+ snprintf(buf, sizeof(buf), TEXT_TYPE);
+ label = xsane_info_table_text_new(table, buf, 0, 2);
+ snprintf(buf, sizeof(buf), "%s", devlist[seldev]->type);
+ label = xsane_info_table_text_new(table, buf, 1, 2);
+
+ snprintf(buf, sizeof(buf), TEXT_DEVICE);
+ label = xsane_info_table_text_new(table, buf, 0, 3);
+ bufptr = strrchr(devlist[seldev]->name, ':');
+ if (bufptr)
+ {
+ snprintf(buf, sizeof(buf), "%s", bufptr+1);
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), devlist[seldev]->name);
+ }
+ label = xsane_info_table_text_new(table, buf, 1, 3);
+
+ snprintf(buf, sizeof(buf), "%s", devlist[seldev]->name);
+ bufptr = strrchr(buf, ':');
+ if (bufptr)
+ {
+ *bufptr = 0;
+ label = xsane_info_table_text_new(table, buf, 1, 4);
+ snprintf(buf, sizeof(buf), TEXT_LOADED_BACKEND);
+ label = xsane_info_table_text_new(table, buf, 0, 4);
+ }
+
+ snprintf(buf, sizeof(buf), TEXT_SANE_VERSION);
+ label = xsane_info_table_text_new(table, buf, 0, 5);
+ snprintf(buf, sizeof(buf), "%d.%d build %d",SANE_VERSION_MAJOR(xsane.sane_backend_versioncode),
+ SANE_VERSION_MINOR(xsane.sane_backend_versioncode),
+ SANE_VERSION_BUILD(xsane.sane_backend_versioncode));
+ label = xsane_info_table_text_new(table, buf, 1, 5);
+
+
+ frame = gtk_frame_new(TEXT_RECENT_VALUES);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+ gtk_widget_show(frame);
+
+ framebox = gtk_vbox_new(/* not homogeneous */ FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), framebox);
+ gtk_widget_show(framebox);
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, TRUE, 5);
+ gtk_widget_show(hbox);
+
+ table = gtk_table_new(4, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 5);
+ gtk_widget_show(table);
+
+ if ((xsane.xsane_color) && (xsane.scanner_gamma_color)) /* color gamma correction by scanner */
+ {
+ const SANE_Option_Descriptor *opt;
+
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_CORR_BY);
+ label = xsane_info_table_text_new(table, buf, 0, 0);
+ snprintf(buf, sizeof(buf), TEXT_SCANNER);
+ label = xsane_info_table_text_new(table, buf, 1, 0);
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector_r);
+
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_INPUT_DEPTH);
+ label = xsane_info_table_text_new(table, buf, 0, 1);
+ snprintf(buf, sizeof(buf), "%d bit", (int) (0.5 + log((double)opt->size / sizeof(opt->type)) / log(2.0)));
+ label = xsane_info_table_text_new(table, buf, 1, 1);
+
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_OUTPUT_DEPTH);
+ label = xsane_info_table_text_new(table, buf, 0, 2);
+ snprintf(buf, sizeof(buf), "%d bit", (int) (0.5 + log(opt->constraint.range->max+1.0) / log(2.0)));
+ label = xsane_info_table_text_new(table, buf, 1, 2);
+ }
+ else if ((!xsane.xsane_color) && (xsane.scanner_gamma_gray)) /* gray gamma correction by scanner */
+ {
+ const SANE_Option_Descriptor *opt;
+
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_CORR_BY);
+ label = xsane_info_table_text_new(table, buf, 0, 0);
+ snprintf(buf, sizeof(buf), TEXT_SCANNER);
+ label = xsane_info_table_text_new(table, buf, 1, 0);
+
+ opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector);
+
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_INPUT_DEPTH);
+ label = xsane_info_table_text_new(table, buf, 0, 1);
+ snprintf(buf, sizeof(buf), "%d bit", (int) (0.5 + log((double)opt->size / sizeof(opt->type)) / log(2.0)));
+ label = xsane_info_table_text_new(table, buf, 1, 1);
+
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_OUTPUT_DEPTH);
+ label = xsane_info_table_text_new(table, buf, 0, 2);
+ snprintf(buf, sizeof(buf), "%d bit", (int) (0.5 + log(opt->constraint.range->max+1.0) / log(2.0)));
+ label = xsane_info_table_text_new(table, buf, 1, 2);
+ }
+ else if (xsane.param.depth != 1) /* gamma correction by xsane */
+ {
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_CORR_BY);
+ label = xsane_info_table_text_new(table, buf, 0, 0);
+ snprintf(buf, sizeof(buf), TEXT_SOFTWARE_XSANE);
+ label = xsane_info_table_text_new(table, buf, 1, 0);
+
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_INPUT_DEPTH);
+ label = xsane_info_table_text_new(table, buf, 0, 1);
+ snprintf(buf, sizeof(buf), "%d bit", xsane.param.depth);
+ label = xsane_info_table_text_new(table, buf, 1, 1);
+
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_OUTPUT_DEPTH);
+ label = xsane_info_table_text_new(table, buf, 0, 2);
+/* snprintf(buf, sizeof(buf), "%d bit", 8); */
+ snprintf(buf, sizeof(buf), "%d bit", xsane.param.depth);
+ label = xsane_info_table_text_new(table, buf, 1, 2);
+ }
+ else /* no gamma enhancement */
+ {
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_CORR_BY);
+ label = xsane_info_table_text_new(table, buf, 0, 0);
+ snprintf(buf, sizeof(buf), TEXT_NONE);
+ label = xsane_info_table_text_new(table, buf, 1, 0);
+
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_INPUT_DEPTH);
+ label = xsane_info_table_text_new(table, buf, 0, 1);
+ snprintf(buf, sizeof(buf), TEXT_NONE);
+ label = xsane_info_table_text_new(table, buf, 1, 1);
+
+ snprintf(buf, sizeof(buf), TEXT_GAMMA_OUTPUT_DEPTH);
+ label = xsane_info_table_text_new(table, buf, 0, 2);
+ snprintf(buf, sizeof(buf), TEXT_NONE);
+ label = xsane_info_table_text_new(table, buf, 1, 2);
+ }
+
+ snprintf(buf, sizeof(buf), TEXT_SCANNER_OUTPUT_DEPTH);
+ label = xsane_info_table_text_new(table, buf, 0, 3);
+ snprintf(buf, sizeof(buf), "%d bit", xsane.param.depth);
+ label = xsane_info_table_text_new(table, buf, 1, 3);
+
+ frame = gtk_frame_new(TEXT_OUTPUT_FORMATS);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+ gtk_widget_show(frame);
+
+ framebox = gtk_vbox_new(/* not homogeneous */ FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), framebox);
+ gtk_widget_show(framebox);
+
+ hbox = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, TRUE, 5);
+ gtk_widget_show(hbox);
+
+ table = gtk_table_new(2, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 5);
+ gtk_widget_show(table);
+
+ snprintf(buf, sizeof(buf), TEXT_8BIT_FORMATS);
+ label = xsane_info_table_text_new(table, buf, 0, 0);
+
+ bufptr=buf;
+
+#ifdef HAVE_LIBJPEG
+ sprintf(bufptr, "JPEG, ");
+ bufptr += strlen(bufptr);
+#endif
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+ sprintf(bufptr, "PNG, ");
+ bufptr += strlen(bufptr);
+#endif
+#endif
+
+ sprintf(bufptr, "PNM, ");
+ bufptr += strlen(bufptr);
+
+ sprintf(bufptr, "PS, ");
+ bufptr += strlen(bufptr);
+
+#ifdef SUPPORT_RGBA
+ sprintf(bufptr, "RGBA, ");
+ bufptr += strlen(bufptr);
+#endif
+
+#ifdef HAVE_LIBTIFF
+ sprintf(bufptr, "TIFF, ");
+ bufptr += strlen(bufptr);
+#endif
+
+ bufptr--;
+ bufptr--;
+ *bufptr = 0; /* erase last comma */
+
+ label = xsane_info_table_text_new(table, buf, 1, 0);
+
+ snprintf(buf, sizeof(buf), TEXT_16BIT_FORMATS);
+ label = xsane_info_table_text_new(table, buf, 0, 1);
+
+ bufptr=buf;
+
+#ifdef HAVE_LIBPNG
+#ifdef HAVE_LIBZ
+ sprintf(bufptr, "PNG, ");
+ bufptr += strlen(bufptr);
+#endif
+#endif
+
+ sprintf(bufptr, "PNM, ");
+ bufptr += strlen(bufptr);
+
+ sprintf(bufptr, "RAW, ");
+ bufptr += strlen(bufptr);
+
+#ifdef SUPPORT_RGBA
+ sprintf(bufptr, "RGBA, ");
+ bufptr += strlen(bufptr);
+#endif
+
+ bufptr--;
+ bufptr--;
+ *bufptr = 0; /* erase last comma */
+
+ label = xsane_info_table_text_new(table, buf, 1, 1);
+
+/* gtk_label_set((GtkLabel *)label, "HALLO"); */
+
+ button = gtk_button_new_with_label(BUTTON_CLOSE);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_close_info_callback, info_dialog);
+ gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+
+ gtk_widget_show(info_dialog);
+
+ xsane_clear_histogram(&xsane.histogram_raw);
+ xsane_clear_histogram(&xsane.histogram_enh);
+
+ xsane_set_sensitivity(FALSE);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_about_dialog(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *about_dialog, *vbox, *button, *label;
+ char buf[256];
+ GtkWidget *pixmapwidget;
+ GdkBitmap *mask;
+ GdkPixmap *pixmap;
+ GtkStyle *style;
+ GdkColor *bg_trans;
+
+
+ about_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
+ gtk_window_set_position(GTK_WINDOW(about_dialog), GTK_WIN_POS_CENTER);
+ gtk_window_set_policy(GTK_WINDOW(about_dialog), FALSE, FALSE, FALSE);
+ gtk_signal_connect(GTK_OBJECT(about_dialog), "destroy", GTK_SIGNAL_FUNC(xsane_close_info_callback), about_dialog);
+ snprintf(buf, sizeof(buf), "%s %s", WINDOW_ABOUT, prog_name);
+ gtk_window_set_title(GTK_WINDOW(about_dialog), buf);
+
+ xsane_set_window_icon(about_dialog, 0);
+
+ vbox = gtk_vbox_new(/* not homogeneous */ FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(about_dialog), vbox);
+ gtk_widget_show(vbox);
+
+ /* xsane logo */
+ gtk_widget_realize(about_dialog);
+
+ style = gtk_widget_get_style(about_dialog);
+ bg_trans = &style->bg[GTK_STATE_NORMAL];
+
+ snprintf(buf, sizeof(buf), "%s/xsane-logo.xpm", STRINGIFY(PATH_SANE_DATA_DIR));
+ pixmap = gdk_pixmap_create_from_xpm(about_dialog->window, &mask, bg_trans, buf);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_box_pack_start(GTK_BOX(vbox), pixmapwidget, FALSE, FALSE, 2);
+ gtk_widget_show(pixmapwidget);
+ gdk_pixmap_unref(pixmap);
+
+ xsane_separator_new(vbox, 5);
+
+ snprintf(buf, sizeof(buf), "XSane %s %s\n", TEXT_VERSION, XSANE_VERSION);
+ label = gtk_label_new(buf);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ snprintf(buf, sizeof(buf), "(c) %s %s\n", XSANE_DATE, XSANE_COPYRIGHT);
+ label = gtk_label_new(buf);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ snprintf(buf, sizeof(buf), "%s %s\n", TEXT_EMAIL, XSANE_EMAIL);
+ label = gtk_label_new(buf);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ button = gtk_button_new_with_label(BUTTON_CLOSE);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_close_info_callback, about_dialog);
+ gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+
+ gtk_widget_show(about_dialog);
+
+ xsane_clear_histogram(&xsane.histogram_raw);
+ xsane_clear_histogram(&xsane.histogram_enh);
+
+ xsane_set_sensitivity(FALSE);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static SANE_Status xsane_get_area_value(int option, float *val, SANE_Int *unit)
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Handle dev;
+ SANE_Word word;
+
+ if (option <= 0)
+ {
+ return -1;
+ }
+
+ if (sane_control_option(dialog->dev, option, SANE_ACTION_GET_VALUE, &word, 0) == SANE_STATUS_GOOD)
+ {
+ dev = dialog->dev;
+ opt = sane_get_option_descriptor(dev, option);
+
+ if (unit)
+ {
+ *unit = opt->unit;
+ }
+
+ if (val)
+ {
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ *val = (float) word / 65536.0;
+ }
+ else
+ {
+ *val = (float) word;
+ }
+ }
+
+ return 0;
+ }
+ else if (val)
+ {
+ *val = 0;
+ }
+ return -2;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifdef XSANE_TEST
+static void xsane_batch_scan_delete_callback(GtkWidget *widget, gpointer list)
+{
+ gtk_list_remove_items(GTK_LIST(list), GTK_LIST(list)->selection);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_batch_scan_add_callback(GtkWidget *widget, gpointer list)
+{
+ GtkWidget *list_item;
+ float tlx, tly, brx, bry;
+ SANE_Int unit;
+ char buf[255];
+
+
+ xsane_get_area_value(dialog->well_known.coord[0], &tlx, &unit);
+ xsane_get_area_value(dialog->well_known.coord[1], &tly, &unit);
+ xsane_get_area_value(dialog->well_known.coord[2], &brx, &unit);
+ xsane_get_area_value(dialog->well_known.coord[3], &bry, &unit);
+
+ if (unit == SANE_UNIT_MM)
+ {
+ snprintf(buf, sizeof(buf), " top left (%7.2fmm, %7.2fmm), bottom right (%7.2fmm, %7.2fmm)", tlx, tly, brx, bry);
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), " top left (%5.0fpx, %5.0fpx), bottom right (%5.0fpx, %5.0fpx)", tlx, tly, brx, bry);
+ }
+
+ list_item = gtk_list_item_new_with_label(buf);
+ gtk_object_set_data(GTK_OBJECT(list_item), "list_item_data", strdup(buf));
+ gtk_container_add(GTK_CONTAINER(list), list_item);
+ gtk_widget_show(list_item);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_batch_scan_dialog(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *batch_scan_dialog, *batch_scan_vbox, *hbox, *button, *scrolled_window, *list;
+ char buf[64];
+
+ batch_scan_dialog = gtk_dialog_new();
+ snprintf(buf, sizeof(buf), "%s %s", prog_name, WINDOW_BATCH_SCAN);
+ gtk_window_set_title(GTK_WINDOW(batch_scan_dialog), buf);
+
+ batch_scan_vbox = GTK_DIALOG(batch_scan_dialog)->vbox;
+
+ scrolled_window = gtk_scrolled_window_new(0, 0);
+ gtk_widget_set_usize(scrolled_window, 400, 200);
+ gtk_container_add(GTK_CONTAINER(batch_scan_vbox), scrolled_window);
+ gtk_widget_show(scrolled_window);
+
+ list = gtk_list_new();
+
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), list);
+
+ gtk_widget_show(list);
+
+
+ /* fill in action area: */
+ hbox = GTK_DIALOG(batch_scan_dialog)->action_area;
+
+ button = gtk_button_new_with_label(BUTTON_OK);
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_close_dialog_callback, batch_scan_dialog);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_grab_default(button);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label(BUTTON_ADD_AREA);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_batch_scan_add_callback, list);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label(BUTTON_DELETE);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_batch_scan_delete_callback, list);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ gtk_widget_show(batch_scan_dialog);
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_dialog_delete()
+{
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_dialog()
+{
+ GtkWidget *fax_dialog, *fax_scan_vbox, *fax_project_vbox, *hbox, *fax_project_exists_hbox, *button;
+ GtkWidget *scrolled_window, *list;
+ char buf[64];
+ GtkWidget *pixmapwidget, *text;
+ GdkBitmap *mask;
+ GdkPixmap *pixmap;
+
+
+ if (xsane.fax_dialog)
+ {
+ return; /* window already is open */
+ }
+
+ fax_dialog = gtk_dialog_new();
+ snprintf(buf, sizeof(buf), "%s %s", prog_name, WINDOW_FAX_PROJECT);
+ gtk_window_set_title(GTK_WINDOW(fax_dialog), buf);
+ gtk_signal_connect(GTK_OBJECT(fax_dialog), "delete_event", (GtkSignalFunc) xsane_fax_dialog_delete, 0);
+ xsane_set_window_icon(fax_dialog, 0);
+
+ fax_scan_vbox = GTK_DIALOG(fax_dialog)->vbox;
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ gtk_box_pack_start(GTK_BOX(fax_scan_vbox), hbox, FALSE, FALSE, 2);
+
+ pixmap = gdk_pixmap_create_from_xpm_d(xsane.shell->window, &mask, xsane.bg_trans, (gchar **) fax_xpm);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_box_pack_start(GTK_BOX(hbox), pixmapwidget, FALSE, FALSE, 2);
+ gdk_pixmap_unref(pixmap);
+
+ text = gtk_entry_new_with_max_length(128);
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAXPROJECT);
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.fax_project);
+ gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 4);
+ gtk_signal_connect(GTK_OBJECT(text), "changed", (GtkSignalFunc) xsane_faxproject_changed_callback, 0);
+
+ xsane.fax_project_entry = text;
+
+ gtk_widget_show(pixmapwidget);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+
+ fax_project_vbox = gtk_vbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(fax_scan_vbox), fax_project_vbox, TRUE, TRUE, 0);
+ gtk_widget_show(fax_project_vbox);
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ gtk_box_pack_start(GTK_BOX(fax_project_vbox), hbox, FALSE, FALSE, 2);
+
+ gtk_widget_realize(fax_dialog);
+
+ pixmap = gdk_pixmap_create_from_xpm_d(fax_dialog->window, &mask, xsane.bg_trans, (gchar **) faxreceiver_xpm);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_box_pack_start(GTK_BOX(hbox), pixmapwidget, FALSE, FALSE, 2);
+ gdk_pixmap_unref(pixmap);
+
+ text = gtk_entry_new_with_max_length(128);
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAXRECEIVER);
+ gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 4);
+ gtk_signal_connect(GTK_OBJECT(text), "changed", (GtkSignalFunc) xsane_faxreceiver_changed_callback, 0);
+
+ xsane.fax_receiver_entry = text;
+
+ gtk_widget_show(pixmapwidget);
+ gtk_widget_show(text);
+ gtk_widget_show(hbox);
+
+
+ scrolled_window = gtk_scrolled_window_new(0, 0);
+ gtk_widget_set_usize(scrolled_window, 200, 100);
+ gtk_container_add(GTK_CONTAINER(fax_project_vbox), scrolled_window);
+ gtk_widget_show(scrolled_window);
+
+ list = gtk_list_new();
+/* gtk_list_set_selection_mode(list, GTK_SELECTION_BROWSE); */
+
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), list);
+
+ gtk_widget_show(list);
+
+ xsane.fax_list = list;
+
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ gtk_box_pack_start(GTK_BOX(fax_project_vbox), hbox, FALSE, FALSE, 2);
+
+ button = gtk_button_new_with_label(BUTTON_SHOW);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_fax_show_callback, list);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label(BUTTON_RENAME);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_fax_entry_rename_callback, list);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label(BUTTON_DELETE);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_fax_entry_delete_callback, list);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ xsane_button_new_with_pixmap(hbox, move_up_xpm, 0, (GtkSignalFunc) xsane_fax_entry_move_up_callback, list);
+ xsane_button_new_with_pixmap(hbox, move_down_xpm, 0, (GtkSignalFunc) xsane_fax_entry_move_down_callback, list);
+
+ gtk_widget_show(hbox);
+
+ xsane.fax_project_box = fax_project_vbox;
+
+ /* fill in action area: */
+ hbox = GTK_DIALOG(fax_dialog)->action_area;
+
+ fax_project_exists_hbox = gtk_hbox_new(FALSE, 2);
+ gtk_box_pack_start(GTK_BOX(hbox), fax_project_exists_hbox, TRUE, TRUE, 0);
+
+ button = gtk_button_new_with_label(BUTTON_SEND_PROJECT);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_fax_send, 0);
+ gtk_box_pack_start(GTK_BOX(fax_project_exists_hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label(BUTTON_DELETE_PROJECT);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_fax_project_delete, 0);
+ gtk_box_pack_start(GTK_BOX(fax_project_exists_hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ gtk_widget_show(fax_project_exists_hbox);
+ xsane.fax_project_exists = fax_project_exists_hbox;
+
+ button = gtk_button_new_with_label(BUTTON_CREATE_PROJECT);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_fax_project_create, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ xsane.fax_project_not_exists = button;
+
+ xsane.fax_dialog = fax_dialog;
+
+ xsane_fax_project_load();
+
+ gtk_widget_show(fax_dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_dialog_close()
+{
+ if (xsane.fax_dialog == 0)
+ {
+ return;
+ }
+
+ gtk_widget_destroy(xsane.fax_dialog);
+
+ xsane.fax_dialog = 0;
+ xsane.fax_list = 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_project_load()
+{
+ FILE *projectfile;
+ char page[256];
+ char buf[256];
+ GtkWidget *list_item;
+ int i;
+ char c;
+
+ gtk_signal_disconnect_by_func(GTK_OBJECT(xsane.fax_receiver_entry), GTK_SIGNAL_FUNC(xsane_faxreceiver_changed_callback), 0);
+ gtk_list_remove_items(GTK_LIST(xsane.fax_list), GTK_LIST(xsane.fax_list)->children);
+
+ snprintf(buf, sizeof(buf), "%s/project-list", preferences.fax_project);
+ projectfile = fopen(buf, "r");
+
+ if ((!projectfile) || (feof(projectfile)))
+ {
+ snprintf(buf, sizeof(buf), "%s/page-001.ps", preferences.fax_project);
+ xsane.fax_filename=strdup(buf);
+
+ xsane.fax_receiver=strdup("");
+ gtk_entry_set_text(GTK_ENTRY(xsane.fax_receiver_entry), (char *) xsane.fax_receiver);
+
+ gtk_widget_set_sensitive(xsane.fax_project_box, FALSE);
+ gtk_widget_hide(xsane.fax_project_exists);
+ gtk_widget_show(xsane.fax_project_not_exists);
+ gtk_widget_set_sensitive(GTK_WIDGET(xsane.start_button), FALSE);
+ }
+ else
+ {
+ i=0;
+ c=0;
+ while ((i<255) && (c != 10) && (c != EOF)) /* first line is receiver phone number or address */
+ {
+ c = fgetc(projectfile);
+ page[i++] = c;
+ }
+ page[i-1] = 0;
+
+ xsane.fax_receiver=strdup(page);
+ gtk_entry_set_text(GTK_ENTRY(xsane.fax_receiver_entry), (char *) xsane.fax_receiver);
+
+
+ i=0;
+ c=0;
+ while ((i<255) && (c != 10) && (c != EOF)) /* second line is next fax filename */
+ {
+ c = fgetc(projectfile);
+ page[i++] = c;
+ }
+ page[i-1] = 0;
+
+ snprintf(buf, sizeof(buf), "%s/%s", preferences.fax_project, page);
+ xsane.fax_filename=strdup(buf);
+
+ while (!feof(projectfile))
+ {
+ i=0;
+ c=0;
+
+ while ((i<255) && (c != 10) && (c != EOF))
+ {
+ c = fgetc(projectfile);
+ page[i++] = c;
+ }
+ page[i-1]=0;
+
+ if (c > 1)
+ {
+ list_item = gtk_list_item_new_with_label(page);
+ gtk_object_set_data(GTK_OBJECT(list_item), "list_item_data", strdup(page));
+ gtk_container_add(GTK_CONTAINER(xsane.fax_list), list_item);
+ gtk_widget_show(list_item);
+ }
+ }
+ gtk_widget_set_sensitive(xsane.fax_project_box, TRUE);
+ gtk_widget_show(xsane.fax_project_exists);
+ gtk_widget_hide(xsane.fax_project_not_exists);
+ gtk_widget_set_sensitive(GTK_WIDGET(xsane.start_button), TRUE);
+ }
+
+ if (projectfile)
+ {
+ fclose(projectfile);
+ }
+
+ gtk_signal_connect(GTK_OBJECT(xsane.fax_receiver_entry), "changed", (GtkSignalFunc) xsane_faxreceiver_changed_callback, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_project_delete()
+{
+ char *page;
+ char file[256];
+ GList *list = (GList *) GTK_LIST(xsane.fax_list)->children;
+ GtkObject *list_item;
+
+ while (list)
+ {
+ list_item = GTK_OBJECT(list->data);
+ page = strdup((char *) gtk_object_get_data(list_item, "list_item_data"));
+ xsane_convert_text_to_filename(&page);
+ snprintf(file, sizeof(file), "%s/%s.ps", preferences.fax_project, page);
+ free(page);
+ remove(file);
+ list = list->next;
+ }
+ snprintf(file, sizeof(file), "%s/project-list", preferences.fax_project);
+ remove(file);
+ snprintf(file, sizeof(file), "%s", preferences.fax_project);
+ rmdir(file);
+
+ xsane_fax_project_load();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_fax_project_save()
+{
+ FILE *projectfile;
+ char *page;
+ char buf[256];
+ GList *list = (GList *) GTK_LIST(xsane.fax_list)->children;
+ GtkObject *list_item;
+
+ umask(preferences.directory_umask); /* define new file permissions */
+ mkdir(preferences.fax_project, 0777); /* make sure directory exists */
+
+ snprintf(buf, sizeof(buf), "%s/project-list", preferences.fax_project);
+ umask(preferences.image_umask); /* define image file permissions */
+ projectfile = fopen(buf, "w");
+ umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
+
+ if (xsane.fax_receiver)
+ {
+ fprintf(projectfile, "%s\n", xsane.fax_receiver); /* first line is receiver phone number or address */
+ }
+ else
+ {
+ fprintf(projectfile, "\n");
+ }
+
+ if (xsane.fax_filename)
+ {
+ fprintf(projectfile, "%s\n", strrchr(xsane.fax_filename, '/')+1); /* second line is next fax filename */
+ }
+ else
+ {
+ fprintf(projectfile, "\n");
+ }
+
+
+ while (list)
+ {
+ list_item = GTK_OBJECT(list->data);
+ page = (char *) gtk_object_get_data(list_item, "list_item_data");
+ fprintf(projectfile, "%s\n", page);
+ list = list->next;
+ }
+ fclose(projectfile);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_project_create()
+{
+ if (strlen(preferences.fax_project))
+ {
+ xsane_fax_project_save();
+ xsane_fax_project_load();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_pref_toggle_tooltips(GtkWidget *widget, gpointer data)
+{
+ preferences.tooltips_enabled = (GTK_CHECK_MENU_ITEM(widget)->active != 0);
+ xsane_back_gtk_set_tooltips(dialog, preferences.tooltips_enabled);
+ xsane_pref_save();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_show_doc_via_nsr(GtkWidget *widget, gpointer data) /* show via netscape remote */
+{
+ char *name = (char *) data;
+ char buf[256];
+ FILE *ns_pipe;
+ pid_t pid;
+ char *arg[3];
+
+ snprintf(buf, sizeof(buf), "netscape -no-about-splash -remote \"openFile(%s/%s-doc.html)\" 2>&1", STRINGIFY(PATH_SANE_DATA_DIR), name);
+ ns_pipe = popen(buf, "r");
+
+ if (ns_pipe)
+ {
+ while (!feof(ns_pipe))
+ {
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ fgetc(ns_pipe); /* remove char from pipe */
+ }
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ if (pclose(ns_pipe)) /* netscape not running, start it */
+ {
+ snprintf(buf, sizeof(buf), "%s/%s-doc.html", STRINGIFY(PATH_SANE_DATA_DIR), name);
+ arg[0] = "netscape";
+ arg[1] = buf;
+ arg[2] = 0;
+
+ pid = fork();
+
+ if (pid == 0) /* new process */
+ {
+ execvp(arg[0], arg); /* does not return if successfully */
+ fprintf(stderr, "%s %s\n", ERR_FAILD_EXEC_DOC_VIEWER, preferences.doc_viewer);
+ _exit(0); /* do not use exit() here! otherwise gtk gets in trouble */
+ }
+ }
+ }
+ else /* execution failed */
+ {
+ snprintf(buf, sizeof(buf), "%s", ERR_NETSCAPE_EXECUTE_FAIL);
+ xsane_back_gtk_error(buf, TRUE);
+ }
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_show_doc(GtkWidget *widget, gpointer data)
+{
+ char *name = (char *) data;
+ char buf[256];
+ pid_t pid;
+ char *arg[3];
+
+ if (!strcmp(preferences.doc_viewer, DOCVIEWERNETSCAPEREMOTE))
+ {
+ xsane_show_doc_via_nsr(widget, data);
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), "%s/%s-doc.html", STRINGIFY(PATH_SANE_DATA_DIR), name);
+ arg[0] = preferences.doc_viewer;
+ arg[1] = buf;
+ arg[2] = 0;
+
+ pid = fork();
+
+ if (pid == 0) /* new process */
+ {
+ execvp(arg[0], arg); /* does not return if successfully */
+ fprintf(stderr, "%s %s\n", ERR_FAILD_EXEC_DOC_VIEWER, preferences.doc_viewer);
+ _exit(0); /* do not use exit() here! otherwise gtk gets in trouble */
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_entrys_swap(GtkWidget *list_item_1, GtkWidget *list_item_2)
+{
+ char *text1;
+ char *text2;
+
+ text1 = (char *) gtk_object_get_data(GTK_OBJECT(list_item_1), "list_item_data");
+ text2 = (char *) gtk_object_get_data(GTK_OBJECT(list_item_2), "list_item_data");
+
+ gtk_label_set(GTK_LABEL(gtk_container_children(GTK_CONTAINER(list_item_1))->data), text2);
+ gtk_label_set(GTK_LABEL(gtk_container_children(GTK_CONTAINER(list_item_2))->data), text1);
+ gtk_object_set_data(GTK_OBJECT(list_item_1), "list_item_data", text2);
+ gtk_object_set_data(GTK_OBJECT(list_item_2), "list_item_data", text1);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_entry_move_up_callback(GtkWidget *widget, gpointer list)
+{
+ GList *select;
+ GList *item = GTK_LIST(list)->children;
+ GtkWidget *list_item_1;
+ GtkWidget *list_item_2;
+ int position;
+ int newpos;
+
+ select = GTK_LIST(list)->selection;
+ if (select)
+ {
+ list_item_1 = select->data;
+
+ position = gtk_list_child_position(GTK_LIST(list), list_item_1);
+ position--; /* move up */
+ newpos = position;
+
+ if (position >= 0)
+ {
+ while (position>0)
+ {
+ item = item->next;
+ position--;
+ }
+
+ list_item_2 = item->data;
+ if (list_item_2)
+ {
+ xsane_fax_entrys_swap(list_item_1, list_item_2);
+ gtk_list_select_item(GTK_LIST(list), newpos);
+ xsane_fax_project_save();
+ }
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_entry_move_down_callback(GtkWidget *widget, gpointer list)
+{
+ GList *select;
+ GList *item = GTK_LIST(list)->children;
+ GtkWidget *list_item_1;
+ GtkWidget *list_item_2;
+ int position;
+ int newpos;
+
+ select = GTK_LIST(list)->selection;
+ if (select)
+ {
+ list_item_1 = select->data;
+
+ position = gtk_list_child_position(GTK_LIST(list), list_item_1);
+ position++; /* move down */
+ newpos = position;
+
+ while ((position>0) && (item))
+ {
+ item = item->next;
+ position--;
+ }
+
+ if (item)
+ {
+ list_item_2 = item->data;
+ if (list_item_2)
+ {
+ xsane_fax_entrys_swap(list_item_1, list_item_2);
+ gtk_list_select_item(GTK_LIST(list), newpos);
+ xsane_fax_project_save();
+ }
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+int xsane_fax_entry_rename;
+
+static void xsane_fax_entry_rename_button_callback(GtkWidget *widget, gpointer data)
+{
+ xsane_fax_entry_rename = (int) data;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_entry_rename_callback(GtkWidget *widget, gpointer list)
+{
+ GtkWidget *list_item;
+ GList *select;
+ char *oldpage;
+ char *newpage;
+ char oldfile[256];
+ char newfile[256];
+
+ select = GTK_LIST(list)->selection;
+ if (select)
+ {
+ GtkWidget *rename_dialog;
+ GtkWidget *text;
+ GtkWidget *button;
+ GtkWidget *vbox, *hbox;
+ char buf[256];
+
+ list_item = select->data;
+ oldpage = strdup((char *) gtk_object_get_data(GTK_OBJECT(list_item), "list_item_data"));
+
+ xsane_set_sensitivity(FALSE);
+
+ rename_dialog = gtk_dialog_new();
+ gtk_window_set_position(GTK_WINDOW(rename_dialog), GTK_WIN_POS_CENTER);
+ gtk_window_set_policy(GTK_WINDOW(rename_dialog), FALSE, FALSE, FALSE);
+ snprintf(buf, sizeof(buf), "%s %s", prog_name, WINDOW_FAX_RENAME);
+ gtk_window_set_title(GTK_WINDOW(rename_dialog), buf);
+ gtk_signal_connect(GTK_OBJECT(rename_dialog), "delete_event", (GtkSignalFunc) xsane_fax_entry_rename_button_callback, (void *) -1);
+ gtk_widget_show(rename_dialog);
+
+ vbox = GTK_DIALOG(rename_dialog)->vbox;
+
+ text = gtk_entry_new_with_max_length(64);
+ xsane_back_gtk_set_tooltip(dialog->tooltips, text, DESC_FAXPAGENAME);
+ gtk_entry_set_text(GTK_ENTRY(text), oldpage);
+ gtk_widget_set_usize(text, 300, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 4);
+ gtk_widget_show(text);
+
+
+ hbox = GTK_DIALOG(rename_dialog)->action_area;
+
+ button = gtk_button_new_with_label("OK");
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_fax_entry_rename_button_callback, (void *) 1);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label("Cancel");
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_fax_entry_rename_button_callback, (void *) -1);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+
+ xsane_fax_entry_rename = 0;
+
+ while (xsane_fax_entry_rename == 0)
+ {
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ }
+
+ newpage = strdup(gtk_entry_get_text(GTK_ENTRY(text)));
+
+ if (xsane_fax_entry_rename == 1)
+ {
+ gtk_label_set(GTK_LABEL(gtk_container_children(GTK_CONTAINER(list_item))->data), newpage);
+ gtk_object_set_data(GTK_OBJECT(list_item), "list_item_data", strdup(newpage));
+
+ xsane_convert_text_to_filename(&oldpage);
+ xsane_convert_text_to_filename(&newpage);
+ snprintf(oldfile, sizeof(oldfile), "%s/%s.ps", preferences.fax_project, oldpage);
+ snprintf(newfile, sizeof(newfile), "%s/%s.ps", preferences.fax_project, newpage);
+
+ rename(oldfile, newfile);
+
+ xsane_fax_project_save();
+ }
+
+ free(oldpage);
+ free(newpage);
+
+ gtk_widget_destroy(rename_dialog);
+
+ xsane_set_sensitivity(TRUE);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_entry_delete_callback(GtkWidget *widget, gpointer list)
+{
+ GtkObject *list_item;
+ GList *select;
+ char *page;
+ char file[256];
+
+ select = GTK_LIST(list)->selection;
+ if (select)
+ {
+ list_item = GTK_OBJECT(select->data);
+ page = strdup((char *) gtk_object_get_data(list_item, "list_item_data"));
+ xsane_convert_text_to_filename(&page);
+ snprintf(file, sizeof(file), "%s/%s.ps", preferences.fax_project, page);
+ free(page);
+ remove(file);
+ gtk_widget_destroy(GTK_WIDGET(list_item));
+ xsane_fax_project_save();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_show_callback(GtkWidget *widget, gpointer list)
+{
+ GtkObject *list_item;
+ GList *select;
+ pid_t pid;
+ char *arg[100];
+ char *page;
+ char buf[256];
+ int argnr;
+
+ select = GTK_LIST(list)->selection;
+ if (select)
+ {
+ argnr = xsane_parse_options(preferences.fax_viewer, arg);
+
+ list_item = GTK_OBJECT(select->data);
+ page = (char *) gtk_object_get_data(list_item, "list_item_data");
+ page = strdup(page);
+ xsane_convert_text_to_filename(&page);
+ snprintf(buf, sizeof(buf), "%s/%s.ps", preferences.fax_project, page);
+ free(page);
+ arg[argnr++] = buf;
+ arg[argnr] = 0;
+
+ pid = fork();
+
+ if (pid == 0) /* new process */
+ {
+ execvp(arg[0], arg); /* does not return if successfully */
+ fprintf(stderr, "%s %s\n", ERR_FAILD_EXEC_FAX_VIEWER, preferences.fax_viewer);
+ _exit(0); /* do not use exit() here! otherwise gtk gets in trouble */
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_fax_send()
+{
+ char *page;
+ GList *list = (GList *) GTK_LIST(xsane.fax_list)->children;
+ GtkObject *list_item;
+ pid_t pid;
+ char *arg[1000];
+ char buf[256];
+ int argnr = 0;
+ int i;
+
+ if (list)
+ {
+ if (!xsane_option_defined(xsane.fax_receiver))
+ {
+ snprintf(buf, sizeof(buf), "%s\n", ERR_SENDFAX_RECEIVER_MISSING);
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ argnr = xsane_parse_options(preferences.fax_command, arg);
+
+ if (xsane.fax_fine_mode) /* fine mode */
+ {
+ if (xsane_option_defined(preferences.fax_fine_option))
+ {
+ arg[argnr++] = strdup(preferences.fax_fine_option);
+ }
+ }
+ else /* normal mode */
+ {
+ if (xsane_option_defined(preferences.fax_normal_option))
+ {
+ arg[argnr++] = strdup(preferences.fax_normal_option);
+ }
+ }
+
+ if (xsane_option_defined(preferences.fax_receiver_option))
+ {
+ arg[argnr++] = strdup(preferences.fax_receiver_option);
+ }
+ arg[argnr++] = strdup(xsane.fax_receiver);
+
+ if (xsane_option_defined(preferences.fax_postscript_option))
+ {
+ arg[argnr++] = strdup(preferences.fax_postscript_option);
+ }
+
+ while ((list) && (argnr<999)) /* add pages to options */
+ {
+ list_item = GTK_OBJECT(list->data);
+ page = strdup((char *) gtk_object_get_data(list_item, "list_item_data"));
+ xsane_convert_text_to_filename(&page);
+ snprintf(buf, sizeof(buf), "%s/%s.ps", preferences.fax_project, page);
+ free(page);
+ arg[argnr++] = strdup(buf);
+ list = list->next;
+ }
+
+ arg[argnr] = 0;
+
+ pid = fork();
+
+ if (pid == 0) /* new process */
+ {
+ execvp(arg[0], arg); /* does not return if successfully */
+ fprintf(stderr, "%s %s\n", ERR_FAILED_EXEC_FAX_CMD, preferences.fax_command);
+ _exit(0); /* do not use exit() here! otherwise gtk gets in trouble */
+ }
+
+ for (i=0; i<argnr; i++)
+ {
+ free(arg[i]);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static GtkWidget *xsane_view_build_menu(void)
+{
+ GtkWidget *menu, *item;
+
+ menu = gtk_menu_new();
+
+
+ /* show tooltips */
+
+ item = gtk_check_menu_item_new_with_label(MENU_ITEM_SHOW_TOOLTIPS);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), preferences.tooltips_enabled);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+ gtk_signal_connect(GTK_OBJECT(item), "toggled", (GtkSignalFunc) xsane_pref_toggle_tooltips, 0);
+
+
+ /* insert separator: */
+
+ item = gtk_menu_item_new();
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+
+
+ /* show preview */
+
+ xsane.show_preview_widget = gtk_check_menu_item_new_with_label(MENU_ITEM_SHOW_PREVIEW);
+ gtk_menu_append(GTK_MENU(menu), xsane.show_preview_widget);
+ gtk_widget_show(xsane.show_preview_widget);
+ gtk_signal_connect(GTK_OBJECT(xsane.show_preview_widget), "toggled", (GtkSignalFunc) xsane_show_preview_callback, 0);
+
+ /* show histogram */
+
+ xsane.show_histogram_widget = gtk_check_menu_item_new_with_label(MENU_ITEM_SHOW_HISTOGRAM);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_histogram_widget), preferences.show_histogram);
+ gtk_menu_append(GTK_MENU(menu), xsane.show_histogram_widget);
+ gtk_widget_show(xsane.show_histogram_widget);
+ gtk_signal_connect(GTK_OBJECT(xsane.show_histogram_widget), "toggled", (GtkSignalFunc) xsane_show_histogram_callback, 0);
+
+
+ /* show standard options */
+
+ xsane.show_standard_options_widget = gtk_check_menu_item_new_with_label(MENU_ITEM_SHOW_STANDARDOPTIONS);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_standard_options_widget), preferences.show_standard_options);
+ gtk_menu_append(GTK_MENU(menu), xsane.show_standard_options_widget);
+ gtk_widget_show(xsane.show_standard_options_widget);
+ gtk_signal_connect(GTK_OBJECT(xsane.show_standard_options_widget), "toggled", (GtkSignalFunc) xsane_show_standard_options_callback, 0);
+
+
+ /* show advanced options */
+
+ xsane.show_advanced_options_widget = gtk_check_menu_item_new_with_label(MENU_ITEM_SHOW_ADVANCEDOPTIONS);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_advanced_options_widget), preferences.show_advanced_options);
+ gtk_menu_append(GTK_MENU(menu), xsane.show_advanced_options_widget);
+ gtk_widget_show(xsane.show_advanced_options_widget);
+ gtk_signal_connect(GTK_OBJECT(xsane.show_advanced_options_widget), "toggled",
+ (GtkSignalFunc) xsane_show_advanced_options_callback, 0);
+
+ return menu;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static GtkWidget *xsane_pref_build_menu(void)
+{
+ GtkWidget *menu, *item, *submenu, *subitem;
+
+ menu = gtk_menu_new();
+
+
+ /* XSane setup dialog */
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_SETUP);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_setup_dialog, 0);
+ gtk_widget_show(item);
+
+ /* insert separator: */
+
+ item = gtk_menu_item_new();
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+
+
+#ifdef XSANE_TEST
+ /* XSane batch scan dialog */
+
+ item = gtk_menu_item_new_with_label("Batch scan");
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_batch_scan_dialog, 0);
+ gtk_widget_show(item);
+
+ /* insert separator: */
+
+ item = gtk_menu_item_new();
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+#endif
+
+
+ /* length unit */
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_LENGTH_UNIT);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+
+ submenu = gtk_menu_new();
+
+ subitem = gtk_check_menu_item_new_with_label(SUBMENU_ITEM_LENGTH_MILLIMETERS);
+ gtk_menu_append(GTK_MENU(submenu), subitem);
+ if (preferences.length_unit == 1.0)
+ {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(subitem), TRUE);
+ }
+ gtk_signal_connect(GTK_OBJECT(subitem), "activate", (GtkSignalFunc) xsane_set_pref_unit_callback, "mm");
+ gtk_widget_show(subitem);
+ xsane.length_unit_mm = subitem;
+
+ subitem = gtk_check_menu_item_new_with_label(SUBMENU_ITEM_LENGTH_CENTIMETERS);
+ gtk_menu_append(GTK_MENU(submenu), subitem);
+ if (preferences.length_unit == 10.0)
+ {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(subitem), TRUE);
+ }
+ gtk_signal_connect(GTK_OBJECT(subitem), "activate", (GtkSignalFunc) xsane_set_pref_unit_callback, "cm");
+ gtk_widget_show(subitem);
+ xsane.length_unit_cm = subitem;
+
+ subitem = gtk_check_menu_item_new_with_label(SUBMENU_ITEM_LENGTH_INCHES);
+ gtk_menu_append(GTK_MENU(submenu), subitem);
+ if (preferences.length_unit == 25.4)
+ {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(subitem), TRUE);
+ }
+ gtk_signal_connect(GTK_OBJECT(subitem), "activate", (GtkSignalFunc) xsane_set_pref_unit_callback, "in");
+ gtk_widget_show(subitem);
+ xsane.length_unit_in = subitem;
+
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
+
+ xsane.length_unit_widget = item;
+
+
+ /* insert separator: */
+
+ item = gtk_menu_item_new();
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+
+ /* update policy */
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_UPDATE_POLICY);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+
+ submenu = gtk_menu_new();
+
+ subitem = gtk_check_menu_item_new_with_label(SUBMENU_ITEM_POLICY_CONTINUOUS);
+ gtk_menu_append(GTK_MENU(submenu), subitem);
+ if (preferences.gtk_update_policy == GTK_UPDATE_CONTINUOUS)
+ {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(subitem), TRUE);
+ }
+ gtk_signal_connect(GTK_OBJECT(subitem), "activate", (GtkSignalFunc) xsane_set_update_policy_callback, (void *) GTK_UPDATE_CONTINUOUS);
+ gtk_widget_show(subitem);
+ xsane.update_policy_continu = subitem;
+
+ subitem = gtk_check_menu_item_new_with_label(SUBMENU_ITEM_POLICY_DISCONTINU);
+ gtk_menu_append(GTK_MENU(submenu), subitem);
+ if (preferences.gtk_update_policy == GTK_UPDATE_DISCONTINUOUS)
+ {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(subitem), TRUE);
+ }
+ gtk_signal_connect(GTK_OBJECT(subitem), "activate", (GtkSignalFunc) xsane_set_update_policy_callback, (void *) GTK_UPDATE_DISCONTINUOUS);
+ gtk_widget_show(subitem);
+ xsane.update_policy_discont = subitem;
+
+ subitem = gtk_check_menu_item_new_with_label(SUBMENU_ITEM_POLICY_DELAYED);
+ gtk_menu_append(GTK_MENU(submenu), subitem);
+ if (preferences.gtk_update_policy == GTK_UPDATE_DELAYED)
+ {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(subitem), TRUE);
+ }
+ gtk_signal_connect(GTK_OBJECT(subitem), "activate", (GtkSignalFunc) xsane_set_update_policy_callback, (void *) GTK_UPDATE_DELAYED);
+ gtk_widget_show(subitem);
+ xsane.update_policy_delayed = subitem;
+
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
+
+
+ /* insert separator: */
+
+ item = gtk_menu_item_new();
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+
+
+ /* show resolution list */
+
+ xsane.show_resolution_list_widget = gtk_check_menu_item_new_with_label(MENU_ITEM_SHOW_RESOLUTIONLIST);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_resolution_list_widget), preferences.show_resolution_list);
+ gtk_menu_append(GTK_MENU(menu), xsane.show_resolution_list_widget);
+ gtk_widget_show(xsane.show_resolution_list_widget);
+ gtk_signal_connect(GTK_OBJECT(xsane.show_resolution_list_widget), "toggled",
+ (GtkSignalFunc) xsane_show_resolution_list_callback, 0);
+
+
+ /* insert separator: */
+
+ item = gtk_menu_item_new();
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+
+
+ /* page orientation */
+
+ item = gtk_check_menu_item_new_with_label(MENU_ITEM_PAGE_ROTATE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), preferences.psrotate);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+ gtk_signal_connect(GTK_OBJECT(item), "toggled", (GtkSignalFunc) xsane_page_rotate_callback, 0);
+
+
+
+ /* insert separator: */
+
+ item = gtk_menu_item_new();
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+
+ /* Save device setting */
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_SAVE_DEVICE_SETTINGS);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_device_preferences_save, 0);
+ gtk_widget_show(item);
+
+ /* Load device setting */
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_LOAD_DEVICE_SETTINGS);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_device_preferences_load, 0);
+ gtk_widget_show(item);
+
+ return menu;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static GtkWidget *xsane_help_build_menu(void)
+{
+ GtkWidget *menu, *item;
+
+ menu = gtk_menu_new();
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_XSANE_DOC);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_show_doc, (void *) "sane-xsane");
+ gtk_widget_show(item);
+
+ item = gtk_menu_item_new();
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+
+ if (xsane.backend)
+ {
+ item = gtk_menu_item_new_with_label(MENU_ITEM_BACKEND_DOC);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_show_doc, (void *) xsane.backend);
+ gtk_widget_show(item);
+ }
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_AVAILABLE_BACKENDS);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_show_doc, (void *) "sane-backends");
+ gtk_widget_show(item);
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_PROBLEMS);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_show_doc, (void *) "sane-problems");
+ gtk_widget_show(item);
+
+ item = gtk_menu_item_new();
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_widget_show(item);
+
+ item = gtk_menu_item_new_with_label(MENU_ITEM_SCANTIPS);
+ gtk_menu_append(GTK_MENU(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_show_doc, (void *) "sane-scantips");
+ gtk_widget_show(item);
+
+ return menu;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_panel_build(GSGDialog *dialog)
+{
+ GtkWidget *xsane_hbox;
+ GtkWidget *standard_hbox, *standard_vbox;
+ GtkWidget *advanced_hbox, *advanced_vbox;
+ GtkWidget *parent, *vbox, *button, *label;
+ const SANE_Option_Descriptor *opt;
+ SANE_Handle dev = dialog->dev;
+ double dval, dmin, dmax, dquant;
+ char *buf, str[16], title[256];
+ GSGDialogElement *elem;
+ SANE_Word quant, val;
+ SANE_Status status;
+ SANE_Int num_words;
+ char **str_list;
+ int i, j;
+ int num_vector_opts = 0;
+ int *vector_opts;
+
+ xsane_hbox = 0;
+
+ /* reset well-known options: */
+ dialog->well_known.scanmode = -1;
+ dialog->well_known.scansource = -1;
+ dialog->well_known.preview = -1;
+ dialog->well_known.dpi = -1;
+ dialog->well_known.dpi_x = -1;
+ dialog->well_known.dpi_y = -1;
+ dialog->well_known.coord[xsane_back_gtk_TL_X] = -1;
+ dialog->well_known.coord[xsane_back_gtk_TL_Y] = -1;
+ dialog->well_known.coord[xsane_back_gtk_BR_X] = -1;
+ dialog->well_known.coord[xsane_back_gtk_BR_Y] = -1;
+ dialog->well_known.gamma_vector = -1;
+ dialog->well_known.gamma_vector_r = -1;
+ dialog->well_known.gamma_vector_g = -1;
+ dialog->well_known.gamma_vector_b = -1;
+ dialog->well_known.bit_depth = -1;
+
+
+ /* standard options */
+ standard_hbox = gtk_hbox_new(FALSE, 2);
+ gtk_widget_show(standard_hbox);
+ standard_vbox = gtk_vbox_new(/* homogeneous */ FALSE, 0);
+ gtk_widget_show(standard_vbox);
+/* gtk_box_pack_start(GTK_BOX(standard_hbox), standard_vbox, FALSE, FALSE, 0); */ /* make frame fixed */
+ gtk_box_pack_start(GTK_BOX(standard_hbox), standard_vbox, TRUE, TRUE, 0); /* make frame sizeable */
+
+ /* advanced options */
+ advanced_hbox = gtk_hbox_new(FALSE, 2);
+ gtk_widget_show(advanced_hbox);
+ advanced_vbox = gtk_vbox_new(/* homogeneous */ FALSE, 0);
+ gtk_widget_show(advanced_vbox);
+/* gtk_box_pack_start(GTK_BOX(advanced_hbox), advanced_vbox, FALSE, FALSE, 0); */ /* make frame fixed */
+ gtk_box_pack_start(GTK_BOX(advanced_hbox), advanced_vbox, TRUE, TRUE, 0); /* make frame sizeable */
+
+
+ vector_opts = alloca(dialog->num_elements * sizeof (int));
+
+ parent = standard_vbox;
+ for (i = 1; i < dialog->num_elements; ++i)
+ {
+ opt = sane_get_option_descriptor (dev, i);
+ if (!SANE_OPTION_IS_ACTIVE(opt->cap))
+ continue;
+
+ /* pick up well-known options as we go: */
+ if (opt->name)
+ {
+ if (strcmp(opt->name, SANE_NAME_PREVIEW) == 0 && opt->type == SANE_TYPE_BOOL)
+ {
+ dialog->well_known.preview = i;
+ continue;
+ }
+ else if (strcmp(opt->name, SANE_NAME_SCAN_RESOLUTION) == 0
+ && opt->unit == SANE_UNIT_DPI && (opt->type == SANE_TYPE_INT || opt->type == SANE_TYPE_FIXED))
+ dialog->well_known.dpi = i;
+ else if (strcmp(opt->name, SANE_NAME_SCAN_X_RESOLUTION) == 0
+ && opt->unit == SANE_UNIT_DPI && (opt->type == SANE_TYPE_INT || opt->type == SANE_TYPE_FIXED))
+ dialog->well_known.dpi_x = i;
+ else if (strcmp(opt->name, SANE_NAME_SCAN_Y_RESOLUTION) == 0
+ && opt->unit == SANE_UNIT_DPI && (opt->type == SANE_TYPE_INT || opt->type == SANE_TYPE_FIXED))
+ dialog->well_known.dpi_y = i;
+ else if (strcmp (opt->name, SANE_NAME_SCAN_MODE) == 0)
+ dialog->well_known.scanmode = i;
+ else if (strcmp (opt->name, SANE_NAME_SCAN_SOURCE) == 0)
+ dialog->well_known.scansource = i;
+ else if (strcmp (opt->name, SANE_NAME_SCAN_TL_X) == 0)
+ dialog->well_known.coord[xsane_back_gtk_TL_X] = i;
+ else if (strcmp (opt->name, SANE_NAME_SCAN_TL_Y) == 0)
+ dialog->well_known.coord[xsane_back_gtk_TL_Y] = i;
+ else if (strcmp (opt->name, SANE_NAME_SCAN_BR_X) == 0)
+ dialog->well_known.coord[xsane_back_gtk_BR_X] = i;
+ else if (strcmp (opt->name, SANE_NAME_SCAN_BR_Y) == 0)
+ dialog->well_known.coord[xsane_back_gtk_BR_Y] = i;
+ else if (strcmp (opt->name, SANE_NAME_GAMMA_VECTOR) == 0)
+ dialog->well_known.gamma_vector = i;
+ else if (strcmp (opt->name, SANE_NAME_GAMMA_VECTOR_R) == 0)
+ dialog->well_known.gamma_vector_r = i;
+ else if (strcmp (opt->name, SANE_NAME_GAMMA_VECTOR_G) == 0)
+ dialog->well_known.gamma_vector_g = i;
+ else if (strcmp (opt->name, SANE_NAME_GAMMA_VECTOR_B) == 0)
+ dialog->well_known.gamma_vector_b = i;
+ else if (strcmp (opt->name, SANE_NAME_BIT_DEPTH) == 0)
+ dialog->well_known.bit_depth = i;
+ }
+
+ elem = dialog->element + i;
+ elem->dialog = dialog;
+
+
+ if (opt->unit == SANE_UNIT_NONE)
+ {
+ snprintf(title, sizeof(title), "%s", _BGT(opt->title));
+ }
+ else
+ {
+ snprintf(title, sizeof(title), "%s [%s]", _BGT(opt->title), xsane_back_gtk_unit_string(opt->unit));
+ }
+
+ switch (opt->type)
+ {
+ case SANE_TYPE_GROUP:
+ /* group a set of options */
+ vbox = standard_vbox;
+ if (opt->cap & SANE_CAP_ADVANCED)
+ {
+ vbox = advanced_vbox;
+ }
+ parent = xsane_back_gtk_group_new(vbox, title);
+ elem->widget = parent;
+ break;
+
+ case SANE_TYPE_BOOL:
+ assert(opt->size == sizeof(SANE_Word));
+ status = sane_control_option(dialog->dev, i, SANE_ACTION_GET_VALUE, &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ goto get_value_failed;
+ }
+ xsane_back_gtk_button_new(parent, title, val, elem, dialog->tooltips, _BGT(opt->desc), SANE_OPTION_IS_SETTABLE(opt->cap));
+ gtk_widget_show(parent->parent);
+ break;
+
+ case SANE_TYPE_INT:
+ if (opt->size != sizeof(SANE_Word))
+ {
+ vector_opts[num_vector_opts++] = i;
+ break;
+ }
+ status = sane_control_option(dialog->dev, i, SANE_ACTION_GET_VALUE, &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ goto get_value_failed;
+ }
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ if ( (strcmp(opt->name, SANE_NAME_SCAN_RESOLUTION) ) && /* do not show resolution */
+ (strcmp(opt->name, SANE_NAME_SCAN_X_RESOLUTION)) && /* do not show x-resolution */
+ (strcmp(opt->name, SANE_NAME_SCAN_Y_RESOLUTION)) ) /* do not show y-resolution */
+ {
+ /* use a scale */
+ quant = opt->constraint.range->quant;
+ if (quant == 0)
+ quant = 1;
+ xsane_back_gtk_scale_new(parent, title, val, opt->constraint.range->min, opt->constraint.range->max, quant,
+ (opt->cap & SANE_CAP_AUTOMATIC), elem, dialog->tooltips, _BGT(opt->desc), SANE_OPTION_IS_SETTABLE(opt->cap));
+ gtk_widget_show(parent->parent);
+ }
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ if ( (strcmp(opt->name, SANE_NAME_SCAN_RESOLUTION) ) && /* do not show resolution */
+ (strcmp(opt->name, SANE_NAME_SCAN_X_RESOLUTION)) && /* do not show x-resolution */
+ (strcmp(opt->name, SANE_NAME_SCAN_Y_RESOLUTION)) ) /* do not show y-resolution */
+ {
+ /* use a "list-selection" widget */
+ num_words = opt->constraint.word_list[0];
+ str_list = malloc((num_words + 1) * sizeof(str_list[0]));
+ for (j = 0; j < num_words; ++j)
+ {
+ sprintf(str, "%d", opt->constraint.word_list[j + 1]);
+ str_list[j] = strdup(str);
+ }
+ str_list[j] = 0;
+ sprintf(str, "%d", val);
+ xsane_back_gtk_option_menu_new(parent, title, str_list, str, elem, dialog->tooltips, _BGT(opt->desc),
+ SANE_OPTION_IS_SETTABLE(opt->cap));
+ free(str_list);
+ gtk_widget_show(parent->parent);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "xsane_panel_build: %s %d!\n", ERR_UNKNOWN_CONSTRAINT_TYPE, opt->constraint_type);
+ break;
+ }
+ break;
+
+ case SANE_TYPE_FIXED:
+ if (opt->size != sizeof (SANE_Word))
+ {
+ vector_opts[num_vector_opts++] = i;
+ break;
+ }
+ status = sane_control_option(dialog->dev, i, SANE_ACTION_GET_VALUE, &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ goto get_value_failed;
+ }
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ if ( (strcmp(opt->name, SANE_NAME_SCAN_RESOLUTION) ) && /* do not show resolution */
+ (strcmp(opt->name, SANE_NAME_SCAN_X_RESOLUTION)) && /* do not show x-resolution */
+ (strcmp(opt->name, SANE_NAME_SCAN_Y_RESOLUTION)) ) /* do not show y-resolution */
+ {
+ /* use a scale */
+ quant = opt->constraint.range->quant;
+ if (quant == 0)
+ quant = 1;
+ dval = SANE_UNFIX(val);
+ dmin = SANE_UNFIX(opt->constraint.range->min);
+ dmax = SANE_UNFIX(opt->constraint.range->max);
+ dquant = SANE_UNFIX(quant);
+ if (opt->unit == SANE_UNIT_MM)
+ {
+ dval /= preferences.length_unit;
+ dmin /= preferences.length_unit;
+ dmax /= preferences.length_unit;
+ dquant /= preferences.length_unit;
+ }
+ xsane_back_gtk_scale_new(parent, title, dval, dmin, dmax, dquant, (opt->cap & SANE_CAP_AUTOMATIC), elem,
+ dialog->tooltips, _BGT(opt->desc), SANE_OPTION_IS_SETTABLE(opt->cap));
+ gtk_widget_show(parent->parent);
+ }
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ if ( (strcmp(opt->name, SANE_NAME_SCAN_RESOLUTION) ) && /* do not show resolution */
+ (strcmp(opt->name, SANE_NAME_SCAN_X_RESOLUTION)) && /* do not show x-resolution */
+ (strcmp(opt->name, SANE_NAME_SCAN_Y_RESOLUTION)) ) /* do not show y-resolution */
+ {
+ /* use a "list-selection" widget */
+ num_words = opt->constraint.word_list[0];
+ str_list = malloc ((num_words + 1) * sizeof (str_list[0]));
+ for (j = 0; j < num_words; ++j)
+ {
+ sprintf(str, "%g", SANE_UNFIX(opt->constraint.word_list[j + 1]));
+ str_list[j] = strdup (str);
+ }
+ str_list[j] = 0;
+ sprintf(str, "%g", SANE_UNFIX (val));
+ xsane_back_gtk_option_menu_new(parent, title, str_list, str, elem, dialog->tooltips, _BGT(opt->desc), SANE_OPTION_IS_SETTABLE(opt->cap));
+ free (str_list);
+ gtk_widget_show(parent->parent);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "xsane_panel_build: %s %d!\n", ERR_UNKNOWN_CONSTRAINT_TYPE, opt->constraint_type);
+ break;
+ }
+ break;
+
+ case SANE_TYPE_STRING:
+ buf = malloc (opt->size);
+ status = sane_control_option(dialog->dev, i, SANE_ACTION_GET_VALUE, buf, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (buf);
+ goto get_value_failed;
+ }
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_STRING_LIST:
+ if ( (strcmp (opt->name, SANE_NAME_SCAN_MODE) != 0) && /* do not show scanmode */
+ (strcmp (opt->name, SANE_NAME_SCAN_SOURCE) != 0) ) /* do not show scansource */
+ {
+ /* use a "list-selection" widget */
+ xsane_back_gtk_option_menu_new(parent, title, (char **) opt->constraint.string_list, buf,
+ elem, dialog->tooltips, _BGT(opt->desc), SANE_OPTION_IS_SETTABLE(opt->cap));
+ gtk_widget_show (parent->parent);
+ }
+ break;
+
+ case SANE_CONSTRAINT_NONE:
+ xsane_back_gtk_text_entry_new(parent, title, buf, elem, dialog->tooltips, _BGT(opt->desc), SANE_OPTION_IS_SETTABLE(opt->cap));
+ gtk_widget_show (parent->parent);
+ break;
+
+ default:
+ fprintf(stderr, "xsane_panel_build: %s %d!\n", ERR_UNKNOWN_CONSTRAINT_TYPE, opt->constraint_type);
+ break;
+ }
+ free (buf);
+ break;
+
+ case SANE_TYPE_BUTTON:
+ button = gtk_button_new();
+ gtk_signal_connect(GTK_OBJECT (button), "clicked", (GtkSignalFunc) xsane_back_gtk_push_button_callback, elem);
+ xsane_back_gtk_set_tooltip(dialog->tooltips, button, _BGT(opt->desc));
+
+ label = gtk_label_new(title);
+ gtk_container_add(GTK_CONTAINER (button), label);
+
+ gtk_box_pack_start(GTK_BOX (parent), button, FALSE, TRUE, 0);
+
+ gtk_widget_show(label);
+ gtk_widget_show(button);
+
+ gtk_widget_set_sensitive(button, SANE_OPTION_IS_SETTABLE(opt->cap));
+
+ elem->widget = button;
+ gtk_widget_show(parent->parent);
+ break;
+
+ default:
+ fprintf(stderr, "xsane_panel_build: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ break;
+ }
+ continue;
+
+ get_value_failed:
+ {
+ char msg[256];
+
+ sprintf(msg, "%s %s: %s.", ERR_GET_OPTION, opt->name, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(msg, TRUE);
+ }
+ }
+
+ if ((dialog->well_known.dpi_x == -1) && (dialog->well_known.dpi_y != -1))
+ {
+ dialog->well_known.dpi_x = dialog->well_known.dpi;
+ }
+
+ xsane_hbox = xsane_update_xsane_callback();
+
+ gtk_container_add(GTK_CONTAINER(dialog->xsane_window), xsane_hbox);
+ gtk_container_add(GTK_CONTAINER(dialog->standard_window), standard_hbox);
+ gtk_container_add(GTK_CONTAINER(dialog->advanced_window), advanced_hbox);
+
+ dialog->xsane_hbox = xsane_hbox;
+ dialog->standard_hbox = standard_hbox;
+ dialog->advanced_hbox = advanced_hbox;
+
+ xsane_update_histogram();
+/*
+ xsane_draw_slider_level(&xsane.slider_gray);
+ xsane_draw_slider_level(&xsane.slider_red);
+ xsane_draw_slider_level(&xsane.slider_green);
+ xsane_draw_slider_level(&xsane.slider_blue);
+*/
+ xsane_update_sliders();
+
+ if (xsane.length_unit_widget)
+ {
+ int unit;
+
+ status = xsane_get_area_value(dialog->well_known.coord[0], 0, &unit);
+
+ if ( (unit == SANE_UNIT_PIXEL) || (status) )
+ {
+ gtk_widget_set_sensitive(xsane.length_unit_widget, FALSE);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(xsane.length_unit_widget, TRUE);
+ }
+ }
+
+ /* now add in vector editor, if necessary: */
+/*
+ if (num_vector_opts)
+ vector_new (dialog, custom_gamma_vbox, num_vector_opts, vector_opts);
+*/
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* Create the main dialog box. */
+
+static void xsane_device_dialog(void)
+{
+ GtkWidget *hbox, *button, *frame, *infobox;
+ GtkWidget *main_dialog_window, *standard_dialog_window, *advanced_dialog_window;
+ GtkWidget *menubar, *menubar_item;
+ const gchar *devname;
+ char buf[256];
+ char windowname[255];
+ char devicetext[255];
+ char *textptr;
+ GtkWidget *xsane_window;
+ GtkWidget *xsane_vbox_main;
+ GtkWidget *xsane_vbox_standard;
+ GtkWidget *xsane_vbox_advanced;
+ GdkColormap *colormap;
+ SANE_Int num_elements;
+ SANE_Status status;
+ SANE_Handle dev;
+
+
+ devname = devlist[seldev]->name;
+
+ status = sane_open(devname, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf(buf, sizeof(buf), "%s `%s':\n %s.", ERR_DEVICE_OPEN_FAILED, devname, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ return;
+ }
+
+ if (sane_control_option(dev, 0, SANE_ACTION_GET_VALUE, &num_elements, 0) != SANE_STATUS_GOOD)
+ {
+ xsane_back_gtk_error(ERR_OPTION_COUNT, TRUE);
+ sane_close(dev);
+ return;
+ }
+
+ snprintf(buf, sizeof(buf), "%s", devlist[seldev]->name); /* generate "sane-BACKENDNAME" */
+ textptr = strrchr(buf, ':'); /* format is midend:midend:midend:backend:device or backend:device */
+ if (textptr)
+ {
+ *textptr = 0; /* erase ":device" at end of text */
+ textptr = strrchr(buf, ':');
+ if (textptr) /* midend:backend:device */
+ {
+ textptr++;
+ }
+ else /* backend:device */
+ {
+ textptr = buf;
+ }
+
+ xsane.backend = malloc(strlen(textptr)+6);
+ sprintf(xsane.backend, "sane-%s", textptr); /* add "sane-" */
+
+ bindtextdomain(xsane.backend, LOCALEDIR); /* set path for backend translation texts */
+ }
+
+ /* create device-text for window titles */
+
+ snprintf(devicetext, sizeof(devicetext), "%s", devlist[seldev]->model);
+ textptr = devicetext + strlen(devicetext);
+ while (*(textptr-1) == ' ') /* erase spaces at end of text */
+ {
+ textptr--;
+ }
+
+ *textptr = ':';
+ textptr++;
+ *textptr = 0;
+
+ if (!strncmp(devname, "net:", 4)) /* network device ? */
+ {
+ sprintf(textptr, "net:");
+ textptr = devicetext + strlen(devicetext);
+ }
+
+ snprintf(buf, sizeof(buf), ":%s", devname);
+ snprintf(buf, sizeof(buf), "/%s", (strrchr(buf, ':')+1));
+ sprintf(textptr, (strrchr(buf, '/')+1));
+
+ device_text = strdup(devicetext);
+
+
+ /* if no preferences filename is given on commandline create one from devicenaname */
+
+ if (!xsane.device_set_filename)
+ {
+ if (!strcmp(devlist[seldev]->vendor, TEXT_UNKNOWN))
+ {
+ snprintf(buf, sizeof(buf), "%s", devlist[seldev]->name);
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), "%s:%s", devlist[seldev]->vendor, devlist[seldev]->model);
+ }
+ xsane.device_set_filename = strdup(buf); /* set preferences filename */
+ }
+
+
+ xsane_pref_restore(); /* restore preferences */
+
+ if (xsane.main_window_fixed == -1) /* no command line option given */
+ {
+ xsane.main_window_fixed = preferences.main_window_fixed;
+ }
+
+
+ /* create the xsane dialog box */
+
+ xsane.shell = gtk_dialog_new();
+ gtk_widget_set_uposition(xsane.shell, XSANE_SHELL_POS_X, XSANE_SHELL_POS_Y);
+ sprintf(windowname, "%s %s %s", prog_name, XSANE_VERSION, device_text);
+ gtk_window_set_title(GTK_WINDOW(xsane.shell), (char *) windowname);
+ gtk_signal_connect(GTK_OBJECT(xsane.shell), "delete_event", GTK_SIGNAL_FUNC(xsane_scan_win_delete), 0);
+
+ xsane_set_window_icon(xsane.shell, 0);
+
+ /* set the main vbox */
+
+ xsane_window = GTK_DIALOG(xsane.shell)->vbox;
+ gtk_widget_show(xsane_window); /* normally not necessary, but to be sure */
+
+ /* create the menubar */
+
+ menubar = gtk_menu_bar_new();
+ gtk_box_pack_start(GTK_BOX(xsane_window), menubar, FALSE, FALSE, 0);
+
+ /* "Files" submenu: */
+ menubar_item = gtk_menu_item_new_with_label(MENU_FILE);
+ gtk_container_add(GTK_CONTAINER(menubar), menubar_item);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menubar_item), xsane_files_build_menu());
+ gtk_widget_show(menubar_item);
+
+ /* "Preferences" submenu: */
+ menubar_item = gtk_menu_item_new_with_label(MENU_PREFERENCES);
+ gtk_container_add(GTK_CONTAINER(menubar), menubar_item);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menubar_item), xsane_pref_build_menu());
+ gtk_widget_show(menubar_item);
+
+ /* "View" submenu: */
+ menubar_item = gtk_menu_item_new_with_label(MENU_VIEW);
+ gtk_container_add(GTK_CONTAINER(menubar), menubar_item);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menubar_item), xsane_view_build_menu());
+ gtk_widget_show(menubar_item);
+
+
+ /* "Help" submenu: */
+ menubar_item = gtk_menu_item_new_with_label(MENU_HELP);
+ gtk_container_add(GTK_CONTAINER(menubar), menubar_item);
+ gtk_menu_item_right_justify((GtkMenuItem *) menubar_item);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menubar_item), xsane_help_build_menu());
+ gtk_widget_show(menubar_item);
+
+ gtk_widget_show(menubar);
+
+ if (xsane.main_window_fixed) /* fixed window: use it like it is */
+ {
+ /* shrink grow auto_shrink */
+ gtk_window_set_policy(GTK_WINDOW(xsane.shell), FALSE, FALSE, TRUE); /* auto size */
+
+ xsane_vbox_main = gtk_vbox_new(TRUE, 5); /* we need this to set the wanted borders */
+ gtk_container_set_border_width(GTK_CONTAINER(xsane_vbox_main), 5);
+ gtk_container_add(GTK_CONTAINER(xsane_window), xsane_vbox_main);
+ }
+ else /* scrolled window: create a scrolled window and put it into the xsane dialog box */
+ {
+ gtk_window_set_default_size(GTK_WINDOW(xsane.shell), XSANE_SHELL_WIDTH, XSANE_SHELL_HEIGHT); /* set default size */
+
+ /* shrink grow auto_shrink */
+ gtk_window_set_policy(GTK_WINDOW(xsane.shell), TRUE, TRUE, FALSE); /* allow resizing */
+
+ xsane.main_dialog_scrolled = gtk_scrolled_window_new(0, 0);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(xsane.main_dialog_scrolled),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ gtk_container_add(GTK_CONTAINER(xsane_window), xsane.main_dialog_scrolled);
+ gtk_widget_show(xsane.main_dialog_scrolled);
+
+ xsane_vbox_main = gtk_vbox_new(TRUE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(xsane_vbox_main), 5);
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(xsane.main_dialog_scrolled), xsane_vbox_main);
+ }
+
+ /* create a subwindow so the standard dialog keeps its position on rebuilds: */
+ main_dialog_window = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(xsane_vbox_main), main_dialog_window, TRUE, TRUE, 0);
+ gtk_widget_show(main_dialog_window);
+
+ gtk_widget_show(xsane_vbox_main);
+
+
+ /* create the scanner standard options dialog box */
+
+ xsane.standard_options_shell = gtk_window_new(GTK_WINDOW_DIALOG);
+ gtk_widget_set_uposition(xsane.standard_options_shell, XSANE_STD_OPTIONS_POS_X, XSANE_STD_OPTIONS_POS_Y);
+ sprintf(windowname, "%s %s", WINDOW_STANDARD_OPTIONS, device_text);
+ gtk_window_set_title(GTK_WINDOW(xsane.standard_options_shell), (char *) windowname);
+
+ /* shrink grow auto_shrink */
+ gtk_window_set_policy(GTK_WINDOW(xsane.standard_options_shell), FALSE, FALSE, TRUE);
+ gtk_signal_connect(GTK_OBJECT(xsane.standard_options_shell), "delete_event",
+ GTK_SIGNAL_FUNC(xsane_standard_option_win_delete), 0);
+
+ xsane_set_window_icon(xsane.standard_options_shell, 0);
+
+ xsane_vbox_standard = gtk_vbox_new(TRUE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(xsane_vbox_standard), 5);
+ gtk_container_add(GTK_CONTAINER(xsane.standard_options_shell), xsane_vbox_standard);
+ gtk_widget_show(xsane_vbox_standard);
+
+ /* create a subwindow so the standard dialog keeps its position on rebuilds: */
+ standard_dialog_window = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(xsane_vbox_standard), standard_dialog_window, TRUE, TRUE, 0);
+ gtk_widget_show(standard_dialog_window);
+
+
+
+ /* create the scanner advanced options dialog box */
+
+ xsane.advanced_options_shell = gtk_window_new(GTK_WINDOW_DIALOG);
+ gtk_widget_set_uposition(xsane.advanced_options_shell, XSANE_ADV_OPTIONS_POS_X, XSANE_ADV_OPTIONS_POS_Y);
+ sprintf(windowname, "%s %s",WINDOW_ADVANCED_OPTIONS, device_text);
+ gtk_window_set_title(GTK_WINDOW(xsane.advanced_options_shell), (char *) windowname);
+
+ /* shrink grow auto_shrink */
+ gtk_window_set_policy(GTK_WINDOW(xsane.advanced_options_shell), FALSE, FALSE, TRUE);
+ gtk_signal_connect(GTK_OBJECT(xsane.advanced_options_shell), "delete_event",
+ GTK_SIGNAL_FUNC(xsane_advanced_option_win_delete), 0);
+
+ xsane_set_window_icon(xsane.advanced_options_shell, 0);
+
+ xsane_vbox_advanced = gtk_vbox_new(TRUE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(xsane_vbox_advanced), 5);
+ gtk_container_add(GTK_CONTAINER(xsane.advanced_options_shell), xsane_vbox_advanced);
+ gtk_widget_show(xsane_vbox_advanced);
+
+ /* create a subwindow so the advanced dialog keeps its position on rebuilds: */
+ advanced_dialog_window = gtk_hbox_new(/* homogeneous */ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(xsane_vbox_advanced), advanced_dialog_window, TRUE, TRUE, 0);
+ gtk_widget_show(advanced_dialog_window);
+
+
+ dialog = malloc(sizeof (*dialog));
+ if (!dialog)
+ {
+ printf("Could not create dialog\n");
+ return;
+ }
+
+ /* fill in dialog structure */
+
+ memset(dialog, 0, sizeof(*dialog));
+
+ dialog->xsane_window = main_dialog_window;
+ dialog->standard_window = standard_dialog_window;
+ dialog->advanced_window = advanced_dialog_window;
+ dialog->dev = dev;
+ dialog->dev_name = strdup(devname);
+ dialog->num_elements = num_elements;
+ dialog->option_reload_callback = xsane_update_preview;
+ dialog->option_reload_arg = 0;
+ dialog->param_change_callback = xsane_update_param;
+ dialog->param_change_arg = 0;
+
+ dialog->element = malloc(num_elements * sizeof(dialog->element[0]));
+ memset(dialog->element, 0, num_elements * sizeof(dialog->element[0]));
+
+
+
+
+ /* realize xsane main dialog */
+ /* normally a realize should be ok, but then
+ the default size of the scrollwed window is ignored
+ so we use a widget_show in that case */
+
+ if (xsane.main_window_fixed)
+ {
+ gtk_widget_realize(xsane.shell);
+ }
+ else
+ {
+ gtk_widget_show(xsane.shell);
+ /* the disadavantage of this is that the main window does
+ not have the focus when every window is shown */
+ }
+
+
+ /* define tooltips colors */
+
+ dialog->tooltips = gtk_tooltips_new();
+ colormap = gdk_window_get_colormap(xsane.shell->window);
+
+/* I don`t know why the following does not work with gtk-1.2.x */
+/* but the gimp has the same problems ;-) */
+ /* use black as foreground: */
+ dialog->tooltips_fg.red = 0;
+ dialog->tooltips_fg.green = 0;
+ dialog->tooltips_fg.blue = 0;
+ gdk_color_alloc(colormap, &dialog->tooltips_fg);
+
+ /* postit yellow (khaki) as background: */
+ dialog->tooltips_bg.red = 61669;
+ dialog->tooltips_bg.green = 59113;
+ dialog->tooltips_bg.blue = 35979;
+ gdk_color_alloc(colormap, &dialog->tooltips_bg);
+
+/* as long as gtk_tooltips_set_colors() does not work : */
+#if 1
+ gtk_tooltips_force_window(dialog->tooltips);
+ {
+ GtkStyle *current_style = gtk_style_copy(gtk_widget_get_style(dialog->tooltips->tip_window));
+
+ current_style->bg[GTK_STATE_NORMAL] = dialog->tooltips_bg;
+ current_style->fg[GTK_STATE_NORMAL] = dialog->tooltips_fg;
+ gtk_widget_set_style(dialog->tooltips->tip_window, current_style);
+ }
+#else
+ gtk_tooltips_set_colors(dialog->tooltips, &dialog->tooltips_bg, &dialog->tooltips_fg);
+#endif
+ xsane_back_gtk_set_tooltips(dialog, preferences.tooltips_enabled);
+
+
+
+ /* create histogram dialog and set colors */
+ xsane_create_histogram_dialog(device_text); /* create the histogram dialog */
+
+
+ /* The bottom row of info and start button */
+
+#if 0
+ hbox = GTK_DIALOG(xsane.shell)->action_area;
+#endif
+
+ hbox = gtk_hbox_new(FALSE, 5);
+ gtk_box_pack_end(GTK_BOX(GTK_DIALOG(xsane.shell)->action_area), hbox, TRUE, TRUE, 0);
+ gtk_widget_show(hbox);
+
+ /* Info frame */
+ frame = gtk_frame_new(0);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
+#if 0
+ gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, FALSE, 0);
+#endif
+ gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show(frame);
+
+ infobox = gtk_hbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(infobox), 2);
+ gtk_container_add(GTK_CONTAINER(frame), infobox);
+ gtk_widget_show(infobox);
+
+ xsane.info_label = gtk_label_new(TEXT_INFO_BOX);
+ gtk_box_pack_start(GTK_BOX(infobox), xsane.info_label, FALSE, FALSE, 0);
+#if 0
+ gtk_box_pack_start(GTK_BOX(infobox), xsane.info_label, TRUE, TRUE, 0);
+#endif
+ gtk_widget_show(xsane.info_label);
+
+
+ /* The Scan button */
+ button = gtk_button_new_with_label(BUTTON_START);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_scan_dialog, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+ xsane.start_button = GTK_OBJECT(button);
+
+
+ /* create backend dependend options */
+ xsane_panel_build(dialog);
+
+
+ /* create preview dialog */
+ xsane.preview = preview_new(dialog);
+ gtk_signal_connect(GTK_OBJECT(xsane.preview->top), "delete_event", GTK_SIGNAL_FUNC(xsane_preview_window_destroyed), 0);
+
+
+ xsane_device_preferences_restore(); /* restore device-settings */
+
+ xsane_update_param(dialog, 0);
+
+ gtk_widget_realize(xsane.standard_options_shell); /* is needed for saving window geometry */
+ gtk_widget_realize(xsane.advanced_options_shell);
+
+ if (preferences.show_standard_options)
+ {
+ gtk_widget_show(xsane.standard_options_shell);
+ }
+
+ if (preferences.show_advanced_options)
+ {
+ gtk_widget_show(xsane.advanced_options_shell);
+ }
+
+ gtk_widget_show(xsane.shell); /* call as last so focus is on it */
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ xsane_device_preferences_restore(); /* restore device-settings */
+
+ xsane_update_sliders();
+
+ if (xsane.show_preview)
+ {
+ gtk_widget_show(xsane.preview->top);
+ }
+ else
+ {
+ gtk_widget_hide(xsane.preview->top);
+ }
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_preview_widget), xsane.show_preview);
+
+ xsane_set_all_resolutions(); /* make sure resolution, resolution_x and resolution_y are up to date */
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_choose_dialog_ok_callback(void)
+{
+ gtk_signal_disconnect_by_func(GTK_OBJECT(choose_device_dialog), GTK_SIGNAL_FUNC(xsane_quit), 0);
+ gtk_widget_destroy(choose_device_dialog);
+ xsane_device_dialog();
+
+ if (!dialog)
+ {
+ xsane_quit();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_select_device_by_key_callback(GtkWidget * widget, gpointer data)
+{
+ seldev = (long) data;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_select_device_by_mouse_callback(GtkWidget * widget, GdkEventButton *event, gpointer data)
+{
+ seldev = (long) data;
+ if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
+ {
+ xsane_choose_dialog_ok_callback();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static gint32 xsane_choose_device(void)
+{
+ GtkWidget *main_vbox, *vbox, *hbox, *button, *device_frame, *device_vbox, *pixmapwidget;
+ GdkBitmap *mask;
+ GdkPixmap *pixmap;
+ GtkStyle *style;
+ GdkColor *bg_trans;
+ GSList *owner;
+ gint i;
+ const SANE_Device *adev;
+ char buf[256];
+ char vendor[9];
+ char model[17];
+ char type[20];
+ int j;
+
+ choose_device_dialog = gtk_dialog_new();
+ gtk_window_set_position(GTK_WINDOW(choose_device_dialog), GTK_WIN_POS_CENTER);
+ gtk_window_set_policy(GTK_WINDOW(choose_device_dialog), FALSE, FALSE, FALSE);
+ gtk_signal_connect(GTK_OBJECT(choose_device_dialog), "destroy", GTK_SIGNAL_FUNC(xsane_quit), 0);
+ snprintf(buf, sizeof(buf), "%s %s", prog_name, WINDOW_DEVICE_SELECTION);
+ gtk_window_set_title(GTK_WINDOW(choose_device_dialog), buf);
+
+ main_vbox = GTK_DIALOG(choose_device_dialog)->vbox;
+
+ vbox = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
+ gtk_box_pack_start(GTK_BOX(main_vbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show(vbox);
+
+ /* xsane logo */
+ gtk_widget_realize(choose_device_dialog);
+
+ style = gtk_widget_get_style(choose_device_dialog);
+ bg_trans = &style->bg[GTK_STATE_NORMAL];
+
+ snprintf(buf, sizeof(buf), "%s/xsane-logo.xpm", STRINGIFY(PATH_SANE_DATA_DIR));
+ pixmap = gdk_pixmap_create_from_xpm(choose_device_dialog->window, &mask, bg_trans, buf);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_box_pack_start(GTK_BOX(vbox), pixmapwidget, FALSE, FALSE, 2);
+ gtk_widget_show(pixmapwidget);
+ gdk_pixmap_unref(pixmap);
+
+ xsane_set_window_icon(choose_device_dialog, (gchar **) 0);
+
+ xsane_separator_new(vbox, 5);
+
+
+ /* list the drivers with radiobuttons */
+ device_frame = gtk_frame_new(TEXT_AVAILABLE_DEVICES);
+ gtk_box_pack_start(GTK_BOX(vbox), device_frame, FALSE, FALSE, 2);
+ gtk_widget_show(device_frame);
+
+ device_vbox = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(device_vbox), 3);
+ gtk_container_add(GTK_CONTAINER(device_frame), device_vbox);
+
+ owner = 0;
+ for (i = 0; i < ndevs; i++)
+ {
+ adev = devlist[i];
+
+ strncpy(vendor, adev->vendor, sizeof(vendor)-1);
+ vendor[sizeof(vendor)-1] = 0;
+ for (j = strlen(vendor); j < sizeof(vendor)-1; j++)
+ {
+ vendor[j] = ' ';
+ }
+
+ strncpy(model, adev->model, sizeof(model)-1);
+ model[sizeof(model)-1] = 0;
+ for (j = strlen(model); j < sizeof(model)-1; j++)
+ {
+ model[j] = ' ';
+ }
+
+ strncpy(type, _(adev->type), sizeof(type)-1); /* allow translation of device type */
+ type[sizeof(type)-1] = 0;
+ for (j = strlen(type); j < sizeof(type)-1; j++)
+ {
+ type[j] = ' ';
+ }
+
+ snprintf(buf, sizeof(buf), "%s %s %s [%s]", vendor, model, type, adev->name);
+ button = gtk_radio_button_new_with_label(owner, (char *) buf);
+ gtk_signal_connect(GTK_OBJECT(button), "button_press_event",
+ (GtkSignalFunc) xsane_select_device_by_mouse_callback, (void *) (long) i);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked",
+ (GtkSignalFunc) xsane_select_device_by_key_callback, (void *) (long) i);
+ gtk_box_pack_start(GTK_BOX(device_vbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+ owner = gtk_radio_button_group(GTK_RADIO_BUTTON(button));;
+ }
+ gtk_widget_show(device_vbox);
+
+ /* The bottom row of buttons */
+ hbox = GTK_DIALOG(choose_device_dialog)->action_area;
+
+ /* The OK button */
+ button = gtk_button_new_with_label(BUTTON_OK);
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_choose_dialog_ok_callback, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_grab_default(button);
+ gtk_widget_show(button);
+
+ /* The Cancel button */
+ button = gtk_button_new_with_label(BUTTON_CANCEL);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_quit, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ gtk_widget_show(choose_device_dialog);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_usage(void)
+{
+ printf("%s %s %s\n", TEXT_USAGE, prog_name, TEXT_USAGE_OPTIONS);
+ printf("\n%s\n\n", TEXT_HELP);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_init(int argc, char **argv)
+{
+ char filename[PATH_MAX];
+ struct stat st;
+
+ gtk_init(&argc, &argv);
+#ifdef HAVE_LIBGIMP_GIMP_H
+ gtk_rc_parse(gimp_gtkrc());
+
+ gdk_set_use_xshm(gimp_use_xshm());
+#endif
+
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, "xsane-style", 0, ".rc", XSANE_PATH_LOCAL_SANE);
+ if (stat(filename, &st) >= 0)
+ {
+ gtk_rc_parse(filename);
+ }
+ else /* no local xsane-style.rc, look for system file */
+ {
+ xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, "xsane-style", 0, ".rc", XSANE_PATH_SYSTEM);
+ if (stat(filename, &st) >= 0)
+ {
+ gtk_rc_parse(filename);
+ }
+ }
+
+ sane_init(&xsane.sane_backend_versioncode, (void *) xsane_authorization_callback);
+ if (SANE_VERSION_MAJOR(xsane.sane_backend_versioncode) != SANE_V_MAJOR)
+ {
+ fprintf(stderr, "\n\n"
+ "%s %s:\n"
+ " %s\n"
+ " %s %d\n"
+ " %s %d\n"
+ "%s\n\n",
+ prog_name, ERR_ERROR,
+ ERR_MAJOR_VERSION_NR_CONFLICT,
+ ERR_XSANE_MAJOR_VERSION, SANE_V_MAJOR,
+ ERR_BACKEND_MAJOR_VERSION, SANE_VERSION_MAJOR(xsane.sane_backend_versioncode),
+ ERR_PROGRAM_ABORTED);
+ return;
+ }
+
+ if (argc > 1)
+ {
+ int ch;
+
+ while((ch = getopt_long(argc, argv, "cd:fghnsvFR", long_options, 0)) != EOF)
+ {
+ switch(ch)
+ {
+ case 'g': /* This options is set when xsane is called from the */
+ /* GIMP. If xsane is compiled without GIMP support */
+ /* then you get the error message when GIMP does */
+ /* query or tries to start the xsane plugin! */
+#ifndef HAVE_LIBGIMP_GIMP_H
+ printf("%s: %s\n", argv[0], ERR_GIMP_SUPPORT_MISSING);
+ exit(0);
+#endif
+ break;
+
+ case 'v': /* --version */
+#ifdef HAVE_LIBGIMP_GIMP_H
+ printf("%s-%s, %s \"%s\", SANE-%d.%d, %s, %s%s\n",
+ prog_name,
+ XSANE_VERSION,
+ TEXT_PACKAGE,
+ PACKAGE_VERSION,
+ SANE_VERSION_MAJOR(xsane.sane_backend_versioncode),
+ SANE_VERSION_MINOR(xsane.sane_backend_versioncode),
+ TEXT_WITH_GIMP_SUPPORT,
+ TEXT_GIMP_VERSION,
+ GIMP_VERSION);
+#else
+ printf("%s-%s, %s \"%s\", SANE-%d.%d, %s\n",
+ prog_name,
+ XSANE_VERSION,
+ TEXT_PACKAGE,
+ PACKAGE_VERSION,
+ SANE_VERSION_MAJOR(xsane.sane_backend_versioncode),
+ SANE_VERSION_MINOR(xsane.sane_backend_versioncode),
+ TEXT_WITHOUT_GIMP_SUPPORT);
+#endif
+ exit(0);
+
+ case 'd': /* --device-settings */
+ xsane.device_set_filename = strdup(optarg);
+ break;
+
+ case 's': /* --scan */
+ xsane.xsane_mode = XSANE_SCAN;
+ break;
+
+ case 'c': /* --copy */
+ xsane.xsane_mode = XSANE_COPY;
+ break;
+
+ case 'n': /* --No-modes-election */
+ xsane.mode_selection = 0;
+ break;
+
+ case 'f': /* --fax */
+ xsane.xsane_mode = XSANE_FAX;
+ break;
+
+ case 'F': /* --Fixed */
+ xsane.main_window_fixed = 1;
+ break;
+
+ case 'R': /* --Resizeable */
+ xsane.main_window_fixed = 0;
+ break;
+
+ case 'h': /* --help */
+ default:
+ xsane_usage();
+ exit(0);
+ }
+ }
+ }
+
+ sane_get_devices(&devlist, SANE_FALSE /* local and network devices */);
+
+ /* if devicename is given try to identify it, if not found, open device list */
+ if (optind < argc)
+ {
+ int ndevs;
+
+ for (ndevs = 0; devlist[ndevs]; ++ndevs)
+ {
+ if (!strncmp(devlist[ndevs]->name, argv[argc - 1], strlen(argv[argc - 1])))
+ {
+ seldev = ndevs;
+ break;
+ }
+ }
+
+ if ((seldev < 0) && (argc > 1))
+ {
+ static SANE_Device dev;
+ static const SANE_Device *device_list[] = { &dev, 0 };
+
+ memset(&dev, 0, sizeof(dev));
+ dev.name = argv[argc - 1];
+ dev.vendor = TEXT_UNKNOWN;
+ dev.type = TEXT_UNKNOWN;
+ dev.model = TEXT_UNKNOWN;
+
+ devlist = device_list;
+ seldev = 0;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_interface(int argc, char **argv)
+{
+ xsane.info_label = 0;
+
+ xsane_init(argc, argv); /* initialize xsane variables if command line option is given, set seldev */
+
+ for (ndevs = 0; devlist[ndevs]; ++ndevs); /* count available devices */
+
+ if (seldev >= 0) /* device name is given on cammand line */
+ {
+ xsane_device_dialog(); /* open device seldev */
+
+ if (!dialog)
+ {
+ xsane_exit();
+ }
+ }
+ else /* no device name given on command line */
+ {
+ if (ndevs > 0) /* devices available */
+ {
+ seldev = 0;
+ if (ndevs == 1)
+ {
+ xsane_device_dialog(); /* open device seldev */
+ if (!dialog)
+ {
+ xsane_exit();
+ }
+ }
+ else
+ {
+ xsane_choose_device(); /* open device selection window and get device */
+ }
+ }
+ else /* ndevs == 0, no devices available */
+ {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%s: %s\n", prog_name, ERR_NO_DEVICES);
+ xsane_back_gtk_error(buf, TRUE);
+ xsane_exit();
+ }
+ }
+
+ gtk_main();
+ sane_exit();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+int main(int argc, char **argv)
+{
+ dialog = 0;
+ memset(&xsane, 0, sizeof(xsane)); /* set all values in xsane to 0 */
+
+ umask(XSANE_DEFAULT_UMASK); /* define permissions of new files */
+
+ xsane.sensitivity = TRUE;
+
+ xsane.main_window_fixed = -1; /* no command line option given, use preferences or fixed */
+
+ xsane.mode = XSANE_STANDALONE;
+ xsane.xsane_mode = XSANE_SCAN;
+ xsane.xsane_output_format = XSANE_PNM;
+ xsane.mode_selection = 1; /* enable selection of xsane mode */
+
+ xsane.input_tag = -1; /* no input tag */
+
+ xsane.histogram_lines = 1;
+
+ xsane.zoom = 1.0;
+ xsane.zoom_x = 1.0;
+ xsane.zoom_y = 1.0;
+ xsane.resolution = 0.0;
+ xsane.resolution_x = 0.0;
+ xsane.resolution_y = 0.0;
+ xsane.copy_number = 1;
+
+ xsane.gamma = 1.0;
+ xsane.gamma_red = 1.0;
+ xsane.gamma_green = 1.0;
+ xsane.gamma_blue = 1.0;
+ xsane.brightness = 0.0;
+ xsane.brightness_red = 0.0;
+ xsane.brightness_green = 0.0;
+ xsane.brightness_blue = 0.0;
+ xsane.contrast = 0.0;
+ xsane.contrast_red = 0.0;
+ xsane.contrast_green = 0.0;
+ xsane.contrast_blue = 0.0;
+ xsane.slider_gray.value[2] = 100.0;
+ xsane.slider_gray.value[1] = 50.0;
+ xsane.slider_gray.value[0] = 0.0;
+ xsane.slider_red.value[2] = 100.0;
+ xsane.slider_red.value[1] = 50.0;
+ xsane.slider_red.value[0] = 0.0;
+ xsane.slider_green.value[2] = 100.0;
+ xsane.slider_green.value[1] = 50.0;
+ xsane.slider_green.value[0] = 0.0;
+ xsane.slider_blue.value[2] = 100.0;
+ xsane.slider_blue.value[1] = 50.0;
+ xsane.slider_blue.value[0] = 0.0;
+ xsane.auto_white = 100.0;
+ xsane.auto_gray = 50.0;
+ xsane.auto_black = 0.0;
+
+ xsane.histogram_red = 1;
+ xsane.histogram_green = 1;
+ xsane.histogram_blue = 1;
+ xsane.histogram_int = 1;
+ xsane.histogram_log = 1;
+
+ xsane.xsane_color = TRUE;
+ xsane.scanner_gamma_color = FALSE;
+ xsane.scanner_gamma_gray = FALSE;
+ xsane.enhancement_rgb_default = TRUE;
+
+ prog_name = strrchr(argv[0], '/');
+ if (prog_name)
+ {
+ ++prog_name;
+ }
+ else
+ {
+ prog_name = argv[0];
+ }
+#if 0
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+ bindtextdomain(prog_name, LOCALEDIR);
+ textdomain(prog_name);
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+ {
+ GPrintFunc old_print_func;
+ int result;
+
+ /* Temporarily install a print function that discards all output.
+ This is to avoid annoying "you must run this program under
+ gimp" messages when xsane gets invoked in stand-alone
+ mode. */
+ old_print_func = g_set_print_handler((GPrintFunc) null_print_func);
+
+ /* gimp_main() returns 1 if xsane wasn't invoked by GIMP */
+ result = gimp_main(argc, argv);
+ g_set_message_handler(old_print_func);
+ if (result)
+ {
+ xsane_interface(argc, argv);
+ }
+ }
+#else
+ xsane_interface(argc, argv);
+#endif
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
diff --git a/frontend/xsane.h b/frontend/xsane.h
new file mode 100644
index 0000000..69e6d2c
--- /dev/null
+++ b/frontend/xsane.h
@@ -0,0 +1,513 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane.h
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifndef XSANE_H
+#define XSANE_H
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* #define XSANE_TEST */
+/* #define SUPPORT_RGBA */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#define XSANE_VERSION "0.50"
+#define XSANE_AUTHOR "Oliver Rauch"
+#define XSANE_COPYRIGHT "Oliver Rauch"
+#define XSANE_DATE "1998-2000"
+#define XSANE_EMAIL "Oliver.Rauch@Wolfsburg.DE"
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#define PATH_SANE_TMP "/tmp"
+// #define XSANE_DEFAULT_UMASK 0157
+#define XSANE_DEFAULT_UMASK 0007
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* needed for most of the xsane sources: */
+
+#ifdef _AIX
+# include <lalloca.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <memory.h>
+#include <pwd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <getopt.h>
+#include <time.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <sane/sane.h>
+#include <sane/saneopts.h>
+
+#include "sane/config.h"
+#include "sane/sanei_signal.h"
+
+#include "xsane-text.h"
+#include "xsane-icons.h"
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(String) gettext (String)
+# ifdef gettext_noop
+# define N_(String) gettext_noop (String)
+# else
+# define N_(String) (String)
+# endif
+#else
+/* Stubs that do something close enough. */
+# define textdomain(String) (String)
+# define gettext(String) (String)
+# define dgettext(Domain,Message) (Message)
+# define dcgettext(Domain,Message,Type) (Message)
+# define bindtextdomain(Domain,Directory) (Domain)
+# define _(String) (String)
+# define N_(String) (String)
+#endif
+
+/* ----------------------------- */
+
+/* needed for xsane.h */
+#include "xsane-back-gtk.h"
+#include "xsane-preferences.h"
+#include "xsane-preview.h"
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+# include <libgimp/gimp.h>
+
+# ifdef HAVE_LIBGIMP_GIMPFEATURES_H
+# include <libgimp/gimpfeatures.h>
+# else
+# define GIMP_CHECK_VERSION(major, minor, micro) 0
+# endif /* HAVE_LIBGIMP_GIMPFEATURES_H */
+
+#endif /* HAVE_LIBGIMP_GIMP_H */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+enum { XSANE_SCAN, XSANE_COPY, XSANE_FAX };
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+extern void xsane_pref_save(void);
+extern void xsane_interface(int argc, char **argv);
+extern void xsane_fax_project_save(void);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+extern const char *prog_name;
+extern const char *device_text;
+extern GtkWidget *choose_device_dialog;
+extern GSGDialog *dialog;
+extern const SANE_Device **devlist;
+extern gint seldev; /* The selected device */
+extern gint ndevs; /* The number of available devices */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+extern int xsane_scanmode_number[];
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#define OUTFILENAME "out.pnm"
+#define FAXPROJECT "faxproject"
+#define FAXFILENAME "page-001.fax"
+#define PRINTERNAME "new printer"
+#define PRINTERCOMMAND "lpr -"
+#define PRINTERCOPYNUMBEROPTION "-#"
+#define FAXCOMMAND "sendfax"
+#define FAXRECEIVEROPT "-d"
+#define FAXPOSTSCRIPTOPT ""
+#define FAXNORMALOPT "-l"
+#define FAXFINEOPT "-m"
+#define FAXVIEWER "xv"
+#define DOCVIEWERNETSCAPEREMOTE "netscape-remote"
+#define DOCVIEWER DOCVIEWERNETSCAPEREMOTE
+
+#define XSANE_BRIGHTNESS_MIN -400.0
+#define XSANE_BRIGHTNESS_MAX 400.0
+#define XSANE_CONTRAST_GRAY_MIN -100.0
+#define XSANE_CONTRAST_MIN -400.0
+#define XSANE_CONTRAST_MAX 400.0
+#define XSANE_GAMMA_MIN 0.3
+#define XSANE_GAMMA_MAX 3.0
+
+#define HIST_WIDTH 256
+#define HIST_HEIGHT 100
+#define XSANE_SHELL_WIDTH 296
+#define XSANE_SHELL_HEIGHT 451
+#define XSANE_SHELL_POS_X 1
+#define XSANE_SHELL_POS_Y 50
+#define XSANE_HISTOGRAM_POS_X 280
+#define XSANE_HISTOGRAM_POS_Y 50
+#define XSANE_STD_OPTIONS_POS_X 1
+#define XSANE_STD_OPTIONS_POS_Y 400
+#define XSANE_ADV_OPTIONS_POS_X 280
+#define XSANE_ADV_OPTIONS_POS_Y 420
+#define XSANE_PREVIEW_POS_X 560
+#define XSANE_PREVIEW_POS_Y 50
+#define XSANE_PREVIEW_WIDTH 100
+#define XSANE_PREVIEW_HEIGHT 100
+
+#define XSANE_SLIDER_ACTIVE 0
+#define XSANE_SLIDER_INACTIVE 4
+#define XSANE_SLIDER_WIDTH 260
+#define XSANE_SLIDER_HEIGHT 10
+#define XSANE_SLIDER_OFFSET 2
+#define XSANE_SLIDER_EVENTS GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | \
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | \
+ GDK_BUTTON1_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK
+#define INF 5.0e9
+#define MM_PER_INCH 25.4
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#ifndef SANE_NAME_DOCUMENT_FEEDER
+#define SANE_NAME_DOCUMENT_FEEDER "Automatic Document Feeder"
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#define STRINGIFY1(x) #x
+#define STRINGIFY(x) STRINGIFY1(x)
+
+#define NELEMS(a) ((int)(sizeof (a) / sizeof (a[0])))
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+enum
+{
+ XSANE_UNKNOWN, XSANE_PNM, XSANE_JPEG, XSANE_PNG, XSANE_PS, XSANE_TIFF, XSANE_RGBA,
+ XSANE_RAW16, XSANE_PNM16
+};
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+enum
+{
+ XSANE_STANDALONE, XSANE_GIMP_EXTENSION
+};
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+typedef struct XsaneProgress_t
+{
+ GtkSignalFunc callback;
+ gpointer callback_data;
+ GtkWidget *shell;
+ GtkWidget *pbar;
+} XsaneProgress_t;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+typedef struct XsanePixmap
+{
+ GtkWidget *frame;
+ GdkPixmap *pixmap;
+ GtkWidget *pixmapwid;
+} XsanePixmap;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+typedef struct XsaneSlider
+{
+ int position[3];
+ double value[3];
+ double min, max;
+ int active;
+ GtkWidget *preview;
+ int r, g, b;
+} XsaneSlider;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+typedef struct Xsane
+{
+ SANE_Int sane_backend_versioncode;
+ char *backend;
+ char *device_set_filename;
+ char *filetype;
+ char *output_filename;
+ char *dummy_filename;
+
+ SANE_Int sensitivity;
+
+ /* dialogs */
+ GtkWidget *shell;
+ GtkWidget *standard_options_shell;
+ GtkWidget *advanced_options_shell;
+ GtkWidget *main_dialog_scrolled;
+ GtkWidget *histogram_dialog;
+ GtkWidget *fax_dialog;
+ GtkWidget *fax_list;
+
+ GtkWidget *fax_project_box;
+ GtkWidget *fax_project_exists;
+ GtkWidget *fax_project_not_exists;
+
+ GdkPixmap *window_icon_pixmap;
+ GdkBitmap *window_icon_mask;
+
+ /* window position and geometry */
+ SANE_Int shell_posx;
+ SANE_Int shell_posy;
+ SANE_Int shell_height;
+ SANE_Int shell_width;
+ SANE_Int standard_options_shell_posx;
+ SANE_Int standard_options_shell_posy;
+ SANE_Int advanced_options_shell_posx;
+ SANE_Int advanced_options_shell_posy;
+ SANE_Int histogram_dialog_posx;
+ SANE_Int histogram_dialog_posy;
+ SANE_Int preview_dialog_posx;
+ SANE_Int preview_dialog_posy;
+ SANE_Int preview_dialog_width;
+ SANE_Int preview_dialog_height;
+
+ GtkWidget *hruler;
+ GtkWidget *vruler;
+ GtkWidget *info_label;
+ GtkObject *start_button;
+ Preview *preview;
+ gint32 mode;
+
+ int main_window_fixed;
+ int mode_selection;
+
+ /* various scanning related state: */
+ size_t num_bytes;
+ size_t bytes_read;
+ XsaneProgress_t *progress;
+ int input_tag;
+ SANE_Parameters param;
+ int x, y;
+
+ /* for standalone mode: */
+ GtkWidget *filename_entry;
+ GtkWidget *fax_project_entry;
+ GtkWidget *fax_receiver_entry;
+ GtkWidget *filetype_option_menu;
+ FILE *out;
+ int xsane_mode;
+ int xsane_output_format;
+ long header_size;
+
+ /* histogram window */
+ struct XsanePixmap histogram_raw;
+ struct XsanePixmap histogram_enh;
+
+ struct XsaneSlider slider_gray;
+ struct XsaneSlider slider_red;
+ struct XsaneSlider slider_green;
+ struct XsaneSlider slider_blue;
+
+ int negative;
+ double gamma;
+ double gamma_red;
+ double gamma_green;
+ double gamma_blue;
+ double brightness;
+ double brightness_red;
+ double brightness_green;
+ double brightness_blue;
+ double contrast;
+ double contrast_red;
+ double contrast_green;
+ double contrast_blue;
+
+ double auto_white;
+ double auto_gray;
+ double auto_black;
+ double auto_white_red;
+ double auto_gray_red;
+ double auto_black_red;
+ double auto_white_green;
+ double auto_gray_green;
+ double auto_black_green;
+ double auto_white_blue;
+ double auto_gray_blue;
+ double auto_black_blue;
+
+ int histogram_red;
+ int histogram_green;
+ int histogram_blue;
+ int histogram_int;
+ int histogram_lines;
+ int histogram_log;
+
+ /* colors */
+ GdkGC *gc_red;
+ GdkGC *gc_green;
+ GdkGC *gc_blue;
+ GdkGC *gc_black;
+ GdkGC *gc_trans;
+ GdkGC *gc_backg;
+ GdkColor *bg_trans;
+
+ int copy_number;
+ double zoom;
+ double zoom_x;
+ double zoom_y;
+ double resolution;
+ double resolution_x;
+ double resolution_y;
+
+ GtkWidget *length_unit_widget;
+ GtkWidget *length_unit_mm;
+ GtkWidget *length_unit_cm;
+ GtkWidget *length_unit_in;
+ GtkWidget *update_policy_continu;
+ GtkWidget *update_policy_discont;
+ GtkWidget *update_policy_delayed;
+ GtkWidget *show_preview_widget;
+ GtkWidget *show_histogram_widget;
+ GtkWidget *show_standard_options_widget;
+ GtkWidget *show_advanced_options_widget;
+ GtkWidget *show_resolution_list_widget;
+ GtkObject *zoom_widget;
+ GtkObject *gamma_widget;
+ GtkObject *gamma_red_widget;
+ GtkObject *gamma_green_widget;
+ GtkObject *gamma_blue_widget;
+ GtkObject *brightness_widget;
+ GtkObject *brightness_red_widget;
+ GtkObject *brightness_green_widget;
+ GtkObject *brightness_blue_widget;
+ GtkObject *contrast_widget;
+ GtkObject *contrast_red_widget;
+ GtkObject *contrast_green_widget;
+ GtkObject *contrast_blue_widget;
+
+ SANE_Int xsane_color;
+ SANE_Bool show_preview;
+ SANE_Bool scanner_gamma_color;
+ SANE_Bool scanner_gamma_gray;
+ SANE_Bool enhancement_rgb_default;
+
+ SANE_Bool fax_fine_mode;
+
+ GtkWidget *outputfilename_entry;
+ GtkWidget *copy_number_entry;
+
+ SANE_Int *gamma_data, *gamma_data_red, *gamma_data_green, *gamma_data_blue;
+ SANE_Int *preview_gamma_data_red, *preview_gamma_data_green, *preview_gamma_data_blue;
+ SANE_Int *histogram_gamma_data_red, *histogram_gamma_data_green, *histogram_gamma_data_blue;
+
+ char *fax_filename;
+ char *fax_receiver;
+
+ int block_update_param;
+
+ int broken_pipe; /* for printercommand pipe */
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+ /* for GIMP mode: */
+ gint32 image_ID;
+ GDrawable *drawable;
+ guchar *tile;
+ unsigned tile_offset;
+ GPixelRgn region;
+ int first_frame; /* used for RED/GREEN/BLUE frames */
+#endif
+} Xsane;
+
+extern struct Xsane xsane;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+typedef struct XsaneSetup
+{
+ GtkWidget *printer_name_entry;
+ GtkWidget *printer_command_entry;
+ GtkWidget *printer_copy_number_option_entry;
+ GtkWidget *printer_resolution_entry;
+ GtkWidget *printer_leftoffset_entry;
+ GtkWidget *printer_bottomoffset_entry;
+ GtkWidget *printer_gamma_entry;
+ GtkWidget *printer_gamma_red_entry;
+ GtkWidget *printer_gamma_green_entry;
+ GtkWidget *printer_gamma_blue_entry;
+ GtkWidget *printer_width_entry;
+ GtkWidget *printer_height_entry;
+
+ GtkWidget *jpeg_image_quality_scale;
+ GtkWidget *pnm_image_compression_scale;
+ GtkWidget *overwrite_warning_button;
+ GtkWidget *increase_filename_counter_button;
+ GtkWidget *skip_existing_numbers_button;
+
+ GtkWidget *main_window_fixed_button;
+
+ GtkWidget *preview_gamma_entry;
+ GtkWidget *preview_gamma_red_entry;
+ GtkWidget *preview_gamma_green_entry;
+ GtkWidget *preview_gamma_blue_entry;
+ GtkWidget *preview_preserve_button;
+ GtkWidget *preview_own_cmap_button;
+ GtkWidget *doc_viewer_entry;
+
+ GtkWidget *fax_command_entry;
+ GtkWidget *fax_receiver_option_entry;
+ GtkWidget *fax_postscript_option_entry;
+ GtkWidget *fax_normal_option_entry;
+ GtkWidget *fax_fine_option_entry;
+ GtkWidget *fax_viewer_entry;
+ GtkWidget *fax_width_entry;
+ GtkWidget *fax_leftoffset_entry;
+ GtkWidget *fax_bottomoffset_entry;
+ GtkWidget *fax_height_entry;
+
+ int tiff_compression_nr;
+ int tiff_compression_1_nr;
+
+ int image_permissions;
+ int directory_permissions;
+
+} XsaneSetup;
+
+extern struct XsaneSetup xsane_setup;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#endif