From 2d113e8792747151bf5d830f1a1485f2f951f940 Mon Sep 17 00:00:00 2001 From: Mattia Rizzolo Date: Fri, 3 Oct 2014 14:04:58 +0000 Subject: Imported Upstream version 0.50 --- frontend/Makefile.in | 85 + frontend/cursor/cursor_pipette_black | 8 + frontend/cursor/cursor_pipette_gray | 8 + frontend/cursor/cursor_pipette_mask | 8 + frontend/cursor/cursor_pipette_white | 8 + frontend/xsane-back-gtk.c | 1426 +++++++++ frontend/xsane-back-gtk.h | 188 ++ frontend/xsane-device-preferences.c | 699 +++++ frontend/xsane-device-preferences.c.old | 761 +++++ frontend/xsane-device-preferences.h | 42 + frontend/xsane-front-gtk.c | 798 +++++ frontend/xsane-front-gtk.h | 73 + frontend/xsane-gamma.c | 1541 ++++++++++ frontend/xsane-gamma.h | 48 + frontend/xsane-icons.c | 1751 +++++++++++ frontend/xsane-icons.h | 87 + frontend/xsane-logo.xpm | 372 +++ frontend/xsane-preferences.c | 292 ++ frontend/xsane-preferences.h | 122 + frontend/xsane-preview.c | 3257 +++++++++++++++++++++ frontend/xsane-preview.h | 178 ++ frontend/xsane-rc-io.c | 903 ++++++ frontend/xsane-rc-io.h | 123 + frontend/xsane-save.c | 975 +++++++ frontend/xsane-save.h | 79 + frontend/xsane-scan.c | 2369 +++++++++++++++ frontend/xsane-scan.h | 35 + frontend/xsane-setup.c | 1565 ++++++++++ frontend/xsane-setup.h | 33 + frontend/xsane-style.rc | 34 + frontend/xsane-text.h | 417 +++ frontend/xsane.c | 4794 +++++++++++++++++++++++++++++++ frontend/xsane.h | 513 ++++ 33 files changed, 23592 insertions(+) create mode 100644 frontend/Makefile.in create mode 100644 frontend/cursor/cursor_pipette_black create mode 100644 frontend/cursor/cursor_pipette_gray create mode 100644 frontend/cursor/cursor_pipette_mask create mode 100644 frontend/cursor/cursor_pipette_white create mode 100644 frontend/xsane-back-gtk.c create mode 100644 frontend/xsane-back-gtk.h create mode 100644 frontend/xsane-device-preferences.c create mode 100644 frontend/xsane-device-preferences.c.old create mode 100644 frontend/xsane-device-preferences.h create mode 100644 frontend/xsane-front-gtk.c create mode 100644 frontend/xsane-front-gtk.h create mode 100644 frontend/xsane-gamma.c create mode 100644 frontend/xsane-gamma.h create mode 100644 frontend/xsane-icons.c create mode 100644 frontend/xsane-icons.h create mode 100644 frontend/xsane-logo.xpm create mode 100644 frontend/xsane-preferences.c create mode 100644 frontend/xsane-preferences.h create mode 100644 frontend/xsane-preview.c create mode 100644 frontend/xsane-preview.h create mode 100644 frontend/xsane-rc-io.c create mode 100644 frontend/xsane-rc-io.h create mode 100644 frontend/xsane-save.c create mode 100644 frontend/xsane-save.h create mode 100644 frontend/xsane-scan.c create mode 100644 frontend/xsane-scan.h create mode 100644 frontend/xsane-setup.c create mode 100644 frontend/xsane-setup.h create mode 100644 frontend/xsane-style.rc create mode 100644 frontend/xsane-text.h create mode 100644 frontend/xsane.c create mode 100644 frontend/xsane.h (limited to 'frontend') 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 + 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 + 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 + +#include + +#include +#include + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +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 + 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 + 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 +#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 + 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 +#include +#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 + 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 +#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 + 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 +#include +#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; yposition[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; ipreview),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 + 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 + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +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 + 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 + 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:aaaC>DDa%DDa$%:Da%nnn:DD%DD::n,xh;uhGSxm2ogbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbuvv#v#u=u=u-u=;=;h;;h=;;=u;Osxm2o+Mbbbbbbbbb", +"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS06jv#GmiAggqn::DD%%DD::D::nn:C1da>r3a:<<%D:::D%DDaN>N>N<7=#-#h9xBiMbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbv0j###=;;===h=hhh;h;=;OOhhhO9&s2i+Agbbbbbbb", +"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcvvv#=miggVDDDDDDD::D::DDDDa$r1$$$<5jjpjXj61X1aC51<%::D%%DaNCCC><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$N186j66@Xjjjjj6j8@jXrN>1C$5zz5dCNaa%DDD::9vf#-OtBiMbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#vv00vu;hOO;O9G=;=uhOh;;hO;;&xSx2iMgbbbbbbb", +"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbm=h&xxbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;v00#=xigglDDD::n:n:D%<3wwwX6jjjjjjjjjjjjjj6jjjjj6pd>NN<13X@6jjjjjjjjjjjjjjjjjjjjj66 z51$><>N96v0#=9sxibbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb00v#v--f;;;;;;;Ohh;;h;=u=u=;uuG&SstBAggbbbbbb", +"bbbbbbbbbbbbbbbbbbbbbbbbbbbbb#0vff##vfh&m2iMgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbff-u&mBAgq:::::DDDDD%%a<>158w66jjjjjjjjjjjjjjjjjjjj@ pzrd$CC>>>NNN<<<$53p @66666jjjjjjjjjjjjjjjjjX8w8z5d$CCN>C>>>N<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$53d%:D%<DaNC$<:n:%aaa%:::Z000#;xiAggbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#j000v#fuuuu;OO;;hhu==u;u===h=;u;G&G;O&xtiAggbbbbbbbb", +"bbbbbbbbbbbbbb6vvvvvvu==;;&G&xtiAgbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-#v-fc42Bo*::D%%a%%::%%::%N1a138 @8%:D:N33%Da5$DD%aa%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%%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:<$1>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%aaNDnnnnn:DaN + 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 + 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 +#include + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +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 + 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 */ +#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 + 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 + +#include +#include + +#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 + 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 /* MUST come first for AIX! */ +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBC_H +# include /* NeXTStep/OpenStep */ +#endif + +#include +#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 + 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 + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#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 + 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 +#endif + +#ifdef HAVE_LIBPNG +#ifdef HAVE_LIBZ +#include +#include +#endif +#endif + +#ifdef HAVE_LIBTIFF +#include +#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=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=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>= 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= 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= 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 + 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 + 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 +#include +#endif +#endif + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_LIBGIMP_GIMP_H + +#include + +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 + 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 + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +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 + 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 +#include +#endif +#endif + +#ifdef HAVE_LIBTIFF +#include +#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 + 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 + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +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 [= ] +# { +#