aboutsummaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2023-01-06 10:02:49 +0200
committerIgor Pashev <pashev.igor@gmail.com>2023-01-06 10:04:59 +0200
commit1145733c29db0a678537ce99ff60e21613f622a8 (patch)
tree63d5d6c324629d4eef1354db3c97f857d6016a34 /frontend
downloadiscan-1145733c29db0a678537ce99ff60e21613f622a8.tar.gz
Import iscan 2.30.4-2
Diffstat (limited to 'frontend')
-rw-r--r--frontend/Makefile.am102
-rw-r--r--frontend/esmod-wrapper.hh282
-rw-r--r--frontend/file-selector.cc1137
-rw-r--r--frontend/file-selector.h135
-rw-r--r--frontend/gimp-plugin.h186
-rw-r--r--frontend/pisa_aleart_dialog.cc144
-rw-r--r--frontend/pisa_aleart_dialog.h52
-rw-r--r--frontend/pisa_change_unit.cc101
-rw-r--r--frontend/pisa_change_unit.h50
-rw-r--r--frontend/pisa_configuration.cc223
-rw-r--r--frontend/pisa_configuration.h63
-rw-r--r--frontend/pisa_default_val.h89
-rw-r--r--frontend/pisa_enums.h248
-rw-r--r--frontend/pisa_error.cc178
-rw-r--r--frontend/pisa_error.h104
-rw-r--r--frontend/pisa_esmod_structs.h75
-rw-r--r--frontend/pisa_gamma_correction.cc718
-rw-r--r--frontend/pisa_gamma_correction.h85
-rw-r--r--frontend/pisa_gimp.cc665
-rw-r--r--frontend/pisa_gimp.h109
-rw-r--r--frontend/pisa_gimp_1_0_patch.h58
-rw-r--r--frontend/pisa_image_controls.cc381
-rw-r--r--frontend/pisa_image_controls.h50
-rw-r--r--frontend/pisa_img_converter.cc99
-rw-r--r--frontend/pisa_img_converter.h45
-rw-r--r--frontend/pisa_main.cc116
-rw-r--r--frontend/pisa_main.h39
-rw-r--r--frontend/pisa_main_window.cc1746
-rw-r--r--frontend/pisa_main_window.h112
-rw-r--r--frontend/pisa_marquee.cc117
-rw-r--r--frontend/pisa_marquee.h83
-rw-r--r--frontend/pisa_preference.cc351
-rw-r--r--frontend/pisa_preference.h68
-rw-r--r--frontend/pisa_preview_window.cc1844
-rw-r--r--frontend/pisa_preview_window.h171
-rw-r--r--frontend/pisa_progress_window.cc263
-rw-r--r--frontend/pisa_progress_window.h118
-rw-r--r--frontend/pisa_sane_scan.cc1214
-rw-r--r--frontend/pisa_sane_scan.h172
-rw-r--r--frontend/pisa_scan_manager.cc1003
-rw-r--r--frontend/pisa_scan_manager.h151
-rw-r--r--frontend/pisa_scan_selector.cc503
-rw-r--r--frontend/pisa_scan_selector.h72
-rw-r--r--frontend/pisa_scan_tool.cc591
-rw-r--r--frontend/pisa_scan_tool.h63
-rw-r--r--frontend/pisa_settings.cc149
-rw-r--r--frontend/pisa_settings.h75
-rw-r--r--frontend/pisa_structs.h226
-rw-r--r--frontend/pisa_tool.cc184
-rw-r--r--frontend/pisa_tool.h79
-rw-r--r--frontend/pisa_view_manager.cc1134
-rw-r--r--frontend/pisa_view_manager.h227
-rw-r--r--frontend/xpm_data.cc1740
-rw-r--r--frontend/xpm_data.h48
54 files changed, 18038 insertions, 0 deletions
diff --git a/frontend/Makefile.am b/frontend/Makefile.am
new file mode 100644
index 0000000..6d7c132
--- /dev/null
+++ b/frontend/Makefile.am
@@ -0,0 +1,102 @@
+## Makefile.am -- an automake template for a Makefile.in file
+## Copyright (C) 2004, 2005, 2007, 2008 Olaf Meeuwissen
+##
+## This file is part of the "Image Scan!" build infra-structure.
+##
+## The "Image Scan!" build infra-structure 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 FITNESS
+## FOR A PARTICULAR PURPOSE or MERCHANTABILITY.
+## See the GNU General Public License for more details.
+##
+## You should have received a verbatim copy of the GNU General Public
+## License along with this program; if not, write to:
+##
+## Free Software Foundation, Inc.
+## 59 Temple Place, Suite 330
+## Boston, MA 02111-1307 USA
+
+if ENABLE_FRONTEND
+bin_PROGRAMS = iscan
+iscan_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/lib \
+ -I$(top_srcdir)/non-free \
+ -DLOCALEDIR=\"$(datadir)/locale\"
+iscan_CXXFLAGS = \
+ @GTK_CFLAGS@ \
+ @GIMP_CFLAGS@ \
+ @GDK_IMLIB_CFLAGS@
+iscan_LDADD = \
+ $(top_builddir)/lib/libimage-stream.la \
+ -lsane \
+ @LIBLTDL@ \
+ @GTK_LIBS@ \
+ @GDK_IMLIB_LIBS@ \
+ $(top_builddir)/non-free/libesmod.so
+iscan_SOURCES = \
+ $(iscan_source_files)
+endif
+
+iscan_source_files = \
+ esmod-wrapper.hh \
+ file-selector.cc \
+ file-selector.h \
+ gimp-plugin.h \
+ pisa_aleart_dialog.cc \
+ pisa_aleart_dialog.h \
+ pisa_change_unit.cc \
+ pisa_change_unit.h \
+ pisa_configuration.cc \
+ pisa_configuration.h \
+ pisa_default_val.h \
+ pisa_enums.h \
+ pisa_error.cc \
+ pisa_error.h \
+ pisa_esmod_structs.h \
+ pisa_gamma_correction.cc \
+ pisa_gamma_correction.h \
+ pisa_gimp.cc \
+ pisa_gimp.h \
+ pisa_gimp_1_0_patch.h \
+ pisa_image_controls.cc \
+ pisa_image_controls.h \
+ pisa_img_converter.cc \
+ pisa_img_converter.h \
+ pisa_main.cc \
+ pisa_main.h \
+ pisa_main_window.cc \
+ pisa_main_window.h \
+ pisa_marquee.cc \
+ pisa_marquee.h \
+ pisa_preference.cc \
+ pisa_preference.h \
+ pisa_preview_window.cc \
+ pisa_preview_window.h \
+ pisa_progress_window.cc \
+ pisa_progress_window.h \
+ pisa_sane_scan.cc \
+ pisa_sane_scan.h \
+ pisa_scan_manager.cc \
+ pisa_scan_manager.h \
+ pisa_scan_selector.cc \
+ pisa_scan_selector.h \
+ pisa_scan_tool.cc \
+ pisa_scan_tool.h \
+ pisa_settings.cc \
+ pisa_settings.h \
+ pisa_structs.h \
+ pisa_tool.cc \
+ pisa_tool.h \
+ pisa_view_manager.cc \
+ pisa_view_manager.h \
+ xpm_data.cc \
+ xpm_data.h
+
+
+EXTRA_DIST = \
+ $(iscan_source_files)
diff --git a/frontend/esmod-wrapper.hh b/frontend/esmod-wrapper.hh
new file mode 100644
index 0000000..b15ed3a
--- /dev/null
+++ b/frontend/esmod-wrapper.hh
@@ -0,0 +1,282 @@
+// esmod-wrapper.hh --
+// Copyright (C) 2008 SEIKO EPSON CORPORATION
+//
+// This file is part of the 'iscan' program.
+//
+// The 'iscan' program is free-ish 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 FITNESS
+// FOR A PARTICULAR PURPOSE or MERCHANTABILITY.
+// See the GNU General Public License for more details.
+//
+// You should have received a verbatim copy of the GNU General Public
+// License along with this program; if not, write to:
+//
+// Free Software Foundation, Inc.
+// 59 Temple Place, Suite 330
+// Boston, MA 02111-1307 USA
+//
+
+// As a special exception, the copyright holders give permission
+// to link the code of this program with the esmod library and
+// distribute linked combinations including the two. You must obey
+// the GNU General Public License in all respects for all of the
+// code used other then esmod.
+
+#ifndef esmod_wrapper_hh_included
+#define esmod_wrapper_hh_included
+
+#ifndef __cplusplus
+#error "This is a C++ header file; use a C++ compiler to compile it."
+#endif
+
+#include "esmod.hh"
+
+#define ISCAN_DEFAULT_GAMMA ESMOD_DEFAULT_GAMMA
+#define ISCAN_DEFAULT_HILITE ESMOD_DEFAULT_HILITE
+#define ISCAN_DEFAULT_SHADOW ESMOD_DEFAULT_SHADOW
+#define ISCAN_DEFAULT_THRESHOLD ESMOD_DEFAULT_THRESHOLD
+
+#include "pisa_enums.h"
+#include "pisa_structs.h"
+#include "pisa_esmod_structs.h"
+#include "pisa_marquee.h"
+#include "pisa_settings.h"
+
+namespace iscan
+{
+
+ class focus : public esmod::focus
+ {
+ public:
+ focus (const pisa_image_info& parms);
+ focus (struct sharp_img_info parms);
+ };
+
+ class moire : public esmod::moire
+ {
+ public:
+ moire (struct moire_img_info parms, bool is_dumb);
+ };
+
+ class scale : public esmod::scale
+ {
+ public:
+ scale (struct resize_img_info parms);
+ };
+
+ // WARNING: These quite likely modify global state in libesmod.
+ void auto_expose (int, int, const pisa_image_info&, const _rectL&,
+ marquee&, bool, bool is_dumb);
+ void build_LUT (int, int, const settings&, marquee&, bool is_dumb);
+
+ esmod::type_type esmod_film_type (int iscan_film_type);
+ esmod::type_type esmod_focus_type (int iscan_focus_type);
+ esmod::type_type esmod_image_type (int iscan_image_type);
+ esmod::type_type esmod_option_type (int iscan_option_type);
+ esmod::type_type esmod_pixel_type (int iscan_pixel_type);
+ esmod::type_type esmod_scale_type (int iscan_scale_type);
+
+} // namespace iscan
+
+inline
+iscan::focus::focus (const pisa_image_info& info)
+ : esmod::focus::focus (info.m_width, info.m_height, info.m_rowbytes,
+ info.m_bits_per_pixel)
+{
+}
+
+inline
+iscan::focus::focus (const struct sharp_img_info info)
+ : esmod::focus::focus (info.in_width, info.in_height, info.in_rowbytes,
+ info.out_width, info.out_height, info.out_rowbytes,
+ info.bits_per_pixel,
+ info.strength, info.radius, info.clipping,
+ esmod_focus_type (info.sharp_flag))
+{
+}
+
+inline
+iscan::moire::moire (const struct moire_img_info info, bool is_dumb)
+ : esmod::moire::moire (info.in_width, info.in_height, info.in_rowbytes,
+ info.out_width, info.out_height, info.out_rowbytes,
+ info.bits_per_pixel,
+ info.resolution, is_dumb)
+{
+}
+
+inline
+iscan::scale::scale (const struct resize_img_info info)
+ : esmod::scale::scale (info.in_width, info.in_height, info.in_rowbytes,
+ info.out_width, info.out_height, info.out_rowbytes,
+ info.bits_per_pixel,
+ esmod_scale_type (info.resize_flag))
+{
+}
+
+inline void
+iscan::auto_expose (int option_type, int film_type,
+ const pisa_image_info& info, const _rectL& r,
+ marquee& m, bool is_doc, bool is_dumb)
+{
+ esmod::auto_expose (esmod_option_type (option_type),
+ esmod_film_type (film_type), info.m_img,
+ info.m_width, info.m_height, info.m_rowbytes,
+ r.top, r.left, r.bottom, r.right,
+ &m.gamma, &m.highlight, &m.shadow, &m.graybalance,
+ m.film_gamma, m.film_yp, m.grayl,
+ is_doc, is_dumb);
+}
+
+inline void
+iscan::build_LUT (int option_type, int film_type, const settings& s,
+ marquee& m, bool is_dumb)
+{
+ esmod::build_LUT (esmod_option_type (option_type),
+ esmod_film_type (film_type),
+ esmod_pixel_type (s.imgtype.pixeltype),
+ esmod_image_type (s.imgtype.ae_option),
+ m.gamma, m.highlight, m.shadow, m.graybalance,
+ m.film_gamma, m.film_yp, m.grayl,
+ m.gamma_table[0], m.gamma_table[1], m.lut.gamma_r,
+ is_dumb);
+}
+
+inline esmod::type_type
+iscan::esmod_film_type (int iscan_film_type)
+{
+ esmod::type_type val;
+
+ switch (iscan_film_type)
+ {
+ case PISA_FT_POSI:
+ val = ESMOD_FILM_POSITIVE;
+ break;
+ case PISA_FT_NEGA:
+ val = ESMOD_FILM_NEGATIVE;
+ break;
+ case PISA_FT_REFLECT:
+ val = -1;
+ break;
+ default:
+ throw;
+ }
+ return val;
+}
+
+inline esmod::type_type
+iscan::esmod_focus_type (int iscan_focus_type)
+{
+ esmod::type_type val;
+
+ switch (iscan_focus_type)
+ {
+ case PISA_SH_UMASK:
+ val = ESMOD_FOCUS_UMASK;
+ break;
+ case PISA_SH_GAUSS:
+ val = ESMOD_FOCUS_GAUSS;
+ break;
+ case PISA_SH_UMASKY:
+ val = ESMOD_FOCUS_UMASK_Y;
+ break;
+ default:
+ throw;
+ }
+ return val;
+}
+
+inline esmod::type_type
+iscan::esmod_image_type (int iscan_image_type)
+{
+ esmod::type_type val;
+
+ switch (iscan_image_type)
+ {
+ case PISA_AE_PHOTO:
+ val = ESMOD_IMAGE_PHOTO;
+ break;
+ case PISA_AE_DOC:
+ val = ESMOD_IMAGE_DOCUMENT;
+ break;
+ case PISA_AE_GRAYED:
+ val = ESMOD_IMAGE_LINE_ART;
+ break;
+ default:
+ throw;
+ }
+ return val;
+}
+
+inline esmod::type_type
+iscan::esmod_option_type (int iscan_option_type)
+{
+ esmod::type_type val;
+
+ switch (iscan_option_type)
+ {
+ case PISA_OP_FLATBED:
+ val = ESMOD_OPTION_FLATBED;
+ break;
+ case PISA_OP_ADF:
+ case PISA_OP_ADFDPLX:
+ val = ESMOD_OPTION_ADF;
+ break;
+ case PISA_OP_TPU:
+ val = ESMOD_OPTION_TPU;
+ break;
+ default:
+ throw;
+ }
+ return val;
+}
+
+inline esmod::type_type
+iscan::esmod_pixel_type (int iscan_pixel_type)
+{
+ esmod::type_type val;
+
+ switch (iscan_pixel_type)
+ {
+ case PISA_PT_BW:
+ val = ESMOD_PIXEL_MONO;
+ break;
+ case PISA_PT_GRAY:
+ val = ESMOD_PIXEL_GRAY;
+ break;
+ case PISA_PT_RGB:
+ val = ESMOD_PIXEL_RGB;
+ break;
+ default:
+ throw;
+ }
+ return val;
+}
+
+inline esmod::type_type
+iscan::esmod_scale_type (int iscan_scale_type)
+{
+ esmod::type_type val;
+
+ switch (iscan_scale_type)
+ {
+ case PISA_RS_NN:
+ val = ESMOD_SCALE_NEAREST_NEIGHBOUR;
+ break;
+ case PISA_RS_BL:
+ val = ESMOD_SCALE_BILINEAR;
+ break;
+ case PISA_RS_BC:
+ val = ESMOD_SCALE_BICUBIC;
+ break;
+ default:
+ throw;
+ }
+ return val;
+}
+
+#endif /* !defined (esmod_wrapper_hh_included) */
diff --git a/frontend/file-selector.cc b/frontend/file-selector.cc
new file mode 100644
index 0000000..4848168
--- /dev/null
+++ b/frontend/file-selector.cc
@@ -0,0 +1,1137 @@
+/* file-selector.cc -- customized file selection dialog
+ Copyright (C) 2003, 2005, 2008 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "file-selector.h"
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+#include "pisa_aleart_dialog.h"
+#include "pisa_enums.h"
+#include "pisa_error.h"
+
+#include "../lib/pcxstream.hh"
+#include "../lib/pnmstream.hh"
+#include "../lib/pngstream.hh"
+#include "../lib/jpegstream.hh"
+#include "../lib/tiffstream.hh"
+#include "../lib/pdfstream.hh"
+
+#ifndef DEBUG
+#define g_print(...)
+#endif
+
+
+#ifdef DONT_HAVE_GTK_2
+#undef HAVE_GTK_2
+#endif
+
+#ifndef HAVE_GTK_2
+#define G_CALLBACK GTK_SIGNAL_FUNC
+#define g_signal_stop_emission_by_name gtk_signal_emit_stop_by_name
+#define g_signal_connect_swapped gtk_signal_connect_object
+#define g_signal_connect gtk_signal_connect
+#define GTK1_OBJ(obj) GTK_OBJECT (obj)
+GtkWidget * // "stolen" from GTK+2.0
+gtk_widget_get_parent (GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ return widget->parent;
+}
+#else
+#define GTK1_OBJ(obj) obj
+#endif
+
+struct menu_info // helper struct for callbacks
+{
+ file_selector *fs;
+ GtkWidget *widget[4]; // holding "ADF" spinboxes and labels
+ int index;
+};
+
+
+// callback implementations
+
+static void
+_destroy (file_selector *fs)
+{
+ g_print ("%s\n", __func__);
+
+ fs->destroy (true);
+}
+
+#if 0
+static void
+_click_ng( GtkWidget *, file_selector * )
+{
+ DBG_FS fprintf( stderr, "_click_ng\n" );
+
+ gtk_main_quit();
+}
+#endif
+
+static void
+_click_ok (file_selector *fs)
+{
+ g_print ("%s\n", __func__);
+
+ if (fs->save_pathname ())
+ {
+ fs->destroy ();
+ gtk_main_quit ();
+ }
+}
+
+// FIXME: this really should be using image stream capability API to
+// determine what is and what is not supported.
+static void
+_change_format (GtkWidget *, struct menu_info *id)
+{
+ id->fs->change_format( id->index ); // fs->get_type() below, requires this
+
+ // adjust "file with all pages" check box
+ bool make_active = (iscan::PDF == id->fs->get_type () ||
+ iscan::TIF == id->fs->get_type ());
+ gtk_widget_set_sensitive (id->widget[0], make_active);
+ if (!make_active) // disable multi-page mode when changing to a format that
+ // does not support it, do not do the opposite!
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (id->widget[0]), false);
+ }
+
+ // adjust "binding edge" selector sensitivity
+ // We only support this for file formats (i.e. `imgstream` classes)
+ // that support "easy" rotation. Yes, this is utterly flawed. The
+ // "binding edge" selector _should_ be in the main part of the GUI.
+ // It has no dependency on saving to file whatsoever, it is an issue
+ // that depends on device capabilities and document characteristics
+ // only.
+ make_active = (iscan::PDF == id->fs->get_type ());
+ gtk_widget_set_sensitive (id->widget[1], make_active);
+ gtk_widget_set_sensitive (id->widget[2], make_active);
+ gtk_widget_set_sensitive (id->widget[3], make_active);
+}
+
+static void
+_delete_text (GtkEditable *ed, gint start, gint end, file_selector *fs)
+{
+ g_print ("%s\n", __func__);
+
+#ifndef HAVE_GTK_2
+ gtk_signal_handler_block_by_func (GTK_OBJECT (ed),
+ GTK_SIGNAL_FUNC (_delete_text), fs);
+ fs->delete_text (start, end);
+ gtk_signal_handler_unblock_by_func (GTK_OBJECT (ed),
+ GTK_SIGNAL_FUNC (_delete_text), fs);
+#else
+ g_signal_handlers_block_by_func (ed, (gpointer) _delete_text, fs);
+ fs->delete_text (start, end);
+ g_signal_handlers_unblock_by_func (ed, (gpointer) _delete_text, fs);
+#endif
+
+ g_signal_stop_emission_by_name (GTK1_OBJ(ed), "delete-text");
+}
+
+static void
+_insert_text (GtkEditable *ed, gchar *text, gint len, gint *pos,
+ file_selector *fs)
+{
+ g_print ("%s\n", __func__);
+
+#ifndef HAVE_GTK_2
+ gtk_signal_handler_block_by_func (GTK_OBJECT (ed),
+ GTK_SIGNAL_FUNC ( _insert_text), fs);
+ fs->insert_text (text, len, pos);
+ gtk_signal_handler_unblock_by_func (GTK_OBJECT (ed),
+ GTK_SIGNAL_FUNC (_insert_text), fs);
+#else
+ g_signal_handlers_block_by_func (ed, (gpointer) _insert_text, fs);
+ fs->insert_text (text, len, pos);
+ g_signal_handlers_unblock_by_func (ed, (gpointer) _insert_text, fs);
+#endif
+
+ g_signal_stop_emission_by_name (GTK1_OBJ(ed), "insert-text");
+}
+
+static void
+_change_seqnum (GtkAdjustment *, file_selector *fs)
+{
+ fs->change_seqnum ();
+}
+
+static void
+_change_digits (GtkAdjustment *, file_selector *fs)
+{
+ fs->change_digits ();
+}
+
+static void
+_toggled_multi_page_mode_fs (GtkWidget *widget, file_selector *fs)
+{
+ fs->change_page_mode (gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (widget)));
+}
+
+static void
+_changed_binding_edge (GtkWidget *widget, file_selector *fs)
+{
+ fs->change_binding_edge (gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (widget)));
+}
+static void
+_toggled_multi_page_mode_widget (GtkWidget *widget, GtkWidget *p)
+{
+ bool on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ gtk_widget_set_sensitive (p, !on);
+}
+
+// class implementation
+
+using std::string;
+using std::stringstream;
+using iscan::file_opener;
+
+ // define static variables
+const file_selector::format
+file_selector::_format[] = {
+ { "PCX", 0, "pcx", "pcx", iscan::PCX },
+ { "PNM", 0, "pnm", "pnm", iscan::PNM },
+ { "PNG", 0, "png", "png", iscan::PNG },
+ { "JPEG", 0, "jpg", "jpg|jpeg", iscan::JPG },
+ { "TIFF", 0, "tiff", "tif|tiff", iscan::TIF },
+ { "PDF", 0, "pdf", "pdf", iscan::PDF },
+ { 0, 0, 0, 0, iscan::PNM } // array terminator
+};
+
+void
+file_selector::init ()
+{
+ g_print ("%s\n", __func__);
+
+ _widget = NULL;
+ _filename = NULL;
+ _pathname = NULL;
+
+ _using_adf = false;
+ _format_index = 0;
+ _deleted_all = false;
+ _multi_page_mode = false;
+ _has_left_edge_binding = true;
+
+ _number = -1;
+ _seqnum = NULL;
+ _digits = NULL;
+
+ int size = 1; // terminating { 0 } element
+ for (int i = 0; 0 != _format[i].name; ++i)
+ ++size;
+ _fmt = new const format * [size];
+ // okay, so we may waste a few
+
+ // Looks like the idea is to only add supported formats to _fmt and
+ // leave unsupported formats out.
+ int cnt = 0;
+ int len = 0;
+ for (int i = 0; i < size - 1; ++i)
+ {
+ _fmt[cnt] = &_format[i]; // FIXME: check support
+ len += strlen (_fmt[cnt]->rgx);
+ ++len; // for the '|'
+ ++cnt;
+ }
+ _fmt[cnt] = 0; // array terminator
+
+ _ext_regex = new char[len]; // overwrite last '|' with '\0'
+ char *pos = _ext_regex;
+ for (int i = 0; _fmt[i]; ++i)
+ {
+ strcpy (pos, _fmt[i]->rgx);
+ pos += strlen (_fmt[i]->rgx);
+ *pos++ = '|'; // overwrite '\0', then advance
+ }
+ *--pos = '\0'; // go back one, then terminate
+
+ g_print ("%s: ext_regex =`%s'\n", __func__, _ext_regex);
+}
+
+void
+file_selector::create_window (GtkWindow *window, GtkWidget *parent,
+ bool do_consecutive_scanning,
+ bool do_duplex,
+ const char* default_format)
+{
+ g_print ("%s: enter\n", __func__);
+
+ _parent = parent;
+ _using_adf = do_consecutive_scanning;
+ _do_duplex = do_duplex;
+
+ _widget = GTK_FILE_SELECTION (gtk_file_selection_new (PACKAGE));
+
+ if (!_widget)
+ throw pisa_error( PISA_STATUS_NO_MEM );
+
+ gtk_window_set_transient_for (GTK_WINDOW (_widget), window);
+ gtk_file_selection_hide_fileop_buttons (_widget);
+
+ gint index = 0;
+ if (is_supported (default_format))
+ index = get_index (default_format);
+ else if (is_supported ("PNG"))
+ index = get_index ("PNG");
+
+ add_dialog_extensions (index);
+ // needs to be called before we canonize
+
+ if (!_filename)
+ _filename = canonize ("default");
+
+ free (_pathname);
+ _pathname = 0;
+ // FIXME? 0 == _filename
+ gtk_file_selection_set_filename (_widget, _filename);
+
+ g_signal_connect_swapped (GTK1_OBJ (_widget->ok_button), "clicked",
+ G_CALLBACK (_click_ok), GTK1_OBJ (this));
+ g_signal_connect_swapped (GTK1_OBJ (_widget->cancel_button), "clicked",
+ G_CALLBACK (_destroy), GTK1_OBJ (this));
+ g_signal_connect_swapped (GTK1_OBJ (_widget), "delete_event",
+ G_CALLBACK (_destroy), GTK1_OBJ (this));
+ g_signal_connect_swapped (GTK1_OBJ (_widget), "destroy",
+ G_CALLBACK (gtk_main_quit), GTK1_OBJ (_widget));
+
+ // We need to interfere with users selecting and editing filenames
+ // to guarantee a correct extension and the appropriate templating
+ // bit when _using_adf.
+ g_signal_connect (GTK1_OBJ (_widget->selection_entry), "delete-text",
+ G_CALLBACK (_delete_text), this);
+ g_signal_connect (GTK1_OBJ (_widget->selection_entry), "insert-text",
+ G_CALLBACK (_insert_text), this);
+
+ gtk_widget_show (GTK_WIDGET (_widget));
+ gtk_grab_add (GTK_WIDGET (_widget));
+ gtk_main ();
+ if (_widget)
+ {
+ gtk_grab_remove (GTK_WIDGET (_widget));
+ }
+ g_print ("%s: exit\n", __func__);
+}
+
+void
+file_selector::destroy ()
+{
+ g_print ("%s\n", __func__);
+
+ if (_using_adf)
+ {
+ hide ();
+ }
+ else
+ {
+ destroy (true);
+ }
+}
+
+void
+file_selector::destroy (bool really)
+{
+ g_print ("%s (%i)\n", __func__, really);
+
+ free (_filename);
+ _filename = NULL;
+
+ if (_widget)
+ {
+ gtk_widget_destroy (GTK_WIDGET (_widget));
+ }
+ _widget = NULL;
+
+ if (_parent)
+ {
+ gtk_widget_set_sensitive (_parent, true);
+ }
+ _parent = NULL;
+
+ _number = -1;
+ _seqnum = NULL; // gtk_widget_destroy cleans these up
+ _digits = NULL;
+
+ _using_adf = false;
+ _do_duplex = false;
+ _deleted_all = false;
+
+ delete [] _fmt;
+ _fmt = NULL;
+ delete [] _ext_regex;
+ _ext_regex = NULL;
+}
+
+char *
+file_selector::get_pathname () const
+{
+ return (_pathname ? strdup (_pathname) : NULL);
+}
+
+int
+file_selector::get_sequence_number () const
+{
+ return _number;
+}
+
+bool
+file_selector::valid_pathname (const string& path) const
+{
+ string::size_type slash = path.rfind (file_opener::dir_sep);
+ string name = path.substr (slash + 1);
+ string::size_type dot = name.rfind (file_opener::ext_sep);
+
+ string::size_type cmp_pos = dot;
+
+ if (_using_adf && !_multi_page_mode)
+ {
+ string::size_type dash = name.find_last_of ("-", dot - 1);
+ cmp_pos = dash;
+ }
+
+ if (128 < path.size () // blame the spec for this one
+ || !permission (path.c_str ())
+ || 0 >= cmp_pos) // 0 length filename
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+file_selector::save_pathname()
+{
+ g_print ("%s\n", __func__);
+
+ if (!_widget)
+ {
+ g_print ("%s: widget's gone!\n", __func__);
+ free (_pathname);
+ _pathname = NULL;
+ return true;
+ }
+
+ const char *current = gtk_file_selection_get_filename (_widget);
+
+ if (!valid_pathname (current))
+ {
+ show_message (pisa_error (PISA_ERR_FILENAME));
+ return false;
+ }
+ if (possible_overwrite (current))
+ {
+ bool ok = show_message (pisa_error (PISA_ERR_OVERWRITE),
+ _("Overwrite"), _("Cancel"));
+ if (!ok)
+ return false;
+ }
+
+ char *new_name = (char *) malloc ((strlen (current) + 1)
+ * sizeof (char));
+ if (!new_name)
+ throw pisa_error (PISA_STATUS_NO_MEM);
+
+ strcpy (new_name, current);
+ free (_pathname);
+ _pathname = new_name;
+ free (_filename);
+ _filename = 0;
+
+ g_print ("%s: pathname = `%s'\n", __func__, _pathname);
+
+ return true;
+}
+
+void
+file_selector::hide () const
+{
+ g_print ("%s\n", __func__);
+
+ if (_using_adf)
+ {
+ g_print ("%s: calling gtk_widget_hide\n", __func__);
+ if (_widget)
+ {
+ gtk_widget_hide (GTK_WIDGET (_widget));
+ }
+ if (_parent)
+ {
+ gtk_widget_set_sensitive (_parent, true);
+ }
+ }
+}
+
+void
+file_selector::set_entry (const char *text)
+{
+ g_print ("%s (%s)\n", __func__, text);
+
+ if (!_filename)
+ return; // logical error but return for now
+
+ if (!text
+ || !*text // empty string
+ || text == _filename
+ || 0 == strcmp (text, _filename))
+ return; // nothing to change
+
+ char *new_name = (canonize (text));
+ if (new_name)
+ {
+ free (_filename);
+ _filename = new_name;
+#ifndef HAVE_GTK_2
+ gtk_signal_handler_block_by_func (GTK_OBJECT (_widget->selection_entry),
+ GTK_SIGNAL_FUNC (_delete_text), this);
+ gtk_signal_handler_block_by_func (GTK_OBJECT (_widget->selection_entry),
+ GTK_SIGNAL_FUNC (_insert_text), this);
+ gtk_entry_set_text (GTK_ENTRY (_widget->selection_entry), _filename);
+ gtk_signal_handler_unblock_by_func (GTK_OBJECT (_widget->selection_entry),
+ GTK_SIGNAL_FUNC (_insert_text), this);
+ gtk_signal_handler_unblock_by_func (GTK_OBJECT (_widget->selection_entry),
+ GTK_SIGNAL_FUNC (_delete_text), this);
+#else
+ g_signal_handlers_block_by_func (GTK_EDITABLE (_widget->selection_entry),
+ (gpointer) _delete_text, this);
+ g_signal_handlers_block_by_func (GTK_EDITABLE (_widget->selection_entry),
+ (gpointer) _insert_text, this);
+ gtk_entry_set_text (GTK_ENTRY (_widget->selection_entry), _filename);
+ g_signal_handlers_unblock_by_func (GTK_EDITABLE (_widget->selection_entry),
+ (gpointer) _insert_text, this);
+ g_signal_handlers_unblock_by_func (GTK_EDITABLE (_widget->selection_entry),
+ (gpointer) _delete_text, this);
+#endif
+ }
+}
+
+void
+file_selector::delete_text (gint start, gint end)
+{
+ g_print ("%s (%d,%d)\n", __func__, start, end);
+ g_print ("%s orig _filename '%s'\n", __func__, _filename);
+
+ _deleted_all = ((end - start) == (gint) strlen (_filename));
+
+ if (0 > end) end = strlen (_filename) + 1;
+ int dot = strrchr (_filename, ((_using_adf && !_multi_page_mode)
+ ? '-' : '.')) - _filename;
+ if (end > dot) end = dot;
+
+ if (start < end)
+ {
+ GtkWidget *ed = _widget->selection_entry;
+ gtk_editable_delete_text (GTK_EDITABLE (ed), start, end);
+ _filename = strdup (gtk_entry_get_text (GTK_ENTRY (ed)));
+
+ g_print ("%s new _filename '%s'\n", __func__, _filename);
+ }
+}
+
+void
+file_selector::insert_text (gchar *text, gint len, gint *pos)
+{
+ g_print ("%s (%s,%d,%d)\n", __func__, text, len, (pos ? *pos : -1));
+ g_print ("%s orig _filename '%s'\n", __func__, _filename);
+
+ if (_deleted_all && pos && (0 == *pos))
+ {
+ _deleted_all = false;
+ text = canonize (text);
+ if (!text) return;
+ len = strlen (text);
+
+ GtkEditable *ed = GTK_EDITABLE (_widget->selection_entry);
+#ifndef HAVE_GTK_2
+ gtk_signal_handler_block_by_func (GTK_OBJECT (ed),
+ GTK_SIGNAL_FUNC (_delete_text), this);
+ gtk_editable_delete_text (ed, 0, -1);
+ gtk_signal_handler_unblock_by_func (GTK_OBJECT (ed),
+ GTK_SIGNAL_FUNC (_delete_text), this);
+#else
+ g_signal_handlers_block_by_func (ed, (gpointer) _delete_text, this);
+ gtk_editable_delete_text (ed, 0, -1);
+ g_signal_handlers_unblock_by_func (ed, (gpointer) _delete_text, this);
+#endif
+ }
+
+ int dot = strrchr (_filename, ((_using_adf && !_multi_page_mode)
+ ? '-' : '.')) - _filename;
+
+ if (pos && (*pos <= dot))
+ {
+ GtkWidget *ed = _widget->selection_entry;
+ gtk_editable_insert_text (GTK_EDITABLE (ed), text, len, pos);
+#ifndef HAVE_GTK_2
+ { // not getting delete-event's for some
+ // reason
+ char *name = canonize (gtk_entry_get_text (GTK_ENTRY (ed)));
+ gtk_entry_set_text (GTK_ENTRY (ed), name);
+ free (name);
+ }
+#endif
+ free (_filename);
+ _filename = strdup (gtk_entry_get_text (GTK_ENTRY (ed)));
+
+ g_print ("%s new _filename '%s'\n", __func__, _filename);
+ }
+}
+
+void
+file_selector::change_format (int index)
+{
+ g_print ("%s (%d)\n", __func__, index);
+
+ _format_index = index;
+ // reflect changes in filename
+ char *canon = canonize (_filename);
+ set_entry (canon);
+ free (canon);
+}
+
+void
+file_selector::change_seqnum ()
+{
+ g_print ("%s\n", __func__);
+
+ _number = int (_seqnum->value);
+}
+
+void
+file_selector::change_digits ()
+{
+ g_print ("%s\n", __func__);
+
+ int max = 9; // there's at least one digit
+ for (int i = 2; i <= int (_digits->value); ++i)
+ {
+ max *= 10;
+ max += 9;
+ }
+
+ if (_seqnum)
+ {
+ _seqnum->upper = max;
+ if (max < _seqnum->value) // also updates the GUI
+ gtk_adjustment_set_value (_seqnum, max);
+
+ gtk_adjustment_changed (_seqnum);
+ }
+
+ if (_filename) // reflect changes in filename
+ {
+ char *canon = canonize (_filename);
+ set_entry (canon);
+ free (canon);
+ }
+}
+
+#include <sstream>
+#include <iostream>
+void
+file_selector::change_page_mode (bool mode)
+{
+ _multi_page_mode = mode;
+
+ string name = _filename;
+ stringstream ss;
+
+ string::size_type dot = name.rfind (file_opener::ext_sep);
+ string::size_type dash = name.find_last_of ("-", dot - 1);
+
+ if (mode)
+ {
+ ss << name.substr (0, dash)
+ << name.substr (dot);
+ }
+ else
+ {
+ ss << name.substr (0, dot) << "-";
+ for (int i = 0; i < int (_digits->value); ++i)
+ ss << file_opener::hash_mark;
+ ss << name.substr (dot);
+ }
+
+ set_entry (ss.str ().c_str ());
+}
+
+void
+file_selector::change_binding_edge (bool is_left_edge)
+{
+ g_print ("%s(%d)\n", __func__, is_left_edge);
+
+ _has_left_edge_binding = is_left_edge;
+}
+
+// caller needs to free returned char *
+char *
+file_selector::canonize (const char *text) const
+{
+ if (!text || 0 == *text)
+ return 0;
+
+ g_print ("%s (%s)\n", __func__, text);
+
+ string name = text;
+ string ext = _fmt[_format_index]->ext;
+ stringstream ss;
+
+ string::size_type dot = name.rfind (file_opener::ext_sep);
+
+ if (_using_adf && !_multi_page_mode)
+ {
+ string::size_type dash = name.find_last_of ("-", dot - 1);
+ ss << name.substr (0, dash)
+ << "-";
+
+ for (int i = 0; i < int (_digits->value); ++i)
+ ss << file_opener::hash_mark;
+ }
+ else
+ {
+ ss << name.substr (0, dot);
+ }
+
+ ss << file_opener::ext_sep
+ << ext;
+
+ return strdup (ss.str ().c_str ());
+}
+
+void
+file_selector::add_dialog_extensions (gint index)
+{
+ g_print ("%s: enter\n", __func__);
+
+ GtkWidget *frame = gtk_frame_new (_("Save Options"));
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
+
+ GtkWidget *align = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (align), 5, 5, 5, 5);
+ gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (align));
+ gtk_widget_show (align);
+
+ unsigned short rows = 1;
+ if (_using_adf) rows = 2;
+ if (_do_duplex) rows = 3;
+ GtkTable *opts
+ = (GtkTable *) gtk_table_new (rows + (_using_adf ? 2 : 0), 3, FALSE);
+ gtk_table_set_col_spacings (opts, 5);
+ gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET (opts));
+ gtk_widget_show (GTK_WIDGET (opts));
+
+ GtkWidget *mi = NULL;
+ GtkWidget *multi =
+ gtk_check_button_new_with_label ( _("Create file with all pages"));
+
+ GtkWidget *bind_edge = gtk_label_new
+ (_("Binding Position (for Double-Sided Scanning)"));
+ GtkWidget *left_edge = gtk_radio_button_new_with_label (NULL, _("Left"));
+ GtkWidget *top_edge = gtk_radio_button_new_with_label
+ (gtk_radio_button_get_group (GTK_RADIO_BUTTON (left_edge)), _("Top"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (left_edge), true);
+
+ GtkWidget *w = 0;
+ { // file type selector
+ w = gtk_label_new (_("Determine File Type:"));
+ gtk_table_attach_defaults (opts, w, 0, 1, 0, 1);
+ gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
+ gtk_widget_show (w);
+
+ w = gtk_option_menu_new ();
+ {
+ GtkWidget *m = gtk_menu_new ();
+
+ for (int i = 0; _fmt[i]; ++i)
+ {
+ mi = gtk_menu_item_new_with_label (_fmt[i]->name);
+ gtk_menu_append (GTK_MENU (m), mi);
+ gtk_widget_show (mi);
+
+ struct menu_info *id = new struct menu_info;
+ id->fs = this;
+ id->widget[0] = multi;
+ id->widget[1] = bind_edge;
+ id->widget[2] = left_edge;
+ id->widget[3] = top_edge;
+ id->index = i;
+
+ g_signal_connect (GTK1_OBJ (mi), "activate",
+ G_CALLBACK (_change_format), (void *) id);
+
+ gtk_widget_set_sensitive (mi, is_supported (_fmt[i]->name));
+ }
+ gtk_option_menu_set_menu( GTK_OPTION_MENU( w ), m );
+ }
+ gtk_table_attach_defaults (opts, w, 1, 3, 0, 1);
+ gtk_option_menu_set_history (GTK_OPTION_MENU (w), index);
+ change_format (index);
+ gtk_widget_show (w);
+ }
+
+ if (_using_adf)
+ {
+ g_print ("%s: using adf\n", __func__);
+
+ gtk_signal_connect (GTK_OBJECT (multi), "toggled",
+ GTK_SIGNAL_FUNC (_toggled_multi_page_mode_fs), this);
+ gtk_table_attach_defaults (opts, multi, 1, 3, 1, 2);
+ gtk_widget_show (multi);
+
+ if (_do_duplex)
+ {
+ gtk_signal_connect (GTK_OBJECT (left_edge), "toggled",
+ GTK_SIGNAL_FUNC (_changed_binding_edge), this);
+ gtk_table_attach_defaults (opts, bind_edge, 0, 1, 2, 3);
+ gtk_misc_set_alignment (GTK_MISC (bind_edge), 1.0, 0.5);
+ gtk_table_attach_defaults (opts, left_edge, 1, 2, 2, 3);
+ gtk_table_attach_defaults (opts, top_edge, 2, 3, 2, 3);
+ gtk_widget_show (bind_edge);
+ gtk_widget_show (left_edge);
+ gtk_widget_show (top_edge);
+ }
+
+ // sequence number selector
+ w = gtk_label_new (_("Start filing at:"));
+ gtk_table_attach_defaults (opts, w, 0, 1, rows + 0, rows + 1);
+ gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
+ gtk_signal_connect (GTK_OBJECT (multi), "toggled",
+ GTK_SIGNAL_FUNC (_toggled_multi_page_mode_widget), w);
+ gtk_widget_show (w);
+
+ if (!_seqnum)
+ _seqnum = (GtkAdjustment *) gtk_adjustment_new (1, 0, 9, 1, 10, 0);
+ if (_seqnum)
+ _number = int (_seqnum->value);
+ else // play it safe and make sure
+ _number = -1;
+
+ w = gtk_spin_button_new (_seqnum, 0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (w), true);
+ gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (w), true);
+ gtk_table_attach_defaults (opts, w, 1, 3, rows + 0, rows + 1);
+ gtk_signal_connect (GTK_OBJECT (multi), "toggled",
+ GTK_SIGNAL_FUNC (_toggled_multi_page_mode_widget), w);
+ gtk_widget_show (w);
+
+ // number of digits selector
+ w = gtk_label_new (_("Number of digits:"));
+ gtk_table_attach_defaults (opts, w, 0, 1, rows + 1, rows + 2);
+ gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
+ gtk_signal_connect (GTK_OBJECT (multi), "toggled",
+ GTK_SIGNAL_FUNC (_toggled_multi_page_mode_widget), w);
+ gtk_widget_show (w);
+
+ if (!_digits)
+ _digits = (GtkAdjustment *) gtk_adjustment_new (3, 1, 6, 1, 1, 0);
+ if (_digits)
+ change_digits();
+
+ w = gtk_spin_button_new (_digits, 0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (w), true);
+ gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (w), true);
+ gtk_table_attach_defaults (opts, w, 1, 3, rows + 1, rows + 2);
+ gtk_signal_connect (GTK_OBJECT (multi), "toggled",
+ GTK_SIGNAL_FUNC (_toggled_multi_page_mode_widget), w);
+ gtk_widget_show (w);
+
+ g_signal_connect (GTK1_OBJ (_seqnum), "value_changed",
+ G_CALLBACK (_change_seqnum), this);
+ g_signal_connect (GTK1_OBJ (_digits), "value_changed",
+ G_CALLBACK (_change_digits), this);
+ }
+
+ gtk_box_pack_end (GTK_BOX (_widget->main_vbox), frame, false, false, 0);
+
+ struct menu_info id;
+ id.fs = this;
+ id.widget[0] = multi;
+ id.widget[1] = bind_edge;
+ id.widget[2] = left_edge;
+ id.widget[3] = top_edge;
+ id.index = index;
+ _change_format (mi, &id);
+ gtk_widget_show (frame);
+ gtk_widget_show (GTK_WIDGET (opts));
+
+ g_print ("%s: exit\n", __func__);
+}
+
+bool
+file_selector::permission( const char *file_in ) const
+{
+ if (!file_in || 0 == strlen (file_in))
+ return false;
+
+ char* file = strdup (file_in);
+
+ if (!file)
+ return false;
+
+ if (0 == access( file, F_OK )) // file exists
+ {
+ bool rv = (0 == access( file, W_OK )); // whether we can write to it
+ free (file);
+ return rv;
+ }
+
+ // check write access to the directory (note that we need execute
+ // privileges as well)
+
+ char *slash = strrchr( file, '/');
+ *slash = '\0'; // temporarily truncate to dirname
+ const char *dir = (file == slash
+ ? "/" // whoops!, file in root directory
+ : file);
+
+ bool w_ok = false; // assume the worst
+ if (0 == access( dir, F_OK ))
+ w_ok = (0 == access( dir, W_OK | X_OK ));
+
+ *slash = '/'; // restore filename
+
+ free (file);
+ return w_ok;
+}
+
+bool
+file_selector::show_message( const pisa_error& oops,
+ const char *yes,
+ const char *no ) const
+{
+ aleart_dialog dlg;
+
+ if (yes && no) // binary question
+ return (1 == dlg.message_box( GTK_WIDGET( _widget ),
+ oops.get_error_string(),
+ yes, no ));
+
+ dlg.message_box( GTK_WIDGET( _widget ), oops.get_error_string() );
+ return true;
+}
+
+void
+file_selector::regerror (int code, regex_t *regex) const
+{
+ size_t length = ::regerror (code, regex, 0, 0);
+ char *message = new char[length];
+
+ ::regerror (code, regex, message, length);
+ fprintf (stderr, "%s\n", message);
+
+ delete[] message;
+}
+
+std::string
+file_selector::get_format_name () const
+{
+ return _format[_format_index].name;
+}
+
+iscan::file_format
+file_selector::get_type () const
+{
+ return _format[_format_index].type;
+}
+
+bool
+file_selector::multi_page_mode ()
+{
+ if (iscan::PDF == get_type () || iscan::TIF == get_type ())
+ return _multi_page_mode;
+
+ return false;
+}
+
+bool
+file_selector::has_left_edge_binding ()
+{
+ return _has_left_edge_binding;
+}
+
+gint
+file_selector::get_index (const char* name) const
+{
+ if (!name) return 0;
+
+ int i = 0;
+ while (_format[i].name && (0 != strcmp (name, _format[i].name)))
+ {
+ ++i;
+ }
+ return (_format[i].name ? i : 0);
+}
+
+bool
+file_selector::is_supported (const char* format) const
+{
+ // FIXME! ugly kludge to "check" for support
+ if (!format)
+ {
+ return false;
+ }
+ if (0 == strcmp ("PCX", format))
+ {
+ return iscan::pcxstream::is_usable ();
+ }
+ if (0 == strcmp ("PNM", format))
+ {
+ return iscan::pnmstream::is_usable ();
+ }
+ if (0 == strcmp ("PNG", format))
+ {
+ return iscan::pngstream::is_usable ();
+ }
+ if (0 == strcmp ("JPEG", format))
+ {
+ return iscan::jpegstream::is_usable ();
+ }
+ if (0 == strcmp ("PDF", format))
+ {
+ return iscan::pdfstream::is_usable ();
+ }
+ if (0 == strcmp ("TIFF", format))
+ {
+ return iscan::tiffstream::is_usable ();
+ }
+
+ return false;
+}
+
+#include <sstream>
+#include <iostream>
+bool
+file_selector::possible_overwrite (const std::string& current) const
+{
+ if (!_using_adf || _multi_page_mode)
+ return (0 == access (current.c_str (), F_OK));
+
+ string::size_type dot = current.rfind (file_opener::ext_sep);
+ string::size_type hash = current.find_last_not_of (file_opener::hash_mark,
+ dot - 1);
+ int digits = dot - ++hash;
+
+ stringstream ss;
+ ss << current.substr (0, hash)
+ << "([1-9][0-9]{" << digits << ",}|[0-9]{" << digits << "})\\"
+ << current.substr (dot);
+
+ return check_overwrite (ss.str ().c_str ());
+}
+
+int
+file_selector::check_overwrite (const char *regexp_in) const
+{
+ bool found = false;
+
+ if (!regexp_in)
+ return false;
+
+ char* regexp = strdup (regexp_in);
+ if (!regexp)
+ return true;
+
+ char *slash = strrchr( regexp, '/' );
+
+ if (!slash)
+ {
+ free (regexp);
+ return true;
+ }
+
+ *slash = '\0'; // regexp now holds the directory name
+ char dirname[ strlen( regexp )];
+ strcpy( dirname, regexp );
+
+ *slash = '^'; // re-anchor the regexp
+
+ regex_t *comp_regex = new regex_t;
+ int comp = regcomp( comp_regex, slash, REG_EXTENDED );
+
+ if (0 == comp)
+ {
+ size_t nsub = comp_regex->re_nsub + 1;
+ regmatch_t match[nsub];
+
+ DIR *dir = opendir( dirname );
+ if (!dir)
+ {
+ regfree( comp_regex );
+ delete comp_regex;
+ free (regexp);
+ return 0; // file creation failure handles this
+ }
+
+ struct dirent *file = 0;
+ while (!found && (file = readdir( dir )))
+ {
+ int result = regexec( comp_regex, file->d_name, nsub, match, 0 );
+ if (0 == result)
+ {
+ size_t digits = match[1].rm_eo - match[1].rm_so;
+ char num[digits + 1];
+ char *c = num;
+ {
+ char *p = file->d_name + match[1].rm_so;
+ while (0 < digits--)
+ *c++ = *p++;
+ }
+ int seq_num = atoi( num );
+
+ found = (seq_num >= get_sequence_number());
+ }
+ else
+ if (REG_NOMATCH != result)
+ regerror( comp, comp_regex );
+ }
+ closedir( dir );
+ }
+ else
+ regerror( comp, comp_regex );
+
+ regfree( comp_regex );
+ delete comp_regex;
+ free (regexp);
+
+ return found;
+}
diff --git a/frontend/file-selector.h b/frontend/file-selector.h
new file mode 100644
index 0000000..3c932cc
--- /dev/null
+++ b/frontend/file-selector.h
@@ -0,0 +1,135 @@
+/* file-selector.h -- customized file selection dialog -*- C++ -*-
+ Copyright (C) 2003, 2005, 2008 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+ */
+
+#ifndef ISCAN_FILE_SELECTOR_H
+#define ISCAN_FILE_SELECTOR_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <regex.h>
+#include <gtk/gtk.h>
+
+#include "pisa_error.h"
+
+#include "imgstream.hh" // for iscan::file_format
+
+#define G_CALLBACK_API public /* EEK! */
+
+class file_selector
+{
+public:
+ void init ();
+ void create_window (GtkWindow *window, GtkWidget *parent,
+ bool do_consecutive_scanning = false,
+ bool show_match_direction = false,
+ const char* default_format = NULL);
+
+ void destroy ();
+
+ char *get_pathname () const;
+
+ int get_sequence_number () const;
+
+ bool valid_pathname (const std::string& path) const;
+ bool save_pathname (); // really G_CALLBACK_API only
+
+ void hide () const;
+
+ std::string get_format_name () const;
+ iscan::file_format get_type () const;
+
+ bool multi_page_mode ();
+ bool has_left_edge_binding ();
+
+G_CALLBACK_API:
+
+ void destroy (bool really);
+ void delete_text (gint start, gint end);
+ void insert_text (gchar *text, gint len, gint *pos);
+ void change_format (int index);
+ void change_seqnum ();
+ void change_digits ();
+ void change_page_mode (bool mode);
+ void change_binding_edge (bool match);
+
+private:
+ char *canonize (const char *text) const;
+
+ void add_dialog_extensions (gint index = 0);
+
+ bool permission (const char *file) const;
+ bool show_message (const pisa_error& oops,
+ const char *yes = NULL,
+ const char *no = NULL) const;
+
+ void set_entry (const char *text);
+
+ void regerror (int code, regex_t *regex) const;
+
+ gint get_index (const char* name) const;
+ bool is_supported (const char* format) const;
+ bool possible_overwrite (const std::string& current) const;
+ int check_overwrite (const char *regexp) const;
+
+ GtkWidget *_parent;
+ GtkFileSelection *_widget;
+ char *_filename; // relative filename
+ char *_pathname; // absolute filename
+
+ bool _using_adf;
+ bool _do_duplex; // indicates whether to display a binding
+ // location selector (default: left edge)
+
+ int _format_index; // index into file format menu indicating
+ // the currently selected item
+ bool _deleted_all;
+ bool _multi_page_mode;
+ bool _has_left_edge_binding; // indicates whether the document is bound
+ // on the left hand side
+
+ int _number; // for consecutive scans
+ GtkAdjustment *_seqnum;
+ GtkAdjustment *_digits;
+
+ typedef struct // graphic format info
+ {
+ const char *name; // used in option menu
+ const char *lib; // library providing support
+ const char *ext; // default filename extension
+ const char *rgx; // filename extension regex
+ const iscan::file_format type;
+ } format;
+
+ static const format _format[];
+ const format **_fmt;
+
+ char *_ext_regex;
+};
+
+#endif /* ISCAN_FILE_SELECTOR_H */
diff --git a/frontend/gimp-plugin.h b/frontend/gimp-plugin.h
new file mode 100644
index 0000000..4e6a86c
--- /dev/null
+++ b/frontend/gimp-plugin.h
@@ -0,0 +1,186 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2005 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+#ifndef GIMP_PLUGIN_H
+#define GIMP_PLUGIN_H
+
+#ifdef HAVE_GIMP_2
+/* Define these structures for using gimp-devel-2 */
+
+struct _GimpParamColor_1
+{
+ guint8 red;
+ guint8 green;
+ guint8 blue;
+};
+typedef struct _GimpParamColor_1 GimpParamColor_1 ;
+
+union _GimpParamData_1
+{
+ gint32 d_int32;
+ gint16 d_int16;
+ gint8 d_int8;
+ gdouble d_float;
+ gchar *d_string;
+ gint32 *d_int32array;
+ gint16 *d_int16array;
+ gint8 *d_int8array;
+ gdouble *d_floatarray;
+ gchar **d_stringarray;
+ GimpParamColor_1 d_color;
+ GimpParamRegion d_region;
+ gint32 d_display;
+ gint32 d_image;
+ gint32 d_layer;
+ gint32 d_layer_mask;
+ gint32 d_channel;
+ gint32 d_drawable;
+ gint32 d_selection;
+ gint32 d_boundary;
+ gint32 d_path;
+ gint32 d_unit;
+ GimpParasite d_parasite;
+ gint32 d_tattoo;
+ GimpPDBStatusType d_status;
+};
+typedef union _GimpParamData_1 GimpParamData_1 ;
+
+struct _GimpParam_1
+{
+ GimpPDBArgType type;
+ GimpParamData_1 data;
+};
+typedef struct _GimpParam_1 GimpParam_1;
+
+typedef void (* GimpInitProc_1) (void);
+typedef void (* GimpQuitProc_1) (void);
+typedef void (* GimpQueryProc_1) (void);
+typedef void (* GimpRunProc_1) (gchar *name,
+ gint n_params,
+ GimpParam_1 *param,
+ gint *n_return_vals,
+ GimpParam_1 **return_vals);
+
+struct _GimpPlugInInfo_1
+{
+ /* called when the gimp application initially starts up */
+ GimpInitProc_1 init_proc;
+
+ /* called when the gimp application exits */
+ GimpQuitProc_1 quit_proc;
+
+ /* called by the gimp so that the plug-in can inform the
+ * gimp of what it does. (ie. installing a procedure database
+ * procedure).
+ */
+ GimpQueryProc_1 query_proc;
+
+ /* called to run a procedure the plug-in installed in the
+ * procedure database.
+ */
+ GimpRunProc_1 run_proc;
+};
+typedef struct _GimpPlugInInfo_1 GimpPlugInInfo_1;
+
+#else
+/* Define these structures for using gimp-devel-1 */
+
+struct _GimpRGB_2
+{
+ gdouble r, g, b, a;
+};
+typedef struct _GimpRGB_2 GimpRGB_2;
+
+union _GimpParamData_2
+{
+ gint32 d_int32;
+ gint16 d_int16;
+ gint8 d_int8;
+ gdouble d_float;
+ gchar *d_string;
+ gint32 *d_int32array;
+ gint16 *d_int16array;
+ gint8 *d_int8array;
+ gdouble *d_floatarray;
+ gchar **d_stringarray;
+ GimpRGB_2 d_color;
+ GimpParamRegion d_region;
+ gint32 d_display;
+ gint32 d_image;
+ gint32 d_layer;
+ gint32 d_layer_mask;
+ gint32 d_channel;
+ gint32 d_drawable;
+ gint32 d_selection;
+ gint32 d_boundary;
+ gint32 d_path;
+ gint32 d_unit;
+ GimpParasite d_parasite;
+ gint32 d_tattoo;
+ GimpPDBStatusType d_status;
+};
+typedef union _GimpParamData_2 GimpParamData_2 ;
+
+struct _GimpParam_2
+{
+ GimpPDBArgType type;
+ GimpParamData_2 data;
+};
+typedef struct _GimpParam_2 GimpParam_2;
+
+typedef void (* GimpInitProc_2) (void);
+typedef void (* GimpQuitProc_2) (void);
+typedef void (* GimpQueryProc_2) (void);
+typedef void (* GimpRunProc_2) (const gchar *name,
+ gint n_params,
+ const GimpParam_2 *param,
+ gint *n_return_vals,
+ GimpParam_2 **return_vals);
+
+struct _GimpPlugInInfo_2
+{
+ /* called when the gimp application initially starts up */
+ GimpInitProc_2 init_proc;
+
+ /* called when the gimp application exits */
+ GimpQuitProc_2 quit_proc;
+
+ /* called by the gimp so that the plug-in can inform the
+ * gimp of what it does. (ie. installing a procedure database
+ * procedure).
+ */
+ GimpQueryProc_2 query_proc;
+
+ /* called to run a procedure the plug-in installed in the
+ * procedure database.
+ */
+ GimpRunProc_2 run_proc;
+};
+typedef struct _GimpPlugInInfo_2 GimpPlugInInfo_2;
+
+#endif // HAVE_GIMP_2
+
+#endif /* GIMP_PLUGIN_H */
diff --git a/frontend/pisa_aleart_dialog.cc b/frontend/pisa_aleart_dialog.cc
new file mode 100644
index 0000000..5b0cb6a
--- /dev/null
+++ b/frontend/pisa_aleart_dialog.cc
@@ -0,0 +1,144 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+
+#include <stdio.h>
+
+/*------------------------------------------------------------*/
+#include "pisa_aleart_dialog.h"
+
+/*------------------------------------------------------------*/
+static gint delete_event ( GtkWidget * widget, gpointer data )
+{
+ widget = widget;
+ data = data;
+
+ ::gtk_main_quit ( );
+
+ return TRUE;
+}
+
+/*------------------------------------------------------------*/
+void set_value ( GtkWidget * widget, gpointer data )
+{
+ aleart_dialog * dlg= ( aleart_dialog * ) data;
+
+ dlg->m_id = GPOINTER_TO_INT ( ::gtk_object_get_data ( GTK_OBJECT ( widget ),
+ "value" ) );
+
+ ::gtk_main_quit ( );
+}
+
+/*------------------------------------------------------------*/
+int aleart_dialog::message_box ( GtkWidget * parent,
+ const char * msg,
+ const char * btn1 ,
+ const char * btn2 )
+{
+ GtkWidget * top;
+ GtkWidget * vbox;
+ GtkWidget * label;
+ GtkWidget * button;
+ char * default_btn = _(" OK ");
+
+ top = ::gtk_dialog_new ( );
+ ::gtk_window_set_policy ( GTK_WINDOW ( top ),
+ FALSE, FALSE, TRUE );
+ ::gtk_window_set_title ( GTK_WINDOW ( top ), PACKAGE );
+ ::gtk_window_set_position ( GTK_WINDOW ( top ), GTK_WIN_POS_CENTER );
+ ::gtk_container_border_width ( GTK_CONTAINER ( GTK_DIALOG ( top )->vbox ),
+ 10 );
+ ::gtk_widget_realize ( top );
+
+ ::gtk_signal_connect ( GTK_OBJECT ( top ), "delete_event",
+ GTK_SIGNAL_FUNC ( ::delete_event ), 0 );
+
+ if ( parent != 0 )
+ ::gtk_window_set_transient_for ( GTK_WINDOW ( top ),
+ GTK_WINDOW ( parent ) );
+
+ vbox = GTK_DIALOG ( top )->vbox;
+
+ label = ::gtk_label_new ( _( msg ) );
+ ::gtk_label_set_line_wrap (GTK_LABEL (label), true);
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), label, TRUE, TRUE, 0 );
+ ::gtk_widget_show ( label );
+
+ if ( btn1 == 0 && btn2 == 0 )
+ btn2 = default_btn;
+
+ if ( btn1 != 0 )
+ {
+ button = ::gtk_button_new_with_label ( btn1 );
+ if ( btn2 == 0 )
+ {
+ GTK_WIDGET_SET_FLAGS ( button, GTK_CAN_DEFAULT );
+ }
+ ::gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG ( top )->action_area ),
+ button, FALSE, FALSE, 5 );
+ ::gtk_object_set_data ( GTK_OBJECT ( button ), "value", ( gpointer ) 1 );
+ ::gtk_signal_connect ( GTK_OBJECT ( button ), "clicked",
+ GTK_SIGNAL_FUNC ( set_value ), this );
+ if ( btn2 == 0 )
+ ::gtk_widget_grab_default ( button );
+ ::gtk_widget_show ( button );
+
+ m_id = 1;
+ }
+
+ if ( btn2 != 0 )
+ {
+ button = ::gtk_button_new_with_label ( btn2 );
+ GTK_WIDGET_SET_FLAGS ( button, GTK_CAN_DEFAULT );
+ ::gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG ( top )->action_area ),
+ button, FALSE, FALSE, 5 );
+ ::gtk_object_set_data ( GTK_OBJECT ( button ), "value", ( gpointer ) 2 );
+ ::gtk_signal_connect ( GTK_OBJECT ( button ), "clicked",
+ GTK_SIGNAL_FUNC ( set_value ), this );
+ ::gtk_widget_grab_default ( button );
+ ::gtk_widget_show ( button );
+
+ m_id = 2;
+ }
+
+ ::gtk_widget_show ( top );
+
+ ::gtk_grab_add ( top );
+ ::gtk_main ( );
+ ::gtk_grab_remove ( top );
+ ::gtk_widget_destroy ( top );
+
+ return m_id;
+}
+
diff --git a/frontend/pisa_aleart_dialog.h b/frontend/pisa_aleart_dialog.h
new file mode 100644
index 0000000..b5c7fcb
--- /dev/null
+++ b/frontend/pisa_aleart_dialog.h
@@ -0,0 +1,52 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_ALEART_DIALOG_H
+#define ___PISA_ALEART_DIALOG_H
+
+#include <gtk/gtk.h>
+
+class aleart_dialog
+{
+
+ public:
+
+ // operation
+ int message_box ( GtkWidget * parent,
+ const char * msg,
+ const char * btn1 = 0,
+ const char * btn2 = 0 );
+
+ int m_id;
+
+};
+
+#endif // ___PISA_ALEART_DIALOG_H
+
diff --git a/frontend/pisa_change_unit.cc b/frontend/pisa_change_unit.cc
new file mode 100644
index 0000000..49f88b5
--- /dev/null
+++ b/frontend/pisa_change_unit.cc
@@ -0,0 +1,101 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include "pisa_change_unit.h"
+
+const double CM_PER_INCH = 2.54; // exact, per definition
+
+
+double
+inches_reflect_zoom (double inch, long zoom)
+{
+ return inch * zoom / 100;
+}
+
+double
+inch2centi (double inch, long zoom)
+{
+ return (inch * CM_PER_INCH * zoom) / 100;
+}
+
+
+long
+inch2width (double inch, long resolution, long zoom, bool monochrome)
+{
+ int boundary = (monochrome ? 32 : 8);
+
+ return (inch2pixel (inch, resolution, zoom) / boundary) * boundary;
+}
+
+long
+inch2height (double inch, long resolution, long zoom)
+{
+ return inch2pixel (inch, resolution, zoom);
+}
+
+long
+inch2pixel (double inch, long resolution, long zoom)
+{
+ return (long) ((inch * resolution * zoom) / 100);
+}
+
+long
+inch2pixel (bool is_width,
+ double inch, long resolution, long zoom)
+{
+ return (is_width
+ ? inch2width (inch, resolution, zoom)
+ : inch2height (inch, resolution, zoom));
+}
+
+
+long
+centi2width (double centi, long resolution, long zoom)
+{
+ return (centi2pixel (centi, resolution, zoom) / 8) * 8;
+}
+
+long
+centi2height (double centi, long resolution, long zoom)
+{
+ return centi2pixel (centi, resolution, zoom);
+}
+
+long
+centi2pixel (double centi, long resolution, long zoom)
+{
+ return (long) ((centi * resolution * zoom) / (100 * CM_PER_INCH));
+}
+
+long
+centi2pixel (bool is_width,
+ double centi, long resolution, long zoom)
+{
+ return (is_width
+ ? centi2width (centi, resolution, zoom)
+ : centi2height (centi, resolution, zoom));
+}
diff --git a/frontend/pisa_change_unit.h b/frontend/pisa_change_unit.h
new file mode 100644
index 0000000..add6116
--- /dev/null
+++ b/frontend/pisa_change_unit.h
@@ -0,0 +1,50 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_CHANGE_UNIT_H
+#define ___PISA_CHANGE_UNIT_H
+
+double inches_reflect_zoom ( double inch, long zoom );
+double inch2centi ( double inch, long zoom = 100 );
+
+// Convenience API to convert dimensions to pixels the same way as the
+// epkowa backend does.
+
+long inch2width (double inch, long resolution, long zoom = 100,
+ bool monochrome = false);
+long inch2height (double inch, long resolution, long zoom = 100);
+long inch2pixel (double inch, long resolution, long zoom = 100);
+long inch2pixel (bool is_width,
+ double inch, long resolution, long zoom = 100);
+
+long centi2width (double centi, long resolution, long zoom = 100);
+long centi2height (double centi, long resolution, long zoom = 100);
+long centi2pixel (double centi, long resolution, long zoom = 100);
+long centi2pixel (bool is_width,
+ double centi, long resolution, long zoom = 100);
+
+#endif // ___PISA_CHANGE_UNIT_H
diff --git a/frontend/pisa_configuration.cc b/frontend/pisa_configuration.cc
new file mode 100644
index 0000000..3a5f52b
--- /dev/null
+++ b/frontend/pisa_configuration.cc
@@ -0,0 +1,223 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+
+/*------------------------------------------------------------*/
+#include <stdio.h>
+#include <string.h>
+
+/*------------------------------------------------------------*/
+#include "pisa_configuration.h"
+#include "pisa_view_manager.h"
+#include "pisa_error.h"
+#include "pisa_default_val.h"
+
+/*------------------------------------------------------------*/
+static gint delete_event ( GtkWidget * widget, gpointer data )
+{
+ widget = widget;
+ data = data;
+
+ ::g_view_manager->close_window ( ID_WINDOW_CONFIG, 1 );
+
+ return TRUE;
+}
+
+/*------------------------------------------------------------*/
+static void click_ok ( GtkWidget * widget, gpointer data )
+{
+ config_window * cf_cls;
+
+ widget = widget;
+
+ cf_cls = ( config_window * ) data;
+
+ cf_cls->m_ok = 1;
+
+ cf_cls->update_filename ( );
+
+ ::g_view_manager->close_window ( ID_WINDOW_CONFIG, 1 );
+}
+
+/*------------------------------------------------------------*/
+static void click_cancel ( GtkWidget * widget, gpointer data )
+{
+ config_window * cf_cls;
+
+ widget = widget;
+
+ cf_cls = ( config_window * ) data;
+
+ cf_cls->m_ok = 0;
+
+ ::g_view_manager->close_window ( ID_WINDOW_CONFIG, 1 );
+}
+
+
+/*------------------------------------------------------------*/
+int config_window::init ( void )
+{
+ int i;
+
+ for ( i = 0; i < WG_CONFIG_NUM; i++ )
+ m_widget [ i ] = 0;
+
+ m_ok = 0;
+ ::strcpy ( m_cmd, "" );
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * config_window::create_window ( GtkWidget * parent )
+{
+ GtkWidget * top;
+ GtkWidget * widget;
+
+ top = ::gtk_dialog_new ( );
+ ::gtk_window_set_policy ( GTK_WINDOW ( top ),
+ FALSE, FALSE, TRUE );
+ ::gtk_container_border_width ( GTK_CONTAINER ( GTK_DIALOG ( top )->vbox ),
+ 5 );
+ ::gtk_window_set_title ( GTK_WINDOW ( top ), _( "Configuration" ) );
+ ::gtk_widget_set_uposition ( top, POS_CONFIG_X, POS_CONFIG_Y );
+ ::gtk_widget_realize ( top );
+
+ ::gtk_signal_connect ( GTK_OBJECT ( top ), "delete_event",
+ GTK_SIGNAL_FUNC ( ::delete_event ), 0 );
+
+ m_widget [ WG_CONFIG_TOP ] = top;
+
+ widget = create_pips_path ( );
+ ::gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG ( top )->vbox ),
+ widget, FALSE, FALSE, 5 );
+ ::gtk_widget_show ( widget );
+
+ create_action_area ( );
+
+ ::gtk_window_set_modal ( GTK_WINDOW ( top ), TRUE );
+ ::gtk_window_set_transient_for ( GTK_WINDOW ( top ), GTK_WINDOW ( parent ) );
+ ::gtk_widget_show ( top );
+
+ return top;
+}
+
+/*------------------------------------------------------------*/
+int config_window::close_window ( int destroy )
+{
+ if ( destroy && m_widget [ WG_CONFIG_TOP ] )
+ ::gtk_widget_destroy ( m_widget [ WG_CONFIG_TOP ] );
+
+ m_widget [ WG_CONFIG_TOP ] = 0;
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+void config_window::update_filename ( void )
+{
+ ::strcpy ( m_cmd,
+ ::gtk_entry_get_text ( GTK_ENTRY ( m_widget [ WG_CONFIG_PATH ] ) ) );
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * config_window::create_pips_path ( void )
+{
+ GtkWidget * frame;
+ GtkWidget * vbox;
+ GtkWidget * hbox;
+ GtkWidget * comment;
+ GtkWidget * edit;
+
+ frame = ::gtk_frame_new ( _( "Print Command" ) );
+ ::gtk_container_border_width ( GTK_CONTAINER ( frame ), 5 );
+
+ vbox = ::gtk_vbox_new ( FALSE, 5 );
+ ::gtk_container_border_width ( GTK_CONTAINER ( vbox ), 5 );
+ ::gtk_container_add ( GTK_CONTAINER ( frame ), vbox );
+ ::gtk_widget_show ( vbox );
+
+ hbox = ::gtk_hbox_new ( FALSE, 5 );
+ ::gtk_container_border_width ( GTK_CONTAINER ( hbox ), 5 );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), hbox, FALSE, FALSE, 5 );
+ ::gtk_widget_show ( hbox );
+
+ edit = ::gtk_entry_new ( );
+ ::gtk_widget_set_usize ( edit, 250, 0 );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), edit, FALSE, FALSE, 5 );
+ ::gtk_widget_show ( edit );
+
+ ::gtk_entry_set_max_length ( GTK_ENTRY ( edit ), 255 );
+ ::gtk_entry_set_text ( GTK_ENTRY ( edit ), m_cmd );
+
+ m_widget [ WG_CONFIG_PATH ] = edit;
+
+ comment = ::gtk_label_new (_("In order to print, your print system "
+ "must be able to handle the PNG file "
+ "format directly. CUPS or Photo Image "
+ "Print System (versions 1.3.1 or later) "
+ "do this by default."));
+ ::gtk_label_set_line_wrap ( GTK_LABEL ( comment ), TRUE );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), comment, FALSE, FALSE, 0 );
+ ::gtk_widget_show ( comment );
+
+ return frame;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * config_window::create_action_area ( void )
+{
+ GtkWidget * button;
+
+ // OK
+ button = ::gtk_button_new_with_label ( _( " OK " ) );
+ GTK_WIDGET_SET_FLAGS ( button, GTK_CAN_DEFAULT );
+ ::gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG ( m_widget [ WG_CONFIG_TOP ] )->action_area ),
+ button, FALSE, FALSE, 0 );
+ ::gtk_signal_connect ( GTK_OBJECT ( button ), "clicked",
+ GTK_SIGNAL_FUNC ( click_ok ), this );
+ ::gtk_widget_grab_default ( button );
+ ::gtk_widget_show ( button );
+
+ // cancel
+ button = ::gtk_button_new_with_label ( _( " Cancel " ) );
+ ::gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG ( m_widget [ WG_CONFIG_TOP ] )->action_area ),
+ button, FALSE, FALSE, 0 );
+ ::gtk_signal_connect ( GTK_OBJECT ( button ), "clicked",
+ GTK_SIGNAL_FUNC ( click_cancel ), this );
+ ::gtk_widget_show ( button );
+
+ return 0;
+}
+
diff --git a/frontend/pisa_configuration.h b/frontend/pisa_configuration.h
new file mode 100644
index 0000000..9950147
--- /dev/null
+++ b/frontend/pisa_configuration.h
@@ -0,0 +1,63 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_CONFIGURATION_H
+#define ___PISA_CONFIGURATION_H
+
+#include <gtk/gtk.h>
+#include <pisa_enums.h>
+
+class config_window
+{
+ public:
+
+ // operation
+ int init ( void );
+ GtkWidget * create_window ( GtkWidget * parent );
+ int close_window ( int destroy );
+
+ void update_filename ( void );
+
+ int m_ok;
+ char m_cmd [ 256 ];
+
+ private:
+
+ // operation
+ GtkWidget * create_pips_path ( void );
+ GtkWidget * create_action_area ( void );
+
+ // attribute
+ GtkWidget * m_widget [ WG_CONFIG_NUM ];
+
+};
+
+#endif // ___PISA_CONFIGURATION_H
+
diff --git a/frontend/pisa_default_val.h b/frontend/pisa_default_val.h
new file mode 100644
index 0000000..fe4f9be
--- /dev/null
+++ b/frontend/pisa_default_val.h
@@ -0,0 +1,89 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_DEFAULT_VAL_H
+#define ___PISA_DEFAULT_VAL_H
+
+#include "esmod-wrapper.hh"
+
+#define PREFERENCE ".iscan_preference"
+
+// gamma
+#define DEFGAMMA ISCAN_DEFAULT_GAMMA
+#define MINGAMMA 0.5
+#define MAXGAMMA 5.0
+#define LINEGAMMA 0.17
+#define PAGEGAMMA 0.34
+
+// highlight
+#define DEFHIGHLIGHT ISCAN_DEFAULT_HILITE
+#define MINHIGHLIGHT 61
+#define MAXHIGHLIGHT 490
+#define LINEHIGHLIGHT 15
+#define PAGEHIGHLIGHT 30
+
+// shadow
+#define DEFSHADOW ISCAN_DEFAULT_SHADOW
+#define MINSHADOW 0
+#define MAXSHADOW 60
+#define LINESHADOW 1
+#define PAGESHADOW 2
+
+// threshold
+#define DEFTHRESHOLD ISCAN_DEFAULT_THRESHOLD
+#define MINTHRESHOLD 0
+#define MAXTHRESHOLD 255
+#define LINETHRESHOLD 1
+#define PAGETHRESHOLD 5
+
+// gray balance
+#define DEFGRAYBALANCE 0
+#define MINGRAYBALANCE 0
+#define MAXGRAYBALANCE 100
+#define LINEGRAYBALANCE 5
+#define PAGEGRAYBALANCE 25
+
+// saturation
+#define DEFSATURATION 0
+#define MINSATURATION -100
+#define MAXSATURATION 100
+#define LINESATURATION 5
+#define PAGESATURATION 25
+
+// window position
+#define POS_MAIN_X 20
+#define POS_MAIN_Y 20
+#define POS_CONFIG_X 100
+#define POS_CONFIG_Y 100
+#define POS_PRINT_X 100
+#define POS_PRINT_Y 100
+
+#endif // ___PISA_DEFAULT_VAL_H
+
diff --git a/frontend/pisa_enums.h b/frontend/pisa_enums.h
new file mode 100644
index 0000000..ab780e5
--- /dev/null
+++ b/frontend/pisa_enums.h
@@ -0,0 +1,248 @@
+/* pisa_enums.h
+ Copyright (C) 2001, 2004, 2008, 2009 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_ENUMS_H
+#define ___PISA_ENUMS_H
+
+/*! Bit flags indicating what scan sources are supported by the device.
+ */
+enum
+{
+ PISA_OP_NONE = 0,
+ PISA_OP_FLATBED = 1 << 0,
+ PISA_OP_ADF = 1 << 1,
+ PISA_OP_ADFDPLX = 1 << 2,
+ PISA_OP_TPU = 1 << 3,
+};
+
+enum
+{
+ PISA_FT_POSI,
+ PISA_FT_NEGA,
+ PISA_FT_REFLECT,
+};
+
+enum
+{
+ PISA_DFD_OFF,
+ PISA_DFD_STD,
+ PISA_DFD_THIN,
+};
+
+// destination
+typedef enum
+{
+ PISA_DE_FILE,
+ PISA_DE_PRINTER,
+ PISA_DE_GIMP
+} pisa_dest_type;
+
+// pixel type
+typedef enum
+{
+ PISA_PT_BW,
+ PISA_PT_GRAY,
+ PISA_PT_RGB
+} pisa_pixel_type;
+
+// bit depth
+typedef enum
+{
+ PISA_BD_1 = 1 << 0,
+ PISA_BD_8 = 1 << 1,
+ PISA_BD_12 = 1 << 2,
+ PISA_BD_14 = 1 << 3,
+ PISA_BD_16 = 1 << 4
+} pisa_bitdepth_type;
+
+// de-screening
+typedef enum
+{
+ PISA_DESCREEN_ON,
+ PISA_DESCREEN_OFF
+} pisa_descreening_type;
+
+// auto exposure option
+typedef enum
+{
+ PISA_AE_PHOTO,
+ PISA_AE_DOC,
+ PISA_AE_GRAYED
+} pisa_aeoption_type;
+
+// dropout
+typedef enum
+{
+ PISA_DO_NONE,
+ PISA_DO_RED,
+ PISA_DO_GREEN,
+ PISA_DO_BLUE
+} pisa_dropout_type;
+
+// mono option
+typedef enum
+{
+ PISA_MO_NONE,
+ PISA_MO_TET,
+ PISA_MO_AAS
+} pisa_monooption_type;
+
+// halftone
+typedef enum
+{
+ PISA_HT_NONE,
+ PISA_HT_TONEA,
+ PISA_HT_TONEB,
+ PISA_HT_TONEC,
+ PISA_HT_DITHERA,
+ PISA_HT_DITHERB,
+ PISA_HT_DITHERC,
+ PISA_HT_DITHERD,
+ PISA_HT_USERA,
+ PISA_HT_USERB
+} pisa_halftone_type;
+
+// unit
+typedef enum
+{
+ PISA_UNIT_INCHES,
+ PISA_UNIT_PIXELS,
+ PISA_UNIT_CM
+} pisa_unit_type;
+
+// cursor
+typedef enum
+{
+ PISA_CS_ARROW,
+ PISA_CS_HAND,
+ PISA_CS_CROSS,
+ PISA_CS_TOP,
+ PISA_CS_BOTTOM,
+ PISA_CS_LEFT,
+ PISA_CS_RIGHT,
+ PISA_CS_LEFTTOP,
+ PISA_CS_LEFTBOTTOM,
+ PISA_CS_RIGHTTOP,
+ PISA_CS_RIGHTBOTTOM
+} pisa_cursor_type;
+
+// cursor gamma table
+typedef enum {
+ PISA_CS_GM_X,
+ PISA_CS_GM_FLEUR,
+ PISA_CS_GM_TCROSS,
+} pisa_cursor_gm_type;
+
+// resize
+typedef enum
+{
+ PISA_RS_NN=1, // nearest neighbor
+ PISA_RS_BL, // bi linear
+ PISA_RS_BC, // bi cubic
+} pisa_rs_type;
+
+// sharp
+typedef enum
+{
+ PISA_SH_UMASK = 1, // unsharp mask
+ PISA_SH_GAUSS, // gauss
+ PISA_SH_UMASKY, // unsahrp mask y
+} pisa_sh_type;
+
+// window
+typedef enum
+{
+ ID_WINDOW_MAIN,
+ ID_WINDOW_PREV,
+ ID_WINDOW_CONFIG,
+} pisa_window_id;
+
+// main window
+enum widget_main_window
+{
+ WG_MAIN_TOP,
+ WG_MAIN_IMG_MENU,
+ WG_MAIN_RES_MENU,
+ WG_MAIN_WIDTH,
+ WG_MAIN_HEIGHT,
+ WG_MAIN_FOCUS_0,
+ WG_MAIN_FOCUS_25,
+ WG_MAIN_DFD,
+ WG_MAIN_DFD_BOX,
+ WG_MAIN_DFD_STD,
+ WG_MAIN_DFD_THIN,
+ WG_MAIN_NUM
+};
+
+// gamma correction
+#define GAMMA_RGB 0x00
+#define GAMMA_RED 0x01
+#define GAMMA_GRN 0x02
+#define GAMMA_BLU 0x03
+
+enum widget_gamma_window
+{
+ WG_GAMMA_TAB_RGB,
+ WG_GAMMA_TAB_RED,
+ WG_GAMMA_TAB_GRN,
+ WG_GAMMA_TAB_BLU,
+ WG_GAMMA_BOX_RGB,
+ WG_GAMMA_BOX_RED,
+ WG_GAMMA_BOX_GRN,
+ WG_GAMMA_BOX_BLU,
+ WG_GAMMA_RESET,
+ WG_GAMMA_NOTE,
+ WG_GAMMA_NUM
+};
+
+// configuration
+enum widget_configuration
+{
+ WG_CONFIG_TOP,
+ WG_CONFIG_PATH,
+ WG_CONFIG_NUM
+};
+
+// file select
+enum widget_file_select
+{
+ WG_FS_TOP,
+ WG_FS_NUM
+};
+
+// print window
+enum widget_print_window
+{
+ WG_PRINT_TOP,
+ WG_PRINT_NUM
+};
+
+#endif // ___PISA_ENUMS_H
+
+
diff --git a/frontend/pisa_error.cc b/frontend/pisa_error.cc
new file mode 100644
index 0000000..13cf51c
--- /dev/null
+++ b/frontend/pisa_error.cc
@@ -0,0 +1,178 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2009 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+
+/*------------------------------------------------------------*/
+#include "pisa_error.h"
+
+pisa_error::pisa_error( pisa_error_id status )
+ : m_id( status )
+{
+}
+
+pisa_error::pisa_error( SANE_Status status )
+ : m_id( pisa_error_id( status | 0xff00 ) )
+{
+}
+
+enum // FIXME: needs sync with epkowa.c!
+ {
+ EXT_SANE_STATUS_NONE,
+ EXT_SANE_STATUS_MULTI_FEED,
+ EXT_SANE_STATUS_TRAY_CLOSED,
+ EXT_SANE_STATUS_MAX,
+ };
+
+pisa_error::pisa_error (SANE_Status status, const sane_scan& ss)
+ : m_id (pisa_error_id (status | 0xff00))
+{
+ SANE_Word value = 0;
+
+ switch (m_id)
+ {
+ case PISA_STATUS_JAMMED:
+ ss.get_value ("ext-sane-status", static_cast<void *> (&value), true);
+ if (EXT_SANE_STATUS_MULTI_FEED == value)
+ m_id = PISA_ERR_MULTI_FEED;
+ break;
+ case PISA_STATUS_COVER_OPEN:
+ ss.get_value ("ext-sane-status", static_cast<void *> (&value), true);
+ if (EXT_SANE_STATUS_TRAY_CLOSED == value)
+ m_id = PISA_ERR_TRAY_CLOSED;
+ break;
+ default:
+ ;
+ }
+}
+
+const char * pisa_error::get_error_string ( void ) const
+{
+ switch (remap())
+ {
+ // Let's get the SANE status IDs out of the way first.
+ case PISA_STATUS_GOOD:
+ return _("Operation completed succesfully.");
+ case PISA_STATUS_UNSUPPORTED:
+ return _("Operation is not supported.");
+ case PISA_STATUS_CANCELLED:
+ return _("Operation was cancelled.");
+ case PISA_STATUS_DEVICE_BUSY:
+ return _("Device is busy---retry later.");
+ case PISA_STATUS_INVAL:
+ return _("Data or argument is invalid.");
+ case PISA_STATUS_EOF:
+ return _("No more data available (end-of-file).");
+ case PISA_STATUS_JAMMED:
+ return _("A paper jam occured. "
+ "Open the Automatic Document Feeder and remove any paper.");
+ case PISA_STATUS_NO_DOCS:
+ return _("Please load the document(s) into the Automatic Document "
+ "Feeder.");
+ case PISA_STATUS_COVER_OPEN:
+ return _("The automatic document feeder or scanner unit is open.\n"
+ "Please close it.");
+ case PISA_STATUS_IO_ERROR:
+ return _("Error during device I/O.");
+ case PISA_STATUS_NO_MEM:
+ return _("Out of memory.");
+ case PISA_STATUS_ACCESS_DENIED:
+ return _("Access to resource has been denied.");
+ // Now we add our own.
+ case PISA_ERR_OUTOFMEMORY:
+ return _( "There is not enough disk space for operation" );
+
+ case PISA_ERR_CONNECT:
+ return _( "Could not send command to scanner.\n"
+ "Check the scanner's status." );
+
+ case PISA_ERR_UNSUPPORT:
+ return _( "Scanner model not supported" );
+
+ case PISA_ERR_AREALARGE:
+ return _( "Selected area is too large for this resolution.\n"
+ "Reduce the selected area or resolution." );
+
+ case PISA_ERR_FILENAME:
+ return _( "Could not create file" );
+
+ case PISA_ERR_FILEOPEN:
+ return _( "Could not create file" );
+
+ case PISA_ERR_OVERWRITE:
+ return _( "A file with the same name already exists.\n"
+ "Click \"Overwrite\" to replace the file or "
+ "\"Cancel\" if you want to use another file name.");
+
+ case PISA_ERR_MRRESTOOHIGH:
+ return _( "The Image Type setting you selected cannot be used "
+ "with this resolution.\n"
+ "Reduce the Resolution or Scale setting." );
+
+ case PISA_ERR_TRAY_CLOSED:
+ return _("Tray cover is closed. Please open the tray cover and "
+ "then scan again.");
+
+ case PISA_ERR_MULTI_FEED:
+ return _("A multi page feed occurred in the auto document feeder.\n"
+ "Open the cover, remove the documents, and then try again. "
+ "If documents remain on the tray, remove them and then reload "
+ "them.");
+
+ default:
+ break;
+ }
+
+ return _( "Unexpected error occurred" );
+}
+
+pisa_error_id
+pisa_error::remap() const
+{
+ switch (m_id)
+ {
+ // do what pase_sane_scan.cc used (yuck!) to do, except for
+ // statuses that we really need to special case
+ case PISA_STATUS_UNSUPPORTED:
+ case PISA_STATUS_DEVICE_BUSY:
+ case PISA_STATUS_INVAL:
+ case PISA_STATUS_EOF:
+ case PISA_STATUS_IO_ERROR:
+ case PISA_STATUS_NO_MEM:
+ case PISA_STATUS_ACCESS_DENIED:
+ return PISA_ERR_CONNECT;
+ default:
+ return m_id;
+ }
+ return PISA_ERR_INVALID_ERROR_ID;
+}
diff --git a/frontend/pisa_error.h b/frontend/pisa_error.h
new file mode 100644
index 0000000..7961207
--- /dev/null
+++ b/frontend/pisa_error.h
@@ -0,0 +1,104 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2008, 2009 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_ERROR_H
+#define ___PISA_ERROR_H
+
+#include "pisa_sane_scan.h"
+
+typedef enum
+{
+ PISA_ERR_SUCCESS,
+ PISA_ERR_PARAMETER,
+ PISA_ERR_OUTOFMEMORY,
+ PISA_ERR_CONNECT,
+ PISA_ERR_UNSUPPORT,
+ PISA_ERR_AREALARGE,
+ PISA_ERR_FILENAME,
+ PISA_ERR_FILEOPEN,
+ PISA_ERR_OVERWRITE,
+ PISA_ERR_MRRESTOOHIGH,
+ PISA_ERR_TRAY_CLOSED,
+ PISA_ERR_MULTI_FEED,
+ // Make sure we can use the SANE status symbols. To avoid name
+ // clashes, replace SANE with PISA and put them in another range.
+ PISA_STATUS_GOOD = 0xff00, // 0 Operation completed succesfully.
+ PISA_STATUS_UNSUPPORTED, // 1 Operation is not supported.
+ PISA_STATUS_CANCELLED, // 2 Operation was cancelled.
+ PISA_STATUS_DEVICE_BUSY, // 3 Device is busy---retry later.
+ PISA_STATUS_INVAL, // 4 Data or argument is invalid.
+ PISA_STATUS_EOF, // 5 No more data available (end-of-file).
+ PISA_STATUS_JAMMED, // 6 Document feeder jammed.
+ PISA_STATUS_NO_DOCS, // 7 Document feeder out of documents.
+ PISA_STATUS_COVER_OPEN, // 8 Scanner cover is open.
+ PISA_STATUS_IO_ERROR, // 9 Error during device I/O.
+ PISA_STATUS_NO_MEM, // 10 Out of memory.
+ PISA_STATUS_ACCESS_DENIED, // 11 Access to resource has been denied.
+
+ PISA_ERR_INVALID_ERROR_ID
+} pisa_error_id;
+
+class pisa_error
+{
+ public:
+
+ // constructor
+ pisa_error ( pisa_error_id id );
+ pisa_error ( SANE_Status id );
+ pisa_error (SANE_Status id, const sane_scan& ss);
+
+ bool operator== (const pisa_error& e) const;
+ bool operator!= (const pisa_error& e) const;
+
+ pisa_error_id get_error_id ( void ) const { return remap(); };
+ const char * get_error_string ( void ) const;
+
+ private:
+
+ // attribute
+ pisa_error_id m_id;
+
+ pisa_error_id remap() const;
+};
+
+inline
+bool pisa_error::operator== (const pisa_error& e) const
+{
+ return e.m_id == m_id;
+}
+
+inline
+bool pisa_error::operator!= (const pisa_error& e) const
+{
+ return !(e == *this);
+}
+
+#endif // ___PISA_ERROR_H
+
diff --git a/frontend/pisa_esmod_structs.h b/frontend/pisa_esmod_structs.h
new file mode 100644
index 0000000..027cd9c
--- /dev/null
+++ b/frontend/pisa_esmod_structs.h
@@ -0,0 +1,75 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_ESMOD_STRUCTES_H
+#define ___PISA_ESMOD_STRUCTES_H
+
+#include "pisa_enums.h"
+
+/*--------------------------------------------------------------*/
+struct img_size
+{
+ long in_width;
+ long in_height;
+ long in_rowbytes;
+
+ long out_width;
+ long out_height;
+ long out_rowbytes;
+
+ short bits_per_pixel;
+};
+
+/*--------------------------------------------------------------*/
+struct moire_img_info : public img_size
+{
+ long resolution;
+ long in_resolution;
+};
+
+/*--------------------------------------------------------------*/
+struct resize_img_info : public img_size
+{
+ pisa_rs_type resize_flag;
+
+ long resolution;
+};
+
+/*--------------------------------------------------------------*/
+struct sharp_img_info : public img_size
+{
+ unsigned long strength;
+ unsigned long radius;
+ unsigned long clipping;
+
+ pisa_sh_type sharp_flag;
+};
+
+#endif // ___PISA_ESMOD_STRUCTES_H
diff --git a/frontend/pisa_gamma_correction.cc b/frontend/pisa_gamma_correction.cc
new file mode 100644
index 0000000..83dd232
--- /dev/null
+++ b/frontend/pisa_gamma_correction.cc
@@ -0,0 +1,718 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005, 2008 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+
+/*------------------------------------------------------------*/
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*------------------------------------------------------------*/
+#include "pisa_view_manager.h"
+#include "pisa_gamma_correction.h"
+#include "xpm_data.h"
+#include "pisa_tool.h"
+#include "pisa_error.h"
+
+#define RADIUS 3
+#define MIN_DISTANCE 8
+#define NUM_POINTS 13
+
+/*------------------------------------------------------------*/
+char ** g_gamma_xpm [ ] =
+{
+ gamma_m_xpm, // mono
+ gamma_r_xpm, // red
+ gamma_g_xpm, // green
+ gamma_b_xpm, // blue
+};
+
+/*------------------------------------------------------------*/
+static void switch_page ( GtkNotebook * notebook,
+ GtkNotebookPage * page,
+ gint page_num,
+ gpointer * data )
+{
+ gamma_correction * gamma_cls;
+
+ notebook = notebook;
+ page = page;
+
+ gamma_cls = ( gamma_correction * ) data;
+
+ gamma_cls->change_page ( page_num );
+}
+
+/*------------------------------------------------------------*/
+static void click_reset ( GtkWidget * widget,
+ gpointer * data )
+{
+ gamma_correction * gamma_cls;
+
+ widget = widget;
+
+ gamma_cls = ( gamma_correction * ) data;
+
+ gamma_cls->reset ( 0 );
+
+ ::g_view_manager->update_lut ( );
+
+ preview_window *prev_cls =
+ (preview_window *) ::g_view_manager->get_window_cls (ID_WINDOW_PREV);
+
+ prev_cls->update_img ( );
+}
+
+/*------------------------------------------------------------*/
+static gint gamma_curve_event ( GtkWidget * widget,
+ GdkEvent * event,
+ gpointer * data )
+{
+ gamma_correction * gamma_cls;
+
+ gamma_cls = ( gamma_correction * ) data;
+
+ return gamma_cls->event ( widget, event );
+}
+
+/*------------------------------------------------------------*/
+int gamma_correction::init ( void )
+{
+ int i;
+
+ m_page = 0;
+
+ for ( i = 0; i < WG_GAMMA_NUM; i++ )
+ m_widget [ i ] = 0;
+
+ for ( i = 0; i < 4; i++ )
+ {
+ m_pixmap [ i ] = 0;
+ m_active [ i ] = 0;
+
+ reset_points ( i );
+ }
+
+ m_basis [ 0 ] [ 0 ] = -0.5; m_basis [ 0 ] [ 1 ] = 1.5; m_basis [ 0 ] [ 2 ] = -1.5; m_basis [ 0 ] [ 3 ] = 0.5;
+ m_basis [ 1 ] [ 0 ] = 1.0; m_basis [ 1 ] [ 1 ] = -2.5; m_basis [ 1 ] [ 2 ] = 2.0; m_basis [ 1 ] [ 3 ] = -0.5;
+ m_basis [ 2 ] [ 0 ] = -0.5; m_basis [ 2 ] [ 1 ] = 0.0; m_basis [ 2 ] [ 2 ] = 0.5; m_basis [ 2 ] [ 3 ] = 0.0;
+ m_basis [ 3 ] [ 0 ] = 0.0; m_basis [ 3 ] [ 1 ] = 1.0; m_basis [ 3 ] [ 2 ] = 0.0; m_basis [ 3 ] [ 3 ] = 0.0;
+
+ for ( i = 0; i < 2; i++ )
+ m_cursor [ i ] = 0;
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * gamma_correction::create_controls ( GtkWidget * parent )
+{
+ GtkWidget * vbox;
+ GtkWidget * widget;
+
+ vbox = ::gtk_vbox_new ( FALSE, 2 );
+ widget = create_gamma_notebook ( parent );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), widget, FALSE, FALSE, 0 );
+ ::gtk_widget_show ( widget );
+
+ widget = create_reset_button ( );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), widget, FALSE, FALSE, 0 );
+ ::gtk_widget_show ( widget );
+
+ m_cursor [ PISA_CS_GM_X ] = ::gdk_cursor_new ( GDK_X_CURSOR );
+ m_cursor [ PISA_CS_GM_FLEUR ] = ::gdk_cursor_new ( GDK_FLEUR );
+ m_cursor [ PISA_CS_GM_TCROSS ] = ::gdk_cursor_new ( GDK_TCROSS );
+
+ return vbox;
+}
+
+/*------------------------------------------------------------*/
+int gamma_correction::close_window ( int destroy )
+{
+ int i;
+
+ destroy = destroy;
+
+ for ( i = 0; i < 2; i++ )
+ {
+ if ( m_cursor [ i ] )
+ ::gdk_cursor_destroy ( m_cursor [ i ] );
+ m_cursor [ i ] = 0;
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+void gamma_correction::sensitive ( int is_prev_img )
+{
+ gboolean enable_rgb, enable_r_g_b;
+
+ settings set = g_view_manager->get_settings ();
+
+ // grayout
+ switch ( set.imgtype.pixeltype )
+ {
+ case PISA_PT_RGB:
+ enable_rgb = TRUE;
+ enable_r_g_b = TRUE;
+ break;
+ case PISA_PT_GRAY:
+ enable_rgb = TRUE;
+ enable_r_g_b = FALSE;
+ break;
+ case PISA_PT_BW:
+ enable_rgb = FALSE;
+ enable_r_g_b = FALSE;
+ break;
+ default:
+ return;
+ }
+
+ if ( is_prev_img == 0 )
+ enable_rgb = enable_r_g_b = FALSE;
+
+ m_active [ 0 ] = enable_rgb;
+ m_active [ 1 ] = enable_r_g_b;
+ m_active [ 2 ] = enable_r_g_b;
+ m_active [ 3 ] = enable_r_g_b;
+
+ ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_TAB_RGB ], enable_rgb );
+ ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_TAB_RED ], enable_r_g_b );
+ ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_TAB_GRN ], enable_r_g_b );
+ ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_TAB_BLU ], enable_r_g_b );
+
+ ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_BOX_RGB ], enable_rgb );
+ ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_BOX_RED ], enable_r_g_b );
+ ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_BOX_GRN ], enable_r_g_b );
+ ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_BOX_BLU ], enable_r_g_b );
+
+ if ( enable_rgb == TRUE && enable_r_g_b == FALSE )
+ ::gtk_notebook_set_page ( GTK_NOTEBOOK ( m_widget [ WG_GAMMA_NOTE ] ),
+ GAMMA_RGB );
+
+ ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_RESET ], m_active [ m_page ] );
+
+ update_gamma ( );
+}
+
+/*------------------------------------------------------------*/
+void gamma_correction::change_page ( int page )
+{
+ m_page = page;
+
+ ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_RESET ], m_active [ m_page ] );
+
+}
+
+
+/*------------------------------------------------------------*/
+void gamma_correction::reset ( int all )
+{
+ if ( all == 1 )
+ {
+ int i;
+
+ for ( i = 0; i < 4; i++ )
+ reset_points ( i );
+ }
+ else
+ reset_points ( m_page );
+
+ calculate_curve ( );
+ update_gamma ( );
+}
+
+/*------------------------------------------------------------*/
+gint gamma_correction::event ( GtkWidget * widget, GdkEvent * event )
+{
+ static int cursor_type = PISA_CS_GM_FLEUR;
+ int new_type;
+ GdkEventMotion * mevent = NULL;
+ gint tx, ty, x, y;
+ gint i, distance, closest_point;
+
+ ::gdk_window_get_pointer ( m_drawarea [ m_page ]->window,
+ & tx, & ty, 0 );
+ x = CLAMP ( ( tx - RADIUS ), 0, GAMMA_WIDTH - 1 );
+ y = CLAMP ( ( ty - RADIUS ), 0, GAMMA_HEIGHT - 1 );
+
+ distance = G_MAXINT;
+ closest_point = 0;
+
+ for ( i = 0; i < NUM_POINTS; i++ )
+ {
+ if ( m_points [ m_page ] [ i ] [ 0 ] != -1 )
+ if ( ::abs ( x - m_points [ m_page ] [ i ] [ 0 ] ) < distance )
+ {
+ distance = ::abs ( x - m_points [ m_page ] [ i ] [ 0 ] );
+ closest_point = i;
+ }
+ }
+
+ if ( distance > MIN_DISTANCE )
+ closest_point = ( x + 8 ) / 16;
+
+ switch ( event->type )
+ {
+ case GDK_EXPOSE:
+ if ( m_pixmap [ m_page ] == 0 )
+ m_pixmap [ m_page ] = ::gdk_pixmap_new ( m_drawarea [ m_page ]->window,
+ GAMMA_WIDTH + RADIUS * 2,
+ GAMMA_HEIGHT + RADIUS * 2,
+ -1 );
+ update_gamma ( );
+ break;
+
+ case GDK_BUTTON_PRESS:
+ new_type = PISA_CS_GM_TCROSS;
+
+ m_leftmost = -1;
+ for ( i = closest_point - 1; i >= 0; i-- )
+ if ( m_points [ m_page ] [ i ] [ 0 ] != -1 )
+ {
+ m_leftmost = m_points [ m_page ] [ i ] [ 0 ];
+ break;
+ }
+ m_rightmost = GAMMA_WIDTH;
+ for ( i = closest_point + 1; i < NUM_POINTS; i++ )
+ if ( m_points [ m_page ] [ i ] [ 0 ] != -1 )
+ {
+ m_rightmost = m_points [ m_page ] [ i ][ 0 ];
+ break;
+ }
+
+ m_grabpoint = closest_point;
+ m_points [ m_page ] [ m_grabpoint ] [ 0 ] = x;
+ m_points [ m_page ] [ m_grabpoint ] [ 1 ] = GAMMA_HEIGHT - 1 - y;
+
+ calculate_curve ( );
+ update_gamma ( );
+ gtk_grab_add ( widget );
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ new_type = PISA_CS_GM_FLEUR;
+ m_grabpoint = -1;
+ gtk_grab_remove ( widget );
+ ::g_view_manager->update_lut ( );
+ {
+ preview_window *prev_cls =
+ (preview_window *) ::g_view_manager->get_window_cls (ID_WINDOW_PREV);
+
+ prev_cls->update_img ( );
+ }
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ mevent = ( GdkEventMotion * ) event;
+
+ if ( mevent->is_hint )
+ {
+ mevent->x = tx;
+ mevent->y = ty;
+ }
+
+ if ( m_grabpoint == -1 )
+ {
+ if ( m_points [ m_page ] [ closest_point ] [ 0 ] != -1 )
+ new_type = PISA_CS_GM_FLEUR;
+ else
+ new_type = PISA_CS_GM_TCROSS;
+ }
+ else
+ {
+ new_type = PISA_CS_GM_TCROSS;
+
+ m_points [ m_page ] [ m_grabpoint ] [ 0 ] = -1;
+
+ if ( x > m_leftmost && x < m_rightmost )
+ {
+ closest_point = ( x + 8 ) / 16;
+ if ( m_points [ m_page ] [ closest_point ] [ 0 ] == -1 )
+ m_grabpoint = closest_point;
+
+ m_points [ m_page ] [ m_grabpoint ] [ 0 ] = x;
+ m_points [ m_page ] [ m_grabpoint ] [ 1 ] = GAMMA_HEIGHT - 1 - y;
+ }
+ calculate_curve ( );
+ update_gamma ( );
+ }
+
+ if ( new_type != cursor_type )
+ {
+ cursor_type = new_type;
+ ::gdk_window_set_cursor ( m_drawarea [ m_page ]->window,
+ m_cursor [ cursor_type ] );
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * gamma_correction::create_gamma_notebook ( GtkWidget * parent )
+{
+ GtkWidget * hbox;
+ GtkWidget * notebook;
+ GtkWidget * vbox;
+ GtkWidget * img;
+ GtkWidget * gamma_curve;
+ int i;
+
+ hbox = ::gtk_hbox_new ( FALSE, 2 );
+
+ notebook = ::gtk_notebook_new ( );
+ ::gtk_container_border_width ( GTK_CONTAINER ( notebook ), 2 );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), notebook, TRUE, FALSE, 0 );
+
+ for ( i = 0; i < 4; i++ )
+ {
+ vbox = gtk_vbox_new ( FALSE, 5 );
+
+ img = ::xpm2widget ( parent, g_gamma_xpm [ i ] );
+
+ m_widget [ i ] = img;
+
+ ::gtk_notebook_append_page ( GTK_NOTEBOOK ( notebook ), vbox, img );
+ ::gtk_widget_show ( vbox );
+
+ gamma_curve = create_gamma_curve ( i );
+
+ m_widget [ i + 4 ] = gamma_curve;
+
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), gamma_curve, FALSE, FALSE, 0 );
+ ::gtk_widget_show ( gamma_curve );
+ }
+
+ ::gtk_signal_connect ( GTK_OBJECT ( notebook ),
+ "switch_page",
+ GTK_SIGNAL_FUNC ( ::switch_page ),
+ this );
+
+ ::gtk_widget_show ( notebook );
+
+ m_widget [ WG_GAMMA_NOTE ] = notebook;
+
+ return hbox;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * gamma_correction::create_gamma_curve ( int i )
+{
+ GtkWidget * frame;
+ GtkWidget * gamma_curve;
+
+ frame = ::gtk_frame_new ( 0 );
+ ::gtk_frame_set_shadow_type ( GTK_FRAME ( frame ), GTK_SHADOW_ETCHED_IN );
+
+ gamma_curve = ::gtk_drawing_area_new ( );
+ ::gtk_drawing_area_size ( GTK_DRAWING_AREA ( gamma_curve ),
+ GAMMA_WIDTH + RADIUS * 2,
+ GAMMA_HEIGHT + RADIUS * 2 );
+
+ ::gtk_widget_set_events ( gamma_curve,
+ GDK_EXPOSURE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON1_MOTION_MASK );
+
+ m_drawarea [ i ] = gamma_curve;
+
+ ::gtk_signal_connect ( GTK_OBJECT ( gamma_curve ), "event",
+ GTK_SIGNAL_FUNC ( gamma_curve_event ), this );
+
+
+ ::gtk_container_add ( GTK_CONTAINER ( frame ), gamma_curve );
+
+ ::gtk_widget_show ( gamma_curve );
+
+ return frame;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * gamma_correction::create_reset_button ( void )
+{
+ GtkWidget * hbox;
+ GtkWidget * button;
+
+ hbox = ::gtk_hbox_new ( FALSE, 5 );
+ ::gtk_container_border_width ( GTK_CONTAINER ( hbox ), 5 );
+
+ button = ::gtk_button_new_with_label ( _( " Reset " ) );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), button, TRUE, FALSE, 0 );
+ ::gtk_signal_connect ( GTK_OBJECT ( button ), "clicked",
+ GTK_SIGNAL_FUNC ( ::click_reset ), this );
+ ::gtk_widget_show ( button );
+
+ m_widget [ WG_GAMMA_RESET ] = button;
+
+ return hbox;
+}
+
+/*------------------------------------------------------------*/
+void gamma_correction::reset_points ( int i )
+{
+ int j;
+
+ m_grabpoint = -1;
+
+ for ( j = 0; j < NUM_POINTS; j++ )
+ {
+ m_points [ i ] [ j ] [ 0 ] = -1;
+ m_points [ i ] [ j ] [ 1 ] = -1;
+ }
+
+ for ( j = 0; j < GAMMA_WIDTH; j++ )
+ m_curve [ i ] [ j ] = j;
+
+ m_points [ i ] [ 0 ] [ 0 ] = 0;
+ m_points [ i ] [ 0 ] [ 1 ] = 0;
+ m_points [ i ] [ NUM_POINTS - 1 ] [ 0 ] = GAMMA_WIDTH - 1;
+ m_points [ i ] [ NUM_POINTS - 1 ] [ 1 ] = GAMMA_HEIGHT - 1;
+}
+
+/*------------------------------------------------------------*/
+void gamma_correction::update_gamma ( void )
+{
+ int i;
+ GdkPoint points [ GAMMA_WIDTH ];
+ GdkGC * gc;
+
+ if ( m_pixmap [ m_page ] == 0 )
+ return;
+
+ // clear the pixmap
+ ::gdk_draw_rectangle ( m_pixmap [ m_page ],
+ m_drawarea [ m_page ]->style->bg_gc [ GTK_STATE_NORMAL ],
+ TRUE, 0, 0,
+ GAMMA_WIDTH + RADIUS * 2, GAMMA_HEIGHT + RADIUS * 2 );
+
+ // draw the grid line
+ for ( i = 0; i < 13; i++ )
+ {
+ ::gdk_draw_line ( m_pixmap [ m_page ],
+ m_drawarea [ m_page ]->style->dark_gc [ GTK_STATE_NORMAL ],
+ RADIUS, i * ( GAMMA_HEIGHT / 12 ) + RADIUS,
+ GAMMA_WIDTH + RADIUS, i * ( GAMMA_HEIGHT / 12 ) + RADIUS );
+ ::gdk_draw_line ( m_pixmap [ m_page ],
+ m_drawarea [ m_page ]->style->dark_gc [ GTK_STATE_NORMAL ],
+ i * ( GAMMA_WIDTH / 12 ) + RADIUS, RADIUS,
+ i * ( GAMMA_WIDTH / 12 ) + RADIUS, GAMMA_HEIGHT + RADIUS );
+ }
+
+ if ( m_active [ m_page ] )
+ gc = m_drawarea [ m_page ]->style->black_gc;
+ else
+ gc = m_drawarea [ m_page ]->style->dark_gc [ GTK_STATE_NORMAL ];
+
+ // draw the curve
+ for ( i = 0; i < GAMMA_WIDTH; i++ )
+ {
+ points [ i ].x = i + RADIUS;
+ points [ i ].y = ( GAMMA_HEIGHT ) - m_curve [ m_page ] [ i ] + RADIUS;
+ }
+
+ ::gdk_draw_points ( m_pixmap [ m_page ],
+ gc, points, GAMMA_WIDTH );
+
+ // draw the points
+ for ( i = 0; i < NUM_POINTS; i++ )
+ {
+ if ( m_points [ m_page ] [ i ] [ 0 ] != -1 )
+ ::gdk_draw_arc ( m_pixmap [ m_page ],
+ gc,
+ TRUE,
+ m_points [ m_page ] [ i ] [ 0 ],
+ ( GAMMA_HEIGHT - 1 ) - m_points [ m_page ] [ i ] [ 1 ],
+ RADIUS * 2, RADIUS * 2, 0, 23040 );
+ }
+
+ ::gdk_draw_pixmap ( m_drawarea [ m_page ]->window,
+ m_drawarea [ m_page ]->style->black_gc,
+ m_pixmap [ m_page ],
+ 0, 0, 0, 0, GAMMA_WIDTH + RADIUS * 2,
+ GAMMA_HEIGHT + RADIUS * 2 );
+ return;
+}
+
+/*------------------------------------------------------------*/
+void gamma_correction::calculate_curve ( void )
+{
+ int i, num;
+ int p1, p2, p3, p4;
+ int points [ NUM_POINTS ];
+ marquee * marq;
+
+ num = 0;
+
+ for ( i = 0; i < NUM_POINTS; i++ )
+ if ( m_points [ m_page ] [ i ] [ 0 ] != -1 )
+ points [ num++ ] = i;
+
+ if ( 0 < num )
+ {
+ for ( i = 0; i < m_points [ m_page ] [ points [ 0 ] ] [ 0 ]; i++ )
+ {
+ m_curve [ m_page ] [ i ] =
+ m_points [ m_page ] [ points [ 0 ] ] [ 1 ];
+ }
+
+ for ( i = m_points [ m_page ] [ points [ num - 1 ] ] [ 0 ]; i < GAMMA_WIDTH; i++ )
+ {
+ m_curve [ m_page ] [ i ] =
+ m_points [ m_page ] [ points [ num - 1 ] ] [ 1 ];
+ }
+ }
+
+ for ( i = 0; i < num - 1; i++ )
+ {
+ p1 = ( i == 0 ) ? points [ i ] : points [ ( i - 1 ) ];
+ p2 = points [ i ];
+ p3 = points [ ( i + 1 ) ];
+ p4 = ( i == ( num - 2 ) ) ? points [ ( num - 1 ) ] : points [ ( i + 2 ) ];
+
+ plot_curve ( p1, p2, p3, p4 );
+ }
+
+ marq = & g_view_manager->get_marquee ();
+
+ for ( i = 0; i < 256; i++ )
+ marq->gamma_table [ m_page ] [ i ] =
+ 255 * ( m_curve [ m_page ] [ 191 * i / 255 ] ) / ( GAMMA_WIDTH - 1 );
+}
+
+/*------------------------------------------------------------*/
+void gamma_correction::plot_curve ( int p1, int p2, int p3, int p4 )
+{
+ matrix geometry;
+ matrix tmp1, tmp2;
+ matrix deltas;
+ double x, dx, dx2, dx3;
+ double y, dy, dy2, dy3;
+ double d, d2, d3;
+ int lastx, lasty;
+ int newx, newy;
+ int i;
+
+ for ( i = 0; i < 4; i++ )
+ {
+ geometry [ i ] [ 0 ] = 0;
+ geometry [ i ] [ 1 ] = 0;
+ geometry [ i ] [ 2 ] = 0;
+ geometry [ i ] [ 3 ] = 0;
+ }
+
+ for ( i = 0; i < 2; i++ )
+ {
+ geometry [ 0 ] [ i ] = m_points [ m_page ] [ p1 ] [ i ];
+ geometry [ 1 ] [ i ] = m_points [ m_page ] [ p2 ] [ i ];
+ geometry [ 2 ] [ i ] = m_points [ m_page ] [ p3 ] [ i ];
+ geometry [ 3 ] [ i ] = m_points [ m_page ] [ p4 ] [ i ];
+ }
+
+ d = 1.0 / 1000;
+ d2 = d * d;
+ d3 = d * d * d;
+
+ tmp2 [ 0 ] [ 0 ] = 0; tmp2 [ 0 ] [ 1 ] = 0; tmp2 [ 0 ] [ 2 ] = 0; tmp2 [ 0 ] [ 3 ] = 1;
+ tmp2 [ 1 ] [ 0 ] = d3; tmp2 [ 1 ] [ 1 ] = d2; tmp2 [ 1 ] [ 2 ] = d; tmp2 [ 1 ] [ 3 ] = 0;
+ tmp2 [ 2 ] [ 0 ] = 6 * d3; tmp2 [ 2 ] [ 1 ] = 2 * d2; tmp2 [ 2 ] [ 2 ] = 0; tmp2 [ 2 ] [ 3 ] = 0;
+ tmp2 [ 3 ] [ 0 ] = 6 * d3; tmp2 [ 3 ] [ 1 ] = 0; tmp2 [ 3 ] [ 2 ] = 0; tmp2 [ 3 ] [ 3 ] = 0;
+
+ compose_curve ( m_basis, geometry, tmp1 );
+
+ compose_curve ( tmp2, tmp1, deltas );
+
+ x = deltas [ 0 ] [ 0 ];
+ dx = deltas [ 1 ] [ 0 ];
+ dx2 = deltas [ 2 ] [ 0 ];
+ dx3 = deltas [ 3 ] [ 0 ];
+
+ y = deltas [ 0 ] [ 1 ];
+ dy = deltas [ 1 ] [ 1 ];
+ dy2 = deltas [ 2 ] [ 1 ];
+ dy3 = deltas [ 3 ] [ 1 ];
+
+ lastx = ( int ) ( CLAMP ( x, 0, GAMMA_WIDTH - 1 ) );
+ lasty = ( int ) ( CLAMP ( y, 0, GAMMA_HEIGHT - 1 ));
+
+ m_curve [ m_page ] [ lastx ] = lasty;
+
+ for ( i = 0; i < 1000; i++ )
+ {
+ x += dx;
+ dx += dx2;
+ dx2 += dx3;
+
+ y += dy;
+ dy += dy2;
+ dy2 += dy3;
+
+ newx = CLAMP ( int ( x + 0.5 ), 0, GAMMA_WIDTH - 1 );
+ newy = CLAMP ( int ( y + 0.5 ), 0, GAMMA_HEIGHT - 1 );
+
+ if ( ( lastx != newx ) || ( lasty != newy ) )
+ m_curve [ m_page ] [ newx ] = newy;
+
+ lastx = newx;
+ lasty = newy;
+ }
+}
+
+/*------------------------------------------------------------*/
+void gamma_correction::compose_curve ( matrix a, matrix b, matrix ab )
+{
+ int i, j;
+
+ for ( i = 0; i < 4; i++ )
+ {
+ for ( j = 0; j < 4; j++ )
+ {
+ ab [ i ] [ j ] = ( a [ i ] [ 0 ] * b [ 0 ] [ j ] +
+ a [ i ] [ 1 ] * b [ 1 ] [ j ] +
+ a [ i ] [ 2 ] * b [ 2 ] [ j ] +
+ a [ i ] [ 3 ] * b [ 3 ] [ j ] );
+ }
+ }
+}
diff --git a/frontend/pisa_gamma_correction.h b/frontend/pisa_gamma_correction.h
new file mode 100644
index 0000000..6848620
--- /dev/null
+++ b/frontend/pisa_gamma_correction.h
@@ -0,0 +1,85 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_GAMMA_CORRECTION_H
+#define ___PISA_GAMMA_CORRECTION_H
+
+#include <gtk/gtk.h>
+#include "pisa_enums.h"
+
+#define GAMMA_WIDTH 192
+#define GAMMA_HEIGHT 192
+
+class gamma_correction
+{
+ public:
+
+ // operation
+ int init ( void );
+ GtkWidget * create_controls ( GtkWidget * parent );
+ int close_window ( int destroy );
+ void sensitive ( int is_prev_img );
+
+ void change_page ( int page );
+ void reset ( int all );
+ gint event ( GtkWidget * widget, GdkEvent * event );
+
+ private:
+
+ typedef double matrix [ 4 ] [ 4 ];
+
+ // operation
+ GtkWidget * create_gamma_notebook ( GtkWidget * parent );
+ GtkWidget * create_gamma_curve ( int i );
+ GtkWidget * create_reset_button ( void );
+
+ void reset_points ( int i );
+ void update_gamma ( void );
+ void calculate_curve ( void );
+ void plot_curve ( int p1, int p2, int p3, int p4 );
+ void compose_curve ( matrix a, matrix b, matrix ab );
+
+ // attribute
+ int m_page;
+ GtkWidget * m_widget [ WG_GAMMA_NUM ];
+ GtkWidget * m_drawarea [ 4 ];
+ GdkPixmap * m_pixmap [ 4 ];
+
+ int m_leftmost, m_rightmost;
+ int m_points [ 4 ] [ 17 ] [ 2 ];
+ unsigned char m_curve[ 4 ] [ GAMMA_WIDTH ];
+ int m_grabpoint;
+ matrix m_basis;
+ int m_active [ 4 ];
+
+ GdkCursor * m_cursor [ 3 ];
+};
+
+#endif // ___PISA_GAMMA_CORRECTION_H
diff --git a/frontend/pisa_gimp.cc b/frontend/pisa_gimp.cc
new file mode 100644
index 0000000..c674c9f
--- /dev/null
+++ b/frontend/pisa_gimp.cc
@@ -0,0 +1,665 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005, 2009 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <gtk/gtk.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+#include "pisa_main.h"
+#include "pisa_gimp.h"
+#include "pisa_error.h"
+#include "pisa_enums.h"
+
+#ifdef HAVE_ANY_GIMP
+/*----------------------------------------------------------*/
+static void * handle_libgimp = 0;
+int g_gimpversion = 1;
+
+typedef gchar* lib_gimp_version (void);
+typedef gint lib_gimp_main ( gint argc, gchar * argv [ ] );
+typedef void lib_gimp_quit ( void );
+typedef void lib_gimp_install_procedure ( gchar * name,
+ gchar * blurb,
+ gchar * help,
+ gchar * author,
+ gchar * copyright,
+ gchar * date,
+ gchar * menu_path,
+ gchar * image_types,
+ gint type,
+ gint nparams,
+ gint nreturn_vals,
+ GimpParamDef * params,
+ GimpParamDef * return_vals );
+typedef guint lib_gimp_tile_height ( void );
+typedef gint32 lib_gimp_image_new ( gint width,
+ gint height,
+ GimpImageBaseType type );
+typedef gint32 lib_gimp_layer_new ( gint32 image_ID,
+ gchar * name,
+ gint width,
+ gint height,
+ GimpImageType type,
+ gdouble opacity,
+ GimpLayerModeEffects mode );
+typedef gboolean lib_gimp_image_add_layer ( gint32 image_ID,
+ gint32 layer_ID,
+ gint position );
+typedef GimpDrawable * lib_gimp_drawable_get ( gint32 drawable_ID );
+typedef void lib_gimp_pixel_rgn_init (GimpPixelRgn *pr,
+ GimpDrawable *drawable,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ gint dirty,
+ gint shadow);
+typedef void lib_gimp_pixel_rgn_set_rect (GimpPixelRgn *pr,
+ guchar *buf,
+ gint x,
+ gint y,
+ gint width,
+ gint height );
+typedef gboolean lib_gimp_image_remove_layer (gint32 image_ID,
+ gint32 layer_ID );
+typedef gboolean lib_gimp_image_delete ( gint32 image_ID );
+typedef void lib_gimp_drawable_flush ( GimpDrawable *drawable );
+typedef void lib_gimp_drawable_detach ( GimpDrawable *drawable );
+typedef gint32 lib_gimp_display_new ( gint32 image_ID );
+typedef gchar * lib_gimp_gtkrc ( void );
+typedef gboolean lib_gimp_use_xshm ( void );
+
+// Define function prototype for plug-in gimp-2
+typedef void lib_gimp_extension_ack_2 ( void );
+typedef void lib_gimp_install_procedure_2 (const gchar * name,
+ const gchar * blurb,
+ const gchar * help,
+ const gchar * author,
+ const gchar * copyright,
+ const gchar * date,
+ const char * menu_path,
+ const gchar * image_types,
+ GimpPDBProcType type,
+ gint nparams,
+ gint nreturn_vals,
+ const GimpParamDef * params,
+ const GimpParamDef * return_vals );
+
+#ifdef HAVE_GIMP_2
+typedef gint lib_gimp_main_2 ( const GimpPlugInInfo *info,
+ gint argc, gchar * argv [ ] );
+#else
+typedef gint lib_gimp_main_2 ( const GimpPlugInInfo_2 *info,
+ gint argc, gchar * argv [ ] );
+#endif
+
+lib_gimp_version * plib_gimp_version;
+lib_gimp_main * plib_gimp_main;
+lib_gimp_quit * plib_gimp_quit;
+lib_gimp_install_procedure * plib_gimp_install_procedure;
+lib_gimp_tile_height * plib_gimp_tile_height;
+lib_gimp_image_new * plib_gimp_image_new;
+lib_gimp_layer_new * plib_gimp_layer_new;
+lib_gimp_image_add_layer * plib_gimp_image_add_layer;
+lib_gimp_drawable_get * plib_gimp_drawable_get;
+lib_gimp_pixel_rgn_init * plib_gimp_pixel_rgn_init;
+lib_gimp_pixel_rgn_set_rect * plib_gimp_pixel_rgn_set_rect;
+lib_gimp_image_remove_layer * plib_gimp_image_remove_layer;
+lib_gimp_image_delete * plib_gimp_image_delete;
+lib_gimp_drawable_flush * plib_gimp_drawable_flush;
+lib_gimp_drawable_detach * plib_gimp_drawable_detach;
+lib_gimp_display_new * plib_gimp_display_new;
+lib_gimp_gtkrc * plib_gimp_gtkrc;
+lib_gimp_use_xshm * plib_gimp_use_xshm;
+
+/* Use for plug-in gimp-2 */
+lib_gimp_main_2 * plib_gimp_main_2;
+lib_gimp_install_procedure_2 * plib_gimp_install_procedure_2;
+lib_gimp_extension_ack_2* plib_gimp_extension_ack_2;
+
+
+static void query ( void );
+#ifdef HAVE_GIMP_2
+static void run ( char * name, int nparams, GimpParam_1 * param,
+ int * nreturn_vals, GimpParam_1 ** return_vals );
+#else
+static void run ( char * name, int nparams, GimpParam * param,
+ int * nreturn_vals, GimpParam ** return_vals );
+#endif // HAVE_GIMP_2
+
+static void gimp2_query ( void );
+#ifdef HAVE_GIMP_2
+static void gimp2_run ( const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *n_return_vals,
+ GimpParam **return_vals);
+#else
+static void gimp2_run ( const gchar *name,
+ gint n_params,
+ const GimpParam_2 *param,
+ gint *n_return_vals,
+ GimpParam_2 **return_vals);
+
+#endif // HAVE_GIMP_2
+
+/*----------------------------------------------------------*/
+// Define these structures for supporting with gimp-1 and gimp-2
+#ifdef HAVE_GIMP_2
+GimpPlugInInfo_1 PLUG_IN_INFO =
+{
+ 0,
+ 0,
+ query,
+ run,
+};
+GimpPlugInInfo PLUG_IN_INFO_2 =
+{
+ 0,
+ 0,
+ gimp2_query,
+ gimp2_run,
+};
+#else
+GimpPlugInInfo PLUG_IN_INFO =
+{
+ 0, // init
+ 0, // quit
+ query, // query
+ run, // run
+};
+GimpPlugInInfo_2 PLUG_IN_INFO_2 = {
+ 0,
+ 0,
+ gimp2_query,
+ gimp2_run,
+};
+#endif // HAVE_GIMP_2
+
+#endif // HAVE_ANY_GIMP
+
+/*----------------------------------------------------------*/
+gint pisa_gimp_main ( gint argc, gchar * argv [ ] )
+{
+#ifdef HAVE_ANY_GIMP
+ int i, j, k;
+ char libname [ 32 ];
+
+ for( k = 1; k <= 2; k++){
+
+ for ( i = 9; 0 <= i; i-- )
+ {
+ if ( handle_libgimp )
+ break;
+
+ for ( j = 9; 0 <= j; j-- )
+ {
+ sprintf ( libname, "libgimp-%d.%d.so.%d", k, i, j );
+ handle_libgimp = dlopen ( libname, RTLD_LAZY );
+ if ( handle_libgimp ){
+ g_gimpversion = k;
+ break;
+ }
+ }
+ }
+ }
+ if ( handle_libgimp == 0 )
+ return 1;
+
+ if ( g_gimpversion == 1){
+ plib_gimp_main = ( lib_gimp_main * ) dlsym ( handle_libgimp, "gimp_main" );
+ plib_gimp_install_procedure = ( lib_gimp_install_procedure * ) dlsym ( handle_libgimp, "gimp_install_procedure" );
+
+ // This function isn't supported in gimp-2
+ plib_gimp_use_xshm = ( lib_gimp_use_xshm * ) dlsym ( handle_libgimp, "gimp_use_xshm" );
+
+ if ( ! plib_gimp_main || ! plib_gimp_install_procedure || ! plib_gimp_use_xshm) {
+ return 1;
+ }
+
+ }
+ else{
+ // These functions are changed in gimp-2
+ plib_gimp_main_2 = ( lib_gimp_main_2 * ) dlsym ( handle_libgimp, "gimp_main" );
+ plib_gimp_install_procedure_2 = ( lib_gimp_install_procedure_2 * ) dlsym ( handle_libgimp, "gimp_install_procedure" );
+
+ // This is a new function in gimp-2
+ plib_gimp_extension_ack_2 = ( lib_gimp_extension_ack_2 *) dlsym(handle_libgimp, "gimp_extension_ack") ;
+
+ if ( ! plib_gimp_main_2 || ! plib_gimp_install_procedure_2 || ! plib_gimp_extension_ack_2 ) {
+ return 1;
+ }
+ }
+
+ plib_gimp_version = ( lib_gimp_version * ) dlsym ( handle_libgimp, "gimp_version" );
+ plib_gimp_quit = ( lib_gimp_quit * ) dlsym ( handle_libgimp, "gimp_quit" );
+ plib_gimp_tile_height = ( lib_gimp_tile_height * ) dlsym ( handle_libgimp, "gimp_tile_height" );
+ plib_gimp_image_new = ( lib_gimp_image_new * ) dlsym ( handle_libgimp, "gimp_image_new" );
+ plib_gimp_layer_new = ( lib_gimp_layer_new * ) dlsym ( handle_libgimp, "gimp_layer_new" );
+ plib_gimp_image_add_layer = ( lib_gimp_image_add_layer * ) dlsym ( handle_libgimp, "gimp_image_add_layer" );
+ plib_gimp_drawable_get = ( lib_gimp_drawable_get * ) dlsym ( handle_libgimp, "gimp_drawable_get" );
+ plib_gimp_pixel_rgn_init = ( lib_gimp_pixel_rgn_init * ) dlsym ( handle_libgimp, "gimp_pixel_rgn_init" );
+ plib_gimp_pixel_rgn_set_rect = ( lib_gimp_pixel_rgn_set_rect * ) dlsym ( handle_libgimp, "gimp_pixel_rgn_set_rect" );
+ plib_gimp_image_remove_layer = ( lib_gimp_image_remove_layer * ) dlsym ( handle_libgimp, "gimp_image_remove_layer" );
+ plib_gimp_image_delete = ( lib_gimp_image_delete * ) dlsym ( handle_libgimp, "gimp_image_delete" );
+ plib_gimp_drawable_flush = ( lib_gimp_drawable_flush * ) dlsym ( handle_libgimp, "gimp_drawable_flush" );
+ plib_gimp_drawable_detach = ( lib_gimp_drawable_detach * ) dlsym ( handle_libgimp, "gimp_drawable_detach" );
+ plib_gimp_display_new = ( lib_gimp_display_new * ) dlsym ( handle_libgimp, "gimp_display_new" );
+ plib_gimp_gtkrc = ( lib_gimp_gtkrc * ) dlsym ( handle_libgimp, "gimp_gtkrc" );
+
+ if ( ! plib_gimp_quit || ! plib_gimp_tile_height || ! plib_gimp_image_new || ! plib_gimp_layer_new ||
+ ! plib_gimp_image_add_layer || ! plib_gimp_drawable_get ||
+ ! plib_gimp_pixel_rgn_init || ! plib_gimp_pixel_rgn_set_rect ||
+ ! plib_gimp_image_remove_layer || ! plib_gimp_image_delete ||
+ ! plib_gimp_drawable_flush || ! plib_gimp_drawable_detach ||
+ ! plib_gimp_display_new || ! plib_gimp_gtkrc )
+ return 1;
+
+ if (g_gimpversion == 1){
+ return plib_gimp_main ( argc, argv );
+ }
+ else{
+ return plib_gimp_main_2 (&PLUG_IN_INFO_2, argc, argv );
+ }
+
+#else
+ return 1;
+#endif // HAVE_ANY_GIMP
+}
+
+/*----------------------------------------------------------*/
+void pisa_gimp_quit ( void )
+{
+#ifdef HAVE_ANY_GIMP
+ plib_gimp_quit ( );
+ dlclose ( handle_libgimp );
+#endif
+}
+
+#ifdef HAVE_ANY_GIMP
+
+/*----------------------------------------------------------*/
+gchar * pisa_gimp_gtkrc ( void )
+{
+ return plib_gimp_gtkrc ( );
+}
+
+/*----------------------------------------------------------*/
+gboolean pisa_gimp_use_xshm ( void )
+{
+ return plib_gimp_use_xshm ( );
+}
+
+/*----------------------------------------------------------*/
+static void query ( void )
+{
+ static GimpParamDef args [ ] =
+ {
+ { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
+ };
+
+ static GimpParamDef * return_vals = 0;
+ static int nargs = sizeof ( args ) / sizeof ( args [ 0 ] );
+ static int nreturn_vals = 0;
+
+ plib_gimp_install_procedure (
+ "Image Scan! for Linux",
+ "Front-end to the SANE interface",
+ "This function provides access to scanners "
+ "and other image acquisition devices through "
+ "the SANE (Scanner Access Now Easy) interface.",
+ "AVASYS CORPORATION",
+ "SEIKO EPSON CORPORATION",
+ "2001, 2005",
+ "<Toolbox>/Xtns/Acquire Image/Scanning (iscan)...",
+ "RGB, GRAY",
+ GIMP_EXTENSION,
+ nargs, nreturn_vals,
+ args, return_vals );
+}
+/*----------------------------------------------------------*/
+static void gimp2_query ( void )
+{
+ static GimpParamDef args [ ] =
+ {
+ { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
+ };
+
+ static GimpParamDef * return_vals = 0;
+ static int nargs = sizeof ( args ) / sizeof ( args [ 0 ] );
+ static int nreturn_vals = 0;
+
+ char* menu = "<Toolbox>/File/Acquire/Scanning (iscan)...";
+ int maj, min = 0;
+ if (!plib_gimp_version ||
+ 2 != sscanf (plib_gimp_version (), "%d.%d.", &maj, &min))
+ {
+ maj = 2;
+ min = 0;
+ }
+ if (2 < maj || (2 == maj && 6 <= min))
+ {
+ // accommodate new menu structure introduced in gimp 2.6
+ menu = "<Image>/File/Create/Acquire/Scanning (iscan)...";
+ }
+
+ // Must call this function for supporting gimp-2
+ plib_gimp_install_procedure_2 (
+ "Image Scan! for Linux",
+ "Front-end to the SANE interface",
+ "This function provides access to scanners "
+ "and other image acquisition devices through "
+ "the SANE (Scanner Access Now Easy) interface.",
+ "AVASYS CORPORATION",
+ "SEIKO EPSON CORPORATION",
+ "2009",
+ menu,
+ "",
+ GIMP_EXTENSION,
+ nargs, nreturn_vals,
+ args, return_vals );
+}
+
+/*----------------------------------------------------------*/
+
+#ifdef HAVE_GIMP_2
+static void run ( char * name, int nparams, GimpParam_1 * param,
+ int * nreturn_vals, GimpParam_1 ** return_vals )
+#else
+static void run ( char * name, int nparams, GimpParam * param,
+ int * nreturn_vals, GimpParam ** return_vals )
+
+#endif // HAVE_GIMP_2
+{
+#ifdef HAVE_GIMP_2
+ static GimpParam_1 values [ 1 ];
+ GimpRunMode run_mode;
+#else
+ static GimpParam values [ 1 ];
+ GimpRunModeType run_mode;
+#endif // HAVE_GIMP_2
+ char * argv [ 1 ];
+ int argc = 1;
+
+ name = name;
+ nparams = nparams;
+
+#ifdef HAVE_GIMP_2
+ run_mode = ( GimpRunMode ) param [ 0 ].data.d_int32;
+#else
+ run_mode = ( GimpRunModeType ) param [ 0 ].data.d_int32;
+#endif
+
+ * nreturn_vals = 1;
+ * return_vals = values;
+
+ values [ 0 ].type = GIMP_PDB_STATUS;
+ values [ 0 ].data.d_status = GIMP_PDB_SUCCESS;
+
+ argv [ 0 ] = "iscan";
+
+ switch ( run_mode )
+ {
+ case GIMP_RUN_INTERACTIVE:
+ pisa_start ( argc, argv, 1);
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+}
+
+#ifdef HAVE_GIMP_2
+static void gimp2_run ( const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *n_return_vals,
+ GimpParam **return_vals)
+#else
+static void gimp2_run ( const gchar *name,
+ gint n_params,
+ const GimpParam_2 *param,
+ gint *n_return_vals,
+ GimpParam_2 **return_vals)
+
+#endif// HAVE_GIMP_2
+{
+#ifdef HAVE_GIMP_2
+ static GimpParam values [ 1 ];
+ GimpRunMode run_mode;
+#else
+ static GimpParam_2 values [ 1 ];
+ GimpRunModeType run_mode;
+#endif// HAVE_GIMP_2
+ char * argv [ 1 ];
+ int argc = 1;
+
+ name = name;
+ n_params = n_params;
+
+#ifdef HAVE_GIMP_2
+ run_mode = ( GimpRunMode ) param [ 0 ].data.d_int32;
+#else
+ run_mode = ( GimpRunModeType ) param [ 0 ].data.d_int32;
+#endif // HAVE_GIMP_2
+
+ * n_return_vals = 1;
+ * return_vals = values;
+
+ values [ 0 ].type = GIMP_PDB_STATUS;
+ values [ 0 ].data.d_status = GIMP_PDB_SUCCESS;
+
+ argv [ 0 ] = "iscan";
+
+ switch ( run_mode )
+ {
+ case GIMP_RUN_INTERACTIVE:
+ // This procedure must be called with GIMP_EXTENSION
+ plib_gimp_extension_ack_2();
+ pisa_start ( argc, argv, 2);
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+/*----------------------------------------------------------*/
+int gimp_scan::create_gimp_image ( int width,
+ int height,
+ int pixeltype,
+ int depth )
+{
+ size_t tile_size;
+ GimpImageBaseType image_type;
+ GimpImageType drawable_type;
+
+ // initialize member variable
+ m_rows = 0;
+ m_width = width;
+ m_height = height;
+ m_pixeltype = pixeltype;
+ m_depth = depth;
+ m_rowbytes = 0;
+
+ m_tile = 0;
+ m_drawable = 0;
+
+ tile_size = width * ::plib_gimp_tile_height ( );
+
+ if ( pixeltype == PISA_PT_RGB )
+ {
+ tile_size *= 3;
+ image_type = GIMP_RGB;
+ drawable_type = GIMP_RGB_IMAGE;
+ m_rowbytes = width * 3;
+ }
+ else if ( pixeltype == PISA_PT_GRAY || pixeltype == PISA_PT_BW )
+ {
+ image_type = GIMP_GRAY;
+ drawable_type = GIMP_GRAY_IMAGE;
+ m_rowbytes = width;
+ }
+ else
+ return PISA_ERR_PARAMETER;
+
+ m_image_id = ::plib_gimp_image_new ( m_width, m_height, image_type );
+
+ m_layer_id = ::plib_gimp_layer_new ( m_image_id, "Background",
+ m_width, m_height, drawable_type,
+ 100.0, GIMP_NORMAL_MODE );
+ ::plib_gimp_image_add_layer ( m_image_id, m_layer_id, 0 );
+
+ m_drawable = ::plib_gimp_drawable_get ( m_layer_id );
+
+ ::plib_gimp_pixel_rgn_init ( & m_region, m_drawable, 0, 0,
+ m_drawable->width, m_drawable->height,
+ TRUE, FALSE );
+
+ m_tile = g_new ( guchar, tile_size );
+
+ if ( m_tile == 0 )
+ return PISA_ERR_OUTOFMEMORY;
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*----------------------------------------------------------*/
+unsigned char * gimp_scan::get_next_buf ( void )
+{
+ int tile_height = ::plib_gimp_tile_height ( );
+
+ return & m_tile [ ( m_rows % tile_height ) * m_rowbytes ];
+}
+
+/*----------------------------------------------------------*/
+int gimp_scan::set_image_rect ( void )
+{
+ int tile_height = ::plib_gimp_tile_height ( );
+
+ m_rows++;
+
+ if ( m_rows % tile_height == 0 )
+ {
+ if ( m_depth == 1 )
+ bw2gray ( );
+
+ ::plib_gimp_pixel_rgn_set_rect ( & m_region, m_tile,
+ 0, m_rows - tile_height,
+ m_width, tile_height );
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*----------------------------------------------------------*/
+int gimp_scan::finish_scan ( int cancel )
+{
+ int remaining;
+
+ if ( cancel )
+ {
+ ::plib_gimp_image_remove_layer ( m_image_id, m_layer_id );
+ ::plib_gimp_image_delete ( m_image_id );
+ ::free ( m_tile );
+ }
+ else
+ {
+ remaining = m_rows % ::plib_gimp_tile_height ( );
+
+ if ( remaining )
+ {
+ if ( m_depth == 1 )
+ bw2gray ( );
+
+ ::plib_gimp_pixel_rgn_set_rect ( & m_region, m_tile,
+ 0, m_rows - remaining,
+ m_width, remaining );
+ }
+
+ ::plib_gimp_drawable_flush ( m_drawable );
+ ::plib_gimp_display_new ( m_image_id );
+ ::plib_gimp_drawable_detach ( m_drawable );
+ ::free ( m_tile );
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*----------------------------------------------------------*/
+int gimp_scan::bw2gray ( void )
+{
+ unsigned char * tmp;
+ unsigned char * bw_buf;
+ long bw_buf_size;
+ long i, j, k, tile_height;
+
+ tile_height = ::plib_gimp_tile_height ( );
+ bw_buf_size = ( ( m_width + 7 ) / 8 );
+
+ bw_buf = new unsigned char [ bw_buf_size ];
+
+ if ( ! bw_buf )
+ return PISA_ERR_OUTOFMEMORY;
+
+ for ( i = 0; i < tile_height; i++ )
+ {
+ ::memcpy ( bw_buf, m_tile + m_width * i, bw_buf_size );
+
+ tmp = bw_buf;
+
+ for ( j = 0; j < m_width; j += 8 )
+ {
+ for ( k = 7; k >= 0; k-- )
+ {
+ if ( j + 7 - k < m_width )
+ {
+ m_tile [ i * m_width + j + ( 7 - k ) ] = ( * tmp & ( 1 << k ) ) ?
+ 0x00 : 0xff;
+ }
+ }
+ tmp++;
+ }
+ }
+
+ delete [ ] bw_buf;
+
+ return PISA_ERR_SUCCESS;
+}
+
+#endif // HAVE_ANY_GIMP
+
+
diff --git a/frontend/pisa_gimp.h b/frontend/pisa_gimp.h
new file mode 100644
index 0000000..7e04c75
--- /dev/null
+++ b/frontend/pisa_gimp.h
@@ -0,0 +1,109 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_GIMP_H
+#define ___PISA_GIMP_H
+
+#include <config.h>
+#include <gtk/gtk.h>
+
+#ifdef HAVE_ANY_GIMP
+#include <libgimp/gimp.h>
+#include "gimp-plugin.h"
+
+#ifndef HAVE_GIMP_2
+#ifdef HAVE_LIBGIMP_GIMPFEATURES_H
+#include <libgimp/gimpfeatures.h>
+#else
+#define GIMP_CHECK_VERSION(major, minor, micro) 0
+#endif /* HAVE_LIBGIMP_GIMPFEATURES_H */
+
+#ifdef GIMP_CHECK_VERSION
+#if GIMP_CHECK_VERSION(1,1,25)
+ /* ok, we have the new gimp interface */
+#else
+ /* we have the old gimp interface and need the compatibility header file */
+#include "pisa_gimp_1_0_patch.h"
+#endif
+#else
+ /* we have the old gimp interface and need the compatibility header file */
+#include "pisa_gimp_1_0_patch.h"
+#endif /*GIMP_CHECK_VERSION*/
+#endif /* HAVE_GIMP_2 */
+#endif /* HAVE_ANY_GIMP */
+
+gint pisa_gimp_main ( gint argc, gchar * argv [ ] );
+void pisa_gimp_quit ( void );
+
+#ifdef HAVE_ANY_GIMP
+
+gchar * pisa_gimp_gtkrc ( void );
+gboolean pisa_gimp_use_xshm ( void );
+
+class gimp_scan
+{
+ public:
+
+ // operation
+ int create_gimp_image ( int width,
+ int height,
+ int pixeltype,
+ int depth );
+
+ unsigned char * get_next_buf ( void );
+ int set_image_rect ( void );
+ int finish_scan ( int cancel );
+
+ private:
+
+ // operation
+ int bw2gray ( void );
+
+ // attribute
+ int m_rows;
+
+ int m_width;
+ int m_height;
+ int m_pixeltype;
+ int m_depth;
+ int m_rowbytes;
+
+ int m_image_id;
+ int m_layer_id;
+ guchar * m_tile;
+ GimpDrawable * m_drawable;
+ GimpPixelRgn m_region;
+
+};
+
+#endif // HAVE_ANY_GIMP
+
+
+#endif // ___PISA_GIMP_H
diff --git a/frontend/pisa_gimp_1_0_patch.h b/frontend/pisa_gimp_1_0_patch.h
new file mode 100644
index 0000000..0d45078
--- /dev/null
+++ b/frontend/pisa_gimp_1_0_patch.h
@@ -0,0 +1,58 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_GIMP_1_0_PATCH_H
+#define ___PISA_GIMP_1_0_PATCH_H
+
+#define GimpPlugInInfo GPlugInInfo
+#define GimpParam GParam
+#define GimpParamDef GParamDef
+#define GimpDrawable GDrawable
+#define GimpPixelRgn GPixelRgn
+#define GimpRunModeType GRunModeType
+#define GimpImageType GImageType
+
+#define GIMP_PDB_INT32 PARAM_INT32
+#define GIMP_PDB_STATUS PARAM_STATUS
+#define GIMP_PDB_CALLING_ERROR STATUS_CALLING_ERROR
+#define GIMP_PDB_SUCCESS STATUS_SUCCESS
+#define GIMP_RUN_INTERACTIVE RUN_INTERACTIVE
+#define GIMP_RUN_NONINTERACTIVE RUN_NONINTERACTIVE
+#define GIMP_RUN_WITH_LAST_VALS RUN_WITH_LAST_VALS
+#define GIMP_EXTENSION PROC_EXTENSION
+#define GIMP_RGB RGB
+#define GIMP_RGB_IMAGE RGB_IMAGE
+#define GIMP_GRAY GRAY
+#define GIMP_GRAY_IMAGE GRAY_IMAGE
+#define GIMP_RGBA_IMAGE RGBA_IMAGE
+#define GIMP_NORMAL_MODE NORMAL_MODE
+
+#endif // ___PISA_GIMP_1_0_PATCH_H
+
diff --git a/frontend/pisa_image_controls.cc b/frontend/pisa_image_controls.cc
new file mode 100644
index 0000000..1889f1d
--- /dev/null
+++ b/frontend/pisa_image_controls.cc
@@ -0,0 +1,381 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005, 2008 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+
+/*------------------------------------------------------------*/
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <string.h>
+
+/*------------------------------------------------------------*/
+#include "pisa_view_manager.h"
+#include "pisa_image_controls.h"
+#include "pisa_tool.h"
+#include "pisa_error.h"
+#include "pisa_structs.h"
+#include "pisa_default_val.h"
+
+/*------------------------------------------------------------*/
+static void value_changed ( GtkAdjustment * adjust, gpointer data );
+
+/*------------------------------------------------------------*/
+#define ID_SCALE_GAMMA 0x0001
+#define ID_SCALE_HIGHLIGHT 0x0002
+#define ID_SCALE_SHADOW 0x0003
+#define ID_SCALE_THRESHOLD 0x0004
+#define ID_SCALE_BRIGHTNESS 0x0005
+#define ID_SCALE_CONTRAST 0x0006
+
+scale_items g_scale_gamma = { ID_SCALE_GAMMA, 0, 0,
+ ( GtkSignalFunc * ) value_changed,
+ DEFGAMMA,
+ MINGAMMA,
+ MAXGAMMA,
+ LINEGAMMA,
+ PAGEGAMMA };
+scale_items g_scale_highlight = { ID_SCALE_HIGHLIGHT, 0, 0,
+ ( GtkSignalFunc * ) value_changed,
+ DEFHIGHLIGHT,
+ MINHIGHLIGHT,
+ MAXHIGHLIGHT,
+ LINEHIGHLIGHT,
+ PAGEHIGHLIGHT };
+scale_items g_scale_shadow = { ID_SCALE_SHADOW, 0, 0,
+ ( GtkSignalFunc * ) value_changed,
+ DEFSHADOW,
+ MINSHADOW,
+ MAXSHADOW,
+ LINESHADOW,
+ PAGESHADOW };
+scale_items g_scale_threshold = { ID_SCALE_THRESHOLD, 0, 0,
+ ( GtkSignalFunc * ) value_changed,
+ DEFTHRESHOLD,
+ MINTHRESHOLD,
+ MAXTHRESHOLD,
+ LINETHRESHOLD,
+ PAGETHRESHOLD };
+scale_items g_scale_brightness = { ID_SCALE_BRIGHTNESS, 0, 0,
+ ( GtkSignalFunc * ) value_changed,
+ 0, /* get value from backend */
+ 0, /* get range.max from backend */
+ 0, /* get range.min from backend */
+ 0, /* get range.quant from backend */
+ 0 };
+scale_items g_scale_contrast = { ID_SCALE_CONTRAST, 0, 0,
+ ( GtkSignalFunc * ) value_changed,
+ 0, /* get value from backend */
+ 0, /* get range.max from backend */
+ 0, /* get range.min from backend */
+ 0, /* get range.quant from backend */
+ 0 };
+
+/*------------------------------------------------------------*/
+
+static void value_changed ( GtkAdjustment * adjust, gpointer data )
+{
+ long id;
+ long val;
+ int change;
+ marquee * marq;
+
+ id = * ( long * ) data;
+ change = 1;
+ marq = & g_view_manager->get_marquee ();
+
+ switch ( id )
+ {
+ case ID_SCALE_GAMMA:
+ val = ( long ) ( adjust->value * 100 );
+ if ( marq->gamma == val )
+ change = 0;
+ else
+ marq->gamma = val;
+ break;
+
+ case ID_SCALE_HIGHLIGHT:
+ val = ( long ) ( adjust->value );
+ if ( marq->highlight == val )
+ change = 0;
+ else
+ marq->highlight = val;
+ break;
+
+ case ID_SCALE_SHADOW:
+ val = ( long ) ( adjust->value );
+ if ( marq->shadow == val )
+ change = 0;
+ else
+ marq->shadow = val;
+ break;
+
+ case ID_SCALE_THRESHOLD:
+ val = ( long ) ( adjust->value );
+ if ( marq->threshold == val )
+ change = 0;
+ else
+ marq->threshold = val;
+ break;
+
+ case ID_SCALE_BRIGHTNESS:
+ val = ( long ) ( adjust->value );
+ if ( marq->brightness != val )
+ {
+ marq->brightness = val;
+ }
+ change = 0;
+ break;
+
+ case ID_SCALE_CONTRAST:
+ val = ( long ) ( adjust->value );
+ if ( marq->contrast != val )
+ {
+ marq->contrast = val;
+ }
+ change = 0;
+ break;
+ }
+
+ if ( change )
+ {
+ ::g_view_manager->update_lut ( );
+ preview_window *prev_cls =
+ (preview_window *) ::g_view_manager->get_window_cls (ID_WINDOW_PREV);
+
+ prev_cls->update_img ( );
+ }
+}
+
+/*------------------------------------------------------------*/
+void image_controls::sensitive ( int is_prev_img )
+{
+ marquee * marq;
+ gboolean enable_color, enable_bw;
+ gboolean enable_brightness_contrast;
+ scan_manager * scan_mgr;
+
+ scan_mgr = g_view_manager->get_scan_manager ();
+
+ marq = & g_view_manager->get_marquee ();
+
+ // update value
+ ::gtk_adjustment_set_value ( ::g_scale_gamma.adjust, marq->gamma / 100.0f );
+ ::gtk_adjustment_set_value ( ::g_scale_highlight.adjust, marq->highlight );
+ ::gtk_adjustment_set_value ( ::g_scale_shadow.adjust, marq->shadow );
+ ::gtk_adjustment_set_value ( ::g_scale_threshold.adjust, marq->threshold );
+ ::gtk_adjustment_set_value ( ::g_scale_brightness.adjust, marq->brightness );
+ ::gtk_adjustment_set_value ( ::g_scale_contrast.adjust, marq->contrast );
+
+ // grayout
+ if ( is_prev_img
+ && g_view_manager->get_settings ().imgtype.pixeltype != PISA_PT_BW )
+ enable_color = TRUE;
+ else
+ enable_color = FALSE;
+
+ if ( is_prev_img
+ && g_view_manager->get_settings ().imgtype.pixeltype == PISA_PT_BW )
+ enable_bw = TRUE;
+ else
+ enable_bw = FALSE;
+
+ ::gtk_widget_set_sensitive ( ::g_scale_gamma.widget, enable_color );
+ ::gtk_widget_set_sensitive ( ::g_scale_highlight.widget, enable_color );
+ ::gtk_widget_set_sensitive ( ::g_scale_shadow.widget, enable_color );
+ ::gtk_widget_set_sensitive ( ::g_scale_threshold.widget, enable_bw );
+
+ // reset brightness, contrast
+ enable_brightness_contrast = TRUE;
+ if ( is_prev_img )
+ {
+ ::gtk_range_set_value ( GTK_RANGE (::g_scale_brightness.widget ),
+ brightness_default );
+ ::gtk_range_set_value ( GTK_RANGE (::g_scale_contrast.widget ),
+ contrast_default );
+ enable_brightness_contrast = FALSE;
+ }
+ ::gtk_widget_set_sensitive ( ::g_scale_brightness.widget,
+ ( enable_brightness_contrast
+ && scan_mgr->has_brightness() ) );
+ ::gtk_widget_set_sensitive ( ::g_scale_contrast.widget,
+ ( enable_brightness_contrast
+ && scan_mgr->has_contrast() ) );
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * image_controls::create_controls ( void )
+{
+ GtkWidget * vbox;
+ GtkWidget * table;
+ GtkWidget * label;
+ GtkWidget * scale;
+ GtkWidget * separator;
+ scan_manager * scan_mgr;
+
+ scan_mgr = g_view_manager->get_scan_manager ();
+
+ table = ::gtk_table_new ( 4, 2, FALSE );
+ ::gtk_container_border_width ( GTK_CONTAINER ( table ), 5 );
+ ::gtk_table_set_row_spacings ( GTK_TABLE ( table ), 3 );
+ ::gtk_table_set_col_spacings ( GTK_TABLE ( table ), 5 );
+
+ // gamma
+ label = ::gtk_label_new ( _( "Gamma:" ) );
+ ::gtk_misc_set_alignment ( GTK_MISC ( label ), 1.0, 0.5 );
+ ::gtk_table_attach ( GTK_TABLE ( table ), label, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( label );
+
+ scale = ::pisa_create_scale ( & ::g_scale_gamma );
+ ::gtk_widget_set_usize ( scale, 200, -1 );
+ ::gtk_scale_set_digits ( GTK_SCALE ( scale ), 2 );
+ ::gtk_range_set_update_policy ( GTK_RANGE ( scale ), GTK_UPDATE_DELAYED );
+ ::gtk_table_attach ( GTK_TABLE ( table ), scale, 1, 2, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( scale );
+
+ // highlight
+ label = ::gtk_label_new ( _( "Highlight:" ) );
+ ::gtk_misc_set_alignment ( GTK_MISC ( label ), 1.0, 0.5 );
+ ::gtk_table_attach ( GTK_TABLE ( table ), label, 0, 1, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( label );
+
+ scale = ::pisa_create_scale ( & ::g_scale_highlight );
+ ::gtk_widget_set_usize ( scale, 200, -1 );
+ ::gtk_scale_set_digits ( GTK_SCALE ( scale ), 0 );
+ ::gtk_range_set_update_policy ( GTK_RANGE ( scale ), GTK_UPDATE_DELAYED );
+ ::gtk_table_attach ( GTK_TABLE ( table ), scale, 1, 2, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( scale );
+
+ // shadow
+ label = ::gtk_label_new ( _( "Shadow:" ) );
+ ::gtk_misc_set_alignment ( GTK_MISC ( label ), 1.0, 0.5 );
+ ::gtk_table_attach ( GTK_TABLE ( table ), label, 0, 1, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( label );
+
+ scale = pisa_create_scale ( & ::g_scale_shadow );
+ ::gtk_widget_set_usize ( scale, 200, -1 );
+ ::gtk_scale_set_digits ( GTK_SCALE ( scale ), 0 );
+ ::gtk_range_set_update_policy ( GTK_RANGE ( scale ), GTK_UPDATE_DELAYED );
+ ::gtk_table_attach ( GTK_TABLE ( table ), scale, 1, 2, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( scale );
+
+ // threshold
+ label = ::gtk_label_new ( _( "Threshold:" ) );
+ ::gtk_misc_set_alignment ( GTK_MISC ( label ), 1.0, 0.5 );
+ ::gtk_table_attach ( GTK_TABLE ( table ), label, 0, 1, 3, 4,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( label );
+
+ scale = pisa_create_scale ( & ::g_scale_threshold );
+ ::gtk_widget_set_usize ( scale, 200, -1 );
+ ::gtk_scale_set_digits ( GTK_SCALE ( scale ), 0 );
+ ::gtk_range_set_update_policy ( GTK_RANGE ( scale ), GTK_UPDATE_DELAYED );
+ ::gtk_table_attach ( GTK_TABLE ( table ), scale, 1, 2, 3, 4,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( scale );
+
+ ::gtk_widget_show ( table );
+
+ vbox = gtk_vbox_new (false, 5);
+ gtk_box_pack_start (GTK_BOX (vbox), table, false, false, 0);
+
+ separator = ::gtk_hseparator_new ( );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), separator, FALSE, FALSE, 2 );
+ ::gtk_widget_show ( separator );
+
+ // new table
+ table = ::gtk_table_new ( 2, 2, FALSE );
+ ::gtk_container_border_width ( GTK_CONTAINER ( table ), 5 );
+ ::gtk_table_set_row_spacings ( GTK_TABLE ( table ), 3 );
+ ::gtk_table_set_col_spacings ( GTK_TABLE ( table ), 5 );
+
+ // Set brightness-method before you get brightness.
+ scan_mgr->set_brightness_method(br_iscan);
+
+ // brightness
+ label = ::gtk_label_new ( _( "Brightness:" ) );
+ ::gtk_misc_set_alignment ( GTK_MISC ( label ), 1.0, 0.5 );
+ ::gtk_table_attach ( GTK_TABLE ( table ), label, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( label );
+
+ scan_mgr->get_value (SANE_NAME_BRIGHTNESS, &g_scale_brightness.val);
+ scan_mgr->get_range (SANE_NAME_BRIGHTNESS,
+ &g_scale_brightness.max,
+ &g_scale_brightness.min,
+ &g_scale_brightness.step);
+ g_scale_brightness.page = g_scale_brightness.step * 5;
+ brightness_default = g_scale_brightness.val;
+
+ scale = pisa_create_scale ( & ::g_scale_brightness );
+ ::gtk_widget_set_usize ( scale, 200, -1 );
+ ::gtk_scale_set_digits ( GTK_SCALE ( scale ), 0 );
+ ::gtk_range_set_update_policy ( GTK_RANGE ( scale ), GTK_UPDATE_DELAYED );
+ ::gtk_table_attach ( GTK_TABLE ( table ), scale, 1, 2, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( scale );
+
+ // contrast
+ label = ::gtk_label_new ( _( "Contrast:" ) );
+ ::gtk_misc_set_alignment ( GTK_MISC ( label ), 1.0, 0.5 );
+ ::gtk_table_attach ( GTK_TABLE ( table ), label, 0, 1, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( label );
+
+ scan_mgr->get_value (SANE_NAME_CONTRAST, &g_scale_contrast.val);
+ scan_mgr->get_range (SANE_NAME_CONTRAST,
+ &g_scale_contrast.max,
+ &g_scale_contrast.min,
+ &g_scale_contrast.step);
+ g_scale_contrast.page = g_scale_contrast.step * 5;
+ contrast_default = g_scale_contrast.val;
+
+ scale = pisa_create_scale ( & ::g_scale_contrast );
+ ::gtk_widget_set_usize ( scale, 200, -1 );
+ ::gtk_scale_set_digits ( GTK_SCALE ( scale ), 0 );
+ ::gtk_range_set_update_policy ( GTK_RANGE ( scale ), GTK_UPDATE_DELAYED );
+ ::gtk_table_attach ( GTK_TABLE ( table ), scale, 1, 2, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( scale );
+
+ ::gtk_widget_show ( table );
+
+ gtk_box_pack_start (GTK_BOX (vbox), table, false, false, 0);
+
+ return vbox;
+}
diff --git a/frontend/pisa_image_controls.h b/frontend/pisa_image_controls.h
new file mode 100644
index 0000000..89216e7
--- /dev/null
+++ b/frontend/pisa_image_controls.h
@@ -0,0 +1,50 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_IMAGE_CONTROLS_H
+#define ___PISA_IMAGE_CONTROLS_H
+
+#include <gtk/gtk.h>
+
+class image_controls
+{
+ float brightness_default;
+ float contrast_default;
+
+ public:
+
+ // operation
+ void sensitive ( int is_prev_img );
+
+ GtkWidget * create_controls ( void );
+};
+
+#endif // ___PISA_IMAGE_CONTROLS_H
+
diff --git a/frontend/pisa_img_converter.cc b/frontend/pisa_img_converter.cc
new file mode 100644
index 0000000..36e18ec
--- /dev/null
+++ b/frontend/pisa_img_converter.cc
@@ -0,0 +1,99 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 07/10/2001 Peter New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+/*--------------------------------------------------------------*/
+#include "pisa_img_converter.h"
+#include "pisa_error.h"
+
+/*--------------------------------------------------------------*/
+
+
+/*Convert 8bpp grayscale pixels to 24bpp rgb pixels*/
+int convert_grayscale_to_rgb(BYTE* in_buf, long width, long height, BYTE* out_rgb_buf)
+{
+ long i,j,ofs;
+ ofs = 0;
+ for( i = 0; i<height; i++)
+ for( j = 0; j<width; j++, ofs+=3)
+ {
+ /*just copy the gray value 3 times to make rgb*/
+ out_rgb_buf[ofs] = in_buf[i*width+j];
+ out_rgb_buf[ofs+1] = out_rgb_buf[ofs];
+ out_rgb_buf[ofs+2] = out_rgb_buf[ofs];
+ }
+ return PISA_ERR_SUCCESS;
+}
+
+/*Convert 1bpp b&w pixels to 24bpp rgb pixels*/
+int convert_binary_to_rgb(BYTE* in_buf, long width, long height, BYTE* out_rgb_buf)
+{
+ long i,j,in_ofs, out_ofs;
+ int rgb_val;
+ int bitshift;
+ unsigned int temp_byte = 0;
+
+ //offset into input image
+ in_ofs = 0;
+ //offset into output image
+ out_ofs = 0;
+
+ for( i = 0; i<height; i++)
+ {
+ j = 0;
+ bitshift = -1;
+ in_ofs = i*(int)((width+7)/8);
+ while( j<width)
+ {
+ //get a new byte every 8 columns
+ if(j%8 == 0)
+ {
+ temp_byte = in_buf[in_ofs+(int)(j/8)];
+ bitshift = 7;
+ }
+
+ //black or white pixel
+ if((temp_byte >> bitshift) & 1)
+ rgb_val = IMG_CONVERTER_RGB_WHITE;
+ else
+ rgb_val = IMG_CONVERTER_RGB_BLACK;
+
+ //write the rgb values
+ out_rgb_buf[out_ofs] = (unsigned int) rgb_val;
+ out_rgb_buf[out_ofs+1] = (unsigned int)rgb_val;
+ out_rgb_buf[out_ofs+2] = (unsigned int)rgb_val;
+ j++;
+ out_ofs+=3;
+ bitshift--;
+ }
+ }
+ return PISA_ERR_SUCCESS;
+}
+
+
diff --git a/frontend/pisa_img_converter.h b/frontend/pisa_img_converter.h
new file mode 100644
index 0000000..8762bbc
--- /dev/null
+++ b/frontend/pisa_img_converter.h
@@ -0,0 +1,45 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 07/10/2001 Peter New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include "pisa_enums.h"
+#include <stdio.h>
+
+#ifndef ___PISA_IMG_CONV_H
+#define ___PISA_IMG_CONV_H
+
+typedef unsigned char BYTE;
+
+#define IMG_CONVERTER_RGB_WHITE 255
+#define IMG_CONVERTER_RGB_BLACK 0
+
+int convert_grayscale_to_rgb(BYTE* in_buf, long width, long height, BYTE* out_rgb_buf);
+int convert_binary_to_rgb(BYTE* in_buf, long width, long height, BYTE* out_rgb_buf);
+
+#endif // ___PISA_IMG_CONV_H
diff --git a/frontend/pisa_main.cc b/frontend/pisa_main.cc
new file mode 100644
index 0000000..7cccf7d
--- /dev/null
+++ b/frontend/pisa_main.cc
@@ -0,0 +1,116 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include "pisa_main.h"
+#include "pisa_gimp.h"
+#include "pisa_view_manager.h"
+#include "pisa_error.h"
+
+int g_gimp_plugin = 1;
+
+/*----------------------------------------------------------*/
+int main ( int argc, char * argv [ ] )
+{
+ int result;
+
+ setlocale ( LC_ALL, "" );
+ bindtextdomain ( PACKAGE, LOCALEDIR );
+#ifdef HAVE_GTK_2
+ bind_textdomain_codeset (PACKAGE, "UTF-8");
+#endif
+ textdomain ( PACKAGE );
+
+ if ( 1 < argc )
+ result = pisa_gimp_main ( argc, argv );
+ else
+ result = 1;
+
+ if ( result )
+ {
+ g_gimp_plugin = 0;
+ pisa_start ( argc, argv );
+ }
+
+ exit ( EXIT_SUCCESS );
+}
+
+/*----------------------------------------------------------*/
+int pisa_gimp_plugin ( void )
+{
+ return g_gimp_plugin;
+}
+
+/*----------------------------------------------------------*/
+void pisa_start ( int argc, char * argv [ ] , int gimpversion)
+{
+ if ( PISA_ERR_SUCCESS != view_manager::create_view_manager ( argc, argv ) )
+ {
+ if ( g_gimp_plugin )
+ pisa_gimp_quit ( );
+ else
+ exit ( EXIT_FAILURE );
+ }
+
+ if ( g_gimp_plugin )
+ {
+#ifdef HAVE_ANY_GIMP
+ gtk_rc_parse ( pisa_gimp_gtkrc ( ) );
+ if (gimpversion == 1)
+ {
+ gdk_set_use_xshm ( pisa_gimp_use_xshm ( ) );
+ }
+ else
+ {
+ gdk_set_use_xshm ( TRUE );
+ }
+#endif // HAVE_ANY_GIMP
+ }
+
+ g_view_manager->create_window ( ID_WINDOW_MAIN );
+ g_view_manager->main ( );
+}
+
+/*----------------------------------------------------------*/
+void pisa_quit ( void )
+{
+ g_view_manager->release_view_manager ( );
+
+ if ( g_gimp_plugin )
+ ::pisa_gimp_quit ( );
+
+ exit ( EXIT_SUCCESS );
+}
+
diff --git a/frontend/pisa_main.h b/frontend/pisa_main.h
new file mode 100644
index 0000000..0860bc1
--- /dev/null
+++ b/frontend/pisa_main.h
@@ -0,0 +1,39 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_MAIN_H
+#define ___PISA_MAIN_H
+
+int pisa_gimp_plugin ( void );
+void pisa_start ( int argc, char * argv [ ], int gimpversion = 0);
+void pisa_quit ( void );
+
+
+#endif // ___PISA_MAIN_H
diff --git a/frontend/pisa_main_window.cc b/frontend/pisa_main_window.cc
new file mode 100644
index 0000000..0aeb86d
--- /dev/null
+++ b/frontend/pisa_main_window.cc
@@ -0,0 +1,1746 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005, 2008, 2009 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+#define N_(msg_id) (msg_id)
+
+/*------------------------------------------------------------*/
+#include <stdio.h>
+#include <string.h>
+#include <cstdlib>
+
+/*------------------------------------------------------------*/
+#include "pisa_main_window.h"
+#include "pisa_error.h"
+#include "pisa_tool.h"
+#include "xpm_data.h"
+#include "pisa_view_manager.h"
+#include "pisa_change_unit.h"
+#include "pisa_default_val.h"
+#include "pisa_scan_selector.h"
+#include "pisa_aleart_dialog.h"
+#include "../lib/pngstream.hh"
+
+#if !GTK_CHECK_VERSION (2,12,0)
+#define gtk_widget_set_tooltip_text(widget,text)
+#endif
+
+/*------------------------------------------------------------*/
+static gint delete_event ( GtkWidget * widget, gpointer data );
+static void click_preview_btn ( void );
+static void click_zoom_btn ( void );
+static void click_expose_btn ( void );
+static void click_scan_btn ( void );
+static void click_scan (GtkWidget *widget, gpointer data);
+static void click_cleaning ( GtkWidget * widget, gpointer data );
+static void click_calibration ( GtkWidget * widget, gpointer data );
+static void click_close ( void );
+
+static void change_destination ( GtkWidget * widget, gpointer data );
+static void change_docsrc ( GtkWidget * widget, gpointer data );
+static void change_imgtype ( GtkWidget * widget, gpointer data );
+static void change_resolution ( GtkWidget * widget, gpointer data );
+static void change_unit ( GtkWidget * widget, gpointer data );
+static void value_changed ( GtkAdjustment * adjust, gpointer data );
+static void toggled_start_button (GtkWidget *widget, gpointer data);
+static void toggled_draft_mode (GtkWidget *widget, gpointer data);
+static void toggled_size_check (GtkWidget *widget, gpointer data);
+static void toggled_deskew (GtkWidget *widget, gpointer data);
+static void change_dfd (GtkWidget *widget, gpointer data);
+static void toggled_usm ( GtkWidget * widget, gpointer data );
+static void check_focus ( GtkWidget * widget, gpointer data );
+static void switch_window_waiting (GtkWidget *widget, gboolean is_waiting, GdkCursor **cursor);
+
+/*------------------------------------------------------------*/
+// destination
+#define ID_MENU_FILE 0x0001
+#define ID_MENU_PRINT 0x0002
+menu_items g_destination_menu_item [ ] =
+{
+ { ID_MENU_FILE, 0, N_("File"), ( GtkSignalFunc * ) change_destination },
+ { ID_MENU_PRINT, 0, N_("Printer"), ( GtkSignalFunc * ) change_destination },
+ { 0, 0, "", 0 }
+};
+
+/*------------------------------------------------------------*/
+// document source
+#define ID_MENU_FLATBED 0x0001
+#define ID_MENU_TPU_NEG 0x0002
+#define ID_MENU_TPU_POS 0x0003
+#define ID_MENU_ADF_SIM 0x0004
+#define ID_MENU_ADF_DUP 0x0005
+menu_items g_docsrc_menu_item [ ] =
+{
+ { ID_MENU_FLATBED, 0, N_("Flatbed"), ( GtkSignalFunc * ) change_docsrc },
+ { ID_MENU_TPU_NEG, 0, N_("TPU - Negative film"), ( GtkSignalFunc * ) change_docsrc },
+ { ID_MENU_TPU_POS, 0, N_("TPU - Positive film"), ( GtkSignalFunc * ) change_docsrc },
+ { ID_MENU_ADF_SIM, 0, N_("ADF - Single-sided"), ( GtkSignalFunc * ) change_docsrc },
+ { ID_MENU_ADF_DUP, 0, N_("ADF - Double-sided"), ( GtkSignalFunc * ) change_docsrc },
+ { 0, 0, "", 0 }
+};
+
+/*------------------------------------------------------------*/
+// image type
+#define ID_MENU_24C_PHOTO 0x0001
+#define ID_MENU_24C_DOC 0x0002
+#define ID_MENU_8G_PHOTO 0x0003
+#define ID_MENU_8G_DOC 0x0004
+#define ID_MENU_LINEART 0x0005
+menu_items g_imgtype_menu_item [ ] =
+{
+ { ID_MENU_24C_PHOTO, 0, N_("Color Photo"), ( GtkSignalFunc * ) change_imgtype },
+ { ID_MENU_24C_DOC, 0, N_("Color Document"), ( GtkSignalFunc * ) change_imgtype },
+ { ID_MENU_8G_PHOTO, 0, N_("Black & White Photo"), ( GtkSignalFunc * ) change_imgtype },
+ { ID_MENU_8G_DOC, 0, N_("Black & White Document"), ( GtkSignalFunc * ) change_imgtype },
+ { ID_MENU_LINEART, 0, N_("Line Art"), ( GtkSignalFunc * ) change_imgtype },
+ { 0, 0, "", 0 }
+};
+
+imagetype g_imagetype_list [ ] =
+{
+ { ID_MENU_24C_PHOTO, PISA_PT_RGB, PISA_BD_8, PISA_DESCREEN_OFF, PISA_AE_PHOTO, PISA_DO_NONE, PISA_MO_NONE, PISA_HT_NONE },
+ { ID_MENU_24C_DOC, PISA_PT_RGB, PISA_BD_8, PISA_DESCREEN_ON, PISA_AE_DOC, PISA_DO_NONE, PISA_MO_NONE, PISA_HT_NONE },
+ { ID_MENU_8G_PHOTO, PISA_PT_GRAY, PISA_BD_8, PISA_DESCREEN_OFF, PISA_AE_PHOTO, PISA_DO_NONE, PISA_MO_NONE, PISA_HT_NONE },
+ { ID_MENU_8G_DOC, PISA_PT_GRAY, PISA_BD_8, PISA_DESCREEN_ON, PISA_AE_DOC, PISA_DO_NONE, PISA_MO_NONE, PISA_HT_NONE },
+ { ID_MENU_LINEART, PISA_PT_BW, PISA_BD_1, PISA_DESCREEN_OFF, PISA_AE_GRAYED, PISA_DO_NONE, PISA_MO_NONE, PISA_HT_NONE }
+};
+
+/*------------------------------------------------------------*/
+// resolution
+menu_items g_resolution_menu_item [ ] =
+{
+ { 50, 0, "50dpi", ( GtkSignalFunc * ) change_resolution },
+ { 72, 0, "72dpi", ( GtkSignalFunc * ) change_resolution },
+ { 96, 0, "96dpi", ( GtkSignalFunc * ) change_resolution },
+ { 150, 0, "150dpi", ( GtkSignalFunc * ) change_resolution },
+ { 200, 0, "200dpi", ( GtkSignalFunc * ) change_resolution },
+ { 240, 0, "240dpi", ( GtkSignalFunc * ) change_resolution },
+ { 300, 0, "300dpi", ( GtkSignalFunc * ) change_resolution },
+ { 360, 0, "360dpi", ( GtkSignalFunc * ) change_resolution },
+ { 400, 0, "400dpi", ( GtkSignalFunc * ) change_resolution },
+ { 600, 0, "600dpi", ( GtkSignalFunc * ) change_resolution },
+ { 720, 0, "720dpi", ( GtkSignalFunc * ) change_resolution },
+ { 800, 0, "800dpi", ( GtkSignalFunc * ) change_resolution },
+ { 1200, 0, "1200dpi", ( GtkSignalFunc * ) change_resolution },
+ { 1600, 0, "1600dpi", ( GtkSignalFunc * ) change_resolution },
+ { 2400, 0, "2400dpi", ( GtkSignalFunc * ) change_resolution },
+ { 3200, 0, "3200dpi", ( GtkSignalFunc * ) change_resolution },
+ { 4800, 0, "4800dpi", ( GtkSignalFunc * ) change_resolution },
+ { 6400, 0, "6400dpi", ( GtkSignalFunc * ) change_resolution },
+ { 9600, 0, "9600dpi", ( GtkSignalFunc * ) change_resolution },
+ {12800, 0,"12800dpi", ( GtkSignalFunc * ) change_resolution },
+ { 0, 0, "", 0 }
+};
+
+/*------------------------------------------------------------*/
+// unit
+menu_items g_unit_menu_item [ ] =
+{
+ { PISA_UNIT_INCHES, 0, N_("inches"), ( GtkSignalFunc * ) change_unit },
+ { PISA_UNIT_PIXELS, 0, N_("pixels"), ( GtkSignalFunc * ) change_unit },
+ { PISA_UNIT_CM , 0, N_("cm"), ( GtkSignalFunc * ) change_unit },
+ { 0, 0, "", 0 }
+};
+
+/*------------------------------------------------------------*/
+// scale
+#define ID_SCALE_ZOOM 0x0001
+
+scale_items g_scale_zoom = { ID_SCALE_ZOOM, 0, 0,
+ ( GtkSignalFunc * ) value_changed,
+ 100, 50, 200, 1, 10 };
+
+/*------------------------------------------------------------*/
+// focus
+
+long g_focus [ ] = { 0, 25 };
+
+/*------------------------------------------------------------*/
+// double feed detection
+menu_items g_double_feed_detection[] =
+{
+ { PISA_DFD_OFF, 0, N_("Off"), (GtkSignalFunc *) change_dfd },
+ { PISA_DFD_STD, 0, N_("Standard"), (GtkSignalFunc *) change_dfd },
+ { PISA_DFD_THIN, 0, N_("Thin"), (GtkSignalFunc *) change_dfd },
+ { 0, 0, "", 0 }
+};
+
+static void
+change_dfd (GtkWidget *widget, gpointer data)
+{
+ scan_manager *sm = g_view_manager->get_scan_manager ();
+ if (sm && sm->has_dfd ()) sm->set_dfd (*(long *) data);
+}
+
+/*------------------------------------------------------------*/
+static gint delete_event ( GtkWidget * widget, gpointer data )
+{
+ widget = widget;
+ data = data;
+
+ ::g_view_manager->close_window ( ID_WINDOW_MAIN, 0 );
+
+ return FALSE;
+}
+
+/*------------------------------------------------------------*/
+void main_window::start_preview ( bool zooming )
+{
+ preview_window * prev_cls;
+ main_window * main_cls;
+
+ prev_cls = ( preview_window * ) ::g_view_manager->get_window_cls ( ID_WINDOW_PREV );
+
+ main_cls = ( main_window * ) ::g_view_manager->get_window_cls ( ID_WINDOW_MAIN );
+
+ scan_manager *sm = g_view_manager->get_scan_manager ();
+
+ if (sm->has_preview ()) sm->set_preview (true);
+ main_cls->do_sensitive_size_check (false);
+
+ if (zooming)
+ {
+ prev_cls->start_zoom ();
+ }
+ else
+ {
+ prev_cls->start_preview ();
+ }
+
+ main_cls->do_sensitive_size_check (true);
+ if (sm->has_preview ()) sm->set_preview (false);
+}
+
+/*------------------------------------------------------------*/
+static void click_preview_btn ( void )
+{
+ main_window *main_cls = ( main_window * ) ::g_view_manager->get_window_cls ( ID_WINDOW_MAIN );
+ main_cls->start_preview (false);
+}
+
+/*------------------------------------------------------------*/
+static void click_zoom_btn ( void )
+{
+ main_window *main_cls = ( main_window * ) ::g_view_manager->get_window_cls ( ID_WINDOW_MAIN );
+ main_cls->start_preview (true);
+}
+
+/*------------------------------------------------------------*/
+static void click_expose_btn ( void )
+{
+ preview_window * prev_cls;
+
+ prev_cls = ( preview_window * ) ::g_view_manager->get_window_cls ( ID_WINDOW_PREV );
+
+ prev_cls->auto_exposure ( );
+ prev_cls->update_img ( );
+}
+
+/*------------------------------------------------------------*/
+static void click_scan_btn ( void )
+{
+ ::g_view_manager->start_scan ( );
+}
+
+static void
+click_scan (GtkWidget *destination, gpointer data)
+{
+ main_window *mw = static_cast <main_window *> (data);
+ mw->start_scan (destination);
+}
+
+static void
+click_calibration ( GtkWidget * widget, gpointer data )
+{
+ scan_manager *scan_mgr = static_cast< scan_manager * > (data);
+ GdkCursor *cur = NULL;
+
+ if (scan_mgr->has_calibrate ())
+ {
+ switch_window_waiting (widget, true, &cur);
+
+ try
+ {
+ scan_mgr->calibrate ();
+ }
+ catch (pisa_error & err)
+ {
+ aleart_dialog aleart_dlg;
+ aleart_dlg.message_box (0, _("Calibration is failed."));
+ }
+
+ switch_window_waiting (widget, false, &cur);
+ }
+
+ return;
+}
+
+static void
+click_cleaning ( GtkWidget * widget, gpointer data )
+{
+ scan_manager *scan_mgr = static_cast< scan_manager * > (data);
+ GdkCursor *cur = NULL;
+
+ if (scan_mgr->has_clean ())
+ {
+ switch_window_waiting (widget, true, &cur);
+
+ try
+ {
+ scan_mgr->clean ();
+ }
+ catch (pisa_error & err)
+ {
+ aleart_dialog aleart_dlg;
+ aleart_dlg.message_box (0, _("Cleaning is failed."));
+ }
+
+ switch_window_waiting (widget, false, &cur);
+ }
+
+ return;
+}
+
+/*------------------------------------------------------------*/
+static void click_close ( void )
+{
+ ::g_view_manager->close_window ( ID_WINDOW_MAIN, 1 );
+}
+
+/*------------------------------------------------------------*/
+static void click_config ( void )
+{
+ ::g_view_manager->create_window ( ID_WINDOW_CONFIG );
+}
+
+/*------------------------------------------------------------*/
+static void change_destination ( GtkWidget * widget, gpointer data )
+{
+ char dest;
+ long id;
+
+ widget = widget;
+ id = * ( long * ) data;
+
+ switch ( id )
+ {
+ case ID_MENU_FILE:
+ dest = PISA_DE_FILE;
+ break;
+
+ case ID_MENU_PRINT:
+ dest = PISA_DE_PRINTER;
+ break;
+
+ default:
+ return;
+ }
+
+ g_view_manager->set_destination (dest);
+}
+
+/*------------------------------------------------------------*/
+static void change_docsrc ( GtkWidget * widget, gpointer data )
+{
+ widget = widget;
+
+ // ASSUMPTION: flatbed is the default at program startup
+ static long current_id = ID_MENU_FLATBED;
+
+ long id = *(long *) data;
+ if (current_id == id) return; // no change
+
+ scan_manager *sm = g_view_manager->get_scan_manager ();
+
+ switch (id)
+ {
+ case ID_MENU_FLATBED:
+ sm->select_flatbed ();
+ break;
+ case ID_MENU_TPU_NEG:
+ case ID_MENU_TPU_POS:
+ sm->select_tpu (ID_MENU_TPU_POS == id);
+ break;
+ case ID_MENU_ADF_SIM:
+ case ID_MENU_ADF_DUP:
+ sm->select_adf (ID_MENU_ADF_DUP == id);
+ break;
+ default:
+ return;
+ }
+ current_id = id;
+
+ if (!g_view_manager->change_document_source (g_imagetype_list))
+ { // reset drop down menu to default
+ gtk_menu_set_active
+ (GTK_MENU (gtk_widget_get_ancestor (widget, GTK_TYPE_MENU)), 0);
+ }
+
+ main_window *mw = (static_cast <main_window *>
+ (g_view_manager->get_window_cls (ID_WINDOW_MAIN)));
+ if (mw->get_widget (WG_MAIN_DFD))
+ gtk_widget_set_sensitive (mw->get_widget (WG_MAIN_DFD), sm->using_adf ());
+}
+
+/*------------------------------------------------------------*/
+static void change_imgtype ( GtkWidget * widget, gpointer data )
+{
+ unsigned i;
+ long id;
+
+ widget = widget;
+ id = * ( long * ) data;
+
+ for ( i = 0; i < sizeof ( ::g_imagetype_list ) /
+ sizeof ( ::g_imagetype_list [ 0 ] ); i++ )
+ {
+ if ( id == g_imagetype_list [ i ].id )
+ {
+ preview_window * prev_cls;
+
+ g_view_manager->set_image_type (&g_imagetype_list[i]);
+ g_view_manager->sensitive ();
+
+ prev_cls = ( preview_window * ) ::g_view_manager->get_window_cls ( ID_WINDOW_PREV );
+
+ prev_cls->auto_exposure ( );
+
+ ::g_view_manager->update_lut ( );
+
+ prev_cls->update_img ( );
+
+ return;
+ }
+ }
+}
+
+/*------------------------------------------------------------*/
+static void change_resolution ( GtkWidget * widget, gpointer data )
+{
+ widget = widget;
+
+ g_view_manager->set_resolution (* (long *) data);
+
+ ::g_view_manager->sensitive ( );
+}
+
+/*------------------------------------------------------------*/
+static void change_unit ( GtkWidget * widget, gpointer data )
+{
+ widget = widget;
+
+ g_view_manager->set_unit (* (long *) data);
+
+ ::g_view_manager->sensitive ( );
+}
+
+/*------------------------------------------------------------*/
+static void value_changed ( GtkAdjustment * adjust, gpointer data )
+{
+ long id;
+ long val;
+ int change;
+ marquee * marq;
+
+ id = * ( long * ) data;
+ change = 1;
+ marq = & g_view_manager->get_marquee ();
+
+ switch ( id )
+ {
+ case ID_SCALE_ZOOM:
+ val = ( long ) ( adjust->value );
+ if ( marq->scale == val )
+ change = 0;
+ else
+ marq->scale = val;
+ break;
+ }
+
+ if ( change )
+ {
+ ::g_view_manager->sensitive ( );
+ }
+}
+
+static void
+toggled_start_button (GtkWidget *widget, gpointer data)
+{
+ data = data;
+
+ g_view_manager->enable_start_button (gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON (widget)));
+}
+
+static void
+toggled_draft_mode (GtkWidget *widget, gpointer data)
+{
+ data = data;
+
+ g_view_manager->enable_draft_mode (gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON (widget)));
+}
+
+static void
+toggled_size_check (GtkWidget *widget, gpointer data)
+{
+ main_window *mw = (main_window *) data;
+ scan_manager *sm = g_view_manager->get_scan_manager ();
+ mw->update_zoom_button ();
+
+ gboolean is_active =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ if ( sm->has_size_check () )
+ {
+ sm->set_size_check (is_active);
+ }
+ if ( sm->has_autocrop () )
+ {
+ sm->set_autocrop (is_active);
+ }
+ mw->sensitive_option();
+}
+
+static void
+toggled_deskew (GtkWidget *widget, gpointer data)
+{
+ main_window *mw = (main_window *) data;
+
+ g_view_manager
+ ->get_scan_manager()
+ ->set_deskew (gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON (widget)));
+ mw->sensitive_option();
+}
+
+/*------------------------------------------------------------*/
+static void toggled_usm ( GtkWidget * widget, gpointer data )
+{
+ data = data;
+
+ if ( ::gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( widget ) ) )
+ g_view_manager->enable_unsharp_mask (true);
+ else
+ g_view_manager->enable_unsharp_mask (false);
+
+ preview_window *prev_cls =
+ (preview_window *) ::g_view_manager->get_window_cls (ID_WINDOW_PREV);
+
+ prev_cls->update_img ( );
+}
+
+/*------------------------------------------------------------*/
+static void check_focus ( GtkWidget * widget, gpointer data )
+{
+ long id;
+ marquee * marq;
+
+ widget = widget;
+
+ marq = & g_view_manager->get_marquee ();
+ id = * ( long * ) data;
+
+ if ( id == g_focus [ 1 ] )
+ marq->focus = 25;
+ else
+ marq->focus = 0;
+}
+
+/*------------------------------------------------------------*/
+static void
+switch_window_waiting (GtkWidget *widget, gboolean is_waiting, GdkCursor **cur)
+{
+ GtkWidget *main_win = ::gtk_widget_get_toplevel (widget);
+ GdkWindow *window = main_win->window;
+
+ if (is_waiting)
+ {
+ ::gtk_widget_set_sensitive (main_win, false);
+
+ *cur = ::gdk_cursor_new (GDK_WATCH);
+ ::gdk_window_set_cursor (window, *cur);
+ }
+ else
+ {
+ ::gdk_window_set_cursor (window, NULL);
+ ::gdk_cursor_unref (*cur);
+
+ ::gtk_widget_set_sensitive (main_win, true);
+ }
+
+ while (::gtk_events_pending ())
+ ::gtk_main_iteration ();
+}
+
+/*------------------------------------------------------------*/
+int main_window::init ( void )
+{
+ unsigned int i;
+
+ for ( i = 0; i < WG_MAIN_NUM; i++ )
+ m_widget [ i ] = 0;
+
+ g_view_manager->set_image_type (&g_imagetype_list[0]);
+
+ _done_preview = false;
+ _do_sensitive_size_check = true;
+
+ return PISA_ERR_SUCCESS;
+}
+
+
+/*------------------------------------------------------------*/
+GtkWidget * main_window::create_window ( GtkWidget * parent )
+{
+ GtkWidget * top;
+ GtkWidget * hbox;
+ GtkWidget * left_area, * right_area;
+
+ parent = parent;
+
+ // determine GUI mode
+ _compact_mode = false;
+ gint padding = 10;
+ GdkScreen * screen = gdk_screen_get_default ();
+ if ((screen && 768 > gdk_screen_get_height (screen))
+ || getenv ("ISCAN_COMPACT_GUI"))
+ {
+ padding = 2;
+ _compact_mode = true;
+ if (getenv ("ISCAN_DEBUG"))
+ fprintf (stderr, "Compact GUI mode enabled.\n");
+ }
+ else
+ if (getenv ("ISCAN_DEBUG"))
+ {
+ if (!screen) fprintf (stderr, "No default screen found. ");
+ fprintf (stderr, "Standard GUI mode enabled.\n");
+ }
+
+
+ // main window
+ top = m_widget [ WG_MAIN_TOP ] = ::gtk_window_new ( GTK_WINDOW_TOPLEVEL );
+ ::gtk_container_border_width ( GTK_CONTAINER ( top ), padding );
+ ::gtk_window_set_title ( GTK_WINDOW ( top ), PACKAGE_STRING );
+ ::gtk_widget_set_uposition ( top, POS_MAIN_X, POS_MAIN_Y );
+
+ ::gtk_signal_connect ( GTK_OBJECT ( top ), "delete_event",
+ GTK_SIGNAL_FUNC ( ::delete_event ), 0 );
+
+ hbox = ::gtk_hbox_new ( FALSE, 5 );
+ ::gtk_container_add ( GTK_CONTAINER ( top ), hbox );
+ ::gtk_widget_show ( hbox );
+
+ // The icons for our buttons rely on the main widget's top level to
+ // be realized. These buttons were created in `create_left_area()`
+ // in the past but as we are heavily rearranging parts of the GUI,
+ // we'd better make sure it's done early.
+ ::gtk_widget_realize ( m_widget [ WG_MAIN_TOP ] );
+
+ create_controls ();
+
+ left_area = create_left_area ( );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), left_area, TRUE, TRUE, 0 );
+ ::gtk_widget_show ( left_area );
+
+ right_area = create_right_area ( );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), right_area, FALSE, FALSE, 0 );
+ ::gtk_widget_show ( right_area );
+
+ ::gtk_widget_show ( top );
+
+ gtk_widget_grab_focus (prev_btn_);
+
+ ::g_view_manager->sensitive ( );
+
+ return top;
+}
+
+/*------------------------------------------------------------*/
+int main_window::close_window ( int destroy )
+{
+ ::g_view_manager->close_window ( ID_WINDOW_PREV, 1 );
+
+ if ( destroy && m_widget [ WG_MAIN_TOP ] )
+ ::gtk_widget_destroy ( m_widget [ WG_MAIN_TOP ] );
+
+ m_widget [ WG_MAIN_TOP ] = 0;
+
+ return PISA_ERR_SUCCESS;
+}
+
+void
+main_window::start_scan (const GtkWidget *destination) const
+{
+ if (destination == scan_btn_)
+ g_view_manager->set_destination (PISA_DE_FILE);
+ else if (destination == print_btn_)
+ g_view_manager->set_destination (PISA_DE_PRINTER);
+ else {
+ fprintf (stderr, "Unsupported scan destination.\n");
+ return;
+ }
+ g_view_manager->start_scan ();
+}
+
+void
+main_window::set_double_feed_detection (const GtkWidget *widget)
+{
+ long value = PISA_DFD_OFF;
+
+ if (widget == m_widget[WG_MAIN_DFD]) {
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
+ gtk_widget_set_sensitive (m_widget[WG_MAIN_DFD_BOX], true);
+ if (gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON (m_widget[WG_MAIN_DFD_STD]))) {
+ value = PISA_DFD_STD;
+ } else if (gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON (m_widget[WG_MAIN_DFD_THIN]))) {
+ value = PISA_DFD_THIN;
+ } else {
+ fprintf (stderr, "No known active DFD value. "
+ "Falling back to 'Standard'.\n");
+ value = PISA_DFD_STD;
+ }
+ } else {
+ gtk_widget_set_sensitive (m_widget[WG_MAIN_DFD_BOX], false);
+ value = PISA_DFD_OFF;
+ }
+ } else {
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
+ if (widget == m_widget[WG_MAIN_DFD_STD]) {
+ value = PISA_DFD_STD;
+ } else if (widget == m_widget[WG_MAIN_DFD_THIN]) {
+ value = PISA_DFD_THIN;
+ } else {
+ fprintf (stderr, "Unsupported DFD value.\n");
+ return;
+ }
+ } else {
+ return; // nothing to do
+ }
+ }
+
+ scan_manager *sm = g_view_manager->get_scan_manager ();
+ if (sm && sm->has_dfd ()) sm->set_dfd (value);
+}
+
+/*------------------------------------------------------------*/
+void main_window::sensitive ( int is_prev_img )
+{
+ gboolean enable_expose, enable_dest, enable_config;
+
+ _done_preview = bool (is_prev_img);
+
+ enable_expose = enable_dest = enable_config= TRUE;
+
+ if ( is_prev_img == 0 ||
+ g_view_manager->get_settings ().imgtype.pixeltype == PISA_PT_BW )
+ enable_expose = FALSE;
+
+ if ( ::g_view_manager->is_gimp ( ) )
+ {
+ enable_dest = FALSE;
+ enable_config = FALSE;
+ }
+
+ update_zoom_button ();
+
+ gtk_widget_set_sensitive (expo_btn_, enable_expose);
+ gtk_widget_set_sensitive ((_compact_mode ? print_btn_ : dest_menu_),
+ enable_dest);
+ gtk_widget_set_sensitive (config_, enable_config);
+
+ sensitive_option ();
+}
+
+void main_window::sensitive_option ()
+{
+ sensitive_image_type ( );
+ sensitive_scale ( );
+ sensitive_resolution ( );
+ sensitive_target ( );
+ sensitive_focus ( );
+ sensitive_usm ( );
+ sensitive_size_check ();
+ sensitive_deskew ();
+}
+
+void
+main_window::enable_start_button (bool yes)
+{
+ gtk_widget_set_sensitive (scan_navi_, yes);
+}
+
+void
+main_window::update_zoom_button ()
+{
+ long marq_size = 0;
+ gboolean size_check_enabled = FALSE;
+ gboolean enable_zoom = TRUE;
+
+ marq_size = g_view_manager->get_marquee_size ();
+ size_check_enabled = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON (size_check_));
+
+ if (marq_size < 2 || size_check_enabled || !_done_preview)
+ {
+ enable_zoom = FALSE;
+ }
+
+ gtk_widget_set_sensitive (zoom_btn_, enable_zoom);
+}
+
+static GtkWidget *
+create_scan_button_(main_window *mw, bool toolbar)
+{
+ GtkWidget *button;
+
+ if (toolbar) {
+ button = GTK_WIDGET (gtk_tool_button_new_from_stock (GTK_STOCK_SAVE_AS));
+ gtk_widget_set_tooltip_text (button, _("Scan to File"));
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (click_scan), mw);
+ } else {
+ button = gtk_button_new ();
+ gtk_container_add (GTK_CONTAINER (button),
+ xpmlabelbox (mw->get_widget (), scan_xpm, _("Scan")));
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (click_scan_btn), mw);
+ }
+
+ return button;
+}
+
+static GtkWidget *
+create_pref_button_(main_window *mw, bool toolbar)
+{
+ GtkWidget *button;
+ const char *text = _("Configuration");
+
+ if (toolbar) {
+ button = GTK_WIDGET (gtk_tool_button_new_from_stock
+ (GTK_STOCK_PREFERENCES));
+ gtk_widget_set_tooltip_text (button, text);
+ } else {
+ button = gtk_button_new_with_label (text);
+ }
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (click_config), mw);
+
+ return button;
+}
+
+//! A constructor-like tool button builder.
+GtkWidget *
+main_window::tool_button (const char *text, GCallback callback, gchar **icon)
+{
+ GtkWidget *widget;
+
+ if (_compact_mode) {
+ widget = GTK_WIDGET (gtk_tool_button_new
+ (xpm2widget (get_widget (), icon), text));
+ gtk_widget_set_tooltip_text (widget, text);
+ } else {
+ widget = gtk_button_new ();
+ gtk_container_add (GTK_CONTAINER (widget),
+ xpmlabelbox (get_widget (), icon, text));
+ }
+ g_signal_connect (G_OBJECT (widget), "clicked", callback, this);
+
+ return widget;
+}
+
+//! A constructor-like check button builder.
+GtkWidget *
+main_window::check_button (const char *text,
+ void (*callback) (GtkWidget *, gpointer))
+{
+ GtkWidget *widget;
+
+ widget = gtk_check_button_new_with_label (text);
+ g_signal_connect (G_OBJECT (widget), "toggled",
+ G_CALLBACK (callback), this);
+
+ return widget;
+}
+
+//! A constructor-like close button builder.
+GtkWidget *
+main_window::close_button ()
+{
+ GtkWidget *widget;
+ const char *text = _("Close");
+
+ if (_compact_mode) {
+ widget = GTK_WIDGET (gtk_tool_button_new_from_stock (GTK_STOCK_CLOSE));
+ gtk_widget_set_tooltip_text (widget, text);
+ } else {
+ widget = gtk_button_new_with_label (text);
+ }
+ g_signal_connect (G_OBJECT (widget), "clicked",
+ G_CALLBACK (click_close), this);
+
+ return widget;
+}
+
+void
+main_window::create_controls ()
+{
+ scan_manager *sm = g_view_manager->get_scan_manager ();
+
+ prev_btn_ = tool_button (_("Preview"), click_preview_btn, preview_xpm);
+ zoom_btn_ = tool_button (_("Zoom"), click_zoom_btn, zoom_xpm);
+ expo_btn_ = tool_button (_("Auto Exposure"), click_expose_btn, auto_xpm);
+ scan_btn_ = create_scan_button_(this, _compact_mode);
+
+ scan_navi_ = check_button (_("enable Start button"), toggled_start_button);
+ draft_mode_ = check_button (_("Speed priority scanning"),
+ toggled_draft_mode);
+ size_check_ = check_button (_("Document Size - Auto Detect"),
+ toggled_size_check);
+ deskew_ = check_button (_("Correct Document Skew"),
+ toggled_deskew);
+
+ config_ = create_pref_button_(this, _compact_mode);
+
+ unsharp_mask_ = check_button (_("Unsharp mask"), toggled_usm);
+
+ if (getenv ("ISCAN_GUI_SHOW_ALL") || sm->has_dfd ()) {
+ GtkWidget *widget;
+ const char *text = _("Detect Double Feed (Paper Thickness)");
+ GtkWidget *menu = pisa_create_option_menu (g_double_feed_detection);
+ GtkWidget *label = gtk_label_new (text);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+ widget = gtk_hbox_new (false, 5);
+ gtk_box_pack_start (GTK_BOX (widget), label, false, false, 5);
+ gtk_box_pack_start (GTK_BOX (widget), menu, false, false, 5);
+ m_widget[WG_MAIN_DFD] = widget;
+ gtk_widget_set_sensitive (m_widget[WG_MAIN_DFD], sm->using_adf ());
+ }
+
+ if (sm->push_button_needs_polling ())
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scan_navi_), true);
+ gtk_widget_set_sensitive (scan_navi_, false);
+ }
+ else
+ {
+ gtk_widget_set_sensitive (scan_navi_, sm->has_flatbed () || sm->has_tpu ());
+ }
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * main_window::create_left_area ( void )
+{
+ GtkWidget *vbox = gtk_vbox_new (false, 0);
+
+ if (!_compact_mode)
+ gtk_box_pack_start (GTK_BOX (vbox), action_button_box (), false, false, 0);
+
+ GtkWidget *widget = g_view_manager->create_window (ID_WINDOW_PREV);
+ // In order to avoid a segmentation error in the preview_window
+ // class (resulting from sloppy image size logic), we add a size
+ // kludge here so that the conditions that trigger it become very
+ // unlikely.
+ // FIXME: remove kludge once preview_window gets fixed.
+ ::gtk_widget_set_usize (GTK_WIDGET (widget), 360, 480);
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), widget, TRUE, TRUE, 0 );
+ ::gtk_widget_show ( widget );
+
+ // destination menu and scan button
+ if (!_compact_mode)
+ {
+ widget = create_scan_button ( );
+ ::gtk_box_pack_end ( GTK_BOX ( vbox ), widget, FALSE, FALSE, 0 );
+ ::gtk_widget_show ( widget );
+ }
+
+ return vbox;
+}
+
+//! Assembles a widget with selected "action" buttons.
+/*! \note Intended for use with the non-compact GUI mode only.
+ */
+GtkWidget *
+main_window::action_button_box ()
+{
+ if (_compact_mode) return NULL;
+
+ GtkWidget *hbox = gtk_hbox_new (false, 5);
+
+ gtk_box_pack_start (GTK_BOX (hbox), prev_btn_, false, false, 5);
+ gtk_box_pack_start (GTK_BOX (hbox), zoom_btn_, false, false, 5);
+ gtk_box_pack_start (GTK_BOX (hbox), expo_btn_, false, false, 5);
+ gtk_widget_show_all (hbox);
+
+ return hbox;
+}
+
+//! Assembles a toolbar widget from selected controls.
+/*! \note Intended for use in compact GUI mode only.
+ */
+GtkWidget *
+main_window::toolbar ()
+{
+ if (!_compact_mode) return NULL;
+
+ const gint at_end = -1;
+ GtkToolbar *toolbar = GTK_TOOLBAR (gtk_toolbar_new ());
+ gtk_toolbar_set_style (toolbar, GTK_TOOLBAR_ICONS);
+ gtk_toolbar_set_show_arrow (toolbar, false);
+
+ GtkToolItem *btn, *sep;
+
+ btn = gtk_tool_button_new_from_stock (GTK_STOCK_PRINT);
+ gtk_widget_set_tooltip_text (GTK_WIDGET (btn), _("Scan to Print"));
+ gtk_signal_connect (GTK_OBJECT (btn), "clicked",
+ GTK_SIGNAL_FUNC (click_scan), this);
+ print_btn_ = GTK_WIDGET (btn);
+
+ // Spring separator to push remaining buttons to the far end of
+ // the toolbar
+ sep = gtk_separator_tool_item_new ();
+ gtk_tool_item_set_expand (sep, true);
+ gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (sep),
+ false);
+
+ // Final toolbar tweaks
+ if (!iscan::pngstream::is_usable ()) {
+ gtk_widget_set_sensitive (print_btn_, false);
+ }
+
+ gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (prev_btn_), at_end);
+ gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (zoom_btn_), at_end);
+ gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (expo_btn_), at_end);
+ gtk_toolbar_insert (toolbar, gtk_separator_tool_item_new (), at_end);
+ gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (scan_btn_), at_end);
+ gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (print_btn_), at_end);
+ gtk_toolbar_insert (toolbar, gtk_separator_tool_item_new (), at_end);
+ gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (config_), at_end);
+ gtk_toolbar_insert (toolbar, sep, at_end);
+ gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (close_button ()), at_end);
+
+ gtk_widget_show_all (GTK_WIDGET (toolbar));
+
+ return GTK_WIDGET (toolbar);
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * main_window::create_scan_button ( void )
+{
+ GtkWidget * table;
+ GtkWidget * label;
+ GtkWidget * menu;
+ GtkWidget * vbox;
+
+ table = ::gtk_table_new ( 2, 3, FALSE );
+ ::gtk_container_border_width ( GTK_CONTAINER ( table ), 5 );
+ ::gtk_table_set_row_spacings ( GTK_TABLE ( table ), 3 );
+ ::gtk_table_set_col_spacings ( GTK_TABLE ( table ), 8 );
+
+ label = ::gtk_label_new ( _( "Destination:" ) );
+ ::gtk_table_attach ( GTK_TABLE ( table ), label, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( label );
+
+ menu = ::pisa_create_option_menu ( g_destination_menu_item );
+ ::gtk_table_attach ( GTK_TABLE ( table ), menu, 1, 2, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ if (!iscan::pngstream::is_usable ()) {
+ gtk_widget_set_sensitive (g_destination_menu_item[1].widget, FALSE);
+ }
+ ::gtk_widget_show ( menu );
+ dest_menu_ = menu;
+
+ gtk_table_attach (GTK_TABLE (table), scan_btn_,
+ 2, 3, 0, 2, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show_all (scan_btn_);
+
+ vbox = gtk_vbox_new ( FALSE, 0 );
+ gtk_table_attach ( GTK_TABLE ( table ), vbox, 0, 2, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ gtk_widget_show ( vbox );
+
+ gtk_box_pack_start (GTK_BOX (vbox), scan_navi_ , false, false, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), draft_mode_, false, false, 0);
+
+ scan_manager *sm = g_view_manager->get_scan_manager ();
+ if (sm->has_start_button ())
+ gtk_widget_show (scan_navi_);
+ if (sm->has_draft_mode ())
+ gtk_widget_show (draft_mode_);
+
+ gtk_widget_show (table);
+ return table;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * main_window::create_right_area ( void )
+{
+ GtkWidget * vbox;
+ GtkWidget * widget;
+
+ vbox = ::gtk_vbox_new ( FALSE, 5 );
+
+ if (!_compact_mode)
+ {
+ #if THE_ORIGINAL_SOURCES_WERE_NOT_SUCH_A_MESS
+ scan_selector *ss = new scan_selector();
+ ::gtk_box_pack_start( GTK_BOX( vbox ), ss->widget(), FALSE, FALSE, 0 );
+ ss->update(); // seed list of available scanners
+ ss->select(); // pick one
+ ss->show();
+ #else
+ widget = create_scan_label ( );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), widget, FALSE, FALSE, 0 );
+ ::gtk_widget_show ( widget );
+ #endif /* THE_ORIGINAL_SOURCES_WERE_NOT_SUCH_A_MESS */
+ }
+ else
+ gtk_box_pack_start (GTK_BOX (vbox), toolbar (), false, false, 0);
+
+ widget = create_notebook ( );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), widget, FALSE, FALSE, 0 );
+ ::gtk_widget_show ( widget );
+
+ if (!_compact_mode)
+ gtk_box_pack_end (GTK_BOX (vbox), close_button_box (), false, false, 0);
+
+ return vbox;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * main_window::create_scan_label ( void )
+{
+ GtkWidget *hbox;
+ GtkWidget *widget;
+
+ hbox = ::gtk_hbox_new( FALSE, 0 );
+
+ widget = ::gtk_label_new( _("Scanner:") );
+ ::gtk_box_pack_start( GTK_BOX( hbox ), widget, FALSE, FALSE, 5 );
+ ::gtk_widget_show( widget );
+
+ widget = ::gtk_label_new( ::g_view_manager->get_device_name() );
+ ::gtk_label_set_line_wrap ( GTK_LABEL (widget), TRUE );
+ ::gtk_box_pack_start( GTK_BOX( hbox ), widget, FALSE, FALSE, 5 );
+ ::gtk_widget_show( widget );
+
+ return hbox;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * main_window::create_notebook ( void )
+{
+ GtkWidget * notebook;
+ GtkWidget * label;
+ GtkWidget * widget;
+
+ notebook = ::gtk_notebook_new ( );
+ ::gtk_container_border_width ( GTK_CONTAINER ( notebook ), 5 );
+
+ // document tab
+ label = ::gtk_label_new ( _( "Document" ) );
+ widget = create_document_tab ( );
+
+ ::gtk_notebook_append_page ( GTK_NOTEBOOK ( notebook ), widget, label );
+ ::gtk_widget_show ( label );
+ ::gtk_widget_show ( widget );
+
+ {
+ GtkWidget *center, *vbox;
+ const guint top = 15;
+
+ label = gtk_label_new (_("Image Controls"));
+ vbox = g_view_manager->get_image_controls ()->create_controls ();
+
+ add_maintenance_ui (vbox);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
+ gtk_widget_show (label);
+ gtk_widget_show_all (vbox);
+
+ label = gtk_label_new (_("Tone Correction"));
+ widget = (::g_view_manager
+ ->get_gamma_correction ()->create_controls (get_widget ()));
+
+ center = gtk_alignment_new (0.5, 0, 0, 0);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (center), top, 0, 0, 0);
+ gtk_container_add (GTK_CONTAINER (center), widget);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), center, label);
+ gtk_widget_show (label);
+ gtk_widget_show_all (center);
+ }
+ return notebook;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * main_window::create_document_tab ( void )
+{
+ GtkWidget * vbox;
+ GtkWidget * table;
+ GtkWidget * label;
+ GtkWidget * menu;
+ GtkWidget * size;
+ GtkWidget * focus;
+ GtkWidget * widget;
+ scan_manager * scan_mgr;
+
+ scan_mgr = g_view_manager->get_scan_manager ();
+
+ vbox = ::gtk_vbox_new ( FALSE, 0 );
+
+ table = ::gtk_table_new ( 3, 2, FALSE );
+ ::gtk_container_border_width ( GTK_CONTAINER ( table ), 5 );
+ ::gtk_table_set_row_spacings ( GTK_TABLE ( table ), 3 );
+ ::gtk_table_set_col_spacings ( GTK_TABLE ( table ), 5 );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), table, FALSE, FALSE, 5 );
+ ::gtk_widget_show ( table );
+
+ label = ::gtk_label_new ( _( "Document Source:" ) );
+ ::gtk_misc_set_alignment ( GTK_MISC ( label ), 1.0, 0.5 );
+ ::gtk_table_attach ( GTK_TABLE ( table ), label, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( label );
+
+ menu = ::pisa_create_option_menu ( g_docsrc_menu_item );
+ ::gtk_table_attach ( GTK_TABLE ( table ), menu, 1, 2, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( menu );
+
+ if (!scan_mgr->has_flatbed ())
+ {
+ gtk_widget_set_sensitive (g_docsrc_menu_item[0].widget, FALSE);
+ }
+ if (!scan_mgr->has_tpu ())
+ {
+ gtk_widget_set_sensitive (g_docsrc_menu_item[1].widget, FALSE);
+ gtk_widget_set_sensitive (g_docsrc_menu_item[2].widget, FALSE);
+ }
+ if (!scan_mgr->has_adf ())
+ {
+ gtk_widget_set_sensitive (g_docsrc_menu_item[3].widget, FALSE);
+ }
+ if (!scan_mgr->has_duplex ())
+ {
+ gtk_widget_set_sensitive (g_docsrc_menu_item[4].widget, FALSE);
+ }
+ ::gtk_option_menu_set_history (GTK_OPTION_MENU (menu),
+ (scan_mgr->has_flatbed ()
+ ? 0 : (scan_mgr->has_adf ()
+ ? 3 : 1)));
+
+ label = ::gtk_label_new ( _( "Image Type:" ) );
+ ::gtk_misc_set_alignment ( GTK_MISC ( label ), 1.0, 0.5 );
+ ::gtk_table_attach ( GTK_TABLE ( table ), label, 0, 1, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( label );
+
+ menu = ::pisa_create_option_menu ( g_imgtype_menu_item );
+ ::gtk_table_attach ( GTK_TABLE ( table ), menu, 1, 2, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( menu );
+ m_widget [ WG_MAIN_IMG_MENU ] = menu;
+
+ label = ::gtk_label_new ( _( "Resolution:" ) );
+ ::gtk_misc_set_alignment ( GTK_MISC ( label ), 1.0, 0.5 );
+ ::gtk_table_attach ( GTK_TABLE ( table ), label, 0, 1, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( label );
+
+ menu = ::pisa_create_option_menu ( g_resolution_menu_item );
+ ::gtk_table_attach ( GTK_TABLE ( table ), menu, 1, 2, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0 );
+ ::gtk_widget_show ( menu );
+ m_widget [ WG_MAIN_RES_MENU ] = menu;
+
+ size = create_target (scan_mgr);
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), size, FALSE, FALSE, 0 );
+ ::gtk_widget_show ( size );
+
+ if (scan_mgr->has_focus ())
+ {
+ focus = create_focus ( );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), focus, FALSE, FALSE, 0 );
+ ::gtk_widget_show ( focus );
+ }
+
+ widget = create_options ();
+ gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0 );
+ gtk_widget_show (widget);
+
+ if (!_compact_mode)
+ {
+ widget = xpm2widget (get_widget (), penguin_xpm);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 10 );
+ gtk_widget_show (widget);
+ }
+
+ return vbox;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * main_window::create_target (const scan_manager *sm)
+{
+ GtkWidget * frame;
+ GtkWidget * vbox;
+ GtkWidget * hbox;
+ GtkWidget * label;
+ GtkWidget * entry;
+ GtkWidget * menu;
+ GtkWidget * scale;
+ GtkWidget * separator;
+
+ frame = ::gtk_frame_new ( _( "Target" ) );
+ ::gtk_container_border_width ( GTK_CONTAINER ( frame ), 5 );
+
+ vbox = ::gtk_vbox_new ( FALSE, 5 );
+ ::gtk_container_add ( GTK_CONTAINER ( frame ), vbox );
+ ::gtk_widget_show ( vbox );
+
+ hbox = ::gtk_hbox_new ( FALSE, 5 );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), hbox, FALSE, FALSE, 2 );
+ ::gtk_widget_show ( hbox );
+
+ label = ::gtk_label_new ( _( "W:" ) );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), label, FALSE, FALSE, 5 );
+ ::gtk_widget_show ( label );
+
+ entry = ::gtk_entry_new ( );
+ ::gtk_widget_set_usize ( entry, 60, -1 );
+ ::gtk_entry_set_text ( GTK_ENTRY ( entry ), "8.5" );
+ ::gtk_widget_set_sensitive ( entry, FALSE );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), entry, FALSE, FALSE, 2 );
+ ::gtk_widget_show ( entry );
+ m_widget [ WG_MAIN_WIDTH ] = entry;
+
+ label = ::gtk_label_new ( _( "H:" ) );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), label, FALSE, FALSE, 5 );
+ ::gtk_widget_show ( label );
+
+ entry = ::gtk_entry_new ( );
+ ::gtk_widget_set_usize ( entry, 60, -1 );
+ ::gtk_entry_set_text ( GTK_ENTRY ( entry ), "11.7" );
+ ::gtk_widget_set_sensitive ( entry, FALSE );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), entry, FALSE, FALSE, 2 );
+ ::gtk_widget_show ( entry );
+ m_widget [ WG_MAIN_HEIGHT ] = entry;
+
+ menu = ::pisa_create_option_menu ( ::g_unit_menu_item );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), menu, FALSE, FALSE, 5 );
+ ::gtk_widget_show ( menu );
+
+ separator = ::gtk_hseparator_new ( );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), separator, FALSE, FALSE, 2 );
+ ::gtk_widget_show ( separator );
+
+ // scale
+ hbox = ::gtk_hbox_new ( FALSE, 0 );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), hbox, FALSE, FALSE, 2 );
+ ::gtk_widget_show ( hbox );
+
+ label = ::gtk_label_new ( _( "Scale" ) );
+ ::gtk_misc_set_alignment ( GTK_MISC ( label ), 0.0, 0.5 );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), label, FALSE, FALSE, 5 );
+ ::gtk_widget_show ( label );
+
+ scale = ::pisa_create_scale ( & ::g_scale_zoom );
+ ::gtk_widget_set_usize ( scale, 200, -1 );
+ ::gtk_scale_set_digits ( GTK_SCALE ( scale ), 0 );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), scale, TRUE, TRUE, 5 );
+ ::gtk_widget_show ( scale );
+
+ gtk_widget_show_all (frame);
+
+ return frame;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * main_window::create_focus ( void )
+{
+ GtkWidget * frame;
+ GtkWidget * hbox;
+ GtkWidget * radio;
+ GSList * group;
+
+ frame = ::gtk_frame_new ( _( "Focus" ) );
+ ::gtk_container_border_width ( GTK_CONTAINER ( frame ), 5 );
+
+ hbox = ::gtk_hbox_new ( FALSE, 5 );
+ ::gtk_container_border_width ( GTK_CONTAINER ( hbox ), 5 );
+ ::gtk_container_add ( GTK_CONTAINER ( frame ), hbox );
+ ::gtk_widget_show ( hbox );
+
+ radio = ::gtk_radio_button_new_with_label ( 0, "0.0" );
+ ::gtk_signal_connect ( GTK_OBJECT ( radio ), "toggled",
+ GTK_SIGNAL_FUNC ( check_focus ), & ::g_focus [ 0 ] );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), radio, TRUE, TRUE, 0 );
+ ::gtk_widget_show ( radio );
+ m_widget [ WG_MAIN_FOCUS_0 ] = radio;
+
+ group = ::gtk_radio_button_group ( GTK_RADIO_BUTTON ( radio ) );
+ radio = ::gtk_radio_button_new_with_label ( group, "2.5" );
+ ::gtk_signal_connect ( GTK_OBJECT ( radio ), "toggled",
+ GTK_SIGNAL_FUNC ( check_focus ), & ::g_focus [ 1 ] );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), radio, TRUE, TRUE, 0 );
+ ::gtk_widget_show ( radio );
+ m_widget [ WG_MAIN_FOCUS_25 ] = radio;
+
+ return frame;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget *
+main_window::create_options (void)
+{
+ GtkContainer *frame = GTK_CONTAINER (gtk_frame_new (_("Options")));
+ GtkBox *vbox = GTK_BOX (gtk_vbox_new (false, 5));
+ scan_manager *sm = g_view_manager->get_scan_manager ();
+
+ gtk_container_border_width (frame, 5);
+ gtk_container_add (frame, GTK_WIDGET (vbox));
+ if (_compact_mode)
+ {
+ gtk_box_pack_start (vbox, scan_navi_ , false, false, 0);
+ gtk_box_pack_start (vbox, draft_mode_, false, false, 0);
+ }
+ gtk_box_pack_start (vbox, unsharp_mask_, false, false, 0);
+ gtk_box_pack_start (vbox, size_check_, false, false, 0);
+ gtk_box_pack_start (vbox, deskew_, false, false, 0);
+
+ if (getenv ("ISCAN_GUI_SHOW_ALL") || sm->has_dfd ())
+ { // double feed detection
+ gtk_box_pack_start (vbox, m_widget[WG_MAIN_DFD], false, false, 5);
+ }
+
+ gtk_widget_show_all (GTK_WIDGET (frame));
+ if (!sm->has_start_button ())
+ gtk_widget_hide (scan_navi_);
+ if (!sm->has_draft_mode ())
+ gtk_widget_hide (draft_mode_);
+
+ return GTK_WIDGET (frame);
+}
+
+//! Assembles a widget with the Configuration and Close buttons.
+/*! \note Intended for use in non-compact GUI mode only.
+ */
+GtkWidget *
+main_window::close_button_box ()
+{
+ if (_compact_mode) return NULL;
+
+ GtkWidget *vbox = gtk_vbox_new (true, 5);
+ GtkWidget *hbox = gtk_hbutton_box_new ();
+
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_SPREAD);
+
+ gtk_box_pack_start (GTK_BOX (vbox), gtk_hseparator_new (), false, false, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, false, false, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), config_, false, false, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), close_button (), false, false, 0);
+
+ gtk_widget_show_all (vbox);
+
+ return vbox;
+}
+
+/*------------------------------------------------------------*/
+void main_window::sensitive_image_type ( void )
+{
+ gboolean enable_bw;
+ int i;
+
+ if (g_view_manager->get_scan_manager ()->using_tpu ())
+ enable_bw = FALSE;
+ else
+ enable_bw = TRUE;
+
+ for ( i = 0; ::g_imgtype_menu_item [ i ].id != 0; i++ )
+ {
+ if ( ::g_imagetype_list [ i ].pixeltype == PISA_PT_BW )
+ ::gtk_widget_set_sensitive ( ::g_imgtype_menu_item [ i ].widget, enable_bw );
+ }
+
+ for ( i = 0; ::g_imgtype_menu_item [ i ].id != 0; i++ )
+ {
+ if ( ::g_imgtype_menu_item [ i ].id == g_view_manager->get_settings ().imgtype.id )
+ {
+ ::gtk_option_menu_set_history ( GTK_OPTION_MENU ( m_widget [ WG_MAIN_IMG_MENU ] ), i );
+ break;
+ }
+ }
+}
+
+/*------------------------------------------------------------*/
+void main_window::sensitive_target ( void )
+{
+ long resolution, unit, scale, scale_px;
+ double w, h;
+ char w_buf [ 32 ], h_buf [ 32 ];
+ marquee * marq;
+ scan_manager * scan_mgr;
+
+ scan_mgr = g_view_manager->get_scan_manager ();
+
+ resolution = g_view_manager->get_settings ().resolution;
+ unit = g_view_manager->get_settings ().unit;
+
+ marq = & g_view_manager->get_marquee ();
+
+ w = marq->area.x;
+ h = marq->area.y;
+ scale = marq->scale;
+
+ resolution = (resolution * scale + 50) / 100;
+
+ scan_mgr->adjust_scan_param (&resolution, &scale_px);
+
+ switch ( unit )
+ {
+ case PISA_UNIT_INCHES:
+ w = ::inches_reflect_zoom ( w, scale ) + 0.005;
+ h = ::inches_reflect_zoom ( h, scale ) + 0.005;
+ ::sprintf ( w_buf, "%u.%02u",
+ ( int ) w,
+ ( int ) ( w * 100 - ( int ) w * 100 ) );
+ ::sprintf ( h_buf, "%u.%02u",
+ ( int ) h,
+ ( int ) ( h * 100 - ( int ) h * 100 ) );
+ break;
+
+ case PISA_UNIT_PIXELS:
+ w = ::inch2width ( w, resolution, scale_px,
+ PISA_PT_BW == g_view_manager->get_settings ().imgtype.pixeltype);
+ h = ::inch2height ( h, resolution, scale_px );
+ ::sprintf ( w_buf, "%u", ( int ) w );
+ ::sprintf ( h_buf, "%u", ( int ) h );
+ break;
+
+ case PISA_UNIT_CM:
+ w = ::inch2centi ( w, scale ) + 0.005;
+ h = ::inch2centi ( h, scale ) + 0.005;
+ ::sprintf ( w_buf, "%u.%02u",
+ ( int ) w,
+ ( int ) ( w * 100 - ( int ) w * 100 ) );
+ ::sprintf ( h_buf, "%u.%02u",
+ ( int ) h,
+ ( int ) ( h * 100 - ( int ) h * 100 ) );
+ break;
+ }
+
+ ::gtk_entry_set_text ( GTK_ENTRY ( m_widget [ WG_MAIN_WIDTH ] ), w_buf );
+ ::gtk_entry_set_text ( GTK_ENTRY ( m_widget [ WG_MAIN_HEIGHT ] ), h_buf );
+}
+
+/*------------------------------------------------------------*/
+void main_window::sensitive_resolution ( void )
+{
+ long max_resolution, i, resolution;
+
+ max_resolution = ::g_view_manager->get_scan_manager()->get_max_resolution ();
+
+ for ( i = 0; ::g_resolution_menu_item [ i ].id != 0; i++ )
+ {
+ if ( max_resolution < ::g_resolution_menu_item [ i ].id )
+ ::gtk_widget_set_sensitive ( ::g_resolution_menu_item [ i ].widget,
+ FALSE );
+ else
+ ::gtk_widget_set_sensitive ( ::g_resolution_menu_item [ i ].widget,
+ TRUE );
+ }
+
+ resolution = g_view_manager->get_settings ().resolution;
+
+ for ( i = 0; ::g_resolution_menu_item [ i ].id != 0; i++ )
+ {
+ if ( ::g_resolution_menu_item [ i ].id == resolution )
+ {
+ ::gtk_option_menu_set_history ( GTK_OPTION_MENU ( m_widget [ WG_MAIN_RES_MENU ] ), i );
+ break;
+ }
+ }
+}
+
+/*------------------------------------------------------------*/
+void main_window::sensitive_scale ( void )
+{
+ const long min_scale = 50;
+ const long max_scale = 200;
+ GtkAdjustment * adjust;
+ marquee * marq;
+
+ marq = & g_view_manager->get_marquee ();
+
+ if ( marq->scale < min_scale )
+ marq->scale = min_scale;
+ if ( marq->scale > max_scale )
+ marq->scale = max_scale;
+
+ adjust = ::gtk_range_get_adjustment ( GTK_RANGE ( ::g_scale_zoom.widget ) );
+ adjust->lower = min_scale;
+ adjust->upper = max_scale;
+ adjust->value = marq->scale;
+
+ ::gtk_signal_emit_by_name ( GTK_OBJECT ( ::g_scale_zoom.adjust ),
+ "changed" );
+}
+
+/*------------------------------------------------------------*/
+void main_window::sensitive_focus ( void )
+{
+ scan_manager * scan_mgr;
+ marquee * marq;
+
+ scan_mgr = g_view_manager->get_scan_manager ();
+
+ if (!scan_mgr->has_focus ())
+ return;
+
+ marq = & g_view_manager->get_marquee ();
+
+ if (m_widget[WG_MAIN_FOCUS_25] && marq->focus == 25)
+ ::gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( m_widget [ WG_MAIN_FOCUS_25 ] ),
+ TRUE );
+ if (m_widget[WG_MAIN_FOCUS_0 ] && marq->focus == 0)
+ ::gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( m_widget [ WG_MAIN_FOCUS_0 ] ),
+ TRUE );
+
+}
+
+/*------------------------------------------------------------*/
+void main_window::sensitive_usm ( void )
+{
+ settings s = g_view_manager->get_settings ();
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (unsharp_mask_), s.usm);
+ gtk_widget_set_sensitive (unsharp_mask_, PISA_PT_BW != s.imgtype.pixeltype);
+}
+
+void
+main_window::do_sensitive_size_check (bool yes)
+{
+ _do_sensitive_size_check = yes;
+}
+
+void
+main_window::sensitive_size_check (void)
+{
+ scan_manager *sm = g_view_manager->get_scan_manager ();
+ bool size_check_active = false;
+ bool autocrop_active = false;
+
+ if (!_do_sensitive_size_check) return;
+
+ if (sm->has_size_check ())
+ {
+ size_check_active = sm->get_size_check ();
+ }
+ if (sm->has_autocrop ())
+ {
+ autocrop_active = sm->get_autocrop ();
+ }
+ if (sm->has_size_check () || sm->has_autocrop ())
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (size_check_),
+ (size_check_active || autocrop_active) );
+ }
+ gtk_widget_set_sensitive (size_check_,
+ (sm->has_size_check () || sm->has_autocrop () ));
+}
+
+void
+main_window::sensitive_deskew (void)
+{
+ scan_manager *sm = g_view_manager->get_scan_manager ();
+
+ if (!_do_sensitive_size_check) return;
+
+ if (sm->has_deskew ())
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (deskew_),
+ sm->get_deskew () );
+ }
+ gtk_widget_set_sensitive (deskew_, sm->has_deskew ());
+}
+
+void
+main_window::add_maintenance_ui (GtkWidget *vbox)
+{
+ GtkWidget *separator;
+ GtkWidget *bbox;
+ GtkWidget *button;
+ scan_manager *scan_mgr = g_view_manager->get_scan_manager ();
+
+ if (scan_mgr->has_clean () || scan_mgr->has_calibrate ())
+ {
+ // add Calibration & Cleaning button
+ separator = ::gtk_hseparator_new ( );
+ ::gtk_box_pack_start ( GTK_BOX ( vbox ), separator, FALSE, FALSE, 2 );
+ ::gtk_widget_show ( separator );
+
+ bbox = ::gtk_vbutton_box_new ();
+ ::gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD);
+
+ if (scan_mgr->has_calibrate ())
+ {
+ button = ::gtk_button_new_with_label ( _( "Calibration" ) );
+ ::gtk_container_border_width ( GTK_CONTAINER ( button ), 5 );
+ ::gtk_box_pack_start (GTK_BOX (bbox), button, false, false, 0);
+ ::g_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (click_calibration), scan_mgr);
+
+ ::gtk_widget_show ( button );
+ }
+
+ if (scan_mgr->has_clean ())
+ {
+ button = ::gtk_button_new_with_label ( _( "Cleaning" ) );
+ ::gtk_container_border_width ( GTK_CONTAINER ( button ), 5 );
+ ::gtk_box_pack_start (GTK_BOX (bbox), button, false, false, 0);
+ ::g_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (click_cleaning), scan_mgr);
+
+ ::gtk_widget_show ( button );
+ }
+
+ ::gtk_box_pack_start (GTK_BOX (vbox), bbox, true, false, 0);
+ ::gtk_widget_show ( bbox );
+
+ // for padding
+ ::gtk_box_pack_start (GTK_BOX (vbox), gtk_vbox_new (false, 0), true, true, 0);
+ }
+}
diff --git a/frontend/pisa_main_window.h b/frontend/pisa_main_window.h
new file mode 100644
index 0000000..8067de9
--- /dev/null
+++ b/frontend/pisa_main_window.h
@@ -0,0 +1,112 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2008, 2009 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_MAIN_WINDOW_H
+#define ___PISA_MAIN_WINDOW_H
+
+#include <gtk/gtk.h>
+#include "pisa_enums.h"
+#include "pisa_scan_manager.h"
+
+class main_window
+{
+ public:
+
+ int init ( void );
+ GtkWidget * create_window ( GtkWidget * parent = 0 );
+ int close_window ( int destroy );
+ void start_scan (const GtkWidget *destination) const;
+ void start_preview ( bool zooming );
+ void set_double_feed_detection (const GtkWidget *widget);
+ void sensitive ( int is_prev_img );
+ void sensitive_option ( );
+ void enable_start_button (bool yes);
+ void update_zoom_button ();
+ void do_sensitive_size_check (bool yes);
+
+ GtkWidget * get_widget (int wg_id = WG_MAIN_TOP) { return m_widget[wg_id]; }
+
+private:
+
+ void create_controls ();
+ GtkWidget *tool_button (const char *text, GCallback callback, char **icon);
+ GtkWidget *check_button (const char *text,
+ void (*callback) (GtkWidget*, gpointer));
+ GtkWidget *close_button ();
+
+ GtkWidget *action_button_box ();
+ GtkWidget *toolbar ();
+ GtkWidget *close_button_box ();
+
+ GtkWidget * create_left_area ( void );
+ GtkWidget * create_scan_button ( void );
+ GtkWidget * create_scan_label ( void );
+ GtkWidget * create_right_area ( void );
+ GtkWidget * create_notebook ( void );
+ GtkWidget * create_document_tab ( void );
+ GtkWidget * create_target (const scan_manager *sm);
+ GtkWidget * create_focus ( void );
+ GtkWidget * create_options ( void );
+
+ void add_maintenance_ui (GtkWidget *vbox);
+
+ void sensitive_image_type ( void );
+ void sensitive_target ( void );
+ void sensitive_resolution ( void );
+ void sensitive_scale ( void );
+ void sensitive_focus ( void );
+ void sensitive_usm ( void );
+ void sensitive_size_check (void);
+ void sensitive_deskew (void);
+
+ GtkWidget *prev_btn_;
+ GtkWidget *zoom_btn_;
+ GtkWidget *expo_btn_;
+
+ GtkWidget *scan_btn_;
+ GtkWidget *print_btn_;
+ GtkWidget *dest_menu_;
+
+ GtkWidget *scan_navi_;
+ GtkWidget *draft_mode_;
+ GtkWidget *size_check_;
+ GtkWidget *deskew_;
+
+ GtkWidget *config_;
+
+ GtkWidget *unsharp_mask_;
+
+ GtkWidget * m_widget [ WG_MAIN_NUM ];
+ bool _done_preview;
+ bool _do_sensitive_size_check;
+ bool _compact_mode;
+};
+
+#endif // ___PISA_MAIN_WINDOW_H
diff --git a/frontend/pisa_marquee.cc b/frontend/pisa_marquee.cc
new file mode 100644
index 0000000..d3f6645
--- /dev/null
+++ b/frontend/pisa_marquee.cc
@@ -0,0 +1,117 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+/*--------------------------------------------------------------*/
+#include "pisa_marquee.h"
+#include "pisa_default_val.h"
+
+/*--------------------------------------------------------------*/
+marquee::marquee (const double w, const double h,
+ const long f, const long b, const long c) :
+ active (0),
+ offset (0.0, 0.0),
+ area (w, h),
+ scale (100),
+ gamma (( long ) ( 100 * DEFGAMMA )),
+ highlight (DEFHIGHLIGHT),
+ shadow (DEFSHADOW),
+ threshold (DEFTHRESHOLD),
+ brightness (b),
+ contrast (c),
+ graybalance (DEFGRAYBALANCE),
+ saturation (DEFSATURATION),
+ focus (f)
+{
+ int i, j;
+
+ for ( i = 0; i < 4; i++ )
+ for ( j = 0; j < 256; j++ )
+ gamma_table [ i ] [ j ] = j;
+
+ for ( i = 0; i < 3; i++ )
+ {
+ film_gamma [ i ] = 1.0;
+ film_yp [ i ] = 0.0;
+ grayl [ i ] = 0.0;
+ }
+
+ for ( i = 0; i < 256; i++ )
+ {
+ lut.gamma_r [ i ] = i;
+ lut.gamma_g [ i ] = i;
+ lut.gamma_b [ i ] = i;
+ }
+}
+
+marquee & marquee::operator= ( const marquee & x )
+{
+ if ( & x != this )
+ {
+ int i, j;
+
+ active = x.active;
+ offset = x.offset;
+ area = x.area;
+ scale = x.scale;
+ gamma = x.gamma;
+ highlight = x.highlight;
+ shadow = x.shadow;
+ threshold = x.threshold;
+ brightness = x.brightness;
+ contrast = x.contrast;
+
+ for ( i = 0; i < 4; i++ )
+ for ( j = 0; j < 256; j++ )
+ gamma_table [ i ] [ j ] = x.gamma_table [ i ] [ j ];
+
+ graybalance = x.graybalance;
+ saturation = x.saturation;
+
+ focus = x.focus;
+
+ for ( i = 0; i < 3; i++ )
+ {
+ film_gamma [ i ] = x.film_gamma [ i ];
+ film_yp [ i ] = x.film_yp [ i ];
+ grayl [ i ] = x.grayl [ i ];
+ }
+
+ for ( i = 0; i < 256; i++ )
+ {
+ lut.gamma_r [ i ] = x.lut.gamma_r [ i ];
+ lut.gamma_g [ i ] = x.lut.gamma_g [ i ];
+ lut.gamma_b [ i ] = x.lut.gamma_b [ i ];
+ }
+ }
+
+ return ( * this );
+}
+
+/*--------------------------------------------------------------*/
diff --git a/frontend/pisa_marquee.h b/frontend/pisa_marquee.h
new file mode 100644
index 0000000..096381b
--- /dev/null
+++ b/frontend/pisa_marquee.h
@@ -0,0 +1,83 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_MARQUEE_H
+#define ___PISA_MARQUEE_H
+
+#include "pisa_structs.h"
+
+/*--------------------------------------------------------------*/
+class marquee
+{
+ public:
+
+ long active;
+
+ _pointD offset;
+ _pointD area;
+
+ long scale;
+
+ long gamma;
+ long highlight;
+ long shadow;
+ long threshold;
+ long brightness;
+ long contrast;
+
+ unsigned char gamma_table [ 4 ] [ 256 ]; // mono,red,green,blue
+
+ long graybalance;
+ long saturation;
+
+ long focus;
+
+ double film_gamma [ 3 ];
+ double film_yp [ 3 ];
+ double grayl [ 3 ];
+
+ gamma_struct lut;
+
+ marquee (const double w = 8.5, const double h = 11.7,
+ const long f = 0, const long b = 0, const long c = 0);
+
+ marquee & operator= ( const marquee & x );
+};
+
+
+/*--------------------------------------------------------------*/
+typedef struct _marquee_node
+{
+ _marquee_node * next;
+ marquee * data;
+} marquee_node;
+
+#endif // ___PISA_MARQUEE_H
+
diff --git a/frontend/pisa_preference.cc b/frontend/pisa_preference.cc
new file mode 100644
index 0000000..89b9c15
--- /dev/null
+++ b/frontend/pisa_preference.cc
@@ -0,0 +1,351 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/13/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "pisa_preference.h"
+
+static char * get_line ( FILE * fp, char ** gotstr );
+static char * dynamic_fgets ( FILE * fp );
+static int parse_line ( char * ptr, cfg_struct * cfg );
+static char * parse_word ( char * ptr, char ** word, cfg_key_type key );
+static int store_value ( cfg_struct * cfg, char * parameter, char * value );
+static char * remove_first_spaces ( char * ptr );
+static void set_word ( FILE * fp, cfg_struct * cfg );
+static void single_or_double_quote ( char * str, char * ret );
+
+/*----------------------------------------------------------*/
+void get_cfg ( char * file, cfg_struct * cfg, int num )
+{
+ FILE * fp;
+ char * ptr;
+ char * line_buf;
+ int i;
+
+ if ( 0 == ( fp = fopen ( file, "r" ) ) )
+ return;
+
+ for ( i = 0; i < num; i++, cfg++ )
+ {
+ rewind ( fp );
+
+ while ( 0 != ( ptr = get_line ( fp, & line_buf ) ) )
+ {
+ if ( parse_line ( ptr, cfg ) )
+ {
+ free ( line_buf );
+ break;
+ }
+
+ free ( line_buf );
+ }
+ }
+
+ fclose ( fp );
+}
+
+/*----------------------------------------------------------*/
+void set_cfg ( char * file, cfg_struct * cfg, int num )
+{
+ FILE * fp;
+ int i;
+
+ if ( 0 == ( fp = fopen ( file, "w" ) ) )
+ return;
+
+ for ( i = 0; i < num; i++, cfg++ )
+ set_word ( fp, cfg );
+
+ fclose ( fp );
+}
+
+/*----------------------------------------------------------*/
+static char * get_line ( FILE * fp, char ** gotstr )
+{
+ char * ptr;
+
+ while ( 1 )
+ {
+ if ( 0 == ( * gotstr = dynamic_fgets ( fp ) ) )
+ return 0;
+
+ ptr = remove_first_spaces ( * gotstr );
+ if ( * ptr != '#' && * ptr != '\0' )
+ return ptr;
+
+ free ( * gotstr );
+ }
+}
+
+/*----------------------------------------------------------*/
+static char * dynamic_fgets ( FILE * fp )
+{
+ char * ptr;
+ char tmp [ 128 ];
+ int i;
+
+ if ( 0 == ( ptr = ( char * ) malloc ( 1 ) ) )
+ return 0;
+
+ * ptr = '\0';
+
+ for ( i = 0;; i++ )
+ {
+ if ( 0 == fgets ( tmp, 128, fp ) )
+ {
+ free ( ptr );
+ return 0;
+ }
+
+ if ( 0 == ( ptr = ( char * ) realloc ( ptr, 127 * ( i + 1 ) + 1 ) ) )
+ return 0;
+
+ strcat ( ptr, tmp );
+
+ if ( 0 != strchr ( tmp, '\n' ) )
+ {
+ * strchr ( ptr, '\n' ) = '\0';
+ return ptr;
+ }
+
+ if ( 0 != feof ( fp ) )
+ return ptr;
+ }
+}
+
+/*----------------------------------------------------------*/
+static char * remove_first_spaces ( char * ptr )
+{
+ while ( * ptr == ' ' || * ptr == '\t' )
+ ptr++;
+
+ return ptr;
+}
+
+/*----------------------------------------------------------*/
+static int parse_line ( char * ptr, cfg_struct * cfg )
+{
+ char * parameter;
+ char * value;
+
+ if ( 0 == ( ptr = parse_word ( ptr, & parameter, CFG_PARAM ) ) )
+ return 0;
+
+ if ( 0 == ( ptr = parse_word ( ptr, & value, CFG_VALUE ) ) )
+ return 0;
+
+ if ( 0 == store_value ( cfg, parameter, value ) )
+ return 0;
+
+ free ( parameter );
+ free ( value );
+
+ return 1;
+}
+
+/*----------------------------------------------------------*/
+static char * parse_word ( char * ptr, char ** word, cfg_key_type key )
+{
+ int len = 0;
+ cfg_quote_type quote;
+
+ switch ( * ptr )
+ {
+ case '\"':
+ quote = CFG_QUOTE_DOUBLE;
+ ptr++;
+ break;
+ case '\'':
+ quote = CFG_QUOTE_SINGLE;
+ ptr++;
+ break;
+ default:
+ quote = CFG_QUOTE_NO;
+ break;
+ }
+
+ while ( 1 )
+ {
+ if ( quote == CFG_QUOTE_NO )
+ {
+ if ( * ( ptr + len ) == ' ' || * ( ptr + len ) == '\t' ||
+ * ( ptr + len ) == '\0' || * ( ptr + len ) == '#' ||
+ ( * ( ptr + len ) == '=' && key == CFG_PARAM ) )
+ break;
+ }
+ else if ( quote == CFG_QUOTE_DOUBLE )
+ {
+ if ( * ( ptr + len ) == '\"' )
+ break;
+ }
+ else if ( quote == CFG_QUOTE_SINGLE )
+ {
+ if ( * ( ptr + len ) == '\'' )
+ break;
+ }
+
+ if ( * ( ptr + len ) == '\0' )
+ return 0;
+
+ len++;
+ }
+
+ if ( 0 == ( * word = ( char * ) malloc ( len + 1 ) ) )
+ return 0;
+
+ strncpy ( * word, ptr, len );
+ * ( * word + len ) = '\0';
+
+ ptr += ( len + ( quote == CFG_QUOTE_NO ? 0 : 1 ) );
+
+ ptr = remove_first_spaces ( ptr );
+
+ switch ( key )
+ {
+ case CFG_PARAM:
+ if ( * ptr != '=' )
+ return 0;
+ ptr++;
+ ptr = remove_first_spaces ( ptr );
+ break;
+ case CFG_VALUE:
+ if ( * ptr != '\0' && * ptr != '#' )
+ return 0;
+ break;
+ }
+
+ return ptr;
+}
+
+/*----------------------------------------------------------*/
+static int store_value ( cfg_struct * cfg, char * parameter, char * value )
+{
+ long tmp, utmp;
+ double dtmp;
+ char * endptr;
+
+ if ( 0 != strcasecmp ( parameter, cfg->name ) )
+ return 0;
+
+ errno = 0;
+
+ switch ( cfg->type )
+ {
+ case CFG_BOOL:
+ if ( 0 == strcasecmp ( value, "TRUE" ) ||
+ 0 == strcasecmp ( value, "YES" ) ||
+ 0 == strcasecmp ( value, "T" ) ||
+ 0 == strcasecmp ( value, "Y" ) ||
+ 0 == strcasecmp ( value, "1" ) )
+ * ( int * ) ( cfg->value ) = 1;
+ else
+ * ( int * ) ( cfg->value ) = 0;
+ return 1;
+ case CFG_LONG:
+ tmp = strtol ( value, & endptr, 10 );
+ if ( * endptr )
+ return 0;
+ if ( errno == ERANGE )
+ return 0;
+ * ( long * ) ( cfg->value ) = tmp;
+ return 1;
+ case CFG_ULONG:
+ utmp = strtoul ( value, & endptr, 10 );
+ if ( * endptr )
+ return 0;
+ if ( errno == ERANGE )
+ return 0;
+ * ( unsigned long * ) ( cfg->value ) = utmp;
+ return 1;
+ case CFG_DOUBLE:
+ dtmp = strtod ( value, & endptr );
+ if ( * endptr )
+ return 0;
+ if ( errno == ERANGE )
+ return 0;
+ * ( double * ) ( cfg->value ) = dtmp;
+ return 1;
+ case CFG_STRING:
+ strcpy ( ( char * ) cfg->value, value );
+ return 1;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------*/
+static void set_word ( FILE * fp, cfg_struct * cfg )
+{
+ char c [ 2 ];
+
+ switch ( cfg->type )
+ {
+ case CFG_BOOL:
+ fprintf ( fp, "%s\t= %s\n", cfg->name,
+ ( * ( int * ) ( cfg->value ) ) ? "true" : "false" );
+ break;
+ case CFG_LONG:
+ fprintf ( fp, "%s\t= %ld\n", cfg->name, * ( long * ) ( cfg->value ) );
+ break;
+ case CFG_ULONG:
+ fprintf ( fp, "%s\t= %lu\n", cfg->name,
+ * ( unsigned long * ) ( cfg->value ) );
+ break;
+ case CFG_DOUBLE:
+ fprintf ( fp, "%s\t= %f\n", cfg->name, * ( double * ) ( cfg->value ) );
+ break;
+ case CFG_STRING:
+ single_or_double_quote ( ( char * ) cfg->value, c );
+ fprintf ( fp, "%s\t= %s%s%s\n", cfg->name,
+ c, ( char * ) cfg->value, c );
+ break;
+ }
+}
+
+/*----------------------------------------------------------*/
+static void single_or_double_quote ( char * str, char * ret )
+{
+ ret [ 1 ] = '\0';
+
+ if ( 0 != strchr ( str, '\"' ) )
+ ret [ 0 ] = '\'';
+ else if ( 0 != strchr ( str, '\'' ) ||
+ 0 != strchr ( str, '#' ) ||
+ 0 != strchr ( str, '\t' ) ||
+ 0 != strchr ( str, ' ' ) )
+ ret [ 0 ] = '\"';
+ else
+ ret [ 0 ] = '\0';
+}
+
diff --git a/frontend/pisa_preference.h b/frontend/pisa_preference.h
new file mode 100644
index 0000000..190d877
--- /dev/null
+++ b/frontend/pisa_preference.h
@@ -0,0 +1,68 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2008 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/13/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+
+#ifndef ___PISA_PREFERENCE_H
+#define ___PISA_PREFERENCE_H
+
+typedef enum
+{
+ CFG_BOOL,
+ CFG_LONG,
+ CFG_ULONG,
+ CFG_DOUBLE,
+ CFG_STRING
+} cfg_value_type;
+
+typedef enum
+{
+ CFG_PARAM,
+ CFG_VALUE
+} cfg_key_type;
+
+typedef enum
+{
+ CFG_QUOTE_NO,
+ CFG_QUOTE_SINGLE,
+ CFG_QUOTE_DOUBLE
+} cfg_quote_type;
+
+typedef struct
+{
+ const char * name;
+ cfg_value_type type;
+ void * value;
+} cfg_struct;
+
+void get_cfg ( char * file, cfg_struct * cfg, int num );
+void set_cfg ( char * file, cfg_struct * cfg, int num );
+
+#endif // ___PISA_PREFERENCE_H
+
diff --git a/frontend/pisa_preview_window.cc b/frontend/pisa_preview_window.cc
new file mode 100644
index 0000000..1928179
--- /dev/null
+++ b/frontend/pisa_preview_window.cc
@@ -0,0 +1,1844 @@
+/* pisa_preview_window.cc
+ Copyright (C) 2001, 2004, 2005, 2008, 2009 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+/*------------------------------------------------------------*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*------------------------------------------------------------*/
+#include "pisa_view_manager.h"
+#include "pisa_preview_window.h"
+#include "pisa_error.h"
+#include "pisa_scan_tool.h"
+#include "pisa_tool.h"
+#include "pisa_default_val.h"
+#include "pisa_aleart_dialog.h"
+#include "pisa_change_unit.h"
+
+/*------------------------------------------------------------*/
+long g_prev_max_x = 320;
+long g_prev_max_y = 440;
+
+const double g_min_marq_size = 0.4;
+
+/*------------------------------------------------------------*/
+static gint expose_event ( GtkWidget * widget,
+ GdkEventExpose * event )
+{
+ preview_window * prev_cls;
+
+ prev_cls = ( preview_window * ) ::g_view_manager->get_window_cls ( ID_WINDOW_PREV );
+
+ return prev_cls->expose_event ( widget, event );
+}
+
+
+/*------------------------------------------------------------*/
+static gint event ( GtkWidget * widget,
+ GdkEvent * event )
+{
+ preview_window * prev_cls;
+
+ prev_cls = ( preview_window * ) ::g_view_manager->get_window_cls ( ID_WINDOW_PREV );
+
+ return prev_cls->event ( widget, event );
+}
+
+/*------------------------------------------------------------*/
+static void size_allocate ( GtkWidget * widget )
+{
+ preview_window * prev_cls;
+
+ prev_cls = ( preview_window * ) ::g_view_manager->get_window_cls ( ID_WINDOW_PREV );
+
+ prev_cls->size_allocate ( widget );
+}
+
+/*------------------------------------------------------------*/
+int preview_window::init ( void )
+{
+ int i;
+
+ m_gc = 0;
+
+ m_img_width = g_prev_max_x;
+ m_img_height = g_prev_max_y;
+
+ m_is_prev = 0;
+ m_drag = 0;
+
+ m_img = new unsigned char [ g_prev_max_x * g_prev_max_y * 3 ];
+
+ if ( m_img == 0 )
+ return PISA_ERR_OUTOFMEMORY;
+
+ m_img_org = new unsigned char [ g_prev_max_x * g_prev_max_y * 3 ];
+
+ if ( m_img_org == 0 )
+ {
+ delete [ ] m_img;
+ m_img = 0;
+ return PISA_ERR_OUTOFMEMORY;
+ }
+
+ for ( i = 0; i < 11; i++ )
+ m_cursor [ i ] = 0;
+
+ m_on_preview = false;
+ m_allocate_width = 0;
+ m_allocate_height = 0;
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * preview_window::create_window ( GtkWidget * parent )
+{
+ m_win = create_darea ( parent );
+
+ create_cursor ( );
+
+ return m_win;
+}
+
+/*------------------------------------------------------------*/
+int preview_window::close_window ( int destroy )
+{
+ destroy = destroy;
+
+ if ( m_gc )
+ {
+ ::gdk_gc_destroy ( m_gc );
+ m_gc = 0;
+ }
+
+ if ( m_img )
+ {
+ delete [ ] m_img;
+ m_img = 0;
+ }
+
+ if ( m_img_org )
+ {
+ delete [ ] m_img_org;
+ m_img_org = 0;
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+int preview_window::resize_window ( void )
+{
+ get_preview_resolution ( );
+ resize_preview_window ( 0, 0 );
+
+ clear_image ( );
+
+ return 0;
+}
+
+/*------------------------------------------------------------*/
+int preview_window::auto_exposure ( void )
+{
+ pisa_image_info info;
+ _rectL region;
+ marquee tmp_marq, * marq;
+ int i, marq_num;
+
+ scan_manager *scan_mgr = g_view_manager->get_scan_manager ();
+
+ if ( m_is_prev == 0 ||
+ g_view_manager->get_settings ().imgtype.pixeltype == PISA_PT_BW )
+ {
+ return PISA_ERR_SUCCESS;
+ }
+
+ info.m_img = m_img_org;
+ info.m_width = m_img_width;
+ info.m_height = m_img_height;
+ info.m_rowbytes = g_prev_max_x * 3;
+
+ marq_num = g_view_manager->get_marquee_size ();
+ marq = & g_view_manager->get_marquee ();
+
+ if ( marq_num < 2 )
+ {
+ region.left = 0;
+ region.top = 0;
+ region.right = m_img_width - 1;
+ region.bottom = m_img_height - 1;
+ }
+ else
+ {
+ _pointL pt_lt, pt_rb;
+
+ get_marquee_point ( marq_num - 1, & pt_lt, & pt_rb );
+
+ region.left = pt_lt.x;
+ region.top = pt_lt.y;
+ region.right = pt_rb.x;
+ region.bottom = pt_rb.y;
+ }
+
+ settings set = g_view_manager->get_settings ();
+ scan_manager *sm = g_view_manager->get_scan_manager ();
+ iscan::auto_expose (sm->get_scan_source (),
+ sm->get_film_type (),
+ info, region, tmp_marq,
+ set.imgtype.ae_option != PISA_AE_DOC,
+ !scan_mgr->has_zoom ());
+
+ marq->gamma = tmp_marq.gamma;
+ marq->highlight = tmp_marq.highlight;
+ marq->shadow = tmp_marq.shadow;
+ marq->graybalance = tmp_marq.graybalance;
+
+ for ( i = 0; i < 3; i++ )
+ {
+ marq->film_gamma [ i ] = tmp_marq.film_gamma [ i ];
+ marq->film_yp [ i ] = tmp_marq.film_yp [ i ];
+ marq->grayl [ i ] = tmp_marq.grayl [ i ];
+ }
+
+ ::g_view_manager->sensitive ( );
+ ::g_view_manager->update_lut ( );
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+int preview_window::update_img ( bool left )
+{
+ int i;
+ pisa_image_info img_info;
+ marquee * marq;
+ double delta;
+ long curr_width, adj_width, resolution;
+
+ if ( m_is_prev == 0 )
+ {
+ clear_image ( );
+ return PISA_ERR_SUCCESS;
+ }
+
+ settings set = g_view_manager->get_settings ();
+ marq = & g_view_manager->get_marquee ();
+
+ resolution = (set.resolution * (marq->scale)) / 100;
+ curr_width = ::inch2pixel ( marq->area.x, resolution );
+ adj_width = ::inch2width ( marq->area.x, resolution );
+ delta = marq->area.x - (marq->area.x) * adj_width /curr_width ;
+
+ // Resize the area
+ marq -> area.x -= delta;
+ if ( left )
+ {
+ // If the left side -> increase the x offset
+ marq ->offset.x += delta;
+ }
+
+ if ( m_img == 0 || m_img_org == 0 )
+ return PISA_ERR_OUTOFMEMORY;
+
+ ::memcpy ( m_img, m_img_org, g_prev_max_x * g_prev_max_y * 3 );
+
+ img_info.m_img = m_img;
+ img_info.m_width = m_img_width;
+ img_info.m_height = m_img_height;
+ img_info.m_rowbytes = g_prev_max_x * 3;
+
+ ::tool_lut ( & img_info, & marq->lut );
+
+ switch (set.imgtype.pixeltype)
+ {
+ case PISA_PT_RGB:
+ tool_matrix (&img_info, marq->saturation, set.coef);
+ if (set.usm)
+ tool_usm (img_info);
+ break;
+
+ case PISA_PT_GRAY:
+ build_gray (&img_info, set.imgtype.dropout);
+ if (set.usm)
+ tool_usm (img_info);
+ break;
+
+ case PISA_PT_BW:
+ build_bw (&img_info,
+ set.imgtype.dropout,
+ set.imgtype.halftone,
+ marq->threshold);
+ break;
+ }
+
+ for ( i = 0; i < m_img_height; i++ )
+ ::gtk_preview_draw_row ( GTK_PREVIEW ( m_prev ),
+ m_img + i * g_prev_max_x * 3,
+ 0, i, m_img_width );
+
+ ::gtk_widget_draw ( m_prev, 0 );
+ ::gdk_flush ( );
+
+ return PISA_ERR_SUCCESS;
+}
+
+void
+preview_window::start_preview (bool zooming)
+{
+ scan_manager * scan_mgr =
+ g_view_manager->get_scan_manager ( );
+ int width, height;
+ int cancel;
+
+ cancel = 0;
+
+ m_is_prev = 0;
+
+ m_on_preview = true;
+ m_allocate_width = 0;
+ m_allocate_height = 0;
+
+ progress_window feedback
+ (static_cast <main_window *> (g_view_manager
+ ->get_window_cls (ID_WINDOW_MAIN))
+ ->get_widget());
+ if (scan_mgr->push_button_needs_polling ())
+ {
+ feedback.set_text (progress_window::WAITING);
+ }
+ else
+ {
+ feedback.set_text (progress_window::WARMING_UP);
+ }
+ feedback.show ();
+
+ while ( ::gtk_events_pending ( ) )
+ ::gtk_main_iteration ( );
+
+ // preview
+ set_preview_param (zooming);
+
+ reset_settings (zooming);
+ ::g_view_manager->sensitive ( );
+
+ try
+ {
+ if (scan_mgr->push_button_needs_polling ())
+ {
+ long usec = scan_mgr->get_polling_time ();
+ while (!scan_mgr->is_button_pressed ()
+ && !feedback.is_cancelled ()) {
+ g_view_manager->microsleep (usec);
+ while (::gtk_events_pending ()) {
+ ::gtk_main_iteration ();
+ }
+ }
+ if (feedback.is_cancelled ())
+ {
+ return;
+ }
+ scan_mgr->disable_wait_for_button();
+ }
+
+ scan_mgr->init_preview (&width, &height);
+
+ if (!zooming)
+ {
+ change_max_scan_area ( width, height );
+ ::g_view_manager->sensitive ( );
+ }
+
+ resize_preview_window ( width, height );
+ clear_image ( );
+
+ while ( ::gtk_events_pending ( ) )
+ ::gtk_main_iteration ( );
+
+ for (int i = 0; i < height; i++ )
+ {
+ scan_mgr->acquire_image ( m_img_org + i * g_prev_max_x * 3,
+ width * 3,
+ 1,
+ cancel );
+
+ if ( i == 0 )
+ feedback.set_text (progress_window::PREVIEWING);
+
+ if (cancel)
+ break;
+
+ feedback.set_progress (i, height);
+ cancel = feedback.is_cancelled ();
+
+ ::gtk_preview_draw_row ( GTK_PREVIEW ( m_prev ),
+ m_img_org + i * g_prev_max_x * 3,
+ 0, i, width );
+ if ( i % 20 == 0 )
+ {
+ ::gtk_preview_put ( GTK_PREVIEW ( m_prev ),
+ m_prev->window,
+ m_prev->style->black_gc,
+ 0, 0, 0, 0, width, height );
+ }
+
+ while ( ::gtk_events_pending ( ) )
+ ::gtk_main_iteration ( );
+ }
+ scan_mgr->acquire_image (0, 1, 1, cancel);
+ feedback.set_progress (height, height);
+ cancel = feedback.is_cancelled ();
+ if (cancel)
+ {
+ m_on_preview = false;
+ update_img ( );
+ change_max_disp_area ( m_allocate_width, m_allocate_height );
+ m_allocate_width = 0;
+ m_allocate_height = 0;
+ }
+ else
+ {
+ m_is_prev = 1;
+ ::gtk_widget_draw ( m_prev, 0 );
+ ::gdk_flush ( );
+
+ m_on_preview = false;
+
+ auto_exposure ( );
+ update_img ( );
+ change_max_disp_area ( m_allocate_width, m_allocate_height );
+ m_allocate_width = 0;
+ m_allocate_height = 0;
+ }
+ }
+ catch ( const pisa_error & err )
+ {
+ main_window * main_cls;
+ aleart_dialog aleart_dlg;
+
+ if ( PISA_STATUS_CANCELLED != err.get_error_id() )
+ {
+ main_cls = ( main_window * ) ::g_view_manager->get_window_cls ( ID_WINDOW_MAIN );
+ aleart_dlg.message_box ( main_cls->get_widget ( ),
+ err.get_error_string ( ) );
+ }
+
+ delete_marquee ( );
+
+ m_on_preview = false;
+ update_img ( );
+ change_max_disp_area ( m_allocate_width, m_allocate_height );
+ m_allocate_width = 0;
+ m_allocate_height = 0;
+ }
+
+ revert_resolution ();
+
+ scan_mgr->release_memory ();
+ scan_mgr->finish_acquire (true);
+
+ ::g_view_manager->sensitive ( );
+
+ {
+ gint x, y;
+ ::gdk_window_get_pointer ( m_prev->window, & x, & y, 0 );
+ set_mouse_cursor ( x, y );
+ change_cursor ( );
+ }
+}
+
+/*--------------------------------------------------------------*/
+gint preview_window::expose_event ( GtkWidget * widget,
+ GdkEventExpose * event )
+{
+ widget = widget;
+ event = event;
+
+ draw_marquee ( );
+
+ return FALSE;
+}
+
+/*--------------------------------------------------------------*/
+gint preview_window::event ( GtkWidget * widget, GdkEvent * event )
+{
+ gint ret;
+
+ widget = widget;
+
+ switch ( event->type )
+ {
+ case GDK_EXPOSE:
+ if ( m_gc )
+ {
+ draw_marquee ( );
+ }
+ ret = FALSE;
+ break;
+
+ case GDK_BUTTON_PRESS:
+ ret = mouse_down ( event );
+ ::gtk_grab_add ( widget );
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ ret = mouse_move ( event );
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ ret = mouse_up ( event );
+ ::gtk_grab_remove ( widget );
+ break;
+
+ default:
+ ret = TRUE;
+ break;
+ }
+
+ return ret;
+}
+
+/*------------------------------------------------------------*/
+void preview_window::size_allocate ( GtkWidget * widget )
+{
+ int x, y;
+
+ x = widget->allocation.width;
+ y = widget->allocation.height;
+
+ if ( m_on_preview )
+ {
+ m_allocate_width = x;
+ m_allocate_height = y;
+ return;
+ }
+
+ change_max_disp_area ( x, y );
+}
+
+
+/*------------------------------------------------------------*/
+GtkWidget * preview_window::create_darea ( GtkWidget * parent )
+{
+ GtkWidget * frame;
+
+ parent = parent;
+
+ frame = ::gtk_frame_new ( 0 );
+ ::gtk_frame_set_shadow_type ( GTK_FRAME ( frame ), GTK_SHADOW_IN );
+ ::gtk_container_border_width ( GTK_CONTAINER ( frame ), 3 );
+
+ m_prev = ::gtk_preview_new ( GTK_PREVIEW_COLOR );
+ ::gtk_preview_set_expand ( GTK_PREVIEW ( m_prev ), TRUE );
+
+ // set size of preview window
+ get_preview_resolution ( );
+ resize_preview_window ( m_img_width, m_img_height );
+
+ ::gtk_container_add ( GTK_CONTAINER ( frame ), m_prev );
+ ::gtk_widget_show ( m_prev );
+
+ clear_image ( );
+
+ // signals
+ ::gtk_signal_connect ( GTK_OBJECT ( m_prev ), "event",
+ GTK_SIGNAL_FUNC ( ::event ), 0 );
+ ::gtk_signal_connect_after ( GTK_OBJECT ( m_prev ), "expose_event",
+ GTK_SIGNAL_FUNC ( ::expose_event ), 0 );
+ ::gtk_signal_connect_after ( GTK_OBJECT ( m_prev ), "size_allocate",
+ GTK_SIGNAL_FUNC ( ::size_allocate ), 0 );
+
+ ::gtk_widget_set_events ( m_prev ,
+ GDK_EXPOSURE_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_BUTTON_RELEASE_MASK );
+
+ return frame;
+}
+
+
+/*------------------------------------------------------------*/
+long preview_window::get_preview_resolution ( const _rectD * img_rect )
+{
+ long resolution;
+ _pointD pt_scan_size;
+ double aspect_max;
+ double aspect_scan;
+
+ if ( img_rect )
+ {
+ pt_scan_size.x = img_rect->right - img_rect->left;
+ pt_scan_size.y = img_rect->bottom - img_rect->top;
+ m_img_rect = * img_rect;
+ }
+ else
+ {
+ pt_scan_size.x = g_view_manager->get_settings ().max_area [ 0 ];
+ pt_scan_size.y = g_view_manager->get_settings ().max_area [ 1 ];
+
+ m_img_rect.left = 0.0;
+ m_img_rect.top = 0.0;
+ m_img_rect.right = pt_scan_size.x;
+ m_img_rect.bottom = pt_scan_size.y;
+ }
+
+ aspect_max = ( float ) g_prev_max_y / g_prev_max_x;
+ aspect_scan = ( float ) pt_scan_size.y / pt_scan_size.x;
+
+ if ( aspect_max < aspect_scan )
+ resolution = ( long ) ( g_prev_max_y / pt_scan_size.y );
+ else
+ resolution = ( long ) ( g_prev_max_x / pt_scan_size.x );
+
+ m_img_width = ( long ) ( resolution * pt_scan_size.x );
+ m_img_height = ( long ) ( resolution * pt_scan_size.y );
+
+ return resolution;
+}
+
+/*------------------------------------------------------------*/
+void preview_window::resize_preview_window ( long width, long height )
+{
+ if ( width != 0 && height != 0 )
+ {
+ m_img_width = width;
+ m_img_height = height;
+ }
+
+ if ( m_img )
+ {
+ delete [ ] m_img;
+ m_img = new unsigned char [ g_prev_max_x * g_prev_max_y * 3 ];
+ }
+
+ if ( m_img_org )
+ {
+ delete [ ] m_img_org;
+ m_img_org = new unsigned char [ g_prev_max_x * g_prev_max_y * 3 ];
+ }
+
+ m_client_rect.left = 0;
+ m_client_rect.top = 0;
+ m_client_rect.right = m_img_width - 1;
+ m_client_rect.bottom = m_img_height - 1;
+
+ modify_max_val ( );
+}
+
+/*------------------------------------------------------------*/
+void preview_window::change_max_scan_area ( long width, long height )
+{
+ _pointD pt_scan_size;
+ marquee * marq;
+
+ if ( m_img_width < width || m_img_height < height )
+ return;
+
+ marq = & g_view_manager->get_marquee ();
+
+ pt_scan_size.x = g_view_manager->get_settings ().max_area [ 0 ];
+ pt_scan_size.y = g_view_manager->get_settings ().max_area [ 1 ];
+
+ m_img_rect.left = 0.0;
+ m_img_rect.top = 0.0;
+ m_img_rect.right = pt_scan_size.x * width / m_img_width;
+ m_img_rect.bottom = pt_scan_size.y * height / m_img_height;
+
+ marq->area.x = m_img_rect.right;
+ marq->area.y = m_img_rect.bottom;
+}
+
+/*------------------------------------------------------------*/
+void preview_window::change_max_disp_area ( long width, long height )
+{
+ long save_prev_x, save_prev_y;
+ long save_img_width, save_img_height;
+ unsigned char * save_img_org;
+ _rectD tmp_rect;
+
+ if ( width < 1 && height < 1 )
+ return;
+
+ if ( g_prev_max_x == width && g_prev_max_y == height )
+ return;
+
+ tmp_rect = m_img_rect;
+
+ if ( m_is_prev == 0 )
+ {
+ g_prev_max_x = width;
+ g_prev_max_y = height;
+
+ get_preview_resolution ( & tmp_rect );
+ resize_preview_window ( 0, 0 );
+
+ clear_image ( );
+
+ return;
+ }
+
+ // save old parameter and image
+ save_prev_x = g_prev_max_x;
+ save_prev_y = g_prev_max_y;
+ save_img_width = m_img_width;
+ save_img_height = m_img_height;
+ save_img_org = new unsigned char [ save_prev_x * save_prev_y * 3 ];
+
+ if ( save_img_org )
+ {
+ ::memcpy ( save_img_org, m_img_org, save_prev_x * save_prev_y * 3 );
+ }
+
+ // resize
+ g_prev_max_x = width;
+ g_prev_max_y = height;
+
+ get_preview_resolution ( & tmp_rect );
+ resize_preview_window ( 0, 0 );
+
+ clear_image ( );
+ m_is_prev = 1;
+
+ // restore image
+ if ( save_img_org )
+ {
+ struct resize_img_info parms;
+
+ parms.in_width = save_img_width;
+ parms.in_height = save_img_height;
+ parms.in_rowbytes = save_prev_x * 3;
+ parms.out_width = m_img_width;
+ parms.out_height = m_img_height;
+ parms.out_rowbytes = g_prev_max_x * 3;
+ parms.bits_per_pixel = 24;
+ parms.resize_flag = PISA_RS_BL;
+ parms.resolution = 0; // just to make sure it's set
+
+ iscan::scale f (parms);
+
+ f.exec (save_img_org, parms.in_height * parms.in_rowbytes,
+ m_img_org, parms.out_height * parms.out_rowbytes);
+
+ delete [] save_img_org;
+ }
+
+ update_img ( );
+}
+
+/*------------------------------------------------------------*/
+void preview_window::clear_image ( void )
+{
+ int i;
+
+ m_is_prev = 0;
+
+ ::memset ( m_img, 0xff, m_img_width * 3 );
+ ::memset ( m_img + ( m_img_width * 3 ),
+ 0xC4,
+ ( g_prev_max_x - m_img_width ) * 3 );
+
+ for ( i = 0; i < m_img_height; i++ )
+ ::gtk_preview_draw_row ( GTK_PREVIEW ( m_prev ), m_img,
+ 0, i, g_prev_max_x );
+
+ ::memset ( m_img, 0xC4, g_prev_max_x * 3 );
+
+ for ( ; i < g_prev_max_y; i++ )
+ ::gtk_preview_draw_row ( GTK_PREVIEW ( m_prev ), m_img,
+ 0, i, g_prev_max_x );
+
+ ::gtk_widget_draw ( m_prev, 0 );
+ ::gdk_flush ( );
+}
+
+
+/*--------------------------------------------------------------*/
+void preview_window::draw_marquee ( void )
+{
+ long i, marq_num;
+ _pointL pt_lefttop, pt_rightbottom;
+
+ if ( m_gc == 0 )
+ {
+ m_gc = ::gdk_gc_new ( m_prev->window );
+ ::gdk_gc_set_function ( m_gc, GDK_INVERT );
+ ::gdk_gc_set_line_attributes ( m_gc, 1, GDK_LINE_ON_OFF_DASH,
+ GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+
+ marq_num = g_view_manager->get_settings ().get_marquee_size ( );
+
+ if ( marq_num < 2 || m_on_preview )
+ return;
+
+ for ( i = 1; i < marq_num; i++ )
+ {
+ get_marquee_point ( i, & pt_lefttop, & pt_rightbottom );
+
+ draw_rect ( pt_lefttop, pt_rightbottom );
+ }
+}
+
+/*--------------------------------------------------------------*/
+void
+preview_window::reset_settings (bool zooming)
+{
+ long i, n, m, marq_num;
+ marquee * marq;
+ gamma_correction * gamma_cls;
+ scan_manager * scan_mgr;
+
+ gamma_cls = g_view_manager->get_gamma_correction ();
+
+ scan_mgr = g_view_manager->get_scan_manager ();
+ marq_num = g_view_manager->get_marquee_size ();
+
+ if (!zooming)
+ {
+ while ( 1 < marq_num )
+ {
+ g_view_manager->del_marquee (marq_num - 1);
+ marq_num = g_view_manager->get_marquee_size ();
+ }
+
+ gamma_cls->reset ( 1 );
+ }
+
+ marq_num = g_view_manager->get_marquee_size ();
+
+ for ( i = 0; i < marq_num; i++ )
+ {
+ marq = & g_view_manager->get_marquee (i);
+
+ if (!zooming)
+ {
+ marq->gamma = ( long ) ( 100 * DEFGAMMA );
+ marq->highlight = DEFHIGHLIGHT;
+ marq->shadow = DEFSHADOW;
+ marq->threshold = DEFTHRESHOLD;
+
+ for ( n = 0; n < 4; n++ )
+ for ( m = 0; m < 256; m++ )
+ marq->gamma_table [ n ] [ m ] = m;
+
+ marq->graybalance = DEFGRAYBALANCE;
+ marq->saturation = DEFSATURATION;
+ }
+
+ for ( n = 0; n < 3; n++ )
+ {
+ marq->film_gamma [ n ] = 1.0;
+ marq->film_yp [ n ] = 0.0;
+ marq->grayl [ n ] = 0.0;
+ }
+
+ iscan::build_LUT (scan_mgr->get_scan_source (),
+ scan_mgr->get_film_type (),
+ g_view_manager->get_settings (),
+ *marq, !scan_mgr->has_zoom ());
+ }
+}
+
+int
+preview_window::set_preview_param (bool zooming)
+{
+ settings set = g_view_manager->get_settings ();
+ marquee marq;
+
+ if (!zooming)
+ {
+ marq.offset.x = 0.0;
+ marq.offset.y = 0.0;
+ marq.area.x = set.max_area[0];
+ marq.area.y = set.max_area[1];
+ }
+ else
+ {
+ float rate = 0.1f;
+ _pointD pt_offset, pt_area, pt_max;
+
+ marquee cur_marq = g_view_manager->get_marquee ();
+
+ marq.focus = cur_marq.focus;
+
+ pt_offset = cur_marq.offset;
+ pt_area = cur_marq.area;
+
+ marquee whole_marq = g_view_manager->get_marquee (0);
+
+ pt_max.x = whole_marq.area.x;
+ pt_max.y = whole_marq.area.y;
+
+ zoom_boundary (pt_offset, pt_area, pt_max, rate);
+
+ marq.offset.x = pt_offset.x;
+ marq.offset.y = pt_offset.y;
+ marq.area.x = pt_area.x;
+ marq.area.y = pt_area.y;
+ }
+
+ _rectD img_rect;
+ img_rect.left = marq.offset.x;
+ img_rect.top = marq.offset.y;
+ img_rect.right = marq.offset.x + marq.area.x;
+ img_rect.bottom = marq.offset.y + marq.area.y;
+
+ long resolution = get_preview_resolution (&img_rect);
+
+ pisa_error_id err = (g_view_manager->get_scan_manager ()
+ ->set_scan_parameters (set, marq, resolution));
+
+ return err;
+}
+
+void
+preview_window::revert_resolution ( void )
+{
+ long set_res = g_view_manager->get_settings ().resolution;
+
+ g_view_manager->set_resolution (set_res);
+}
+
+/*------------------------------------------------------------*/
+void preview_window::tool_usm (const pisa_image_info& info)
+{
+ iscan::focus f (info);
+
+ f.exec (info.m_img, info.m_height * info.m_rowbytes,
+ info.m_img, info.m_height * info.m_rowbytes);
+}
+
+void
+preview_window::zoom_boundary (_pointD& pt_offset, _pointD& pt_area,
+ const _pointD& pt_max, float rate) const
+{
+ _pointD pt_result_offset, pt_result_area;
+ double bound_size;
+
+ if (pt_area.x < pt_area.y)
+ bound_size = pt_area.y * rate;
+ else
+ bound_size = pt_area.x * rate;
+
+ bound_size = (0.0 == bound_size) ? 0.01 : bound_size;
+
+ // left
+ pt_result_offset.x = pt_offset.x - bound_size;
+ if (0.0 > pt_result_offset.x)
+ pt_result_offset.x = 0.0;
+
+ // top
+ pt_result_offset.y = pt_offset.y - bound_size;
+ if (0.0 > pt_result_offset.y)
+ pt_result_offset.y = 0.0;
+
+ // right
+ pt_result_area.x = ((pt_offset.x - pt_result_offset.x) +
+ pt_area.x + bound_size);
+ if (pt_max.x < pt_result_offset.x + pt_result_area.x)
+ pt_result_area.x = pt_max.x - pt_result_offset.x;
+
+ // bottom
+ pt_result_area.y = ((pt_offset.y - pt_result_offset.y) +
+ pt_area.y + bound_size);
+ if (pt_max.y < pt_result_offset.y + pt_result_area.y)
+ pt_result_area.y = pt_max.y - pt_result_offset.y;
+
+ pt_offset = pt_result_offset;
+ pt_area = pt_result_area;
+}
+
+/*--------------------------------------------------------------*/
+void preview_window::create_cursor ( void )
+{
+ m_cursor [ PISA_CS_ARROW ] = ::gdk_cursor_new ( GDK_ARROW );
+ m_cursor [ PISA_CS_HAND ] = ::gdk_cursor_new ( GDK_HAND2 );
+ m_cursor [ PISA_CS_CROSS ] = ::gdk_cursor_new ( GDK_CROSS );
+ m_cursor [ PISA_CS_TOP ] = ::gdk_cursor_new ( GDK_TOP_SIDE );
+ m_cursor [ PISA_CS_BOTTOM ] = ::gdk_cursor_new ( GDK_BOTTOM_SIDE );
+ m_cursor [ PISA_CS_LEFT ] = ::gdk_cursor_new ( GDK_LEFT_SIDE );
+ m_cursor [ PISA_CS_RIGHT ] = ::gdk_cursor_new ( GDK_RIGHT_SIDE );
+ m_cursor [ PISA_CS_LEFTTOP ] = ::gdk_cursor_new ( GDK_TOP_LEFT_CORNER );
+ m_cursor [ PISA_CS_LEFTBOTTOM ] = ::gdk_cursor_new ( GDK_BOTTOM_LEFT_CORNER );
+ m_cursor [ PISA_CS_RIGHTTOP ] = ::gdk_cursor_new ( GDK_TOP_RIGHT_CORNER );
+ m_cursor [ PISA_CS_RIGHTBOTTOM ] = ::gdk_cursor_new ( GDK_BOTTOM_RIGHT_CORNER );
+}
+
+
+/*--------------------------------------------------------------*/
+int preview_window::set_mouse_cursor ( int x, int y )
+{
+ const long ADDSIZE = 3;
+ long marq_num;
+ _pointL pt_lefttop, pt_rightbottom;
+ int cursor_state;
+ _pointL pt;
+ int pt_in_cur_marq;
+ _rectL judge_rect;
+ _pointL pt_lt, pt_rb;
+ int i;
+
+ if ( m_drag == 1 )
+ return FALSE;
+
+ pt.x = x;
+ pt.y = y;
+
+ if ( 0 == ::pt_in_rect ( m_client_rect, pt ) )
+ {
+ m_cursor_state = PISA_CS_ARROW;
+ return FALSE;
+ }
+
+ marq_num = g_view_manager->get_settings ().get_marquee_size ( );
+
+ if ( marq_num < 2 )
+ {
+ m_cursor_state = PISA_CS_CROSS;
+ return FALSE;
+ }
+
+ get_marquee_point ( marq_num - 1, & pt_lefttop, & pt_rightbottom );
+
+ cursor_state = search_cursor_state ( pt_lefttop, pt_rightbottom, pt );
+ if ( cursor_state != PISA_CS_CROSS )
+ {
+ m_cursor_state = cursor_state;
+ return FALSE;
+ }
+
+ judge_rect.left = pt_lefttop.x;
+ judge_rect.right = pt_rightbottom.x;
+ judge_rect.top = pt_lefttop.y;
+ judge_rect.bottom = pt_rightbottom.y;
+ if ( pt_in_rect ( judge_rect, pt ) )
+ pt_in_cur_marq = 1;
+ else
+ pt_in_cur_marq = 0;
+
+ for ( i = marq_num - 1; 0 < i; i-- )
+ {
+ if ( marq_num - 1 == i )
+ continue;
+
+ get_marquee_point ( i, & pt_lt, & pt_rb );
+
+ if ( pt_in_cur_marq )
+ {
+ cursor_state = search_cursor_state ( pt_lt, pt_rb, pt );
+ if ( PISA_CS_CROSS != cursor_state )
+ {
+ m_cursor_state = PISA_CS_ARROW;
+ return i;
+ }
+ }
+ else
+ {
+ judge_rect.left = pt_lt.x - ADDSIZE;
+ judge_rect.right = pt_rb.x + ADDSIZE;
+ judge_rect.top = pt_lt.y - ADDSIZE;
+ judge_rect.bottom = pt_rb.y + ADDSIZE;
+ if ( pt_in_rect ( judge_rect, pt ) )
+ {
+ m_cursor_state = PISA_CS_ARROW;
+ return i;
+ }
+ }
+ }
+
+ if ( pt_in_cur_marq )
+ {
+ m_cursor_state = PISA_CS_HAND;
+ return 0;
+ }
+
+ m_cursor_state = PISA_CS_CROSS;
+
+ return 0;
+}
+
+/*--------------------------------------------------------------*/
+int preview_window::search_cursor_state ( const _pointL & pt_lt,
+ const _pointL & pt_rb,
+ const _pointL & pt )
+{
+ const long ADDSIZE = 3;
+ _rectL judge_rect;
+
+ // check left top
+ judge_rect.left = pt_lt.x - ADDSIZE;
+ judge_rect.right = pt_lt.x + ADDSIZE;
+ judge_rect.top = pt_lt.y - ADDSIZE;
+ judge_rect.bottom = pt_lt.y + ADDSIZE;
+ if ( ::pt_in_rect ( judge_rect, pt ) )
+ return PISA_CS_LEFTTOP;
+
+ // check right bottom
+ judge_rect.left = pt_rb.x - ADDSIZE;
+ judge_rect.right = pt_rb.x + ADDSIZE;
+ judge_rect.top = pt_rb.y - ADDSIZE;
+ judge_rect.bottom = pt_rb.y + ADDSIZE;
+ if ( ::pt_in_rect ( judge_rect, pt ) )
+ return PISA_CS_RIGHTBOTTOM;
+
+ // check right top
+ judge_rect.left = pt_rb.x - ADDSIZE;
+ judge_rect.right = pt_rb.x + ADDSIZE;
+ judge_rect.top = pt_lt.y - ADDSIZE;
+ judge_rect.bottom = pt_lt.y + ADDSIZE;
+ if ( ::pt_in_rect ( judge_rect, pt ) )
+ return PISA_CS_RIGHTTOP;
+
+ // check left bottom
+ judge_rect.left = pt_lt.x - ADDSIZE;
+ judge_rect.right = pt_lt.x + ADDSIZE;
+ judge_rect.top = pt_rb.y - ADDSIZE;
+ judge_rect.bottom = pt_rb.y + ADDSIZE;
+ if ( ::pt_in_rect ( judge_rect, pt ) )
+ return PISA_CS_LEFTBOTTOM;
+
+ // check left resizing
+ judge_rect.left = pt_lt.x - ADDSIZE;
+ judge_rect.right = pt_lt.x + ADDSIZE;
+ judge_rect.top = pt_lt.y;
+ judge_rect.bottom = pt_rb.y;
+ if ( ::pt_in_rect ( judge_rect, pt ) )
+ return PISA_CS_LEFT;
+
+ // check right resizing
+ judge_rect.left = pt_rb.x - ADDSIZE;
+ judge_rect.right = pt_rb.x + ADDSIZE;
+ judge_rect.top = pt_lt.y;
+ judge_rect.bottom = pt_rb.y;
+ if ( ::pt_in_rect ( judge_rect, pt ) )
+ return PISA_CS_RIGHT;
+
+ // check top resizing
+ judge_rect.left = pt_lt.x;
+ judge_rect.right = pt_rb.x;
+ judge_rect.top = pt_lt.y - ADDSIZE;
+ judge_rect.bottom = pt_lt.y + ADDSIZE;
+ if ( ::pt_in_rect ( judge_rect, pt ) )
+ return PISA_CS_TOP;
+
+ // check bottom resizing
+ judge_rect.left = pt_lt.x;
+ judge_rect.right = pt_rb.x;
+ judge_rect.top = pt_rb.y - ADDSIZE;
+ judge_rect.bottom = pt_rb.y + ADDSIZE;
+ if ( ::pt_in_rect ( judge_rect, pt ) )
+ return PISA_CS_BOTTOM;
+
+ return PISA_CS_CROSS;
+}
+
+/*--------------------------------------------------------------*/
+void preview_window::change_cursor ( void )
+{
+ ::gdk_window_set_cursor ( m_prev->window,
+ m_cursor [ m_cursor_state ] );
+}
+
+/*--------------------------------------------------------------*/
+void preview_window::modify_max_val ( void )
+{
+ marquee * whole_marq;
+ _pointD pt_min_D, pt_max_D;
+
+ whole_marq = & g_view_manager->get_marquee (0);
+ m_max_img_rect.top = 0.0;
+ m_max_img_rect.left = 0.0;
+ m_max_img_rect.right = whole_marq->area.x;
+ m_max_img_rect.bottom = whole_marq->area.y;
+
+ pt_min_D.x = m_max_img_rect.left;
+ pt_min_D.y = m_max_img_rect.top;
+ pt_max_D.x = m_max_img_rect.right;
+ pt_max_D.y = m_max_img_rect.bottom;
+ m_pt_max_rb = inches2clientpix ( pt_max_D );
+ m_pt_max_lt.x = 0;
+ m_pt_max_lt.y = 0;
+ m_pt_max_rb.x = m_img_width - 1;
+ m_pt_max_rb.y = m_img_height - 1;
+}
+
+/*--------------------------------------------------------------*/
+gint preview_window::mouse_down ( GdkEvent * event )
+{
+ marquee * cur_marq;
+ _pointL pt_lefttop, pt_rightbottom;
+ long id_cur_marquee;
+
+ if ( event->button.button != 1 )
+ return TRUE;
+
+ if ( m_img_width - 1 < ( int ) event->button.x || m_img_height - 1 < ( int ) event->button.y )
+ return TRUE;
+
+ m_pt_begin.x = ( int ) event->button.x;
+ m_pt_begin.y = ( int ) event->button.y;
+
+ m_drag = 1;
+
+ m_pt_old = m_pt_begin;
+
+ id_cur_marquee = g_view_manager->get_marquee_size () - 1;
+ get_marquee_point ( id_cur_marquee, & pt_lefttop, & pt_rightbottom );
+
+ cur_marq = & g_view_manager->get_marquee ();
+
+ m_pt_save_offset = cur_marq->offset;
+ m_pt_save_area = cur_marq->area;
+
+ switch ( m_cursor_state )
+ {
+ case PISA_CS_CROSS:
+ m_pt_old_lt = m_pt_old_rb = m_pt_begin;
+ delete_marquee ( );
+ create_marquee ( m_pt_old_lt, m_pt_old_rb );
+ break;
+ case PISA_CS_HAND:
+ case PISA_CS_TOP:
+ case PISA_CS_BOTTOM:
+ case PISA_CS_LEFT:
+ case PISA_CS_RIGHT:
+ case PISA_CS_LEFTTOP:
+ case PISA_CS_LEFTBOTTOM:
+ case PISA_CS_RIGHTTOP:
+ case PISA_CS_RIGHTBOTTOM:
+ m_pt_old_lt = pt_lefttop;
+ m_pt_old_rb = pt_rightbottom;
+ break;
+ }
+
+ return FALSE;
+}
+
+/*--------------------------------------------------------------*/
+gint preview_window::mouse_move ( GdkEvent * event )
+{
+ int x, y;
+ GdkModifierType state;
+ _pointL pt_mouse, pt_move, pt_tmp_lt, pt_tmp_rb, pt_min_L;
+ _pointD pt_lefttop, pt_rightbottom, pt_min_D, pt_zero_D;
+
+ if ( event->motion.is_hint )
+ {
+ ::gdk_window_get_pointer ( m_prev->window, & x, & y, & state );
+ }
+ else
+ return TRUE;
+
+ if ( m_drag == 0 )
+ {
+ set_mouse_cursor ( x, y );
+ change_cursor ( );
+ return FALSE;
+ }
+
+ if ( x < 0 ) x = 0;
+ if ( y < 0 ) y = 0;
+ if ( m_client_rect.right < x ) x = m_client_rect.right;
+ if ( m_client_rect.bottom < y ) y = m_client_rect.bottom;
+
+ pt_mouse.x = x;
+ pt_mouse.y = y;
+
+ // clear previous marquee
+ draw_rect ( m_pt_old_lt, m_pt_old_rb );
+
+ pt_lefttop = m_pt_save_offset;
+ pt_rightbottom = m_pt_save_offset + m_pt_save_area;
+
+ pt_tmp_lt = inches2clientpix ( pt_lefttop );
+ pt_tmp_rb = inches2clientpix ( pt_rightbottom );
+
+ pt_zero_D.x = 0.0;
+ pt_zero_D.y = 0.0;
+ pt_min_D.x = g_min_marq_size;
+ pt_min_D.y = g_min_marq_size;
+ pt_min_L = inches2clientpix ( pt_min_D ) - inches2clientpix ( pt_zero_D );
+
+ pt_move = pt_mouse - m_pt_begin;
+
+ // initialize
+ begin_mouse_move ( & pt_tmp_lt, & pt_tmp_rb );
+
+ move_rect ( & pt_tmp_lt, & pt_tmp_rb, pt_move );
+
+ switch ( m_cursor_state )
+ {
+ case PISA_CS_CROSS:
+ m_pt_old_rb = pt_mouse;
+ break;
+ case PISA_CS_HAND:
+ m_pt_old_rb = pt_tmp_lt + ( m_pt_old_rb - m_pt_old_lt );
+ m_pt_old_lt = pt_tmp_lt;
+ break;
+ case PISA_CS_TOP:
+ if ( m_pt_old_rb.y - pt_min_L.y > pt_tmp_lt.y )
+ m_pt_old_lt.y = pt_tmp_lt.y;
+ else
+ m_pt_old_lt.y = m_pt_old_rb.y - pt_min_L.y;
+ break;
+ case PISA_CS_BOTTOM:
+ if ( m_pt_old_lt.y + pt_min_L.y < pt_tmp_rb.y )
+ m_pt_old_rb.y = pt_tmp_rb.y;
+ else
+ m_pt_old_rb.y = m_pt_old_lt.y + pt_min_L.y;
+ break;
+ case PISA_CS_LEFT:
+ if ( m_pt_old_rb.x - pt_min_L.x > pt_tmp_lt.x )
+ m_pt_old_lt.x = pt_tmp_lt.x;
+ else
+ m_pt_old_lt.x = m_pt_old_rb.x - pt_min_L.x;
+ break;
+ case PISA_CS_RIGHT:
+ if ( m_pt_old_lt.x + pt_min_L.x < pt_tmp_rb.x )
+ m_pt_old_rb.x = pt_tmp_rb.x;
+ else
+ m_pt_old_rb.x = m_pt_old_lt.x + pt_min_L.x;
+ break;
+ case PISA_CS_LEFTTOP:
+ if ( m_pt_old_rb.y - pt_min_L.y > pt_tmp_lt.y )
+ m_pt_old_lt.y = pt_tmp_lt.y;
+ else
+ m_pt_old_lt.y = m_pt_old_rb.y - pt_min_L.y;
+ if ( m_pt_old_rb.x - pt_min_L.x > pt_tmp_lt.x )
+ m_pt_old_lt.x = pt_tmp_lt.x;
+ else
+ m_pt_old_lt.x = m_pt_old_rb.x - pt_min_L.x;
+ break;
+ case PISA_CS_LEFTBOTTOM:
+ if ( m_pt_old_rb.x - pt_min_L.x > pt_tmp_lt.x )
+ m_pt_old_lt.x = pt_tmp_lt.x;
+ else
+ m_pt_old_lt.x = m_pt_old_rb.x - pt_min_L.x;
+ if ( m_pt_old_lt.y + pt_min_L.y < pt_tmp_rb.y )
+ m_pt_old_rb.y = pt_tmp_rb.y;
+ else
+ m_pt_old_rb.y = m_pt_old_lt.y + pt_min_L.y;
+ break;
+ case PISA_CS_RIGHTTOP:
+ if ( m_pt_old_rb.y - pt_min_L.y > pt_tmp_lt.y )
+ m_pt_old_lt.y = pt_tmp_lt.y;
+ else
+ m_pt_old_lt.y = m_pt_old_rb.y - pt_min_L.y;
+ if ( m_pt_old_lt.x + pt_min_L.x < pt_tmp_rb.x )
+ m_pt_old_rb.x = pt_tmp_rb.x;
+ else
+ m_pt_old_rb.x = m_pt_old_lt.x + pt_min_L.x;
+ break;
+ case PISA_CS_RIGHTBOTTOM:
+ if ( m_pt_old_lt.y + pt_min_L.y < pt_tmp_rb.y )
+ m_pt_old_rb.y = pt_tmp_rb.y;
+ else
+ m_pt_old_rb.y = m_pt_old_lt.y + pt_min_L.y;
+ if ( m_pt_old_lt.x + pt_min_L.x < pt_tmp_rb.x )
+ m_pt_old_rb.x = pt_tmp_rb.x;
+ else
+ m_pt_old_rb.x = m_pt_old_lt.x + pt_min_L.x;
+ break;
+ }
+
+ draw_rect ( m_pt_old_lt, m_pt_old_rb );
+
+ if ( m_cursor_state != PISA_CS_HAND )
+ resize_marquee ( m_pt_old_lt, m_pt_old_rb );
+
+ m_pt_old = pt_mouse;
+
+ return FALSE;
+}
+
+/*--------------------------------------------------------------*/
+gint preview_window::mouse_up ( GdkEvent * event )
+{
+ int min_check;
+ _pointL pt_mouse;
+ long marq_num;
+
+ if ( 0 == m_drag )
+ return FALSE;
+
+ pt_mouse.x = ( int ) event->button.x;
+ pt_mouse.y = ( int ) event->button.y;
+
+ // clear previous marquee
+ marq_num = g_view_manager->get_marquee_size ();
+ if ( 1 < marq_num )
+ draw_rect ( m_pt_old_lt, m_pt_old_rb );
+
+ switch ( m_cursor_state )
+ {
+ case PISA_CS_CROSS:
+ min_check = check_min_size ( m_pt_old_lt, m_pt_old_rb );
+ if ( min_check == 0 )
+ delete_marquee ( );
+ update_img ( );
+ break;
+ case PISA_CS_ARROW:
+ break;
+ case PISA_CS_HAND:
+ move_marquee ( m_pt_old_lt, m_pt_old_rb );
+ update_img ( );
+ break;
+ case PISA_CS_LEFT:
+ case PISA_CS_LEFTTOP:
+ case PISA_CS_LEFTBOTTOM:
+ case PISA_CS_RIGHT:
+ case PISA_CS_RIGHTTOP:
+ case PISA_CS_RIGHTBOTTOM:
+ case PISA_CS_TOP:
+ case PISA_CS_BOTTOM:
+ if ( 0 == check_min_size ( m_pt_old_lt, m_pt_old_rb ) )
+ delete_marquee ( );
+ update_img ( PISA_CS_LEFT == m_cursor_state
+ || PISA_CS_LEFTTOP == m_cursor_state
+ || PISA_CS_LEFTBOTTOM == m_cursor_state);
+ break;
+ }
+
+ m_drag = 0;
+
+ set_mouse_cursor ( pt_mouse.x, pt_mouse.y );
+ change_cursor ( );
+
+ return FALSE;
+}
+
+/*--------------------------------------------------------------*/
+int preview_window::create_marquee ( const _pointL & pt_lt, const _pointL & pt_rb )
+{
+ _pointL pt_lefttop_L, pt_rightbottom_L;
+ _pointD pt_lefttop_D, pt_rightbottom_D, pt_area_D;
+ marquee * new_marq, * whole_marq;
+
+ pt_lefttop_L = pt_lt;
+ pt_rightbottom_L = pt_rb;
+ check_ltrb ( & pt_lefttop_L, & pt_rightbottom_L );
+
+ pt_lefttop_D = clientpix2inches ( pt_lefttop_L );
+ pt_rightbottom_D = clientpix2inches ( pt_rightbottom_L );
+ pt_area_D = pt_rightbottom_D - pt_lefttop_D;
+
+ check_max_size ( & pt_lefttop_D, & pt_area_D, 0 );
+
+ whole_marq = & g_view_manager->get_marquee (0);
+ new_marq = new marquee;
+ * new_marq = * whole_marq;
+
+ new_marq->offset = pt_lefttop_D;
+ new_marq->area = pt_area_D;
+
+ g_view_manager->add_marquee (&new_marq);
+
+ ::g_view_manager->sensitive ( );
+
+ return 1;
+}
+
+/*--------------------------------------------------------------*/
+int preview_window::delete_marquee ( void )
+{
+ long num_marq;
+ int i, j;
+ marquee * cur_marq, * whole_marq;
+
+ num_marq = g_view_manager->get_marquee_size ();
+
+ // no marquee
+ if ( num_marq < 2 )
+ return PISA_ERR_SUCCESS;
+
+ whole_marq = & g_view_manager->get_marquee (0);
+ cur_marq = & g_view_manager->get_marquee ();
+
+ whole_marq->gamma = cur_marq->gamma;
+ whole_marq->highlight = cur_marq->highlight;
+ whole_marq->shadow = cur_marq->shadow;
+ whole_marq->threshold = cur_marq->threshold;
+
+ for ( i = 0; i < 4; i++ )
+ for ( j = 0; j < 256; j++ )
+ whole_marq->gamma_table [ i ] [ j ] = cur_marq->gamma_table [ i ] [ j ];
+
+ whole_marq->graybalance = cur_marq->graybalance;
+ whole_marq->saturation = cur_marq->saturation;
+
+ whole_marq->scale = cur_marq->scale;
+
+ whole_marq->focus = cur_marq->focus;
+
+ for ( i = 0; i < 3; i++ )
+ {
+ whole_marq->film_gamma [ i ] = cur_marq->film_gamma [ i ];
+ whole_marq->film_yp [ i ] = cur_marq->film_yp [ i ];
+ whole_marq->grayl [ i ] = cur_marq->grayl [ i ];
+ }
+
+ for ( i = 0; i < 256; i++ )
+ {
+ whole_marq->lut.gamma_r [ i ] = cur_marq->lut.gamma_r [ i ];
+ whole_marq->lut.gamma_g [ i ] = cur_marq->lut.gamma_g [ i ];
+ whole_marq->lut.gamma_b [ i ] = cur_marq->lut.gamma_b [ i ];
+ }
+
+ g_view_manager->del_marquee ();
+
+ ::g_view_manager->sensitive ( );
+ update_img ( );
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*--------------------------------------------------------------*/
+void preview_window::move_marquee ( const _pointL & pt_lt,
+ const _pointL & pt_rb )
+{
+ _pointL pt_lefttop_L, pt_rightbottom_L, pt_tmp;
+ _pointD pt_lefttop_D, pt_rightbottom_D, pt_offset, pt_area;
+ marquee * cur_marq;
+
+ pt_tmp = pt_rb;
+
+ pt_lefttop_L = pt_lt;
+ pt_lefttop_D = clientpix2inches ( pt_lefttop_L );
+ pt_rightbottom_D = clientpix2inches ( pt_rightbottom_L );
+
+ pt_offset = pt_lefttop_D;
+ pt_area = pt_rightbottom_D - pt_lefttop_D;
+
+ check_max_size ( & pt_offset, & pt_area, 1 );
+
+ cur_marq = & g_view_manager->get_marquee ();
+
+ cur_marq->offset = pt_offset;
+
+ ::g_view_manager->sensitive ( );
+}
+
+/*--------------------------------------------------------------*/
+void preview_window::resize_marquee ( const _pointL & pt_lt,
+ const _pointL & pt_rb )
+{
+ _pointL pt_lefttop_L, pt_rightbottom_L;
+ _pointD pt_lefttop_D, pt_rightbottom_D, pt_offset, pt_area;
+ marquee * cur_marq;
+
+ pt_lefttop_L = pt_lt;
+ pt_rightbottom_L = pt_rb;
+
+ check_ltrb ( & pt_lefttop_L, & pt_rightbottom_L );
+
+ pt_lefttop_D = clientpix2inches ( pt_lefttop_L );
+ pt_rightbottom_D = clientpix2inches ( pt_rightbottom_L );
+
+ pt_offset = pt_lefttop_D;
+ pt_area = pt_rightbottom_D - pt_lefttop_D;
+
+ check_max_size ( & pt_offset, & pt_area, 0 );
+
+ cur_marq = & g_view_manager->get_marquee ();
+
+ cur_marq->offset = pt_offset;
+ cur_marq->area = pt_area;
+
+ ::g_view_manager->sensitive ( );
+}
+
+/*--------------------------------------------------------------*/
+void preview_window::begin_mouse_move ( _pointL * pt_lt, _pointL * pt_rb )
+{
+ switch ( m_cursor_state )
+ {
+ case PISA_CS_CROSS:
+ case PISA_CS_HAND:
+ break;
+ case PISA_CS_TOP:
+ pt_lt->x = pt_rb->x;
+ pt_rb->y = pt_lt->y;
+ break;
+ case PISA_CS_BOTTOM:
+ * pt_lt = * pt_rb;
+ break;
+ case PISA_CS_LEFT:
+ pt_lt->y = pt_rb->y;
+ pt_rb->x = pt_lt->x;
+ break;
+ case PISA_CS_RIGHT:
+ * pt_lt = * pt_rb;
+ break;
+ case PISA_CS_LEFTTOP:
+ * pt_rb = * pt_lt;
+ break;
+ case PISA_CS_LEFTBOTTOM:
+ pt_lt->y = pt_rb->y;
+ pt_rb->x = pt_lt->x;
+ break;
+ case PISA_CS_RIGHTBOTTOM:
+ * pt_lt = * pt_rb;
+ break;
+ case PISA_CS_RIGHTTOP:
+ pt_lt->x = pt_rb->x;
+ pt_rb->y = pt_lt->y;
+ break;
+ }
+}
+
+/*--------------------------------------------------------------*/
+void preview_window::move_rect ( _pointL * pt_lt, _pointL * pt_rb,
+ const _pointL & pt_move )
+{
+ // move direction
+ typedef enum { DIR_LT, DIR_LB, DIR_RT, DIR_RB } MOVE_DIR;
+ MOVE_DIR direction;
+ _pointL pt_tmp, pt_tmp_move ( pt_move );
+ _pointL pt_size;
+ _pointD pt_lefttop_D, pt_rightbottom_D;
+ _pointL pt_lefttop_L, pt_rightbottom_L;
+
+ if ( pt_move.x < 0 )
+ {
+ if ( pt_move.y < 0 )
+ direction = DIR_LT;
+ else
+ direction = DIR_LB;
+ }
+ else
+ {
+ if ( pt_move.y < 0 )
+ direction = DIR_RT;
+ else
+ direction = DIR_RB;
+ }
+
+ pt_lefttop_D = m_pt_save_offset;
+ pt_rightbottom_D = m_pt_save_offset + m_pt_save_area;
+
+ pt_lefttop_L = inches2clientpix ( pt_lefttop_D );
+ pt_rightbottom_L = inches2clientpix ( pt_rightbottom_D );
+
+ pt_size.x = pt_rightbottom_L.x - pt_lefttop_L.x + 1;
+ pt_size.y = pt_rightbottom_L.y - pt_lefttop_L.y + 1;
+
+ switch ( direction )
+ {
+ case DIR_LT:
+ pt_tmp.x = pt_lt->x + pt_tmp_move.x;
+ if ( pt_tmp.x < m_pt_max_lt.x )
+ pt_tmp.x = m_pt_max_lt.x;
+ pt_tmp.y = pt_lt->y + pt_tmp_move.y;
+ if ( pt_tmp.y < m_pt_max_lt.y )
+ pt_tmp.y = m_pt_max_lt.y;
+ * pt_rb = pt_tmp + ( * pt_rb - * pt_lt );
+ * pt_lt = pt_tmp;
+ break;
+ case DIR_LB:
+ pt_tmp.x = pt_lt->x + pt_tmp_move.x;
+ if ( pt_tmp.x < m_pt_max_lt.x )
+ pt_tmp.x = m_pt_max_lt.x;
+ pt_tmp.y = pt_rb->y + pt_tmp_move.y;
+ if ( m_pt_max_rb.y < pt_tmp.y )
+ pt_tmp.y = m_pt_max_rb.y;
+ pt_lt->y = pt_tmp.y - ( pt_rb->y - pt_lt->y );
+ pt_rb->x = pt_tmp.x - ( pt_rb->x - pt_lt->x );
+ pt_lt->x = pt_tmp.x;
+ pt_rb->y = pt_tmp.y;
+ break;
+ case DIR_RT:
+ pt_tmp.x = pt_rb->x + pt_tmp_move.x;
+ if ( m_pt_max_rb.x < pt_tmp.x )
+ pt_tmp.x = m_pt_max_rb.x;
+ pt_tmp.y = pt_lt->y + pt_tmp_move.y;
+ if ( pt_tmp.y < m_pt_max_lt.y )
+ pt_tmp.y = m_pt_max_lt.y;
+ pt_lt->x = pt_tmp.x - ( pt_rb->x - pt_lt->x );
+ pt_rb->y = pt_tmp.y + ( pt_rb->y - pt_lt->y );
+ pt_lt->y = pt_tmp.y;
+ pt_rb->x = pt_tmp.x;
+ break;
+ case DIR_RB:
+ pt_tmp.x = pt_rb->x + pt_tmp_move.x;
+ if ( m_pt_max_rb.x < pt_tmp.x )
+ pt_tmp.x = m_pt_max_rb.x;
+ pt_tmp.y = pt_rb->y + pt_tmp_move.y;
+ if ( m_pt_max_rb.y < pt_tmp.y )
+ pt_tmp.y = m_pt_max_rb.y;
+ * pt_lt = pt_tmp - ( * pt_rb - * pt_lt );
+ * pt_rb = pt_tmp;
+ break;
+ }
+}
+
+/*--------------------------------------------------------------*/
+_pointD preview_window::clientpix2inches ( _pointL & pt )
+{
+ _pointD pt_result;
+ _pointD pt_tmp;
+ double da, db;
+
+ da = m_client_rect.right - m_client_rect.left;
+ db = m_img_rect.right - m_img_rect.left;
+ pt_tmp.x = similarity ( ( double ) pt.x, da, db );
+
+ da = m_client_rect.bottom - m_client_rect.top;
+ db = m_img_rect.bottom - m_img_rect.top;
+ pt_tmp.y = similarity ( ( double ) pt.y, da, db );
+
+
+ pt_result.x = m_img_rect.left + pt_tmp.x;
+ pt_result.y = m_img_rect.top + pt_tmp.y;
+
+ return pt_result;
+}
+
+/*--------------------------------------------------------------*/
+_pointL preview_window::inches2clientpix ( _pointD & pt )
+{
+ _pointD pt_tmp;
+ _pointL pt_result;
+ double da, db;
+
+ pt_tmp.x = pt.x - m_img_rect.left;
+ pt_tmp.y = pt.y - m_img_rect.top;
+
+ da = m_img_rect.right - m_img_rect.left;
+ db = m_client_rect.right - m_client_rect.left;
+ pt_result.x = ( long ) similarity ( pt_tmp.x, da, db );
+
+ da = m_img_rect.bottom - m_img_rect.top;
+ db = m_client_rect.bottom - m_client_rect.top;
+ pt_result.y = ( long ) similarity ( pt_tmp.y, da, db );
+
+ return pt_result;
+}
+
+/*--------------------------------------------------------------*/
+int preview_window::get_marquee_point ( long i, _pointL * pt_lt, _pointL * pt_rb )
+{
+ marquee * marq;
+ _pointD pt_tmp_lt, pt_tmp_rb;
+
+ if (0 == (marq = & g_view_manager->get_marquee (i)))
+ return 0;
+
+ pt_tmp_lt = marq->offset;
+ pt_tmp_rb = marq->offset + marq->area;
+
+ * pt_lt = inches2clientpix ( pt_tmp_lt );
+ * pt_rb = inches2clientpix ( pt_tmp_rb );
+
+ return 1;
+}
+
+/*--------------------------------------------------------------*/
+int preview_window::check_min_size ( const _pointL & pt_lt, const _pointL & pt_rb )
+{
+ _pointD pt_zero_D, pt_min_D;
+ _pointL pt_min_L;
+
+ pt_zero_D.x = 0.0;
+ pt_zero_D.y = 0.0;
+ pt_min_D.x = g_min_marq_size;
+ pt_min_D.y = g_min_marq_size;
+ pt_min_L = inches2clientpix ( pt_min_D ) - inches2clientpix ( pt_zero_D );
+
+ if ( ::abs ( pt_lt.x - pt_rb.x ) < pt_min_L.x ||
+ ::abs ( pt_lt.y - pt_rb.y ) < pt_min_L.y )
+ return 0;
+ else
+ return 1;
+}
+
+/*--------------------------------------------------------------*/
+void preview_window::check_max_size ( _pointD * pt_offset, _pointD * pt_area,
+ int offset )
+{
+ if ( pt_offset->x < 0.0 ) pt_offset->x = 0.0;
+ if ( pt_offset->y < 0.0 ) pt_offset->y = 0.0;
+
+ if ( pt_area->x > m_max_img_rect.right ) pt_area->x = m_max_img_rect.right;
+ if ( pt_area->y > m_max_img_rect.bottom ) pt_area->y = m_max_img_rect.bottom;
+
+ if ( offset )
+ {
+ if ( m_max_img_rect.right < pt_offset->x + pt_area->x )
+ pt_area->x = m_max_img_rect.right - pt_offset->x;
+ if ( m_max_img_rect.bottom < pt_offset->y + pt_area->y )
+ pt_area->y = m_max_img_rect.bottom - pt_offset->y;
+ }
+ else
+ {
+ if ( m_max_img_rect.right < pt_offset->x + pt_area->x )
+ pt_offset->x = m_max_img_rect.right - pt_area->x;
+ if ( m_max_img_rect.bottom < pt_offset->y + pt_area->y )
+ pt_offset->y = m_max_img_rect.bottom - pt_area->y;
+ }
+}
+
+/*--------------------------------------------------------------*/
+void preview_window::check_ltrb ( _pointL * pt_lt, _pointL * pt_rb )
+{
+ _pointL pt_ret_lt, pt_ret_rb;
+
+ pt_ret_lt.x = ( pt_lt->x < pt_rb->x ) ? pt_lt->x : pt_rb->x;
+ pt_ret_lt.y = ( pt_lt->y < pt_rb->y ) ? pt_lt->y : pt_rb->y;
+ pt_ret_rb.x = ( pt_lt->x > pt_rb->x ) ? pt_lt->x : pt_rb->x;
+ pt_ret_rb.y = ( pt_lt->y > pt_rb->y ) ? pt_lt->y : pt_rb->y;
+
+ * pt_lt = pt_ret_lt;
+ * pt_rb = pt_ret_rb;
+}
+
+/*--------------------------------------------------------------*/
+void preview_window::draw_rect ( const _pointL & pt_lt,
+ const _pointL & pt_rb )
+{
+ int x, y, w, h;
+
+ if ( m_gc == 0 )
+ {
+ m_gc = ::gdk_gc_new ( m_prev->window );
+ ::gdk_gc_set_function ( m_gc, GDK_INVERT );
+ ::gdk_gc_set_line_attributes ( m_gc, 1, GDK_LINE_ON_OFF_DASH,
+ GDK_CAP_BUTT, GDK_JOIN_MITER );
+ }
+
+ x = ( pt_lt.x < pt_rb.x ) ? pt_lt.x : pt_rb.x;
+ y = ( pt_lt.y < pt_rb.y ) ? pt_lt.y : pt_rb.y;
+ w = ( pt_lt.x > pt_rb.x ) ? pt_lt.x - pt_rb.x : pt_rb.x - pt_lt.x;
+ h = ( pt_lt.y > pt_rb.y ) ? pt_lt.y - pt_rb.y : pt_rb.y - pt_lt.y;
+
+ ::gdk_draw_rectangle ( m_prev->window, m_gc, FALSE,
+ x, y, w, h );
+}
+
+
diff --git a/frontend/pisa_preview_window.h b/frontend/pisa_preview_window.h
new file mode 100644
index 0000000..a8aaba7
--- /dev/null
+++ b/frontend/pisa_preview_window.h
@@ -0,0 +1,171 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005, 2008 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_PREVIEW_WINDOW_H
+#define ___PISA_PREVIEW_WINDOW_H
+
+#include <gtk/gtk.h>
+#include "pisa_enums.h"
+#include "pisa_structs.h"
+
+class preview_window
+{
+ public:
+
+ preview_window ( ) { m_gc = NULL; }
+
+ // operation
+ int init ( void );
+ GtkWidget * create_window ( GtkWidget * parent );
+ int close_window ( int destroy );
+
+ int is_prev_img ( void ) { return m_is_prev; }
+
+ int resize_window ( void );
+
+ int auto_exposure ( void );
+
+ int update_img ( bool left = false );
+
+ void start_preview (void);
+ void start_zoom (void);
+
+ gint expose_event ( GtkWidget * widget, GdkEventExpose * event );
+ gint event ( GtkWidget * widget, GdkEvent * event );
+
+ void size_allocate ( GtkWidget * widget );
+
+ private:
+
+ // operation
+ GtkWidget * create_darea ( GtkWidget * parent );
+
+ long get_preview_resolution ( const _rectD * img_rect = 0 );
+ void resize_preview_window ( long width, long height );
+ void change_max_scan_area ( long width, long height );
+ void change_max_disp_area ( long width, long height );
+ void clear_image ( void );
+
+ void modify_max_val ( void );
+
+ void draw_marquee ( void );
+
+ // preview, zoom
+ void start_preview (bool zooming);
+ void reset_settings (bool zooming);
+ int set_preview_param (bool zooming);
+ void revert_resolution ( void );
+
+ void tool_usm (const pisa_image_info& info);
+ void zoom_boundary (_pointD& pt_offset, _pointD& pt_area,
+ const _pointD& pt_max, float rate) const;
+
+ // cursor
+ void create_cursor ( void );
+ int set_mouse_cursor ( int x, int y );
+ int search_cursor_state ( const _pointL & pt_lt,
+ const _pointL & pt_rb,
+ const _pointL & pt );
+ void change_cursor ( void );
+
+ // marquee
+ gint mouse_down ( GdkEvent * event );
+ gint mouse_move ( GdkEvent * event );
+ gint mouse_up ( GdkEvent * event );
+
+ int create_marquee ( const _pointL & pt_lt, const _pointL & pt_rb );
+ int delete_marquee ( void );
+
+ void move_marquee ( const _pointL & pt_lt, const _pointL & pt_rb );
+ void resize_marquee ( const _pointL & pt_lt, const _pointL & pt_rb );
+
+ void begin_mouse_move ( _pointL * pt_lt, _pointL * pt_rb );
+ void move_rect ( _pointL * pt_lt, _pointL * pt_rb, const _pointL & pt_move );
+
+ _pointD clientpix2inches ( _pointL & pt );
+ _pointL inches2clientpix ( _pointD & pt );
+ int get_marquee_point ( long i, _pointL * pt_lt, _pointL * pt_rb );
+
+ int check_min_size ( const _pointL & pt_lt, const _pointL & pt_rb );
+ void check_max_size ( _pointD * pt_offset, _pointD * pt_area,
+ int offset );
+ void check_ltrb ( _pointL * pt_lt, _pointL * pt_rb );
+
+ void draw_rect ( const _pointL & pt_lt, const _pointL & pt_rb );
+
+ // attribute
+ GtkWidget * m_win;
+ GtkWidget * m_prev;
+
+ long m_is_prev;
+ long m_img_width, m_img_height;
+ unsigned char * m_img;
+ unsigned char * m_img_org;
+
+ GdkGC * m_gc;
+
+ int m_cursor_state;
+ GdkCursor * m_cursor [ 11 ];
+
+ bool m_on_preview;
+ long m_allocate_width;
+ long m_allocate_height;
+
+ // for marquee
+ int m_drag;
+ _rectL m_client_rect;
+ _rectD m_max_img_rect;
+ _rectD m_img_rect;
+
+ _pointL m_pt_max_lt;
+ _pointL m_pt_max_rb;
+
+ _pointL m_pt_begin;
+ _pointL m_pt_old;
+ _pointL m_pt_old_lt;
+ _pointL m_pt_old_rb;
+ _pointD m_pt_save_offset;
+ _pointD m_pt_save_area;
+
+};
+
+inline void
+preview_window::start_preview (void)
+{
+ start_preview (false);
+}
+
+inline void
+preview_window::start_zoom (void)
+{
+ start_preview (true);
+}
+
+#endif // ___PISA_PREVIEW_WINDOW_H
diff --git a/frontend/pisa_progress_window.cc b/frontend/pisa_progress_window.cc
new file mode 100644
index 0000000..6cc5b50
--- /dev/null
+++ b/frontend/pisa_progress_window.cc
@@ -0,0 +1,263 @@
+/* pisa_progress_window.cc
+ Copyright (C) 2001, 2004 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <config.h>
+
+#include "pisa_progress_window.h"
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+
+#include <cmath>
+#include <iostream>
+
+#include "pisa_main_window.h"
+#include "pisa_view_manager.h"
+
+
+// callbacks for use by the GTK+ toolkit
+static gint _delete (GtkWidget *, progress_window *);
+static void _cancel (GtkWidget *, progress_window *);
+
+
+/*! Creates an instance as the child of a \a parent widget. The \a
+ parent widget, by default, is the root window.
+
+ Initially, the message will be set to progress_window::WARMING_UP
+ with the control button disabled. The instance will \e not be made
+ visible. Use the show() and hide() members to control visibility.
+*/
+progress_window:: progress_window (GtkWidget *parent)
+ : _msg (-1), _can (false), _last_step (0)
+{
+ // first, let's gtk_*_new all the main players
+ _box = (GtkDialog *) gtk_dialog_new ();
+ _txt = (GtkLabel *) gtk_label_new ("");
+ _bar = (GtkProgressBar *) gtk_progress_bar_new ();
+ _btn = (GtkButton *) gtk_button_new ();
+
+ _btn_cancel = gtk_label_new (_(" Cancel "));
+ _btn_finish = gtk_label_new (_(" Finish "));
+
+ if (!_box || !_txt || !_bar || !_btn
+ || !_btn_cancel || !_btn_finish) {
+ if (_btn_finish) gtk_widget_destroy (_btn_finish);
+ if (_btn_cancel) gtk_widget_destroy (_btn_cancel);
+
+ if (_btn) gtk_widget_destroy (GTK_WIDGET (_btn));
+ if (_bar) gtk_widget_destroy (GTK_WIDGET (_bar));
+ if (_txt) gtk_widget_destroy (GTK_WIDGET (_txt));
+ if (_box) gtk_widget_destroy (GTK_WIDGET (_box));
+ throw std::bad_alloc ();
+ }
+
+ // container
+ gtk_window_set_policy (GTK_WINDOW (_box), false, false, true);
+ gtk_window_set_title (GTK_WINDOW (_box), PACKAGE);
+ gtk_container_border_width (GTK_CONTAINER (_box->vbox), 5);
+ gtk_widget_realize (GTK_WIDGET (_box));
+ gtk_signal_connect (GTK_OBJECT (_box), "delete_event",
+ GTK_SIGNAL_FUNC (_delete), this);
+
+ // message
+ gtk_box_pack_start (GTK_BOX (_box->vbox), GTK_WIDGET(_txt),
+ false, false, 5);
+
+ // progress
+ gtk_widget_set_usize (GTK_WIDGET (_bar), 350, 20);
+ gtk_box_pack_start (GTK_BOX (_box->vbox), GTK_WIDGET(_bar),
+ false, false, 5);
+
+ // actions
+ GTK_WIDGET_SET_FLAGS (GTK_WIDGET (_btn), GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (_box->action_area), GTK_WIDGET (_btn),
+ false, false, 0);
+ gtk_signal_connect (GTK_OBJECT (_btn), "clicked",
+ GTK_SIGNAL_FUNC (_cancel), this);
+ gtk_widget_grab_default (GTK_WIDGET (_btn));
+
+ set_text (WARMING_UP);
+
+ gtk_window_set_modal (GTK_WINDOW (_box), true);
+ gtk_window_set_transient_for (GTK_WINDOW (_box), GTK_WINDOW (parent));
+}
+
+progress_window::~progress_window (void)
+{
+ gtk_widget_destroy (GTK_WIDGET (_btn_cancel));
+ gtk_widget_destroy (GTK_WIDGET (_btn_finish));
+
+ gtk_widget_destroy (GTK_WIDGET (_box));
+}
+
+void
+progress_window::show (void)
+{
+ gtk_widget_show_all (GTK_WIDGET (_box));
+}
+
+void
+progress_window::hide (void)
+{
+ gtk_widget_hide (GTK_WIDGET (_box));
+}
+
+/*! This member function sets the progress indicator to show the
+ percentage of \a progress that has been made in terms of an \a
+ estimated_total.
+
+ In many situations the estimated total can be known beforehand and
+ will not vary, but in some circumstances this estimate may change,
+ or become more precisely known, so that you may want to update the
+ progress indicator even if there was \e no \a progress.
+*/
+void
+progress_window::set_progress (double progress, double estimated_total)
+{
+ if (0 == estimated_total) return;
+
+ int step = _step_count * (progress / estimated_total);
+
+ if (_last_step != step)
+ {
+ gtk_progress_bar_set_fraction (_bar, progress / estimated_total);
+ _last_step = step;
+ }
+}
+
+/*! Changes the text in the message area to an arbitrary \a message.
+ */
+void
+progress_window::set_text (const char *message)
+{
+ gtk_label_set_text (_txt, message);
+}
+
+/*! Changes the text in the message area to one of the "canned"
+ messages and adjusts the control button's look and behaviour as
+ specified in the progress_window::message documentation.
+ */
+void
+progress_window::set_text (message id)
+{
+ _last_step = 0;
+ if (id == _msg) // nothing to do
+ return;
+
+ switch (id) {
+ case WARMING_UP:
+ set_text (_("Scanner is warming up. Please wait..."));
+ break;
+ case PREVIEWING:
+ set_text (_("Pre-scanning in Progress"));
+ break;
+ case SCANNING:
+ set_text (_("Scanning in Progress"));
+ break;
+ case WAITING:
+ set_text (_("Starting a sequence of scans.\n"
+ "Press to scanner's Start button to start each scan."));
+ break;
+ default: // should have been caught by the compiler!
+ throw;
+ }
+
+ if (-1 == _msg) { // we were called by the constructor
+ GtkWidget *lbl = (WAITING == id) ? _btn_finish : _btn_cancel;
+ gtk_container_add (GTK_CONTAINER (_btn), lbl);
+ }
+
+ if (WAITING == id && WAITING != _msg) {
+ flip_label (_btn_cancel, _btn_finish);
+ }
+ if (WAITING != id && WAITING == _msg) {
+ flip_label (_btn_finish, _btn_cancel);
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (_btn), (WARMING_UP != id));
+
+ _msg = id;
+}
+
+/*! Returns true when the user has requested cancellation of the
+ action(s) the object is reporting progress on, false otherwise.
+ */
+bool
+progress_window::is_cancelled (void) const
+{
+ return _can;
+}
+
+/*! Mainly intended for internal use (by the GTK+ callbacks, to be
+ precise), this member function raises a flag indicating that the
+ user requested cancellation of the action(s) that this object is
+ reporting on.
+
+ Cancellation requests can not be revoked.
+
+ \note It is up to the application to check for and honour such
+ requests.
+ */
+void
+progress_window::cancel (void)
+{
+ if (WARMING_UP != _msg)
+ _can = true;
+}
+
+/*! Changes the control button's label \a from one \a to another.
+
+ \internal Just removing the label from its container would result in
+ its destruction unless someone holds onto a reference for the label
+ object. Since we may want to reuse the label, we add a reference
+ before its removal.
+ */
+void
+progress_window::flip_label (GtkWidget *from, GtkWidget *to)
+{
+ gtk_object_ref (GTK_OBJECT (from));
+ gtk_container_remove (GTK_CONTAINER (_btn), from);
+ gtk_container_add (GTK_CONTAINER (_btn), to);
+ gtk_widget_show (to);
+}
+
+
+static
+gint
+_delete (GtkWidget *, progress_window *p)
+{
+ if (p)
+ p->cancel ();
+
+ return true;
+}
+
+static
+void
+_cancel (GtkWidget *, progress_window *p)
+{
+ if (p)
+ p->cancel ();
+}
diff --git a/frontend/pisa_progress_window.h b/frontend/pisa_progress_window.h
new file mode 100644
index 0000000..87ec010
--- /dev/null
+++ b/frontend/pisa_progress_window.h
@@ -0,0 +1,118 @@
+/* pisa_progress_window.h -*- C++ -*-
+ Copyright (C) 2001, 2004 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_PROGRESS_WINDOW_H
+#define ___PISA_PROGRESS_WINDOW_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+//! A progress feedback dialog
+/*! A progress_window provides the user visual feedback about what is
+ going on. It has space for a short to medium length message at the
+ top, a progress indicator in the middle and a control button at the
+ the bottom of the window.
+
+ The message may be changed to anything at any convenient time, but
+ there are also several "canned" messages available. These messages
+ carry some additional logic with them as well.
+
+ The control button can be used to flag cancellation of the action(s)
+ that the dialog is reporting on. However, it is the responsibility
+ of the application to check for and honour such requests.
+ */
+class progress_window
+{
+public:
+ progress_window (GtkWidget *parent = 0);
+ ~progress_window (void);
+
+ //! Makes the progress window visible.
+ void show (void);
+ //! Makes the progress window invisible.
+ void hide (void);
+
+ //! Updates the progress indicator.
+ void set_progress (double progress, double estimated_total);
+
+ //! IDs for "canned" message texts.
+ enum message {
+ /*! This message can be shown whenever it takes a while before the
+ device is ready to carry out an action. Setting this message
+ will disable the control button because things like warming up
+ typically should not be interrupted.
+ */
+ WARMING_UP,
+ /*! This message can be shown during a preview scan. The control
+ button is enabled during a preview.
+ */
+ PREVIEWING,
+ /*! This message can be shown during a normal scan. Just as with
+ a preview scan, the control button is enabled.
+ */
+ SCANNING,
+ /*! This message can be shown when the application is waiting for
+ the user to press a scanner button. The control button will be
+ enabled. Note that it may labelled differently from the other
+ cases.
+ */
+ WAITING
+ };
+
+ //! Updates the message indicating the stage of progress.
+ void set_text (const char *message);
+ //! Sets a "canned" message indicating the stage of progress.
+ void set_text (message id);
+
+ //! Indicates whether the user requested cancellation.
+ bool is_cancelled (void) const;
+
+ //! Flags a cancellation request.
+ void cancel (void);
+
+private:
+ //! Changes the text on the control button.
+ void flip_label (GtkWidget *from, GtkWidget *to);
+
+private:
+ GtkDialog *_box;
+ GtkLabel *_txt; //!< message area
+ GtkProgressBar *_bar; //!< progress indicator
+ GtkButton *_btn; //!< control button
+ GtkWidget *_btn_cancel;
+ GtkWidget *_btn_finish;
+
+ int _msg;
+ bool _can;
+
+ static const int _step_count = 50;
+ int _last_step;
+};
+
+#endif // ___PISA_PROGRESS_WINDOW_H
diff --git a/frontend/pisa_sane_scan.cc b/frontend/pisa_sane_scan.cc
new file mode 100644
index 0000000..7a60ab3
--- /dev/null
+++ b/frontend/pisa_sane_scan.cc
@@ -0,0 +1,1214 @@
+/* pisa_sane_scan.cc
+ Copyright (C) 2001, 2004, 2005, 2008, 2009 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string>
+
+#include "pisa_sane_scan.h"
+#include "pisa_error.h"
+#include "pisa_change_unit.h"
+#include "pisa_enums.h"
+
+
+#define MM_PER_INCH 25.4
+
+#define SCSI_STR "SCSI"
+#define PIO_STR "PIO"
+#define USB_STR "USB"
+
+
+bool
+sane_scan::has_flatbed (void) const
+{
+ return (PISA_OP_FLATBED & support_option);
+}
+
+void
+sane_scan::select_flatbed (void)
+{
+ if (!has_flatbed ()) return;
+
+ _source = PISA_OP_FLATBED;
+ _film = PISA_FT_REFLECT;
+}
+
+bool
+sane_scan::using_flatbed (void) const
+{
+ return (PISA_OP_FLATBED == _source);
+}
+
+bool
+sane_scan::has_adf (void) const
+{
+ return ((PISA_OP_ADF | PISA_OP_ADFDPLX) & support_option);
+}
+
+void
+sane_scan::select_adf (bool duplex)
+{
+ if (!has_duplex ()) duplex = false;
+ if (!has_adf ()) return;
+
+ _source = (duplex ? PISA_OP_ADFDPLX : PISA_OP_ADF);
+ _film = PISA_FT_REFLECT;
+}
+
+bool
+sane_scan::using_adf (void) const
+{
+ return (PISA_OP_ADF == _source || PISA_OP_ADFDPLX == _source);
+}
+
+bool
+sane_scan::has_duplex (void) const
+{
+ return (PISA_OP_ADFDPLX & support_option);
+}
+
+bool
+sane_scan::using_duplex (void) const
+{
+ return (PISA_OP_ADFDPLX == _source);
+}
+
+bool
+sane_scan::has_tpu (void) const
+{
+ return (PISA_OP_TPU & support_option);
+}
+
+void
+sane_scan::select_tpu (bool positive)
+{
+ if (!has_tpu ()) return;
+
+ _source = PISA_OP_TPU;
+ _film = (positive ? PISA_FT_POSI : PISA_FT_NEGA);
+}
+
+bool
+sane_scan::using_tpu (void) const
+{
+ return (PISA_OP_TPU == _source);
+}
+
+bool
+sane_scan::has_dfd (void) const
+{
+ const SANE_Option_Descriptor *sod = NULL;
+ int id = 0;
+
+ try {
+ id = get_option_id ("double-feed-detection-sensitivity");
+ sod = sane_get_option_descriptor (m_hdevice, id);
+ return (sod && !(SANE_CAP_INACTIVE & sod->cap));
+ }
+ catch (pisa_error& e) {
+ if (pisa_error (PISA_ERR_UNSUPPORT) == e) return false;
+ throw e;
+ }
+}
+
+void
+sane_scan::set_dfd (long idx)
+{
+ const SANE_Option_Descriptor *sod = NULL;
+ int id = get_option_id ("double-feed-detection-sensitivity");
+
+ sod = sane_get_option_descriptor (m_hdevice, id);
+ if (!sod
+ || SANE_CAP_INACTIVE & sod->cap
+ || SANE_TYPE_STRING != sod->type
+ || SANE_CONSTRAINT_STRING_LIST != sod->constraint_type)
+ throw pisa_error (PISA_STATUS_UNSUPPORTED);
+
+ const SANE_String_Const *p = sod->constraint.string_list;
+ while (p && *p && ++p)
+ ;
+
+ if (idx < p - sod->constraint.string_list)
+ set_value ("double-feed-detection-sensitivity",
+ sod->constraint.string_list[idx]);
+ else
+ throw pisa_error (PISA_STATUS_UNSUPPORTED);
+}
+
+char
+sane_scan::get_scan_source (void) const
+{
+ if (PISA_OP_NONE == _source)
+ throw pisa_error (PISA_ERR_CONNECT);
+
+ return _source;
+}
+
+char
+sane_scan::get_film_type (void) const
+{
+ return _film;
+}
+
+void
+sane_scan::clear_button_status (void)
+{
+ // We (re)set the scan source. This indirectly resets the scanner's
+ // push button status. I'd like to do something less kludgy but for
+ // the moment can't think of anything better :-(
+ set_scan_source ();
+}
+
+void
+sane_scan::init (void)
+{
+ m_hdevice = 0;
+
+ name = 0;
+ max_resolution = -1;
+
+ _source = PISA_OP_NONE;
+ _film = PISA_FT_REFLECT;
+
+ atexit (sane_exit);
+ sane_init (0, 0);
+}
+
+void
+sane_scan::open_device (char *name)
+{
+ SANE_Status status;
+ char *device_name = 0;
+
+ if (!name)
+ {
+ device_name = new char[64];
+ query_device (device_name);
+ }
+ else
+ device_name = name;
+
+ SANE_Handle device;
+ status = sane_open (device_name, &device);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ if (device_name != name)
+ delete[] device_name;
+ throw pisa_error (status);
+ }
+
+ // we successfully opened a new device, now close whatever we are
+ // hanging onto and rebind to the newly opened device
+
+ close_device ();
+ m_hdevice = device;
+
+ get_scanner_info (device_name);
+
+ if (device_name != name)
+ delete[] device_name;
+}
+
+void
+sane_scan::close_device (void)
+{
+ if (m_hdevice)
+ sane_close (m_hdevice);
+
+ m_hdevice = 0;
+}
+
+void
+sane_scan::set_scan_source (void)
+{
+ if (!m_hdevice)
+ throw pisa_error (PISA_ERR_CONNECT);
+
+ char source[32];
+
+ if (using_flatbed ())
+ strcpy (source, "Flatbed");
+ else if (using_adf ())
+ strcpy (source, "Automatic Document Feeder");
+ else if (using_tpu ())
+ strcpy (source, "Transparency Unit");
+ else
+ throw pisa_error (PISA_ERR_PARAMETER);
+
+ set_value (SANE_NAME_SCAN_SOURCE, source);
+
+ if (using_adf ())
+ {
+ char mode[16];
+
+ strcpy (mode, (using_duplex () ? "Duplex" : "Simplex"));
+ set_value ("adf-mode", mode);
+ }
+
+ max_resolution = -1;
+}
+
+void
+sane_scan::set_film_type (void)
+{
+ char film_type[32];
+
+ switch (_film)
+ {
+ case PISA_FT_POSI:
+ strcpy (film_type, "Positive Film");
+ break;
+
+ case PISA_FT_NEGA:
+ strcpy (film_type, "Negative Film");
+ break;
+
+ case PISA_FT_REFLECT:
+ return;
+ }
+
+ set_value ("film-type", (void *) film_type);
+}
+
+void
+sane_scan::get_current_max_size (double *width, double *height)
+{
+ SANE_Int max_x = 0;
+ SANE_Int max_y = 0;
+
+ if (!m_hdevice)
+ throw pisa_error ( PISA_ERR_CONNECT);
+
+ set_scan_source ();
+ set_film_type ();
+
+ int opt_id = 0;
+ const SANE_Option_Descriptor *opt_desc = NULL;
+
+ opt_id = get_option_id (SANE_NAME_SCAN_BR_X);
+ opt_desc = sane_get_option_descriptor (m_hdevice, opt_id);
+ if (!opt_desc || SANE_CONSTRAINT_RANGE != opt_desc->constraint_type)
+ throw pisa_error (PISA_STATUS_UNSUPPORTED);
+
+ max_x = opt_desc->constraint.range->max;
+
+ opt_id = get_option_id (SANE_NAME_SCAN_BR_Y);
+ opt_desc = sane_get_option_descriptor (m_hdevice, opt_id);
+ if (!opt_desc || SANE_CONSTRAINT_RANGE != opt_desc->constraint_type)
+ throw pisa_error (PISA_STATUS_UNSUPPORTED);
+
+ max_y = opt_desc->constraint.range->max;
+
+ *width = SANE_UNFIX (max_x) / MM_PER_INCH;
+ *height = SANE_UNFIX (max_y) / MM_PER_INCH;
+}
+
+
+void
+sane_scan::get_color_profile (double *coef) const
+{
+ SANE_Word value;
+ char option_name[16];
+ int i;
+
+ if (!m_hdevice)
+ throw pisa_error (PISA_ERR_CONNECT);
+
+ for (i = 0; i < 9; i++)
+ {
+ sprintf (option_name, "cct-%d", i + 1);
+ get_value (option_name, (void *) &value);
+ coef[i] = SANE_UNFIX (value);
+ }
+}
+
+void
+sane_scan::start_scan (int * width, int * height)
+{
+ SANE_Status status;
+
+ if (! m_hdevice)
+ throw pisa_error (PISA_ERR_CONNECT);
+
+ if (SANE_STATUS_GOOD != (status = sane_start (m_hdevice)))
+ {
+ pisa_error oops (status, *this);
+ if (!using_adf ()
+ || SANE_STATUS_NO_DOCS != status) sane_cancel (m_hdevice);
+ throw oops;
+ }
+
+ if (SANE_STATUS_GOOD != (status = sane_get_parameters (m_hdevice,
+ &m_sane_para)))
+ {
+ pisa_error oops (status, *this);
+ sane_cancel (m_hdevice);
+ throw oops;
+ }
+
+ *width = m_sane_para.pixels_per_line;
+ *height = m_sane_para.lines;
+
+ m_rows = 0;
+}
+
+SANE_Status
+sane_scan::acquire_image (unsigned char *img, int row_bytes,
+ int height, int cancel)
+{
+ if (!m_hdevice)
+ throw pisa_error (PISA_ERR_CONNECT);
+
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char *cur_pos = img;
+
+ for (int i = 0; i < height; i++, cur_pos += row_bytes)
+ {
+ m_rows++;
+ if (cancel)
+ {
+ sane_cancel (m_hdevice);
+ return SANE_STATUS_CANCELLED;
+ }
+
+ // The SANE standard does not promise to return as much data as
+ // we request, so we keep asking until we got all that we want.
+
+ int cnt = row_bytes;
+ int len = 0;
+ while (SANE_STATUS_GOOD == status && cnt > 0)
+ {
+ status = sane_read (m_hdevice, cur_pos + (row_bytes - cnt),
+ cnt, &len);
+
+ cnt -= len;
+ }
+
+ if (status == SANE_STATUS_EOF)
+ break;
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ throw pisa_error (status, *this);
+ }
+
+ }
+
+ return status;
+}
+
+/*! \brief Returns the largest resolution not larger than a \a cutoff.
+
+ Returns the largest supported hardware resolution that does not
+ exceed the \a cutoff. Passing a negative value for the \a cutoff,
+ which is the default, returns the maximum resolution supported by
+ the hardware.
+
+ Throws an unsupported exception when no suitable resolution can be
+ determined.
+ */
+long
+sane_scan::get_max_resolution (long cutoff) const
+{
+ if (0 > cutoff // use cached value if possible
+ && 0 < max_resolution)
+ return max_resolution;
+
+ long result = -1;
+
+ try
+ {
+ long r;
+
+ r = get_max_resolution (SANE_NAME_SCAN_X_RESOLUTION, cutoff);
+ if (r > result) result = r;
+ r = get_max_resolution (SANE_NAME_SCAN_Y_RESOLUTION, cutoff);
+ if (r > result) result = r;
+ }
+ catch (const pisa_error& e)
+ {
+ pisa_error expected (PISA_STATUS_UNSUPPORTED);
+
+ if (e != expected) throw;
+ }
+
+ if (0 > result)
+ throw pisa_error (PISA_STATUS_UNSUPPORTED);
+
+ if (0 > cutoff // update cached value if necessary
+ && 0 > max_resolution)
+ const_cast <sane_scan *> (this)->max_resolution = result;
+
+ return result;
+}
+
+long
+sane_scan::get_max_resolution (SANE_String_Const option_name,
+ long cutoff) const
+{
+ int opt_id = get_option_id (option_name);
+
+ const SANE_Option_Descriptor
+ *opt_desc = sane_get_option_descriptor (m_hdevice, opt_id);
+
+ if (!opt_desc || SANE_TYPE_INT != opt_desc->type)
+ throw pisa_error (PISA_STATUS_UNSUPPORTED);
+
+ long result = -1;
+
+ switch (opt_desc->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ {
+ const SANE_Range *const range = opt_desc->constraint.range;
+
+ if (0 > cutoff) cutoff = range->max;
+ if (0 == range->quant)
+ {
+ result = cutoff;
+ }
+ else
+ { // relies on integer arithmetic
+ result = (((cutoff - range->min) / range->quant)
+ * range->quant) + range->min;
+ }
+ break;
+ }
+ case SANE_CONSTRAINT_WORD_LIST:
+ { // assumes list is in ascending order
+ const SANE_Word *list = opt_desc->constraint.word_list;
+
+ size_t last = list[0];
+
+ if (0 > cutoff) cutoff = list[last];
+ while (0 < last && cutoff < list[last])
+ {
+ --last;
+ }
+ if (0 < last) result = list[last];
+ break;
+ }
+ default:
+ throw pisa_error (PISA_STATUS_UNSUPPORTED);
+ }
+
+ if (0 > result)
+ throw pisa_error (PISA_STATUS_UNSUPPORTED);
+
+ return result;
+}
+
+long
+sane_scan::get_resolution (SANE_String_Const scan_direction,
+ int min_res) const
+{
+ SANE_Int def_res_tbl[] = { 75, 150, 300, 600, 1200 };
+ SANE_Int def_res_cnt = 4;
+
+ const SANE_Int *res_tbl;
+ SANE_Int res_cnt;
+
+ { // initialize resolution table
+ int opt_id = get_option_id (scan_direction);
+ const SANE_Option_Descriptor *opt_desc
+ = sane_get_option_descriptor (m_hdevice, opt_id);
+
+ if (opt_desc && SANE_CONSTRAINT_RANGE == opt_desc->constraint_type)
+ {
+ long res = min_res;
+ long min = opt_desc->constraint.range->min;
+ long max = opt_desc->constraint.range->max;
+
+ if (res < min) res = min;
+ if (res > max) res = max;
+
+ return res;
+ }
+ else if (opt_desc
+ && SANE_TYPE_INT == opt_desc->type
+ && SANE_CONSTRAINT_WORD_LIST == opt_desc->constraint_type)
+ {
+ res_cnt = opt_desc->constraint.word_list[0];
+ res_tbl = &(opt_desc->constraint.word_list[1]);
+ }
+ else
+ {
+ res_cnt = def_res_cnt;
+ res_tbl = def_res_tbl;
+
+ if (1200 == get_max_resolution ())
+ ++res_cnt;
+ }
+ }
+
+ { // find appropriate resolution
+ int i = 0;
+ while (i < res_cnt && res_tbl[i] < min_res)
+ {
+ ++i;
+ }
+ return (res_cnt == i ? 0 : res_tbl[i]);
+ }
+}
+
+void
+sane_scan::get_scanner_info (const char *dev_name)
+{
+ int i, opt_id;
+ const SANE_Option_Descriptor *opt_desc;
+ const char *fbf = "Flatbed";
+ const char *tpu = "Transparency Unit";
+ const char *adf = "Automatic Document Feeder";
+
+ if (dev_name)
+ {
+ char *copy = strdup (dev_name);
+ if (!copy)
+ throw pisa_error (PISA_STATUS_NO_MEM);
+
+ if (name)
+ free (name);
+ name = copy;
+ }
+ support_option = 0;
+ opt_id = get_option_id (SANE_NAME_SCAN_SOURCE);
+ opt_desc = sane_get_option_descriptor (m_hdevice, opt_id);
+
+ if (opt_desc->type != SANE_TYPE_STRING ||
+ opt_desc->constraint_type != SANE_CONSTRAINT_STRING_LIST)
+ throw pisa_error (PISA_ERR_CONNECT);
+
+ for (i = 0; opt_desc->constraint.string_list[i]; i++)
+ {
+ if (0 == strcmp (opt_desc->constraint.string_list[i], fbf))
+ {
+ support_option |= PISA_OP_FLATBED;
+ }
+ if (0 == strcmp (opt_desc->constraint.string_list[i], tpu))
+ {
+ support_option |= PISA_OP_TPU;
+ }
+ if (0 == strcmp (opt_desc->constraint.string_list[i], adf))
+ {
+ support_option |= PISA_OP_ADF;
+ }
+ }
+
+ if (PISA_OP_NONE == _source)
+ {
+ if (has_flatbed ())
+ _source = PISA_OP_FLATBED;
+ else if (has_adf ())
+ _source = PISA_OP_ADF;
+ else if (has_tpu ())
+ _source = PISA_OP_TPU;
+ else
+ throw pisa_error (PISA_ERR_CONNECT);
+ }
+
+ if (has_adf ())
+ {
+ char old = _source;
+ _source = PISA_OP_ADF;
+ set_scan_source ();
+ if (is_activate ("adf-mode"))
+ support_option |= PISA_OP_ADFDPLX;
+ _source = old;
+ }
+ set_scan_source ();
+
+ max_resolution = get_max_resolution ();
+}
+
+void
+sane_scan::query_device (char *device_name) const
+{
+ const SANE_Device ** ppdevice_list;
+ SANE_Status status;
+ int i;
+
+ status = sane_get_devices (& ppdevice_list, SANE_TRUE);
+
+ if (status != SANE_STATUS_GOOD)
+ throw pisa_error (status);
+
+ if (*ppdevice_list == 0 ||
+ ppdevice_list[0]->vendor == 0)
+ throw pisa_error (PISA_ERR_CONNECT);
+
+ for (i = 0; ppdevice_list[i]; i++)
+ {
+ if (0 == strcasecmp (ppdevice_list[i]->vendor, "epson"))
+ {
+ strcpy (device_name, ppdevice_list[i]->name);
+ return;
+ }
+ }
+
+ throw pisa_error (PISA_ERR_CONNECT);
+}
+
+void
+sane_scan::finish_acquire (bool eject)
+{
+ if (eject && using_adf ())
+ {
+ set_value ("eject", NULL);
+ }
+ sane_cancel (m_hdevice);
+}
+
+int
+sane_scan::is_activate (const char *option_name) const
+{
+ const SANE_Option_Descriptor * opt_desc;
+ SANE_Int num_dev_options;
+ SANE_Status status;
+ int i;
+ int ret;
+
+ ret = 0;
+ status = sane_control_option (m_hdevice, 0,
+ SANE_ACTION_GET_VALUE,
+ &num_dev_options,
+ 0);
+
+ if (status != SANE_STATUS_GOOD)
+ throw pisa_error (status, *this);
+
+ for (i = 0; i < num_dev_options; i++)
+ {
+ opt_desc = sane_get_option_descriptor (m_hdevice, i);
+
+ if (opt_desc->name && strcmp (opt_desc->name, option_name) == 0)
+ {
+ if (SANE_OPTION_IS_ACTIVE (opt_desc->cap))
+ {
+ ret = 1;
+ break;
+ }
+ else
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int
+sane_scan::get_option_id (const char *option_name) const
+{
+ const SANE_Option_Descriptor * opt_desc;
+ SANE_Int num_dev_options;
+ SANE_Status status;
+ int i;
+
+ status = sane_control_option (m_hdevice, 0,
+ SANE_ACTION_GET_VALUE,
+ &num_dev_options,
+ 0);
+
+ if (status != SANE_STATUS_GOOD)
+ throw pisa_error (status, *this);
+
+ for (i = 0; i < num_dev_options; i++)
+ {
+ opt_desc = sane_get_option_descriptor (m_hdevice, i);
+
+ if (opt_desc->name && strcmp (opt_desc->name, option_name) == 0)
+ return i; // found
+ }
+
+ throw pisa_error (PISA_ERR_UNSUPPORT);
+}
+
+void
+sane_scan::set_value (const char * option_name, const void * value)
+{
+ SANE_Status status;
+ int option_id;
+
+ option_id = get_option_id (option_name);
+
+ status = sane_control_option (m_hdevice,
+ option_id,
+ SANE_ACTION_SET_VALUE,
+ const_cast<void *>(value),
+ 0);
+
+ if (status != SANE_STATUS_GOOD)
+ throw pisa_error (status, *this);
+}
+
+void
+sane_scan::get_value (const char *option_name, void *value, bool nothrow) const
+{
+ SANE_Status status;
+ int option_id;
+
+ try
+ {
+ option_id = get_option_id (option_name);
+ }
+ catch (pisa_error& oops)
+ {
+ if (!nothrow) throw oops;
+ return;
+ }
+
+ status = sane_control_option (m_hdevice, option_id,
+ SANE_ACTION_GET_VALUE,
+ value,
+ 0);
+
+ if (!nothrow && status != SANE_STATUS_GOOD)
+ throw pisa_error (status, *this);
+}
+
+void
+sane_scan::get_range (const char *option_name,
+ float *max, float *min, float *step)
+{
+ const SANE_Option_Descriptor *opt_desc = NULL;
+ int opt_id = 0;
+
+ if (!option_name)
+ {
+ throw pisa_error (PISA_ERR_PARAMETER);
+ }
+ if (!m_hdevice)
+ {
+ throw pisa_error (PISA_ERR_CONNECT);
+ }
+ opt_id = get_option_id (option_name);
+ opt_desc = sane_get_option_descriptor (m_hdevice, opt_id);
+ if (!opt_desc
+ || SANE_CONSTRAINT_RANGE != opt_desc->constraint_type)
+ {
+ throw pisa_error (PISA_STATUS_UNSUPPORTED);
+ }
+
+ if ( SANE_TYPE_FIXED == opt_desc->type )
+ {
+ if (max)
+ {
+ *max = SANE_UNFIX (opt_desc->constraint.range->max);
+ }
+ if (min)
+ {
+ *min = SANE_UNFIX (opt_desc->constraint.range->min);
+ }
+ if (step)
+ {
+ *step = SANE_UNFIX (opt_desc->constraint.range->quant);
+ }
+ }
+ else
+ {
+ if (max)
+ {
+ *max = opt_desc->constraint.range->max;
+ }
+ if (min)
+ {
+ *min = opt_desc->constraint.range->min;
+ }
+ if (step)
+ {
+ *step = opt_desc->constraint.range->quant;
+ }
+ }
+}
+
+void
+sane_scan::set_preview (bool value)
+{
+ SANE_Bool v = value;
+ set_value ("preview", &v);
+}
+
+bool
+sane_scan::has_preview (void) const
+{
+ return is_activate ("preview");
+}
+
+void
+sane_scan::set_focus (long position)
+{
+ char focus[32];
+
+ if (0 == is_activate ("focus-position"))
+ return;
+
+ if (position == 25)
+ strcpy (focus, "Focus 2.5mm above glass");
+ else
+ strcpy (focus, "Focus on glass");
+
+ set_value ("focus-position", (void *) focus);
+}
+
+void
+sane_scan::set_speed (long speed)
+{
+ SANE_Bool value;
+
+ if (0 == is_activate ("speed"))
+ return;
+
+ if (speed == 1)
+ value = SANE_TRUE;
+ else
+ value = SANE_FALSE;
+
+ set_value ("speed", (void *) & value);
+}
+
+bool
+sane_scan::has_zoom (void) const
+{
+ return is_activate ("zoom");
+}
+
+bool
+sane_scan::has_focus (void) const
+{
+ return is_activate ("focus-position");
+
+}
+
+bool
+sane_scan::has_draft_mode (void) const
+{
+ return is_activate ("speed");
+}
+
+bool
+sane_scan::get_size_check (void) const
+{
+ SANE_Bool value;
+ get_value ("detect-doc-size", &value);
+ return value;
+}
+
+void
+sane_scan::set_size_check (bool value)
+{
+ SANE_Bool v = value;
+ set_value ("detect-doc-size", &v);
+}
+
+bool
+sane_scan::has_size_check (void) const
+{
+ return is_activate ("detect-doc-size");
+}
+
+bool
+sane_scan::get_autocrop (void) const
+{
+ SANE_Bool value;
+ get_value ("autocrop", &value);
+ return value;
+}
+
+void
+sane_scan::set_autocrop (bool value)
+{
+ SANE_Bool v = value;
+ set_value ("autocrop", &v);
+}
+
+bool
+sane_scan::has_autocrop (void) const
+{
+ return is_activate ("autocrop");
+}
+
+bool
+sane_scan::get_deskew (void) const
+{
+ SANE_Bool value;
+ get_value ("deskew", &value);
+ return value;
+}
+
+void
+sane_scan::set_deskew (bool value)
+{
+ SANE_Bool v = value;
+ set_value ("deskew", &v);
+}
+
+bool
+sane_scan::has_deskew (void) const
+{
+ return is_activate ("deskew");
+}
+
+bool
+sane_scan::has_start_button (void) const
+{
+ return is_activate ("monitor-button");
+}
+
+bool
+sane_scan::is_button_pressed (void) const
+{
+ SANE_Bool value = false;
+
+ get_value ("monitor-button", (void *) &value);
+
+ return value;
+}
+
+long
+sane_scan::get_polling_time (void) const
+{
+ SANE_Word value = 0;
+
+ get_value ("polling-time", (void *) &value);
+
+ return value;
+}
+
+void
+sane_scan::disable_wait_for_button (void)
+{
+ SANE_Bool value = false;
+ SANE_Bool off = false;
+ if (is_activate ("wait-for-button"))
+ {
+ get_value ("wait-for-button", &value);
+ }
+ if (value)
+ {
+ set_value ("wait-for-button", &off);
+ }
+}
+
+void
+sane_scan::set_color_mode (char pixeltype, char bitdepth)
+{
+ char color_mode[32];
+ SANE_Word depth;
+
+ switch (pixeltype)
+ {
+ case PISA_PT_RGB:
+ strcpy (color_mode, "Color");
+ break;
+
+ case PISA_PT_GRAY:
+ strcpy (color_mode, "Gray");
+ break;
+
+ case PISA_PT_BW:
+ strcpy (color_mode, "Binary");
+ break;
+ }
+
+ switch (bitdepth)
+ {
+ case PISA_BD_1:
+ depth = 1;
+ break;
+
+ case PISA_BD_8:
+ depth = 8;
+ break;
+ }
+
+ if (is_activate (SANE_NAME_SCAN_MODE))
+ {
+ set_value (SANE_NAME_SCAN_MODE, (void *) color_mode);
+ }
+ if (is_activate (SANE_NAME_BIT_DEPTH))
+ {
+ set_value (SANE_NAME_BIT_DEPTH, (void *) &depth);
+ }
+}
+
+void
+sane_scan::set_gamma_table (const unsigned char *gamma_table)
+{
+ SANE_Word table[256];
+ int i;
+ char user_defined[32];
+
+ strcpy (user_defined, "User defined (Gamma=1.8)");
+
+ set_value ("gamma-correction", (void *) user_defined);
+
+ // red
+ for (i = 0; i < 256; i++)
+ table[i] = gamma_table[256 * 0 + i];
+ set_value (SANE_NAME_GAMMA_VECTOR_R, (void *) table);
+
+ // green
+ for (i = 0; i < 256; i++)
+ table[i] = gamma_table[256 * 1 + i];
+ set_value (SANE_NAME_GAMMA_VECTOR_G, (void *) table);
+
+ // blue
+ for (i = 0; i < 256; i++)
+ table[i] = gamma_table[256 * 2 + i];
+ set_value (SANE_NAME_GAMMA_VECTOR_B, (void *) table );
+}
+
+void
+sane_scan::set_threshold (long threshold)
+{
+ set_value (SANE_NAME_THRESHOLD, (void *) & threshold);
+}
+
+void
+sane_scan::set_color_profile (const double * coef)
+{
+ SANE_Word value;
+ char option_name[16];
+ int i;
+ char user_defined[32];
+
+ strcpy (user_defined, "User defined");
+
+ set_value ("color-correction", (void *) user_defined);
+
+ for (i = 0; i < 9; i++)
+ {
+ sprintf (option_name, "cct-%d", i + 1);
+ value = SANE_FIX (coef[i]);
+ set_value (option_name, (void *) &value);
+ }
+}
+
+void
+sane_scan::set_brightness (long brightness)
+{
+ set_value (SANE_NAME_BRIGHTNESS, (void *) & brightness);
+}
+
+bool
+sane_scan::has_brightness (void) const
+{
+ return is_activate (SANE_NAME_BRIGHTNESS);
+}
+
+void
+sane_scan::set_contrast (long contrast)
+{
+ set_value (SANE_NAME_CONTRAST, (void *) & contrast);
+}
+
+bool
+sane_scan::has_contrast (void) const
+{
+ return is_activate (SANE_NAME_CONTRAST);
+}
+
+void
+sane_scan::set_brightness_method (br_method_val val)
+{
+ char method[32];
+
+ switch (val)
+ {
+ case br_gimp:
+ strcpy (method, "gimp");
+ break;
+ case br_iscan:
+ default:
+ strcpy (method, "iscan");
+ break;
+ }
+
+ set_value ("brightness-method", (void *) method);
+}
+
+void
+sane_scan::set_scan_resolution (int resolution_x, int resolution_y)
+{
+ set_value (SANE_NAME_SCAN_X_RESOLUTION, (void *) &resolution_x);
+ set_value (SANE_NAME_SCAN_Y_RESOLUTION, (void *) &resolution_y);
+}
+
+void
+sane_scan::set_scan_zoom (int zoom_x, int zoom_y)
+{
+ set_value ("zoom", (void *) & zoom_x);
+
+ zoom_y = zoom_y;
+}
+
+void
+sane_scan::set_scan_area (double offset_x, double offset_y,
+ double width, double height)
+{
+ SANE_Word value;
+ double tmp;
+
+ value = SANE_FIX (offset_x * MM_PER_INCH);
+ set_value (SANE_NAME_SCAN_TL_X, (void *) &value);
+
+ value = SANE_FIX (offset_y * MM_PER_INCH);
+ set_value (SANE_NAME_SCAN_TL_Y, (void *) &value);
+
+ tmp = offset_x + width;
+ value = SANE_FIX (tmp * MM_PER_INCH);
+ set_value (SANE_NAME_SCAN_BR_X, (void *) &value);
+
+ tmp = offset_y + height;
+ value = SANE_FIX (tmp * MM_PER_INCH);
+ set_value (SANE_NAME_SCAN_BR_Y, (void *) &value);
+}
+
+bool
+sane_scan::has_clean (void) const
+{
+ return is_activate ("clean");
+}
+
+bool
+sane_scan::has_calibrate (void) const
+{
+ return is_activate ("calibrate");
+}
+
+void
+sane_scan::clean (void)
+{
+ set_value ("clean", NULL);
+}
+
+void
+sane_scan::calibrate (void)
+{
+ set_value ("calibrate", NULL);
+}
diff --git a/frontend/pisa_sane_scan.h b/frontend/pisa_sane_scan.h
new file mode 100644
index 0000000..a6efdfa
--- /dev/null
+++ b/frontend/pisa_sane_scan.h
@@ -0,0 +1,172 @@
+/* pisa_sane_scan.h -*- C++ -*-
+ Copyright (C) 2001, 2004, 2008, 2009 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_SANE_SCAN_H
+#define ___PISA_SANE_SCAN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sane/sane.h>
+#include <sane/saneopts.h>
+
+#undef SANE_NAME_SCAN_X_RESOLUTION
+#define SANE_NAME_SCAN_X_RESOLUTION "x-resolution"
+
+#ifdef __cplusplus
+}
+#endif
+
+enum br_method_val
+{
+ br_iscan,
+ br_gimp
+};
+
+class sane_scan
+{
+public:
+ bool has_flatbed (void) const;
+ void select_flatbed (void);
+ bool using_flatbed (void) const;
+
+ bool has_adf (void) const;
+ void select_adf (bool duplex);
+ bool using_adf (void) const;
+
+ bool has_duplex (void) const;
+ bool using_duplex (void) const;
+
+ bool has_tpu (void) const;
+ void select_tpu (bool positive);
+ bool using_tpu (void) const;
+
+ bool has_dfd (void) const;
+ void set_dfd (long idx);
+
+ char get_scan_source (void) const;
+ char get_film_type (void) const;
+
+ void clear_button_status (void);
+
+
+ void close_device (void);
+
+ // queries
+ void get_current_max_size (double *width, double *height);
+ long get_max_resolution (long cutoff = -1) const;
+ void set_scan_resolution (int resolution_x, int resolution_y);
+ void get_color_profile (double *coef) const;
+ bool has_zoom (void) const;
+ bool has_focus (void) const;
+ bool has_draft_mode (void) const;
+ bool get_size_check (void) const;
+ void set_size_check (bool value);
+ bool has_size_check (void) const;
+ bool get_autocrop (void) const;
+ void set_autocrop (bool value);
+ bool has_autocrop (void) const;
+ bool has_start_button (void) const;
+ bool get_deskew (void) const;
+ void set_deskew (bool value);
+ bool has_deskew (void) const;
+ void set_brightness (long brightness);
+ bool has_brightness (void) const;
+ void set_contrast (long contrast);
+ bool has_contrast (void) const;
+
+ void set_brightness_method (br_method_val val);
+ void set_color_mode (char pixeltype, char bitdepth);
+
+ bool is_button_pressed (void) const;
+ long get_polling_time (void) const;
+
+ void disable_wait_for_button (void);
+
+ void finish_acquire (bool eject = false);
+
+ void get_value (const char *option_name, void *value,
+ bool nothrow = false) const;
+
+ void get_range (const char *option_name,
+ float *max, float *min, float *step);
+
+ void set_preview (bool value);
+ bool has_preview (void) const;
+
+ bool has_clean (void) const;
+ bool has_calibrate (void) const;
+ void clean (void);
+ void calibrate (void);
+
+protected:
+
+ // mutators
+ void set_scan_source (void);
+ void set_film_type (void);
+
+ void init (void);
+ void open_device (char *name = 0);
+
+ void start_scan (int *width, int *height);
+ SANE_Status acquire_image (unsigned char *img, int row_bytes, int height,
+ int cancel);
+
+ int is_activate (const char *option_name) const;
+ long get_resolution (SANE_String_Const direction, int min_res) const;
+ long get_max_resolution (const char *option_name, long cutoff = -1) const;
+ int get_option_id (const char *option_name) const;
+
+ void set_value (const char *option_name, const void *value);
+
+ void query_device (char *device_name) const;
+ void get_scanner_info (const char *name = 0);
+
+ void set_color_profile (const double *coef);
+ void set_focus (long position);
+ void set_gamma_table (const unsigned char *gamma_table);
+ void set_scan_area (double offset_x, double offset_y,
+ double width, double height);
+ void set_scan_zoom (int zoom_x, int zoom_y);
+ void set_speed (long speed);
+ void set_threshold (long threshold);
+
+private:
+ SANE_Handle m_hdevice;
+ SANE_Parameters m_sane_para;
+ long m_rows;
+
+ char * name;
+ char support_option;
+ long max_resolution;
+
+ char _source;
+ char _film;
+};
+
+
+#endif // ___PISA_SANE_SCAN_H
diff --git a/frontend/pisa_scan_manager.cc b/frontend/pisa_scan_manager.cc
new file mode 100644
index 0000000..479fbb4
--- /dev/null
+++ b/frontend/pisa_scan_manager.cc
@@ -0,0 +1,1003 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005, 2008, 2009 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+/*------------------------------------------------------------*/
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <new>
+using std::bad_alloc;
+
+
+/*------------------------------------------------------------*/
+#include "pisa_scan_manager.h"
+#include "pisa_error.h"
+#include "pisa_scan_tool.h"
+#include "pisa_change_unit.h"
+
+/*------------------------------------------------------------*/
+#define SCALING_INPUT_WIDTH_LIMIT 21840
+
+/*------------------------------------------------------------*/
+void scan_manager::open_device ( char * name )
+{
+ sane_scan::init ( );
+
+ m_resize_cls = 0;
+ m_moire_cls = 0;
+ m_sharp_cls = 0;
+ m_resize_img_info = 0;
+ m_moire_img_info = 0;
+ m_sharp_img_info = 0;
+
+ _has_prev_img = false;
+
+ sane_scan::open_device (name);
+ _max_descreen_resolution = get_max_resolution (has_zoom () ? 800 : 600);
+}
+
+void
+scan_manager::set_scan_parameters (bool is_preview)
+{
+ for (int i = 0; i < 2; ++i)
+ {
+ adjust_scan_param (&_resolution[i], &_zoom[i]);
+ }
+ init_img_process_info ();
+ update_settings (is_preview);
+}
+
+void
+scan_manager::start_scan (int *width, int *height)
+{
+ sane_scan::start_scan (width, height);
+ modify_img_info (width, height);
+ create_img_cls ();
+}
+
+void
+scan_manager::init_preview (int *width, int *height)
+{
+ set_scan_parameters (true);
+ start_scan (width, height);
+}
+
+void
+scan_manager::init_scan (int *width, int *height, bool reset_params)
+{
+ if (reset_params) set_scan_parameters ();
+
+ if (area_is_too_large ())
+ {
+ if (getenv ("ISCAN_DEBUG"))
+ {
+ fprintf (stderr, "Backend will try to scan with these settings\n");
+ fprintf (stderr, "Resolution (main,sub): %ld,%ld\n",
+ _resolution[0], _resolution[1]);
+ fprintf (stderr, "Zoom (main,sub): %ld,%ld\n",
+ _zoom[0], _zoom[1]);
+ fprintf (stderr, "Offset (main,sub): %ld,%ld\n",
+ inch2pixel (_offset[0], _resolution[0], _zoom[0]),
+ inch2pixel (_offset[1], _resolution[1], _zoom[1]));
+ fprintf (stderr, "Area (main,sub): %ld,%ld\n",
+ inch2pixel (true , _area[0], _resolution[0], _zoom[0]),
+ inch2pixel (false, _area[1], _resolution[1], _zoom[1]));
+ }
+
+ throw pisa_error (PISA_ERR_AREALARGE);
+ }
+
+ start_scan (width, height);
+}
+
+/*------------------------------------------------------------*/
+void scan_manager::acquire_image ( unsigned char * img,
+ int row_bytes,
+ int height,
+ int cancel )
+{
+ if ( m_resize || m_moire || m_sharp )
+ {
+ int in_rowbytes = 0, in_line;
+ unsigned char * in_img = 0;
+
+ if ( m_sharp )
+ in_rowbytes = m_sharp_info.in_rowbytes;
+ if ( m_moire )
+ in_rowbytes = m_moire_info.in_rowbytes;
+ if ( m_resize )
+ in_rowbytes = m_resize_info.in_rowbytes;
+
+ if ( cancel || img == 0 )
+ {
+ if (0 < row_bytes)
+ {
+ // Run kludge to trigger a SANE_STATUS_EOF return value
+ // on sane_read() in sane_scan::acquire_image(), as per
+ // SANE specification requirements.
+ // Fixes problems when used with `net` backend.
+
+ unsigned char b[512];
+ SANE_Status s = SANE_STATUS_GOOD;
+ while (SANE_STATUS_GOOD == s)
+ {
+ s = sane_scan::acquire_image (b, sizeof (b), 1, cancel);
+ }
+ return;
+ }
+ else
+ {
+ sane_scan::acquire_image (0, 0, height, cancel);
+ }
+ }
+ else
+ {
+ in_line = get_send_in_line ( 1 );
+
+ if ( 0 < in_line )
+ {
+ int buf_size = in_line * in_rowbytes;
+
+ in_img = new unsigned char [ buf_size + in_rowbytes ];
+
+ if ( in_img == 0 )
+ {
+ sane_scan::acquire_image ( 0, 0, 1, 1 );
+
+ throw pisa_error ( PISA_ERR_OUTOFMEMORY );
+ }
+
+ sane_scan::acquire_image ( in_img,
+ in_rowbytes,
+ in_line,
+ cancel );
+ }
+ }
+
+ ::memset ( img, 128, row_bytes );
+ if ( cancel == 0 && PISA_ERR_SUCCESS != image_process ( in_img, img ) )
+ {
+ delete [ ] in_img;
+ sane_scan::acquire_image ( 0, 0, 1, 1 );
+ throw pisa_error ( PISA_ERR_OUTOFMEMORY );
+ }
+
+ if ( in_img )
+ delete [ ] in_img;
+ }
+ else
+ sane_scan::acquire_image ( img, row_bytes, height, cancel );
+}
+
+int
+scan_manager::init_img_process_info ()
+{
+ // set flag
+ if (!has_zoom ())
+ m_resize = 1;
+ else
+ m_resize = 0;
+
+ if (_de_screening && !get_size_check()) // color or B/W document
+ {
+ m_moire = 1;
+ m_resize = 0;
+ }
+ else
+ m_moire = 0;
+
+ m_sharp = _usm;
+
+ if ( m_resize )
+ init_zoom (&m_resize_info);
+
+ if ( m_moire )
+ init_moire (&m_moire_info);
+
+ if ( m_sharp )
+ init_sharp (&m_sharp_info);
+
+ return PISA_ERR_PARAMETER;
+}
+
+int
+scan_manager::init_zoom (resize_img_info *info)
+{
+ long act_res[2];
+
+ info->resolution = _resolution[0];
+
+ for (int i = 0; i < 2; ++i)
+ {
+ act_res[i] = _resolution[i] * _zoom[i] / 100;
+ }
+
+ info->out_width = ::inch2width (_area[0], act_res[0]);
+ info->out_height = ::inch2height (_area[1], act_res[1]);
+ info->out_rowbytes = calc_rowbytes (info->out_width,
+ static_cast <pisa_pixel_type>
+ (_pixeltype));
+
+ for (int i = 0; i < 2; ++i)
+ {
+ _resolution[i] = act_res[i];
+ _zoom[i] = 100;
+ }
+
+ try
+ {
+ get_valid_resolution (&_resolution[0], &_resolution[1], true);
+ }
+ catch (const pisa_error& e)
+ {
+ if (pisa_error (PISA_STATUS_UNSUPPORTED) != e)
+ throw (e);
+ }
+
+ if ( act_res[0] == _resolution[0]
+ && act_res[1] == _resolution[1])
+ {
+ m_resize = 0;
+ return PISA_ERR_SUCCESS;
+ }
+
+ info->in_width = ::inch2width (_area[0], _resolution[0]);
+ info->in_height = ::inch2height (_area[1], _resolution[1]);
+ info->in_rowbytes = calc_rowbytes (info->in_width,
+ static_cast <pisa_pixel_type>
+ (_pixeltype));
+
+ info->bits_per_pixel = calc_bitperpix (static_cast <pisa_pixel_type>
+ (_pixeltype),
+ static_cast <pisa_bitdepth_type>
+ (_bitdepth));
+
+ if (_pixeltype == PISA_PT_BW)
+ info->resize_flag = PISA_RS_NN;
+ else
+ info->resize_flag = PISA_RS_BC;
+
+ return PISA_ERR_SUCCESS;
+}
+
+int
+scan_manager::init_moire (moire_img_info *info)
+{
+ info->resolution = _resolution[0] * _zoom[0] / 100;
+
+ _resolution[0] =
+ _resolution[1] =
+ iscan::moire::get_res_quote (info->resolution, !has_zoom ());
+
+ if (!has_zoom ())
+ {
+ try
+ {
+ get_valid_resolution (&_resolution[0], &_resolution[1]);
+ }
+ catch (const pisa_error& e)
+ {
+ if (pisa_error (PISA_STATUS_UNSUPPORTED) != e)
+ throw (e);
+ // else do nothing
+ }
+ }
+
+ info->in_resolution = _resolution[0];
+
+ _zoom [ 0 ] = 100;
+ _zoom [ 1 ] = 100;
+
+ info->in_width = ::inch2width (_area[0], _resolution[0]);
+ info->in_height = ::inch2height (_area[1], _resolution[1]);
+ info->in_rowbytes = calc_rowbytes (info->in_width,
+ static_cast <pisa_pixel_type>
+ (_pixeltype));
+
+ info->out_width = ::inch2width (_area[0], info->resolution);
+ info->out_height = ::inch2height (_area[1], info->resolution);
+ info->out_rowbytes = calc_rowbytes (info->out_width,
+ static_cast <pisa_pixel_type>
+ (_pixeltype));
+
+ info->bits_per_pixel = calc_bitperpix (static_cast <pisa_pixel_type>
+ (_pixeltype),
+ static_cast <pisa_bitdepth_type>
+ (_bitdepth));
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+int
+scan_manager::init_sharp (sharp_img_info *info)
+{
+ long resolution;
+
+ if ( m_resize )
+ {
+ info->in_width = m_resize_info.out_width;
+ info->in_height = m_resize_info.out_height;
+
+ resolution = m_resize_info.resolution;
+ }
+ else if ( m_moire )
+ {
+ info->in_width = m_moire_info.out_width;
+ info->in_height = m_moire_info.out_height;
+
+ resolution = m_moire_info.resolution;
+ }
+ else
+ {
+ info->in_width = ::inch2width (_area[0], _resolution[0], _zoom[0]);
+ info->in_height = ::inch2height (_area[1], _resolution[1], _zoom[1]);
+
+ resolution = _resolution[0];
+ }
+
+ info->in_rowbytes = calc_rowbytes (info->in_width,
+ static_cast <pisa_pixel_type>
+ (_pixeltype));
+
+ info->bits_per_pixel = calc_bitperpix (static_cast <pisa_pixel_type>
+ (_pixeltype),
+ static_cast <pisa_bitdepth_type>
+ (_bitdepth));
+
+ info->out_width = info->in_width;
+ info->out_height = info->in_height;
+ info->out_rowbytes = info->in_rowbytes;
+
+ m_sharp_cls->set_parms (resolution,
+ using_tpu (),
+ !has_zoom (),
+ & info->strength,
+ & info->radius,
+ & info->clipping);
+
+ info->sharp_flag = PISA_SH_UMASK;
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+int scan_manager::modify_img_info ( int * width, int * height )
+{
+ // resize
+ if ( m_resize )
+ {
+ if ( m_resize_info.in_width == m_resize_info.out_width )
+ {
+ m_resize_info.out_width = * width;
+ }
+ else if ( ( has_autocrop () && get_autocrop () )
+ || ( has_size_check () && get_size_check () ) )
+ {
+ m_resize_info.out_width =
+ (*width * m_resize_info.out_width + 0.5) / m_resize_info.in_width;
+ }
+ m_resize_info.out_rowbytes = ( m_resize_info.out_width *
+ m_resize_info.bits_per_pixel +
+ 7 ) / 8;
+
+ m_resize_info.in_width = * width;
+ m_resize_info.in_rowbytes = ( m_resize_info.in_width *
+ m_resize_info.bits_per_pixel +
+ 7 ) / 8;
+
+ if ( m_resize_info.in_height == m_resize_info.out_height )
+ {
+ m_resize_info.out_height = * height;
+ }
+ else if ( ( has_autocrop () && get_autocrop () )
+ || ( has_size_check () && get_size_check () ) )
+ {
+ m_resize_info.out_height =
+ (*height * m_resize_info.out_height + 0.5) / m_resize_info.in_height;
+ }
+
+ m_resize_info.in_height = * height;
+ }
+
+ if ( m_moire )
+ {
+ if ( ( has_autocrop () && get_autocrop () )
+ || ( has_size_check () && get_size_check () ) )
+ {
+ if ( m_moire_info.in_width == m_moire_info.out_width )
+ {
+ m_moire_info.out_width = * width;
+ }
+ else
+ {
+ m_moire_info.out_width =
+ (*width * m_moire_info.resolution + 0.5) / _resolution[0];
+ }
+ if ( m_moire_info.in_height == m_moire_info.out_height )
+ {
+ m_moire_info.out_height = * height;
+ }
+ else
+ {
+ m_moire_info.out_height =
+ (*height * m_moire_info.resolution + 0.5) / _resolution[1];
+ }
+ m_moire_info.in_width = * width;
+ m_moire_info.in_height = * height;
+ m_moire_info.in_rowbytes =
+ calc_rowbytes (m_moire_info.in_width,
+ static_cast <pisa_pixel_type> (_pixeltype));
+ m_moire_info.out_rowbytes =
+ calc_rowbytes (m_moire_info.out_width,
+ static_cast <pisa_pixel_type> (_pixeltype));
+ }
+ }
+
+ if ( m_sharp )
+ {
+ m_sharp_info.in_width = * width;
+ m_sharp_info.in_height = * height;
+ m_sharp_info.in_rowbytes = ( m_sharp_info.in_width *
+ m_sharp_info.bits_per_pixel +
+ 7 ) / 8;
+
+ if ( m_resize )
+ {
+ m_sharp_info.in_width = m_resize_info.out_width;
+ m_sharp_info.in_height = m_resize_info.out_height;
+ m_sharp_info.in_rowbytes = m_resize_info.out_rowbytes;
+ }
+
+ if ( m_moire )
+ {
+ m_sharp_info.in_width = m_moire_info.out_width;
+ m_sharp_info.in_height = m_moire_info.out_height;
+ m_sharp_info.in_rowbytes = m_moire_info.out_rowbytes;
+ }
+
+ m_sharp_info.out_width = m_sharp_info.in_width;
+ m_sharp_info.out_height = m_sharp_info.in_height;
+ m_sharp_info.out_rowbytes = m_sharp_info.in_rowbytes;
+ }
+
+ // update width and height
+ if ( m_resize )
+ {
+ * width = m_resize_info.out_width;
+ * height = m_resize_info.out_height;
+ }
+
+ if ( m_moire )
+ {
+ * width = m_moire_info.out_width;
+ * height = m_moire_info.out_height;
+ }
+
+ if ( m_sharp )
+ {
+ * width = m_sharp_info.out_width;
+ * height = m_sharp_info.out_height;
+ }
+
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+int scan_manager::create_img_cls ( void )
+{
+ release_memory ();
+
+ if (m_sharp)
+ {
+ m_sharp_img_info = new IMAGE_INFO [ 2 ];
+
+ set_img_info ( & m_sharp_img_info [ _IN ],
+ & m_sharp_img_info [ _OUT ],
+ m_sharp_info );
+
+ m_sharp_cls = new iscan::focus (m_sharp_info);
+ }
+
+ if ( m_moire )
+ {
+ m_moire_img_info = new IMAGE_INFO [ 2 ];
+
+ set_img_info ( & m_moire_img_info [ _IN ],
+ & m_moire_img_info [ _OUT ],
+ m_moire_info );
+
+ m_moire_cls = new iscan::moire (m_moire_info, !has_zoom ());
+ }
+
+ if ( m_resize )
+ {
+ m_resize_img_info = new IMAGE_INFO [ 2 ];
+
+ set_img_info ( & m_resize_img_info [ _IN ],
+ & m_resize_img_info [ _OUT ],
+ m_resize_info );
+
+ m_resize_cls = new iscan::scale (m_resize_info);
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+int scan_manager::release_memory ( void )
+{
+ if ( m_resize_cls )
+ delete m_resize_cls;
+ m_resize_cls = 0;
+
+ if ( m_moire_cls )
+ delete m_moire_cls;
+ m_moire_cls = 0;
+
+ if ( m_sharp_cls )
+ delete m_sharp_cls;
+ m_sharp_cls = 0;
+
+ if ( m_resize_img_info )
+ delete [] m_resize_img_info;
+ m_resize_img_info = 0;
+
+ if ( m_moire_img_info )
+ delete [] m_moire_img_info;
+ m_moire_img_info = 0;
+
+ if ( m_sharp_img_info )
+ delete [] m_sharp_img_info;
+ m_sharp_img_info = 0;
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+void scan_manager::set_img_info ( LPIMAGE_INFO in_img_info,
+ LPIMAGE_INFO out_img_info,
+ const img_size & size )
+{
+ in_img_info->pImg_Buf = 0;
+ in_img_info->Img_Width = size.in_width;
+ in_img_info->Img_Height = size.in_height;
+ in_img_info->Img_RowBytes = size.in_rowbytes;
+ in_img_info->BitsPerPixel = size.bits_per_pixel;
+
+ out_img_info->pImg_Buf = 0;
+ out_img_info->Img_Width = size.out_width;
+ out_img_info->Img_Height = size.out_height;
+ out_img_info->Img_RowBytes = size.out_rowbytes;
+ out_img_info->BitsPerPixel = size.bits_per_pixel;
+}
+
+/*------------------------------------------------------------*/
+int scan_manager::get_send_in_line ( int out_line )
+{
+ size_t quote = out_line;
+
+ if (m_sharp)
+ {
+ m_sharp_img_info [ _OUT ].Img_Height = quote;
+ quote = m_sharp_cls->get_line_quote (quote);
+ m_sharp_img_info [ _IN ].Img_Height = quote;
+ }
+ if (m_moire)
+ {
+ m_moire_img_info [ _OUT ].Img_Height = quote;
+ quote = m_moire_cls->get_line_quote (quote);
+ m_moire_img_info [ _IN ].Img_Height = quote;
+ }
+ if (m_resize)
+ {
+ m_resize_img_info [ _OUT ].Img_Height = quote;
+ quote = m_resize_cls->get_line_quote (quote);
+ m_resize_img_info [ _IN ].Img_Height = quote;
+ }
+
+ return quote;
+}
+
+/*------------------------------------------------------------*/
+int scan_manager::image_process ( unsigned char * in_img,
+ unsigned char * out_img )
+{
+ unsigned char *inbuf, *outbuf;
+ size_t in_sz, out_sz;
+
+ unsigned char *tmpbuf = NULL;
+ size_t tmp_sz = 0;
+ bool zapbuf = false;
+
+ try
+ {
+ if (m_resize)
+ {
+ in_sz = m_resize_img_info [ _IN ].Img_Height * m_resize_img_info [ _IN].Img_RowBytes;
+ inbuf = in_img;
+
+ if (m_sharp)
+ {
+ out_sz = tmp_sz = (m_resize_img_info [ _OUT ].Img_Height
+ * m_resize_img_info [ _OUT ].Img_RowBytes);
+ outbuf = tmpbuf = new unsigned char [out_sz];
+ zapbuf = true;
+ }
+ else
+ {
+ out_sz = m_resize_img_info [ _OUT ].Img_Height * m_resize_img_info [ _OUT ].Img_RowBytes;
+ outbuf = out_img;
+ }
+ m_resize_cls->exec (inbuf, in_sz, outbuf, out_sz);
+ }
+
+ if (m_moire)
+ {
+ in_sz = m_moire_img_info [ _IN ].Img_Height * m_moire_img_info [ _IN ].Img_RowBytes;
+ inbuf = in_img;
+
+ if (m_sharp)
+ {
+ out_sz = tmp_sz = (m_moire_img_info [ _OUT ].Img_Height
+ * m_moire_img_info [ _OUT ].Img_RowBytes);
+ outbuf = tmpbuf = new unsigned char [out_sz];
+ zapbuf = true;
+ }
+ else
+ {
+ out_sz = m_moire_img_info [ _OUT ].Img_Height * m_moire_img_info [ _OUT ].Img_RowBytes;
+ outbuf = out_img;
+ }
+ m_moire_cls->exec (inbuf, in_sz, outbuf, out_sz);
+ }
+
+ if (m_sharp)
+ {
+ out_sz = m_sharp_img_info [ _OUT ].Img_Height * m_sharp_img_info [ _OUT ].Img_RowBytes;
+ outbuf = out_img;
+
+ if (m_resize || m_moire)
+ {
+ in_sz = tmp_sz;
+ inbuf = tmpbuf;
+ }
+ else
+ {
+ in_sz = m_sharp_img_info [ _IN ].Img_Height * m_sharp_img_info [ _IN ].Img_RowBytes;
+ inbuf = in_img;
+ }
+ m_sharp_cls->exec (inbuf, in_sz, outbuf, out_sz);
+ }
+
+ if (zapbuf)
+ {
+ delete [] tmpbuf;
+ }
+ }
+ catch (bad_alloc& oops)
+ {
+ if (zapbuf)
+ {
+ delete [] tmpbuf;
+ }
+ return PISA_ERR_OUTOFMEMORY;
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+void
+scan_manager::adjust_scan_param (long *resolution, long *scale) const
+{
+ int min_res = 50;
+ int max_res = get_max_resolution ();
+ int adj_res = *resolution;
+
+ int min_scale = 50;
+ int max_scale = 200;
+ int adj_scale = 100; // assume no scaling is needed
+
+ if (adj_res < min_res)
+ {
+ adj_scale = adj_res * 100 / min_res;
+
+ if (adj_scale < min_scale)
+ {
+ adj_scale = min_scale;
+ }
+ adj_res = min_res;
+ }
+
+ if (max_res < adj_res)
+ {
+ adj_scale = adj_res * 100 / max_res;
+
+ if (adj_scale > max_scale)
+ {
+ adj_scale = max_scale;
+ }
+ adj_res = max_res;
+ }
+
+ *resolution = adj_res;
+ *scale = adj_scale;
+}
+
+/*! Sets main and sub resolutions to the best available value.
+
+ The best available value is the first resolution not smaller than
+ the value passed. If no such value is available, the largest
+ available resolution will be used unless \a use_max is \c false.
+
+ In the latter case an exception will be thrown instead.
+ */
+void
+scan_manager::get_valid_resolution (long int *x_res, long int *y_res,
+ bool use_max) const
+{
+ if (!x_res || !y_res)
+ throw pisa_error (PISA_ERR_PARAMETER);
+
+ SANE_Int res_x = get_resolution (SANE_NAME_SCAN_X_RESOLUTION, *x_res);
+ SANE_Int res_y = get_resolution (SANE_NAME_SCAN_Y_RESOLUTION, *y_res);
+
+ if (0 == res_x || 0 == res_y)
+ {
+ if (!use_max)
+ throw pisa_error (PISA_STATUS_UNSUPPORTED);
+ if (0 == res_x)
+ res_x = get_max_resolution (SANE_NAME_SCAN_X_RESOLUTION);
+ if (0 == res_y)
+ res_y = get_max_resolution (SANE_NAME_SCAN_Y_RESOLUTION);
+ }
+
+ *x_res = res_x;
+ *y_res = res_y;
+}
+
+static bool
+scaling_too_large (const img_size &info)
+{
+ return info.in_width > SCALING_INPUT_WIDTH_LIMIT
+ && info.in_width != info.out_width;
+}
+
+bool
+scan_manager::area_is_too_large (void) const
+{
+ int area_is_valid;
+
+ // due to a bug in esmod, need to limit the input width when scaling
+ if (m_resize && scaling_too_large (m_resize_info)) return true;
+ if (m_moire && scaling_too_large (m_moire_info)) return true;
+
+ get_value ("scan-area-is-valid", &area_is_valid);
+ return (0 == area_is_valid);
+}
+
+bool
+scan_manager::adf_duplex_direction_matches (void) const
+{
+ int rv;
+
+ // if option is inactive, assume direction does not match
+ if (!is_activate ("adf-duplex-direction-matches"))
+ return false;
+
+ try
+ {
+ get_value ("adf-duplex-direction-matches", &rv);
+ }
+ catch (pisa_error& oops)
+ { // if no option available, assume direction does not match
+ return false;
+ }
+ return (SANE_TRUE == rv);
+}
+
+void
+scan_manager::has_prev_img ( int has_prev )
+{
+ _has_prev_img = has_prev;
+}
+
+bool
+scan_manager::push_button_needs_polling (void) const
+{
+ SANE_Bool result = false;
+
+ if (is_activate ("needs-polling"))
+ get_value ("needs-polling", &result);
+
+ return result;
+}
+
+void
+scan_manager::update_settings (bool is_preview)
+{
+ SANE_Bool auto_scan = !is_preview;
+
+ set_scan_source ();
+
+ if (is_activate ("adf-auto-scan"))
+ set_value ("adf-auto-scan", &auto_scan);
+
+ _max_descreen_resolution = get_max_resolution (has_zoom () ? 800 : 600);
+
+ set_film_type ();
+ set_focus (_focus);
+ set_speed (_speed);
+ set_deskew (_deskew);
+ set_color_mode (_pixeltype, _bitdepth);
+
+ if (is_preview || _has_prev_img)
+ {
+ set_gamma_table (_gamma.gamma_r);
+ if (_pixeltype == PISA_PT_BW)
+ set_threshold (_threshold);
+ }
+ if (_pixeltype == PISA_PT_RGB)
+ set_color_profile (_coef);
+ set_brightness_method (br_iscan);
+ if (has_brightness())
+ {
+ set_brightness (_brightness);
+ }
+ if (has_contrast())
+ {
+ set_contrast (_contrast);
+ }
+ set_scan_resolution (_resolution[0], _resolution[1]);
+ set_scan_zoom (_zoom[0], _zoom[1]);
+ set_scan_area (_offset[0], _offset[1],
+ _area[0], _area[1]);
+}
+
+pisa_error_id
+scan_manager::set_scan_parameters (const settings& set, const marquee& marq)
+{
+ _pixeltype = set.imgtype.pixeltype;
+ _bitdepth = set.imgtype.bitdepth;
+ _dropout = set.imgtype.dropout;
+ _monoopt = set.imgtype.monoopt;
+ _halftone = set.imgtype.halftone;
+
+ _offset[0] = marq.offset.x;
+ _offset[1] = marq.offset.y;
+ _area[0] = marq.area.x;
+ _area[1] = marq.area.y;
+ _resolution[0]= (set.resolution * marq.scale + 50) / 100;
+ _resolution[1]= (set.resolution * marq.scale + 50) / 100;
+ _zoom[0] = 100;
+ _zoom[1] = 100;
+
+ _brightness = marq.brightness;
+ _contrast = marq.contrast;
+ _deskew = get_deskew();
+
+ for (int i = 0; i < 256; ++i)
+ {
+ _gamma.gamma_r[i] = marq.lut.gamma_r[i];
+ _gamma.gamma_g[i] = marq.lut.gamma_g[i];
+ _gamma.gamma_b[i] = marq.lut.gamma_b[i];
+ }
+
+ if (set.imgtype.pixeltype == PISA_PT_RGB)
+ {
+ generate_color_coef (_coef, set.coef, marq.saturation);
+ }
+ else // use identity matrix
+ { // FIXME: fold into generate_color_coef()
+ for (int i = 0; i < 9; ++i)
+ _coef[i] = 0.0;
+
+ _coef[0] = 1.0;
+ _coef[4] = 1.0;
+ _coef[8] = 1.0;
+ }
+
+ _threshold = marq.threshold;
+ _speed = set.enable_draft_mode;
+ _focus = marq.focus;
+
+ if (PISA_PT_BW == set.imgtype.pixeltype)
+ _usm = 0;
+ else
+ _usm = set.usm;
+
+ if (PISA_DESCREEN_ON == set.imgtype.de_screening)
+ _de_screening = 1;
+ else
+ _de_screening = 0;
+
+
+ // check for error conditions
+ pisa_error_id err = PISA_ERR_SUCCESS;
+
+ if (set.imgtype.de_screening == PISA_DESCREEN_ON)
+ {
+ if ( _resolution[0] > _max_descreen_resolution
+ || _resolution[1] > _max_descreen_resolution)
+ err = PISA_ERR_MRRESTOOHIGH;
+ }
+ return err;
+}
+
+#include "pisa_view_manager.h"
+pisa_error_id
+scan_manager::set_scan_parameters (const settings& set, const marquee& marq,
+ int resolution)
+{
+ _pixeltype = PISA_PT_RGB;
+ _bitdepth = PISA_BD_8;
+ _dropout = PISA_DO_NONE;
+ _monoopt = PISA_MO_NONE;
+ _halftone = PISA_HT_NONE;
+
+ _offset[0] = marq.offset.x;
+ _offset[1] = marq.offset.y;
+ _area[0] = marq.area.x;
+ _area[1] = marq.area.y;
+ _resolution[0]= resolution;
+ _resolution[1]= resolution;
+ _zoom[0] = 100;
+ _zoom[1] = 100;
+
+ _brightness = marq.brightness;
+ _contrast = marq.contrast;
+ _deskew = get_deskew();
+
+ // gamma table
+ for (int i = 0; i < 256; ++i)
+ {
+ _gamma.gamma_r[i] = i;
+ _gamma.gamma_g[i] = i;
+ _gamma.gamma_b[i] = i;
+ }
+
+ // profile matrix
+ for (int i = 0; i < 9; ++i)
+ _coef[i] = 0.0;
+
+ _coef[0] = 1.0;
+ _coef[4] = 1.0;
+ _coef[8] = 1.0;
+
+ _threshold = 0;
+ _speed = 1;
+ _focus = marq.focus;
+ _usm = 0;
+ _de_screening = 0;
+
+ return PISA_ERR_SUCCESS;
+}
diff --git a/frontend/pisa_scan_manager.h b/frontend/pisa_scan_manager.h
new file mode 100644
index 0000000..18002ef
--- /dev/null
+++ b/frontend/pisa_scan_manager.h
@@ -0,0 +1,151 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005, 2008, 2009 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_SCAN_MANAGER_H
+#define ___PISA_SCAN_MANAGER_H
+
+#include "esmod-wrapper.hh"
+
+#include "pisa_sane_scan.h"
+#include "pisa_error.h"
+
+class scan_manager : public sane_scan
+{
+ public:
+
+ // operator
+ void open_device ( char * name = 0 );
+
+ void init_preview (int *width, int *height);
+ void init_scan (int *width, int *height, bool reset_params = true);
+ void adjust_scan_param (long *resolution, long *scale) const;
+ void get_valid_resolution (long int *x_res, long int *y_res,
+ bool use_max = false) const;
+
+ void acquire_image ( unsigned char * img,
+ int row_bytes,
+ int height,
+ int cancel );
+ int release_memory ( void );
+
+ pisa_error_id set_scan_parameters (const settings& set,
+ const marquee& marq);
+ pisa_error_id set_scan_parameters (const settings& set,
+ const marquee& marq,
+ int resolution);
+
+ bool adf_duplex_direction_matches (void) const;
+
+ void has_prev_img ( int has_prev );
+
+ bool push_button_needs_polling (void) const;
+
+ protected:
+ void set_scan_parameters (bool is_preview = false);
+ void start_scan (int *width, int *height);
+
+ private:
+
+ typedef struct _IMAGE_INFO {
+ unsigned char* pImg_Buf;
+ long Img_Width;
+ long Img_Height;
+ unsigned long Img_RowBytes;
+ short BitsPerPixel;
+ } IMAGE_INFO, *LPIMAGE_INFO;
+
+ // operation
+ int init_img_process_info (void);
+ int init_zoom (resize_img_info *info);
+ int init_moire (moire_img_info *info);
+ int init_sharp (sharp_img_info *info);
+
+ int modify_img_info ( int * width, int * height );
+
+ int create_img_cls ( void );
+
+ void set_img_info ( LPIMAGE_INFO in_img_info,
+ LPIMAGE_INFO out_img_info,
+ const img_size & size );
+
+ void update_settings (bool is_preview);
+ bool area_is_too_large (void) const;
+
+ // FIXME: the following block of member functions should not be the
+ // responsibility of the scan_manager. They can be removed
+ // once the filter class hierarchy has support for chaining
+ // of filters and this class no longer needs to baby sit the
+ // data transfers.
+ int get_send_in_line ( int out_line );
+ int image_process ( unsigned char * in_img, unsigned char * out_img );
+
+ // for image module
+ long m_resize;
+ long m_moire;
+ long m_sharp;
+
+ resize_img_info m_resize_info;
+ moire_img_info m_moire_info;
+ sharp_img_info m_sharp_info;
+
+ iscan::scale * m_resize_cls;
+ iscan::moire * m_moire_cls;
+ iscan::focus * m_sharp_cls;
+
+ enum { _IN, _OUT };
+ IMAGE_INFO * m_resize_img_info;
+ IMAGE_INFO * m_moire_img_info;
+ IMAGE_INFO * m_sharp_img_info;
+
+ char _pixeltype;
+ char _bitdepth;
+ char _dropout;
+ char _monoopt;
+ char _halftone;
+ double _offset[2];
+ double _area[2];
+ long _resolution[2];
+ long _zoom[2];
+ gamma_struct _gamma;
+ double _coef[9];
+ long _threshold;
+ long _speed;
+ long _focus;
+ long _usm;
+ long _brightness;
+ long _contrast;
+ long _deskew;
+ long _de_screening;
+ long _max_descreen_resolution;
+ int _has_prev_img;
+};
+
+#endif // ___PISA_SCAN_MANAGER_H
+
diff --git a/frontend/pisa_scan_selector.cc b/frontend/pisa_scan_selector.cc
new file mode 100644
index 0000000..8eea853
--- /dev/null
+++ b/frontend/pisa_scan_selector.cc
@@ -0,0 +1,503 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2003, 2008, 2009 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+
+/*------------------------------------------------------------*/
+#include <gtk/gtk.h>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <regex.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <sane/sane.h>
+#include <sane/saneopts.h>
+#ifdef __cplusplus
+}
+#endif
+#include "pisa_error.h"
+#include "pisa_main.h"
+#include "pisa_scan_selector.h"
+#include "pisa_view_manager.h"
+#include "pisa_gimp.h"
+/*------------------------------------------------------------*/
+
+static void
+_delete( GtkWidget *widget, scan_selector *ss )
+{
+ ss->cancel(); // same process as cancel
+}
+
+static void
+_select( GtkEditable *, scan_selector *ss )
+{
+ ss->select(); // just forward the call
+}
+
+static void
+_update( GtkButton *, scan_selector *ss )
+{
+ ss->update(); // just forward the call
+}
+
+static void
+_cancel( GtkButton *, scan_selector *ss )
+{
+ ss->cancel(); // just forward the call
+}
+
+
+scan_selector::~scan_selector()
+{
+ free( _current_gui );
+ free( _current_dev );
+ g_slist_free( _gui_names );
+ g_slist_free( _dev_names );
+ g_slist_free( _items );
+ ::gtk_widget_destroy( m_dbox );
+}
+
+scan_selector::scan_selector( bool is_dialog )
+ : m_local_only( SANE_FALSE ),
+ m_dbox( 0 ), m_hbox( 0 ), m_menu( 0 ), _items( 0 ),
+ _dev_names( 0 ), _gui_names( 0 ),
+ _current_dev( 0 ), _current_gui( 0 )
+{
+ GtkWidget *vbox = 0; // dialog container
+
+ if (is_dialog)
+ {
+ { // dialog box
+ GtkWidget *w = ::gtk_dialog_new ();
+ ::gtk_object_set_data( GTK_OBJECT( w ), "m_dbox", w );
+ ::gtk_window_set_title( GTK_WINDOW( w ),
+ _("Scan Selector Dialog") );
+
+#ifdef HAVE_GTK_2
+ GTK_WINDOW( w )->type = GTK_WINDOW_TOPLEVEL;
+#else
+ GTK_WINDOW( w )->type = GTK_WINDOW_DIALOG;
+#endif
+ ::gtk_window_set_modal( GTK_WINDOW( w ), TRUE );
+ ::gtk_window_set_policy( GTK_WINDOW( w ), FALSE, TRUE, FALSE );
+
+ ::gtk_signal_connect( GTK_OBJECT( w ), "delete_event",
+ GTK_SIGNAL_FUNC( ::_delete ), this );
+
+ m_dbox = w;
+ }
+
+ {
+ GtkWidget *w = GTK_DIALOG( m_dbox )->vbox;
+ ::gtk_object_set_data( GTK_OBJECT( m_dbox ), "vbox", w );
+ ::gtk_container_set_border_width( GTK_CONTAINER( w ), 5 );
+ ::gtk_widget_show( w );
+
+ vbox = w;
+ }
+ }
+
+ { // selector container
+ GtkWidget *w = ::gtk_hbox_new( FALSE, 5 );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( w ), "m_hbox", w,
+ (GtkDestroyNotify) ::gtk_widget_unref );
+ if (vbox) // only when in a dialog
+ ::gtk_box_pack_start( GTK_BOX( vbox ), w, TRUE, TRUE, 0 );
+ ::gtk_container_set_border_width( GTK_CONTAINER( w ), 5 );
+
+ m_hbox = w;
+ }
+
+ { // scanner label
+ GtkWidget *w = ::gtk_label_new (_("Scanner:"));
+ ::gtk_box_pack_start( GTK_BOX( m_hbox ), w, FALSE, FALSE, 0 );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT (m_hbox), "label", w,
+ (GtkDestroyNotify) ::gtk_widget_unref );
+ ::gtk_widget_show( w );
+ }
+
+ { // option menu
+ GtkWidget *w = ::gtk_option_menu_new ();
+ ::gtk_box_pack_start( GTK_BOX( m_hbox ), w, FALSE, FALSE, 0 );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( m_hbox ), "option", w,
+ (GtkDestroyNotify) ::gtk_widget_unref );
+ ::gtk_widget_show( w );
+
+ m_opts = w;
+ }
+
+ if (!is_dialog)
+ { // update button
+ GtkWidget *w = ::gtk_button_new_with_label( _(" Update ") );
+ ::gtk_container_set_border_width( GTK_CONTAINER( w ), 5 );
+ ::gtk_box_pack_start( GTK_BOX( m_hbox ), w, FALSE, FALSE, 0 );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( m_hbox ), "update", w,
+ (GtkDestroyNotify) ::gtk_widget_unref );
+ ::gtk_widget_show( w );
+
+ ::gtk_signal_connect( GTK_OBJECT( w ), "clicked",
+ GTK_SIGNAL_FUNC( ::_update ), this );
+ }
+ else
+ {
+ GtkWidget *action_area;
+ { // dialog action button area
+ GtkWidget *w = GTK_DIALOG (m_dbox)->action_area;
+ ::gtk_object_set_data( GTK_OBJECT( m_dbox ), "action_area", w );
+ ::gtk_container_set_border_width( GTK_CONTAINER( w ), 5 );
+ ::gtk_widget_show( w );
+
+ action_area = w;
+ }
+
+ GtkWidget *button_box;
+ {
+ GtkWidget *w = gtk_hbutton_box_new();
+ gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( m_dbox ), "button_box", w,
+ (GtkDestroyNotify) gtk_widget_unref );
+ ::gtk_box_pack_start( GTK_BOX( action_area ), w, TRUE, FALSE, 0 );
+ ::gtk_widget_show( w );
+
+ button_box = w;
+ }
+
+ { // okay button
+ GtkWidget *w = ::gtk_button_new_with_label( _(" OK ") );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( m_dbox ), "okay_button", w,
+ (GtkDestroyNotify) gtk_widget_unref );
+ ::gtk_box_pack_start( GTK_BOX( button_box ), w, TRUE, FALSE, 0 );
+ GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
+ ::gtk_widget_grab_default( w );
+ ::gtk_widget_show( w );
+
+ ::gtk_signal_connect( GTK_OBJECT( w ), "clicked",
+ GTK_SIGNAL_FUNC( ::_select ), this );
+ }
+
+ { // cancel button
+ GtkWidget *w = ::gtk_button_new_with_label( _(" Cancel ") );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( m_dbox ), "cancel_button", w,
+ (GtkDestroyNotify) gtk_widget_unref );
+ ::gtk_box_pack_start( GTK_BOX( button_box ), w, TRUE, FALSE, 0 );
+ ::gtk_widget_show( w );
+
+ ::gtk_signal_connect( GTK_OBJECT( w ), "clicked",
+ GTK_SIGNAL_FUNC( ::_cancel ), this );
+ }
+ }
+}
+
+void
+scan_selector::show() const
+{
+ gtk_widget_show( m_hbox );
+ if (m_dbox)
+ gtk_widget_show( m_dbox );
+}
+
+GtkWidget *
+scan_selector::widget() const
+{
+ return (m_dbox ? m_dbox : m_hbox);
+}
+
+void
+scan_selector::update()
+{
+ const SANE_Device **device;
+ SANE_Status status;
+ status = sane_get_devices ( &device, m_local_only );
+
+ if (SANE_STATUS_GOOD != status)
+ throw pisa_error( status );
+
+ // prepare a new menu
+ GtkWidget *menu = gtk_menu_new();
+ GSList *items = 0;
+ GSList *dev_names = 0;
+ GSList *gui_names = 0;
+
+ int cnt = 0;
+ while (*device)
+ {
+ char *name = rewrite_name( *device );
+ if (name)
+ {
+ GtkWidget *item = gtk_menu_item_new ();
+ GtkWidget *label = gtk_label_new( name );
+ gtk_label_set_line_wrap( GTK_LABEL( label ), TRUE );
+ gtk_container_add( GTK_CONTAINER(item), label );
+ ++cnt;
+
+ if (!m_dbox)
+ gtk_signal_connect( GTK_OBJECT( item ), "activate",
+ GTK_SIGNAL_FUNC( _select ), this );
+
+ items = g_slist_append( items, item );
+ dev_names = g_slist_append( dev_names,
+ (void *) ((*device)->name) );
+ gui_names = g_slist_append( gui_names, (void *) name );
+
+ gtk_menu_append( GTK_MENU( menu ), item );
+ gtk_widget_show( label );
+ gtk_widget_show( item );
+ }
+ ++device;
+ }
+
+ if (!cnt)
+ throw pisa_error( PISA_ERR_CONNECT );
+
+ if (m_menu) // get rid of the old menu
+ gtk_option_menu_remove_menu( GTK_OPTION_MENU( m_opts ) );
+
+ m_menu = menu;
+ gtk_option_menu_set_menu( GTK_OPTION_MENU( m_opts ), m_menu );
+
+ if (_items)
+ {
+ GSList *item;
+ if (!m_dbox)
+ for (item = _items; item; item = item->next )
+ gtk_signal_disconnect_by_func( GTK_OBJECT( item->data ),
+ GTK_SIGNAL_FUNC( _select ), this );
+ g_slist_free( _items );
+ }
+ if (_dev_names)
+ g_slist_free( _dev_names );
+ if (_gui_names)
+ g_slist_free( _gui_names );
+
+ _items = items;
+ _dev_names = dev_names;
+ _gui_names = gui_names;
+
+ if (!_current_dev)
+ {
+ char *name = (char *) _dev_names->data;
+ _current_dev = (char *) malloc( strlen( name ) + 1 );
+ if (!_current_dev)
+ throw pisa_error( PISA_STATUS_NO_MEM );
+ strcpy( _current_dev, name );
+ }
+ if (!_current_gui)
+ {
+ char *name = (char *) _gui_names->data;
+ _current_gui = (char *) malloc( strlen( name ) + 1 );
+ if (!_current_gui)
+ throw pisa_error( PISA_STATUS_NO_MEM );
+ strcpy( _current_gui, name );
+ }
+}
+
+void
+scan_selector::cancel()
+{
+ ::gtk_main_quit();
+ sane_exit ();
+ if( pisa_gimp_plugin() )
+ pisa_gimp_quit();
+ exit( 0 );
+}
+
+void
+scan_selector::select()
+{
+ GtkWidget *item = gtk_menu_get_active( GTK_MENU( m_menu ) );
+
+ gint pos = g_slist_position( _items, g_slist_find( _items, item ) );
+ if (-1 == pos)
+ {
+ fprintf( stderr, "scan_selector: internal inconsistency\n" );
+ abort();
+ }
+
+ char *dev_name = (char *) g_slist_nth_data( _dev_names, (guint) pos );
+ char *gui_name = (char *) g_slist_nth_data( _gui_names, (guint) pos );
+
+ if (0 != strcmp( _current_dev, dev_name ))
+ {
+ char *new_name = (char *) malloc( (strlen( dev_name ) + 1)
+ * sizeof( char ) );
+ if (!new_name)
+ throw pisa_error( PISA_STATUS_NO_MEM );
+
+ strcpy( new_name, dev_name );
+ free( _current_dev );
+ _current_dev = new_name;
+ }
+ if (0 != strcmp( _current_gui, gui_name ))
+ {
+ char *new_name = (char *) malloc( (strlen( gui_name ) + 1)
+ * sizeof( char ) );
+ if (!new_name)
+ throw pisa_error( PISA_STATUS_NO_MEM );
+
+ strcpy( new_name, gui_name );
+ free( _current_gui );
+ _current_gui = new_name;
+ }
+
+ if (m_dbox)
+ {
+ ::gtk_widget_hide( m_dbox );
+ ::gtk_main_quit();
+ }
+ else
+ g_view_manager->set_device( dev_name );
+}
+
+char *
+scan_selector::get_device( bool rewrite )
+{
+ if (m_dbox && !rewrite)
+ {
+ update();
+ if (1 < g_slist_length( _items ))
+ {
+ show();
+ ::gtk_grab_add( m_dbox );
+ ::gtk_main();
+ ::gtk_grab_remove( m_dbox );
+ }
+ }
+ return (rewrite
+ ? _current_gui // GUI device name
+ : _current_dev); // raw device name
+}
+
+char *
+scan_selector::rewrite_name( const SANE_Device *device )
+{
+ if (!device)
+ return 0;
+
+ if (0 != strcmp( device->vendor, "Epson" ))
+ return 0; // sorry 'bout that, everyone ;-}
+
+ char *name = const_cast< char * >( device->name );
+ if (!name)
+ return 0;
+
+ // disable host-base scanners accessed
+ // through the net backend (they crash
+ // way too often)
+ if (0 == strncmp( name, "net:", strlen( "net:" ) )
+ && ( 0 == strcmp( device->model, "GT-7200" )
+ || 0 == strcmp( device->model, "GT-7300" )
+ || 0 == strcmp( device->model, "Perfection1250" )
+ || 0 == strcmp( device->model, "Perfection1260" )
+ ))
+ return 0;
+
+ { // dropping other backend EPSON devices
+ regex_t *comp_regex = new regex_t;
+ const char *regex = "^(net:([^:]+:))?(epson|epson2|plustek|snapscan):(.*)$";
+ int comp = regcomp( comp_regex, regex, REG_EXTENDED );
+
+ if (0 == comp)
+ {
+ int result = regexec( comp_regex, device->name, 0, 0, 0 );
+ if (0 == result)
+ {
+ name = 0; // other backend controlled device
+ }
+ else
+ if (REG_NOMATCH != result)
+ regerror( comp, comp_regex );
+ }
+ else
+ regerror( comp, comp_regex );
+
+ regfree( comp_regex );
+ delete comp_regex;
+
+ if (!name)
+ return 0; // sorry 'bout that everyone ;-}
+ }
+
+ // if we are still here we have an epkowa backend supported EPSON
+ // scanner OR, heaven forbid, an aliased scanner
+
+ {
+ // rewrite things into a string of the following form:
+ // "device->vendor device->model [device->name]"
+
+ size_t length = (strlen (device->vendor) + 1 // " "
+ + strlen (device->model) + 2 // " ["
+ + strlen (device->name) + 1 + 1); // "]" + \0
+
+ name = (char *) malloc( length * sizeof( char ) );
+ if (!name)
+ throw pisa_error( PISA_STATUS_NO_MEM );
+
+ strncpy (name, device->vendor, strlen (device->vendor));
+
+ char *c = name + strlen (device->vendor);
+ *c++ = ' ';
+ {
+ const char *p = device->model;
+ while (*p)
+ *c++ = *p++;
+ }
+ *c++ = ' ';
+ *c++ = '[';
+ { // copy device name
+ const char *p = device->name;
+ while (*p)
+ *c++ = *p++;
+ }
+ *c++ = ']';
+ *c = '\0';
+ }
+
+ return name;
+}
+
+void
+scan_selector::regerror( int code, regex_t *regex )
+{
+ size_t length = ::regerror( code, regex, 0, 0 );
+ char *message = new char[length];
+
+ ::regerror( code, regex, message, length );
+ fprintf( stderr, "%s\n", message );
+
+ delete[] message;
+}
diff --git a/frontend/pisa_scan_selector.h b/frontend/pisa_scan_selector.h
new file mode 100644
index 0000000..ceee58f
--- /dev/null
+++ b/frontend/pisa_scan_selector.h
@@ -0,0 +1,72 @@
+/* -*- C++ -*-
+ SANE EPSON backend
+ Copyright (C) 2003 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 2003-03-13 S.Wong New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_SCAN_SELECTOR_H
+#define ___PISA_SCAN_SELECTOR_H
+
+#include <gtk/gtk.h>
+#include <regex.h>
+
+class scan_selector
+{
+public:
+ ~scan_selector();
+ scan_selector( bool is_dialog = false );
+
+ void show() const;
+
+ GtkWidget * widget() const;
+
+ void update();
+ void cancel();
+ void select();
+ char * get_device( bool rewrite = false );
+
+private:
+
+ char * rewrite_name( const SANE_Device *device );
+ void regerror( int code, regex_t *regex );
+
+ bool m_local_only;
+
+ GtkWidget *m_dbox;
+ GtkWidget *m_hbox;
+ GtkWidget *m_opts;
+ GtkWidget *m_menu;
+
+ GSList *_items;
+ GSList *_dev_names;
+ GSList *_gui_names;
+
+ char *_current_dev;
+ char *_current_gui;
+};
+
+#endif // ___PISA_SCAN_SELECTOR_H
diff --git a/frontend/pisa_scan_tool.cc b/frontend/pisa_scan_tool.cc
new file mode 100644
index 0000000..2ebc577
--- /dev/null
+++ b/frontend/pisa_scan_tool.cc
@@ -0,0 +1,591 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005, 2008 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+/*------------------------------------------------------------*/
+#include <stdio.h>
+#include <string.h>
+
+/*------------------------------------------------------------*/
+#include "pisa_scan_tool.h"
+#include "pisa_error.h"
+#include "pisa_default_val.h"
+
+/*------------------------------------------------------------*/
+static int build_lineart ( pisa_image_info * info,
+ unsigned char do_clr,
+ long threshold );
+static int build_halftone ( pisa_image_info * info,
+ unsigned char do_clr,
+ unsigned char halftone,
+ long threshold );
+static int build_dither ( pisa_image_info * info,
+ unsigned char do_clr,
+ unsigned char halftone,
+ long threshold );
+static unsigned char dropout ( unsigned char red,
+ unsigned char grn,
+ unsigned char blu,
+ unsigned char do_clr,
+ long thdiff = 0 );
+
+/*------------------------------------------------------------*/
+int calc_rowbytes ( int width, pisa_pixel_type type )
+{
+ int rowbytes;
+
+ rowbytes = width;
+
+ switch ( type )
+ {
+ case PISA_PT_BW:
+ rowbytes = ( width + 7 ) / 8;
+ break;
+
+ case PISA_PT_GRAY:
+ rowbytes = width;
+ break;
+
+ case PISA_PT_RGB:
+ rowbytes = width * 3;
+ break;
+ }
+
+ return rowbytes;
+}
+
+/*------------------------------------------------------------*/
+int calc_bitperpix ( pisa_pixel_type pixel, pisa_bitdepth_type bitdepth )
+{
+ int bitperpix;
+
+ bitperpix = 8;
+ bitdepth = bitdepth;
+
+ switch ( pixel )
+ {
+ case PISA_PT_BW:
+ bitperpix = 1;
+ break;
+
+ case PISA_PT_GRAY:
+ bitperpix = 8;
+ break;
+
+ case PISA_PT_RGB:
+ bitperpix = 24;
+ break;
+ }
+
+ return bitperpix;
+}
+
+/*------------------------------------------------------------*/
+int tool_lut ( pisa_image_info * info,
+ const gamma_struct * lut )
+{
+ int i, j;
+ unsigned char * img;
+
+ for ( i = 0; i < info->m_height; i++ )
+ {
+ img = info->m_img + i * info->m_rowbytes;
+
+ for ( j = 0; j < info->m_width; j++ )
+ {
+ // red
+ * img = lut->gamma_r [ * img ];
+ img++;
+ // green
+ * img = lut->gamma_g [ * img ];
+ img++;
+ // blue
+ * img = lut->gamma_b [ * img ];
+ img++;
+ }
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+
+/*------------------------------------------------------------*/
+int tool_matrix ( pisa_image_info * info,
+ int saturation,
+ const double * color_profile )
+{
+ int i, j;
+ int color [ 9 ];
+ double color_corr [ 9 ];
+ int red, grn, blu;
+ int result_red, result_grn, result_blu;
+ unsigned char * img;
+ int max;
+
+ max = 0xff << 10;
+
+ generate_color_coef ( color_corr, color_profile, saturation );
+
+ for ( i = 0; i < 9; i++ )
+ color [ i ] = ( int ) ( color_corr [ i ] * ( 1 << 10 ) );
+
+ for ( i = 0; i < info->m_height; i++ )
+ {
+ img = info->m_img + i * info->m_rowbytes;
+
+ for ( j = 0; j < info->m_width; j++ )
+ {
+ red = * ( img + 0 );
+ grn = * ( img + 1 );
+ blu = * ( img + 2 );
+
+ result_red = ( int ) ( color [ 0 ] * red + color [ 1 ] * grn + color [ 2 ] * blu );
+ result_grn = ( int ) ( color [ 3 ] * red + color [ 4 ] * grn + color [ 5 ] * blu );
+ result_blu = ( int ) ( color [ 6 ] * red + color [ 7 ] * grn + color [ 8 ] * blu );
+
+ if ( result_red < 0 ) result_red = 0;
+ if ( result_red > max ) result_red = max;
+ if ( result_grn < 0 ) result_grn = 0;
+ if ( result_grn > max ) result_grn = max;
+ if ( result_blu < 0 ) result_blu = 0;
+ if ( result_blu > max ) result_blu = max;
+
+ * ( img + 0 ) = result_red >> 10;
+ * ( img + 1 ) = result_grn >> 10;
+ * ( img + 2 ) = result_blu >> 10;
+
+ img += 3;
+ }
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+int generate_color_coef ( double * dst,
+ const double * src,
+ int saturation )
+{
+ int i, j;
+ int color_corr [ 3 ] [ 3 ];
+ double tmp_color [ 3 ] [ 3 ];
+ int val;
+
+ // build saturate
+ val = 300 + 2 * saturation;
+
+ for ( i = 0; i < 3; i++ )
+ for ( j = 0; j < 3; j++ )
+ color_corr [ i ] [ j ] = -saturation;
+
+ color_corr [ 0 ] [ 0 ] =
+ color_corr [ 1 ] [ 1 ] =
+ color_corr [ 2 ] [ 2 ] = val;
+
+ // build color filter
+ for ( i = 0; i < 3; i++ )
+ for ( j = 0; j < 3; j++ )
+ {
+ tmp_color [ i ] [ j ] = color_corr [ 0 ] [ j ] * src [ i * 3 + 0 ];
+ tmp_color [ i ] [ j ] += color_corr [ 1 ] [ j ] * src [ i * 3 + 1 ];
+ tmp_color [ i ] [ j ] += color_corr [ 2 ] [ j ] * src [ i * 3 + 2 ];
+ }
+
+ for ( i = 0; i < 3; i++ )
+ for ( j = 0; j < 3; j++ )
+ dst [ i * 3 + j ] = tmp_color [ i ] [ j ] / 300.0;
+
+ return PISA_ERR_SUCCESS;
+}
+
+
+/*------------------------------------------------------------*/
+int build_gray ( pisa_image_info * info,
+ unsigned char do_clr )
+{
+ long i, j;
+ unsigned char val;
+ unsigned char * img;
+
+ for ( i = 0; i < info->m_height; i++ )
+ {
+ img = info->m_img + i * info->m_rowbytes;
+ for ( j = 0; j < info->m_width; j++ )
+ {
+ val = dropout ( * ( img + 0 ), * ( img + 1 ), * ( img + 2 ),
+ do_clr );
+ * ( img + 0 ) = val;
+ * ( img + 1 ) = val;
+ * ( img + 2 ) = val;
+ img += 3;
+ }
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+
+/*------------------------------------------------------------*/
+int build_bw ( pisa_image_info * info,
+ unsigned char do_clr,
+ unsigned char halftone,
+ long threshold )
+{
+ int ret;
+
+ ret = PISA_ERR_PARAMETER;
+
+ switch ( halftone )
+ {
+ case PISA_HT_NONE:
+ ret = build_lineart ( info, do_clr, threshold );
+ break;
+
+ case PISA_HT_TONEA:
+ case PISA_HT_TONEB:
+ case PISA_HT_TONEC:
+ ret = build_halftone ( info, do_clr, halftone, threshold );
+ break;
+
+ case PISA_HT_DITHERA:
+ case PISA_HT_DITHERB:
+ case PISA_HT_DITHERC:
+ case PISA_HT_DITHERD:
+ ret = build_dither ( info, do_clr, halftone, threshold );
+ break;
+ }
+
+ return ret;
+}
+
+
+
+/*------------------------------------------------------------*/
+static int build_lineart ( pisa_image_info * info,
+ unsigned char do_clr,
+ long threshold )
+{
+ int i, j;
+ unsigned char val;
+ unsigned char * img;
+ long thdiff;
+
+ thdiff = 128 - threshold;
+
+ for ( i = 0; i < info->m_height; i++ )
+ {
+ img = info->m_img + i * info->m_rowbytes;
+ for ( j = 0; j < info->m_width; j++ )
+ {
+ val = dropout ( * img, * ( img + 1 ), * ( img + 2 ),
+ do_clr, thdiff );
+ if ( val > 128 )
+ val = 255;
+ else
+ val = 0;
+
+ * ( img + 0 ) = val;
+ * ( img + 1 ) = val;
+ * ( img + 2 ) = val;
+ img += 3;
+ }
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+
+/*------------------------------------------------------------*/
+static long Hard [ 8 ] [ 8 ] =
+{
+ { 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128 }
+};
+
+static long Soft [ 8 ] [ 8 ] =
+{
+ { 131, 136, 136, 131, 124, 119, 119, 124 },
+ { 136, 149, 149, 136, 119, 106, 106, 119 },
+ { 136, 149, 149, 136, 119, 106, 106, 119 },
+ { 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 131, 136, 136, 131, 124, 119, 119, 124 },
+ { 124, 119, 119, 124, 131, 136, 136, 131 },
+ { 119, 106, 106, 119, 136, 149, 149, 136 },
+ { 124, 119, 119, 124, 131, 136, 136, 131 }
+};
+
+static long Net [ 8 ] [ 8 ] =
+{
+ { 146, 173, 173, 146, 109, 82, 82, 109 },
+ { 173, 236, 236, 173, 82, 19, 19, 82 },
+ { 173, 236, 236, 173, 82, 19, 19, 82 },
+ { 146, 173, 173, 146, 109, 82, 82, 109 },
+ { 109, 82, 82, 109, 148, 173, 173, 146 },
+ { 82, 19, 19, 82, 173, 236, 236, 173 },
+ { 82, 19, 19, 82, 173, 236, 236, 173 },
+ { 109, 89, 82, 109, 146, 173, 173, 146 }
+};
+
+static long Bayer [ 4 ] [ 4 ] =
+{
+ { 248, 120, 216, 88 },
+ { 56, 184, 24, 152 },
+ { 200, 72, 232, 104 },
+ { 8, 136, 40, 168 }
+};
+
+static long Spiral [ 4 ] [ 4 ] =
+{
+ { 40, 152, 136, 24 },
+ { 168, 248, 232, 120 },
+ { 184, 200, 216, 104 },
+ { 56, 72, 88, 8 }
+};
+
+static long Net4x4 [ 4 ] [ 4 ] =
+{
+ { 24, 40, 152, 104 },
+ { 56, 248, 232, 136 },
+ { 168, 200, 216, 88 },
+ { 120, 184, 72, 8 }
+};
+
+static long Net8x8 [ 8 ] [ 8 ] =
+{
+ { 236, 188, 52, 4, 68, 100, 164, 228 },
+ { 180, 44, 12, 140, 132, 92, 108, 172 },
+ { 36, 20, 148, 212, 204, 124, 84, 76 },
+ { 28, 156, 220, 252, 244, 196, 116, 60 },
+ { 68, 100, 164, 228, 236, 188, 52, 4 },
+ { 132, 92, 108, 172, 180, 44, 12, 140 },
+ { 204, 124, 84, 76, 36, 20, 148, 212 },
+ { 244, 196, 116, 60, 28, 156, 220, 252 }
+};
+
+static struct _halftone
+{
+ long * matrix;
+ long size;
+} proc_halftone [ 8 ] =
+{
+ { 0, 8 },
+ { Hard [ 0 ], 8 },
+ { Soft [ 0 ], 8 },
+ { Net [ 0 ], 8 },
+ { Bayer [ 0 ], 4 },
+ { Spiral [ 0 ], 4 },
+ { Net4x4 [ 0 ], 4 },
+ { Net8x8 [ 0 ], 8 }
+};
+
+static int build_halftone ( pisa_image_info * info,
+ unsigned char do_clr,
+ unsigned char halftone,
+ long threshold )
+{
+ int i, j, k;
+ long sc1, sc2, sc3, sc4, sc5, sc6, long_val;
+ unsigned char val;
+ unsigned char * img;
+ long thdiff, thre;
+ long * pattern;
+ long matrix_size;
+ long * rowa, * rowb, * rowc;
+
+ thdiff = 128 - threshold;
+
+ pattern = proc_halftone [ halftone ].matrix;
+ if ( pattern == 0 )
+ return PISA_ERR_PARAMETER;
+
+ matrix_size = proc_halftone [ halftone ].size;
+
+ rowa = new long [ info->m_width + 10 ];
+ rowb = new long [ info->m_width + 10 ];
+ rowc = new long [ info->m_width + 10 ];
+
+ ::memset ( rowa, 0, ( info->m_width + 10 ) * sizeof ( long ) );
+ ::memset ( rowb, 0, ( info->m_width + 10 ) * sizeof ( long ) );
+ ::memset ( rowc, 0, ( info->m_width + 10 ) * sizeof ( long ) );
+
+ for ( i = 0; i < info->m_height; i++ )
+ {
+ img = info->m_img + i * info->m_rowbytes;
+
+ for ( j = 0; j < info->m_width; j++ )
+ {
+ k = j + 2;
+ sc1 = * ( rowa + k );
+ sc2 = * ( rowb + k - 1 );
+ sc3 = * ( rowb + k );
+ sc4 = * ( rowb + k + 1 );
+ sc5 = * ( rowc + k - 2 );
+ sc6 = * ( rowc + k - 1 );
+
+ val = dropout ( * img, * ( img + 1 ), * ( img + 2 ),
+ do_clr, thdiff );
+ long_val = val + ( sc3 + sc6 ) / 4 + ( sc1 + sc2 + sc4 + sc5 ) / 8;
+ thre = * ( pattern + ( i & ( matrix_size - 1 ) ) * matrix_size +
+ ( j & ( matrix_size - 1 ) ) );
+ if ( long_val >= thre )
+ {
+ val = 255;
+ * ( rowc + k ) = long_val - 255;
+ }
+ else
+ {
+ val = 0;
+ * ( rowc + k ) = long_val;
+ }
+
+ * ( img + 0 ) = val;
+ * ( img + 1 ) = val;
+ * ( img + 2 ) = val;
+ img += 3;
+ }
+ ::memcpy ( rowa, rowb, ( info->m_width + 10 ) * sizeof ( long ) );
+ ::memcpy ( rowb, rowc, ( info->m_width + 10 ) * sizeof ( long ) );
+ ::memset ( rowc, 0, ( info->m_width + 10 ) * sizeof ( long ) );
+ }
+
+ delete [ ] rowa;
+ delete [ ] rowb;
+ delete [ ] rowc;
+
+ return PISA_ERR_SUCCESS;
+}
+
+
+/*------------------------------------------------------------*/
+static int build_dither ( pisa_image_info * info,
+ unsigned char do_clr,
+ unsigned char halftone,
+ long threshold )
+{
+ int i, j;
+ unsigned char val;
+ unsigned char * img;
+ long thdiff, thre;
+ long * pattern;
+ long matrix_size;
+
+ thdiff = 128 - threshold;
+
+ pattern = proc_halftone [ halftone ].matrix;
+ if ( pattern == 0 )
+ return PISA_ERR_PARAMETER;
+
+ matrix_size = proc_halftone [ halftone ].size;
+
+ for ( i = 0; i < info->m_height; i++ )
+ {
+ img = info->m_img + i * info->m_rowbytes;
+ for ( j = 0; j < info->m_width; j++ )
+ {
+ val = dropout ( * img, * ( img + 1 ), * ( img + 2 ),
+ do_clr, thdiff );
+ thre = * ( pattern + ( i & ( matrix_size - 1 ) ) * matrix_size +
+ ( j & ( matrix_size - 1 ) ) );
+ if ( ( long ) val >= thre )
+ val = 255;
+ else
+ val = 0;
+ * ( img + 0 ) = val;
+ * ( img + 1 ) = val;
+ * ( img + 2 ) = val;
+ }
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+static unsigned char dropout ( unsigned char red,
+ unsigned char grn,
+ unsigned char blu,
+ unsigned char do_clr,
+ long thdiff )
+{
+ unsigned char ret;
+ long val;
+
+ ret = 0;
+
+ if ( thdiff != 0 )
+ {
+ // red
+ val = red + thdiff;
+ if ( val < 0 ) val = 0;
+ if ( val > 255 ) val = 255;
+ red = val;
+
+ // green
+ val = grn + thdiff;
+ if ( val < 0 ) val = 0;
+ if ( val > 255 ) val = 255;
+ grn = val;
+
+ // blue
+ val = blu + thdiff;
+ if ( val < 0 ) val = 0;
+ if ( val > 255 ) val = 255;
+ blu = val;
+ }
+
+ switch ( do_clr )
+ {
+ case PISA_DO_NONE:
+ val = red * 2 + grn * 6 + blu * 2;
+ if ( val == 0 )
+ ret = 0;
+ else
+ ret = ( unsigned char ) ( val / 10 );
+ break;
+
+ case PISA_DO_RED:
+ ret = red;
+ break;
+
+ case PISA_DO_GREEN:
+ ret = grn;
+ break;
+
+ case PISA_DO_BLUE:
+ ret = blu;
+ break;
+ }
+
+ return ret;
+}
diff --git a/frontend/pisa_scan_tool.h b/frontend/pisa_scan_tool.h
new file mode 100644
index 0000000..eeb86e9
--- /dev/null
+++ b/frontend/pisa_scan_tool.h
@@ -0,0 +1,63 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005, 2008 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___EPS_LIVE_PREVIEW_H
+#define ___EPS_LIVE_PREVIEW_H
+
+#include "pisa_structs.h"
+#include "pisa_enums.h"
+
+/*------------------------------------------------------------*/
+/* FIXME: calc_*() only used by scan_manager --> make member function
+ */
+int calc_rowbytes ( int width, pisa_pixel_type type );
+int calc_bitperpix ( pisa_pixel_type pixel, pisa_bitdepth_type bitdepth );
+
+int tool_lut ( pisa_image_info * info,
+ const gamma_struct * lut );
+int tool_matrix ( pisa_image_info * info,
+ int saturation,
+ const double * color_profile );
+
+int generate_color_coef ( double * dst,
+ const double * src,
+ int saturation );
+
+int build_gray ( pisa_image_info * info,
+ unsigned char do_clr );
+
+int build_bw ( pisa_image_info * info,
+ unsigned char do_clr,
+ unsigned char halftone,
+ long threshold );
+
+#endif // ___EPS_LIVE_PREVIEW_H
+
+
diff --git a/frontend/pisa_settings.cc b/frontend/pisa_settings.cc
new file mode 100644
index 0000000..fada091
--- /dev/null
+++ b/frontend/pisa_settings.cc
@@ -0,0 +1,149 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2008 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+/*------------------------------------------------------------*/
+#include <stdio.h>
+
+/*------------------------------------------------------------*/
+#include "pisa_structs.h"
+#include "pisa_settings.h"
+
+/*------------------------------------------------------------*/
+void settings::init ( marquee ** marq )
+{
+ marquee_node * new_node;
+
+ new_node = new marquee_node;
+
+ new_node->next = 0;
+ new_node->data = * marq;
+
+ top = new_node;
+}
+
+long
+settings::get_marquee_size (void) const
+{
+ long size;
+ marquee_node * tmp;
+
+ size = 1;
+ tmp = top;
+
+ while ( tmp->next )
+ {
+ tmp = tmp->next;
+ size++;
+ }
+
+ return size;
+}
+
+const marquee&
+settings::get_marquee (long index) const
+{
+ return const_cast <settings *> (this)->get_marquee (index);
+}
+
+marquee&
+settings::get_marquee (long index)
+{
+ if (0 > index)
+ index = get_marquee_size () - 1;
+
+ marquee_node *current = top;
+ long counter = 0;
+
+ while (index != counter++)
+ current = current->next;
+
+ return *current->data;
+}
+
+/*------------------------------------------------------------*/
+void settings::add_marquee ( marquee ** marq )
+{
+ marquee_node * tmp;
+ marquee_node * new_node;
+
+ new_node = new marquee_node;
+
+ tmp = top;
+
+ while ( tmp->next )
+ tmp = tmp->next;
+
+ new_node->next = 0;
+ new_node->data = * marq;
+
+ tmp->next = new_node;
+}
+
+
+void
+settings::del_marquee (long index)
+{
+ if (!top) return;
+
+ if (0 > index)
+ index = get_marquee_size () - 1;
+
+ int cnt = 0;
+ marquee_node *del_node = top;
+ marquee_node *pre_node = top;
+
+ while (cnt != index - 1)
+ {
+ if (pre_node->next)
+ {
+ pre_node = pre_node->next;
+ ++index;
+ }
+ else
+ return; // error
+ }
+
+ del_node = pre_node->next;
+ pre_node->next = del_node->next;
+
+ delete del_node->data;
+ delete del_node;
+}
+
+/*------------------------------------------------------------*/
+void settings::delete_all ( void )
+{
+ while ( top )
+ {
+ delete top->data;
+ top = top->next;
+ }
+}
+
diff --git a/frontend/pisa_settings.h b/frontend/pisa_settings.h
new file mode 100644
index 0000000..f7554fa
--- /dev/null
+++ b/frontend/pisa_settings.h
@@ -0,0 +1,75 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2008 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_SETTINGS_H
+#define ___PISA_SETTINGS_H
+
+#include "pisa_structs.h"
+#include "pisa_marquee.h"
+
+class settings
+{
+ public:
+
+ // attribute
+ char destination;
+
+ imagetype imgtype;
+ long resolution;
+ bool enable_start_button;
+ bool enable_draft_mode;
+ long usm;
+
+ double max_area [ 2 ];
+ double coef [ 9 ];
+
+ long unit;
+
+ // operation
+ void init ( marquee ** marq );
+
+ long get_marquee_size (void) const;
+
+ const marquee& get_marquee (long index = -1) const;
+ marquee& get_marquee (long index = -1);
+
+ void add_marquee ( marquee ** marq );
+ void del_marquee (long index = -1);
+ void delete_all ( void );
+
+ private:
+
+ // attribute
+ marquee_node * top;
+
+};
+
+#endif // ___PISA_SETTINGS_H
+
diff --git a/frontend/pisa_structs.h b/frontend/pisa_structs.h
new file mode 100644
index 0000000..29b5a61
--- /dev/null
+++ b/frontend/pisa_structs.h
@@ -0,0 +1,226 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005, 2008 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_STRUCTS_H
+#define ___PISA_STRUCTS_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_GTK_GTK_H
+#include <gtk/gtk.h>
+#ifndef HAVE_GTK_2
+#include <gdk_imlib.h>
+#endif
+#endif
+
+// scanner
+/*--------------------------------------------------------------*/
+typedef struct _gamma_struct
+{
+ unsigned char gamma_r [ 256 ];
+ unsigned char gamma_g [ 256 ];
+ unsigned char gamma_b [ 256 ];
+} gamma_struct;
+
+
+/*--------------------------------------------------------------*/
+typedef struct _imagetype
+{
+ long id;
+ char pixeltype;
+ char bitdepth;
+ char de_screening;
+ char ae_option;
+ char dropout;
+ char monoopt;
+ char halftone;
+} imagetype;
+
+// ui
+/*--------------------------------------------------------------*/
+template <class Type>
+struct _point
+{
+ Type x, y;
+
+ _point ( ) { x = y = 0; }
+ _point ( Type u, Type v = 0 ) { x = u, y = v; }
+ _point ( const _point & pt ) { x = pt.x; y = pt.y; }
+ _point & operator= ( const _point & pt )
+ {
+ if ( & pt != this )
+ {
+ x = pt.x;
+ y = pt.y;
+ }
+ return ( * this );
+ }
+
+ // == operator
+ friend int operator== ( const _point & pt1, const _point & pt2 )
+ {
+ return ( pt1.x == pt2.x && pt1.y == pt2.y );
+ }
+
+ // - operator
+ friend _point operator- ( const _point & pt1, const _point & pt2 )
+ {
+ return ( _point ( pt1.x - pt2.x, pt1.y - pt2.y ) );
+ }
+
+ // + operator
+ friend _point operator+ ( const _point & pt1, const _point & pt2 )
+ {
+ return ( _point ( pt1.x + pt2.x, pt1.y + pt2.y ) );
+ }
+};
+
+typedef _point <double> _pointD;
+typedef _point <long> _pointL;
+
+/*--------------------------------------------------------------*/
+template <class Type>
+struct _rect
+{
+ Type top, bottom, right, left;
+
+ _rect ( ) { top = bottom = right = left = Type ( 0 ); }
+ _rect ( Type _top, Type _bottom, Type _right, Type _left ):
+ top ( _top ), bottom ( _bottom ), right ( _right ), left ( _left ) { }
+ _rect ( const _rect & x )
+ {
+ top = x.top, bottom = x.bottom;
+ right = x.right, left = x.left;
+ }
+ _rect & operator= ( const _rect & x )
+ {
+ if ( & x != this )
+ {
+ top = x.top, bottom = x.bottom;
+ right = x.right, left = x.left;
+ }
+ return ( * this );
+ }
+
+ // == operator
+ friend int operator== ( const _rect & rc1, const _rect & rc2 )
+ {
+ return ( rc1.top == rc2.top && rc1.bottom == rc2.bottom &&
+ rc1.right == rc2.right && rc1.left == rc2.left );
+ }
+
+ // - operator
+ friend _rect operator- ( const _rect & rc1, const _rect & rc2 )
+ {
+ return ( _rect ( rc1.top - rc2.top, rc1.bottom - rc2.bottom,
+ rc1.right - rc2.right, rc1.left - rc2.left ) );
+ }
+
+ // + operator
+ friend _rect operator+ ( const _rect & rc1, const _rect & rc2 )
+ {
+ return ( _rect ( rc1.top + rc2.top, rc1.bottom + rc2.bottom,
+ rc1.right + rc2.right, rc1.left + rc2.left ) );
+ }
+
+ // empty?
+ friend int is_rect_empty ( const _rect & rc )
+ {
+ return ( rc.top == 0 && rc.bottom == 0 && rc.right == 0 && rc.left == 0 );
+ }
+};
+
+typedef _rect <_point <long> > _rectPL;
+typedef _rect <double> _rectD;
+typedef _rect <long> _rectL;
+
+
+#ifdef HAVE_GTK_GTK_H
+/*--------------------------------------------------------------*/
+typedef struct _toolbar_items
+{
+ GtkWidget * widget;
+ char ** xpm;
+ GtkSignalFunc * func;
+} toolbar_items;
+
+/*--------------------------------------------------------------*/
+typedef struct _menu_items
+{
+ long id;
+ GtkWidget * widget;
+ const char * name;
+ GtkSignalFunc * func;
+} menu_items;
+
+
+/*------------------------------------------------------------*/
+typedef struct _scale_items
+{
+ long id;
+ GtkWidget * widget;
+ GtkAdjustment * adjust;
+ GtkSignalFunc * func;
+ float val;
+ float min;
+ float max;
+ float step;
+ float page;
+} scale_items;
+#endif
+
+typedef struct
+{
+ unsigned char * m_img;
+ long m_width;
+ long m_height;
+ long m_rowbytes;
+} _pisa_image_info;
+
+#include <cstddef>
+
+class pisa_image_info : public _pisa_image_info
+{
+public:
+ size_t m_bits_per_pixel;
+
+ pisa_image_info () : m_bits_per_pixel (24)
+ {
+ m_img = 0;
+ m_width = 0;
+ m_height = 0;
+ m_rowbytes = 0;
+ };
+};
+
+#endif // ___PISA_STRUCTS_H
+
diff --git a/frontend/pisa_tool.cc b/frontend/pisa_tool.cc
new file mode 100644
index 0000000..34864db
--- /dev/null
+++ b/frontend/pisa_tool.cc
@@ -0,0 +1,184 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+/*--------------------------------------------------------------*/
+#include "pisa_tool.h"
+
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+
+template <>
+double similarity <double> ( const double & A,
+ const double & a,
+ const double & b )
+{
+ double B;
+
+ if ( a == 0 )
+ return 0;
+
+ B = ( b * A ) / a;
+
+ return B;
+}
+
+/*--------------------------------------------------------------*/
+GtkWidget * xpm2widget ( GtkWidget * widget, char ** xpm_data )
+{
+ GdkBitmap * mask;
+ GdkPixmap * pixmap;
+ GtkWidget * pixmap_widget;
+ GtkStyle * style;
+
+ style = ::gtk_widget_get_style ( widget );
+
+ pixmap = ::gdk_pixmap_create_from_xpm_d ( widget->window,
+ & mask,
+ & style->bg [ GTK_STATE_NORMAL ],
+ xpm_data );
+
+ pixmap_widget = ::gtk_pixmap_new ( pixmap, mask );
+
+ ::gtk_widget_show ( pixmap_widget );
+
+ return pixmap_widget;
+}
+
+
+/*------------------------------------------------------------*/
+GtkWidget * xpmlabelbox ( GtkWidget * parent, char ** xpm_data,
+ const char * text )
+{
+ GtkWidget * hbox;
+ GtkWidget * label;
+ GtkWidget * img;
+
+ hbox = ::gtk_hbox_new ( FALSE, 0 );
+ ::gtk_container_border_width ( GTK_CONTAINER ( hbox ), 3 );
+
+ img = ::xpm2widget ( parent, xpm_data );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), img, FALSE, FALSE, 3 );
+ ::gtk_widget_show ( img );
+
+ label = ::gtk_label_new ( text );
+ ::gtk_box_pack_start ( GTK_BOX ( hbox ), label, FALSE, FALSE, 3 );
+ ::gtk_widget_show ( label );
+
+ return hbox;
+}
+
+
+/*------------------------------------------------------------*/
+GtkWidget * pisa_create_toolbar ( const GtkWidget * parent,
+ toolbar_items * list )
+{
+ GtkWidget * toolbar;
+
+#ifdef HAVE_GTK_2
+ toolbar = ::gtk_toolbar_new ();
+#else
+ toolbar = ::gtk_toolbar_new ( GTK_ORIENTATION_HORIZONTAL,
+ GTK_TOOLBAR_ICONS );
+#endif
+
+ while ( list->func )
+ {
+ if ( list->xpm )
+ {
+ list->widget = ::xpm2widget ( GTK_WIDGET ( parent ), list->xpm );
+ ::gtk_toolbar_append_item ( GTK_TOOLBAR ( toolbar ),
+ 0, 0, 0,
+ list->widget,
+ GTK_SIGNAL_FUNC ( list->func ),
+ 0 );
+ }
+ else
+ ::gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
+
+ list++;
+ }
+
+ return toolbar;
+}
+
+
+/*------------------------------------------------------------*/
+GtkWidget * pisa_create_option_menu ( menu_items * list )
+{
+ GtkWidget * option_menu;
+ GtkWidget * menu, * item;
+
+ option_menu = ::gtk_option_menu_new ( );
+ ::gtk_widget_show ( option_menu );
+
+ menu = ::gtk_menu_new ( );
+
+ while ( list->func )
+ {
+ list->widget = item = ::gtk_menu_item_new_with_label (_(list->name));
+ ::gtk_signal_connect ( GTK_OBJECT ( item ), "activate",
+ GTK_SIGNAL_FUNC ( list->func ),
+ ( void * ) & list->id );
+ ::gtk_menu_append ( GTK_MENU ( menu ), item );
+ ::gtk_widget_show ( item );
+
+ list++;
+ }
+
+ ::gtk_option_menu_set_menu ( GTK_OPTION_MENU ( option_menu ), menu );
+
+ return option_menu;
+}
+
+
+/*--------------------------------------------------------------*/
+GtkWidget * pisa_create_scale ( scale_items * list )
+{
+ GtkWidget * scale;
+ GtkObject * adjust;
+
+ adjust = ::gtk_adjustment_new ( list->val,
+ list->min,
+ list->max,
+ list->step,
+ list->page,
+ 0 );
+ list->adjust = GTK_ADJUSTMENT ( adjust );
+
+ ::gtk_signal_connect ( adjust, "value_changed",
+ GTK_SIGNAL_FUNC ( list->func ),
+ ( void * ) & list->id );
+
+ scale = ::gtk_hscale_new ( GTK_ADJUSTMENT ( adjust ) );
+
+ list->widget = scale;
+
+ return scale;
+}
diff --git a/frontend/pisa_tool.h b/frontend/pisa_tool.h
new file mode 100644
index 0000000..e4edae3
--- /dev/null
+++ b/frontend/pisa_tool.h
@@ -0,0 +1,79 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_TOOL_H
+#define ___PISA_TOOL_H
+
+#include <gtk/gtk.h>
+#include <config.h>
+#include "pisa_structs.h"
+
+template <class Type>
+Type similarity ( const Type & A,
+ const Type & a,
+ const Type & b )
+{
+ Type B;
+
+ if ( a == 0 )
+ return 0;
+
+ if ( 0 < A )
+ B = ( b * A + a / 2 ) / a;
+ else
+ B = ( b * A - a / 2 ) / a;
+
+ return B;
+}
+
+
+template <class Type>
+int pt_in_rect ( const _rect <Type> & rect, const _point <Type> & pt )
+{
+ if ( rect.left < pt.x && pt.x < rect.right &&
+ rect.top < pt.y && pt.y < rect.bottom )
+ return 1;
+ else
+ return 0;
+}
+
+GtkWidget * xpm2widget ( GtkWidget * widget, char ** xpm_data );
+GtkWidget * xpmlabelbox ( GtkWidget * parent, char ** xpm_data,
+ const char * text );
+
+GtkWidget * pisa_create_toolbar ( const GtkWidget * parent,
+ toolbar_items * list );
+GtkWidget * pisa_create_option_menu ( menu_items * list );
+GtkWidget * pisa_create_scale ( scale_items * items );
+
+
+
+#endif // ___PISA_TOOL_H
+
diff --git a/frontend/pisa_view_manager.cc b/frontend/pisa_view_manager.cc
new file mode 100644
index 0000000..ef7a966
--- /dev/null
+++ b/frontend/pisa_view_manager.cc
@@ -0,0 +1,1134 @@
+/* pisa_view_manager.cc
+ Copyright (C) 2001, 2004, 2005, 2008, 2009 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+
+/*------------------------------------------------------------*/
+#include <gtk/gtk.h>
+#ifndef HAVE_GTK_2
+#include <gdk_imlib.h>
+#endif
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <regex.h>
+#include <dirent.h>
+#include <locale.h>
+#include <libgen.h>
+#include <time.h>
+
+/*------------------------------------------------------------*/
+#include "pisa_view_manager.h"
+#include "pisa_error.h"
+#include "pisa_main.h"
+#include "pisa_scan_tool.h"
+#include "pisa_default_val.h"
+#include "pisa_gimp.h"
+#include "pisa_aleart_dialog.h"
+#include "pisa_change_unit.h"
+#include "pisa_preference.h"
+#include "pisa_scan_selector.h"
+
+#include "file-selector.h"
+
+#include "imgstream.hh"
+
+
+#define DEFAULT_RESOLUTION 300 // dpi
+
+/*------------------------------------------------------------*/
+view_manager * g_view_manager = 0;
+
+/*------------------------------------------------------------*/
+int view_manager::create_view_manager ( int argc, char * argv [ ] )
+{
+ ::gtk_set_locale ( );
+ ::setlocale (LC_NUMERIC, "C");
+ ::gtk_init ( & argc, & argv );
+
+#ifndef HAVE_GTK_2
+ ::gdk_imlib_init ( );
+ ::gtk_widget_push_visual ( ::gdk_imlib_get_visual ( ) );
+ ::gtk_widget_push_colormap ( ::gdk_imlib_get_colormap ( ) );
+#endif
+
+ try
+ {
+ if ( ::g_view_manager )
+ throw pisa_error ( PISA_ERR_PARAMETER );
+
+ ::g_view_manager = new view_manager;
+ if ( ::g_view_manager == 0 )
+ throw pisa_error ( PISA_ERR_OUTOFMEMORY );
+
+ // initialize parameters
+ ::g_view_manager->init ( );
+
+ }
+ catch ( pisa_error & err )
+ {
+ aleart_dialog aleart_dlg;
+
+ aleart_dlg.message_box ( 0, err.get_error_string ( ) );
+
+ if ( ::g_view_manager )
+ {
+ delete ::g_view_manager;
+ ::g_view_manager = 0;
+ }
+
+ return err.get_error_id ( );
+ }
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+void view_manager::release_view_manager ( void )
+{
+ if ( ::g_view_manager )
+ {
+ delete ::g_view_manager;
+ ::g_view_manager = 0;
+ }
+}
+
+/*------------------------------------------------------------*/
+int view_manager::main ( void )
+{
+ gtk_main ( );
+
+ return PISA_ERR_SUCCESS;
+}
+
+/*------------------------------------------------------------*/
+void view_manager::init ( void )
+{
+ // initialize
+ m_scanmanager_cls = 0;
+ m_main_cls = 0;
+ m_prev_cls = 0;
+ m_imgctrl_cls = 0;
+ m_gamma_cls = 0;
+ m_config_cls = 0;
+ m_filsel_cls = 0;
+ m_scansel_cls = 0;
+
+ // open scanner
+ m_scanmanager_cls = new scan_manager;
+
+ if ( ! m_scanmanager_cls )
+ throw pisa_error ( PISA_ERR_OUTOFMEMORY );
+
+ open_device ( );
+
+ m_set.resolution = DEFAULT_RESOLUTION;
+ m_set.enable_start_button =
+ m_scanmanager_cls->push_button_needs_polling ();
+ m_set.enable_draft_mode = false;
+ m_set.usm = 1;
+ m_set.unit = PISA_UNIT_INCHES;
+
+ if ( pisa_gimp_plugin ( ) )
+ m_set.destination = PISA_DE_GIMP;
+ else
+ m_set.destination = PISA_DE_FILE;
+
+ m_main_cls = new main_window;
+ m_prev_cls = new preview_window;
+ m_imgctrl_cls = new image_controls;
+ m_gamma_cls = new gamma_correction;
+ m_config_cls = new config_window;
+ m_filsel_cls = new file_selector;
+
+ if ( ! m_main_cls ||
+ ! m_prev_cls ||
+ ! m_imgctrl_cls ||
+ ! m_gamma_cls ||
+ ! m_config_cls ||
+ ! m_filsel_cls )
+ {
+ destroy ( );
+ throw ( PISA_ERR_OUTOFMEMORY );
+ }
+
+ init_img_info ();
+
+ m_main_cls->init ( );
+ m_prev_cls->init ( );
+ m_gamma_cls->init ( );
+ m_config_cls->init ( );
+ m_filsel_cls->init ( );
+
+ load_preference ( );
+
+}
+
+/*------------------------------------------------------------*/
+void view_manager::destroy ( void )
+{
+ save_preference ( );
+
+ m_set.delete_all ( );
+
+ if ( m_scanmanager_cls )
+ {
+ try
+ {
+ close_device ( );
+ }
+ catch ( pisa_error & err )
+ {
+ aleart_dialog aleart_dlg;
+
+ aleart_dlg.message_box ( 0, err.get_error_string ( ) );
+ }
+
+ delete m_scanmanager_cls;
+ m_scanmanager_cls = 0;
+ }
+
+ if ( m_main_cls )
+ {
+ delete m_main_cls;
+ m_main_cls = 0;
+ }
+
+ if ( m_prev_cls )
+ {
+ delete m_prev_cls;
+ m_prev_cls = 0;
+ }
+
+ if ( m_imgctrl_cls )
+ {
+ delete m_imgctrl_cls;
+ m_imgctrl_cls = 0;
+ }
+
+ if ( m_gamma_cls )
+ {
+ delete m_gamma_cls;
+ m_gamma_cls = 0;
+ }
+
+ if ( m_config_cls )
+ {
+ delete m_config_cls;
+ m_config_cls = 0;
+ }
+
+ if ( m_filsel_cls )
+ {
+ delete m_filsel_cls;
+ m_filsel_cls = 0;
+ }
+
+ if ( m_scansel_cls )
+ {
+ delete m_scansel_cls;
+ m_scansel_cls = 0;
+ }
+
+ ::pisa_quit ( );
+}
+
+/*------------------------------------------------------------*/
+GtkWidget * view_manager::create_window ( pisa_window_id id )
+{
+ GtkWidget * ret = 0;
+
+ switch ( id )
+ {
+ case ID_WINDOW_MAIN:
+ ret = m_main_cls->create_window ( 0 );
+ break;
+
+ case ID_WINDOW_PREV:
+ ret = m_prev_cls->create_window ( 0 );
+ break;
+
+ case ID_WINDOW_CONFIG:
+ ret = m_config_cls->create_window ( m_main_cls->get_widget ( ) );
+ break;
+ }
+
+ return ret;
+}
+
+/*------------------------------------------------------------*/
+int view_manager::close_window ( pisa_window_id id, int destroy_flag )
+{
+ switch ( id )
+ {
+ case ID_WINDOW_MAIN:
+ m_main_cls->close_window ( destroy_flag );
+ destroy ( );
+ break;
+
+ case ID_WINDOW_PREV:
+ m_prev_cls->close_window ( destroy_flag );
+ break;
+
+ case ID_WINDOW_CONFIG:
+ m_config_cls->close_window ( destroy_flag );
+ break;
+
+ default:
+ return PISA_ERR_PARAMETER;
+ }
+
+ return PISA_ERR_PARAMETER;
+}
+
+image_controls *
+view_manager::get_image_controls ()
+{
+ return m_imgctrl_cls;
+}
+
+gamma_correction *
+view_manager::get_gamma_correction ()
+{
+ return m_gamma_cls;
+}
+
+/*------------------------------------------------------------*/
+void * view_manager::get_window_cls ( pisa_window_id id )
+{
+ void * ret = 0;
+
+ switch ( id )
+ {
+ case ID_WINDOW_MAIN:
+ ret = m_main_cls;
+ break;
+
+ case ID_WINDOW_PREV:
+ ret = m_prev_cls;
+ break;
+
+ case ID_WINDOW_CONFIG:
+ ret = m_config_cls;
+ break;
+ }
+
+ return ret;
+}
+
+/*------------------------------------------------------------*/
+void view_manager::sensitive ( void )
+{
+ int is_img = m_prev_cls->is_prev_img ( );
+
+ m_main_cls->sensitive ( is_img );
+ m_imgctrl_cls->sensitive ( is_img );
+ m_gamma_cls->sensitive ( is_img );
+
+ m_scanmanager_cls->has_prev_img ( is_img );
+}
+
+/*------------------------------------------------------------*/
+int view_manager::is_gimp ( void )
+{
+ return pisa_gimp_plugin ( );
+}
+
+bool
+view_manager::needs_duplex_rotation (void) const
+{
+ if (!m_scanmanager_cls->using_duplex ())
+ return false;
+
+ if (!m_scanmanager_cls->adf_duplex_direction_matches ())
+ { // double pass ADF
+ return m_filsel_cls->has_left_edge_binding ();
+ }
+ else
+ { // single pass ADF
+ return !m_filsel_cls->has_left_edge_binding ();
+ }
+}
+
+/*------------------------------------------------------------*/
+void view_manager::start_scan ( void )
+{
+ using iscan::file_opener;
+ using iscan::imgstream;
+
+ if (PISA_ERR_SUCCESS != init_scan_param ())
+ return;
+
+ file_opener *fo = NULL;
+ imgstream *is = NULL;
+
+ bool remove = false;
+
+ // All types of scans need to provide visual feedback on their
+ // progress and for consecutive scans (via ADF or with the start
+ // button enabled) it is better to create it here. That way, it
+ // will not disappear and reappear between calls to scan_file() or
+ // scan_gimp(), eliminating rather annoying flicker.
+ _feedback = new progress_window (m_main_cls->get_widget ());
+ switch ( m_set.destination )
+ {
+ case PISA_DE_FILE:
+ {
+ m_filsel_cls->init ();
+#ifndef HAVE_GTK_2
+ m_filsel_cls->create_window (m_main_cls->get_widget (), m_set.option,
+ m_set.enable_start_button,
+ PISA_PT_BW == m_set.imgtype.pixeltype);
+#else
+ GtkWidget *w = m_main_cls->get_widget ();
+ m_filsel_cls->create_window (GTK_WINDOW (gtk_widget_get_parent (w)), w,
+ (m_scanmanager_cls->using_adf ()
+ || m_set.enable_start_button),
+ m_scanmanager_cls->using_duplex (),
+ _image_format.c_str ());
+#endif /* HAVE_GTK_2 */
+ m_filsel_cls->hide ();
+ if (!m_filsel_cls->get_pathname ()) // dialog cancelled
+ break;
+
+ // FIXME: the file_selector (or file name generator) should
+ // signal a change in file format and all interested
+ // parties, e.g. an object managing the configuration
+ // file, should take note and appropriate action
+ _image_format = m_filsel_cls->get_format_name ();
+
+ if (is_multi_image ()
+ && !m_filsel_cls->multi_page_mode ())
+ fo = new file_opener (std::string (m_filsel_cls->get_pathname ()),
+ m_filsel_cls->get_sequence_number ());
+ else
+ fo = new file_opener (std::string (m_filsel_cls->get_pathname ()));
+
+ try
+ {
+ try
+ {
+ is = iscan::create_imgstream (*fo, m_filsel_cls->get_type (),
+ needs_duplex_rotation ());
+ }
+ catch (std::exception& oops)
+ {
+ throw pisa_error ( PISA_ERR_OUTOFMEMORY );
+ }
+ }
+ catch (pisa_error& err)
+ {
+ aleart_dialog aleart_dlg;
+ aleart_dlg.message_box (m_main_cls->get_widget (),
+ err.get_error_string ());
+ m_filsel_cls->destroy ();
+ remove = true;
+ break;
+ }
+ remove = do_scan (*is, *fo);
+ m_filsel_cls->destroy ();
+ }
+ break;
+
+ case PISA_DE_PRINTER:
+ {
+ fo = new file_opener (false);
+ is = new imgstream (*fo, iscan::PNG);
+ remove = do_scan (*is, *fo);
+ }
+ break;
+
+ case PISA_DE_GIMP:
+ do_scan_gimp ();
+ break;
+ }
+ delete is;
+ try
+ {
+ if (fo && remove) fo->remove ();
+ }
+ catch (std::exception& oops) {}
+ delete fo;
+ delete _feedback;
+
+ m_scanmanager_cls->finish_acquire ();
+}
+
+int
+view_manager::update_lut (long index)
+{
+ return update_lut (m_set.get_marquee (index));
+}
+
+int
+view_manager::update_lut (marquee& m)
+{
+ iscan::build_LUT (m_scanmanager_cls->get_scan_source (),
+ m_scanmanager_cls->get_film_type (),
+ m_set, m, !m_scanmanager_cls->has_zoom ());
+
+ return PISA_ERR_SUCCESS;
+}
+
+bool
+view_manager::change_document_source (const imagetype *img_type)
+{
+ bool success = true;
+
+ try
+ {
+ set_image_type (img_type);
+ m_set.delete_all ();
+ init_img_info (); // calls m_set.init()
+
+ main_window *main_cls
+ = static_cast <main_window *> (get_window_cls (ID_WINDOW_MAIN));
+ main_cls->enable_start_button (!m_scanmanager_cls->using_adf ());
+ }
+ catch (pisa_error& err)
+ {
+ aleart_dialog aleart_dlg;
+
+ aleart_dlg.message_box (m_main_cls->get_widget (),
+ err.get_error_string ());
+ success = false;
+ }
+
+ set_resolution (DEFAULT_RESOLUTION);
+ m_gamma_cls->reset (1);
+ m_prev_cls->resize_window ();
+ sensitive ();
+
+ return success;
+}
+
+void view_manager::set_device( char * name )
+{
+ m_scanmanager_cls->close_device();
+ m_scanmanager_cls->open_device( name );
+}
+
+char * view_manager::get_device_name() const
+{
+ return m_scansel_cls->get_device( true );
+}
+
+/*------------------------------------------------------------*/
+void view_manager::open_device ( void )
+{
+ sane_init( 0, 0 );
+ if (!m_scansel_cls)
+ m_scansel_cls = new scan_selector( true ); // dialog box
+ m_scanmanager_cls->open_device ( m_scansel_cls->get_device() );
+}
+
+/*------------------------------------------------------------*/
+void view_manager::close_device ( void )
+{
+ m_scanmanager_cls->close_device ( );
+}
+
+/*------------------------------------------------------------*/
+void view_manager::load_preference ( void )
+{
+ char pref_path [ 256 ];
+ char pips_path [ 1024 ] = "lpr";
+ char image_format [ 1024 ] = "PNG";
+
+ cfg_struct cfg [ ] =
+ {
+ { "IMG", CFG_STRING, image_format },
+ { "PIPS", CFG_STRING, pips_path }
+ };
+
+ ::strcpy ( pref_path, ::getenv ( "HOME" ) );
+ ::strcat ( pref_path, "/" );
+ ::strcat ( pref_path, PREFERENCE );
+
+ ::get_cfg ( pref_path, cfg, sizeof ( cfg ) / sizeof ( cfg [ 0 ] ) );
+
+ ::strcpy ( m_config_cls->m_cmd, pips_path );
+ _image_format = image_format;
+}
+
+/*------------------------------------------------------------*/
+void view_manager::save_preference ( void )
+{
+ char pref_path [ 256 ];
+ char pips_path [ 1024 ] = "";
+
+ cfg_struct cfg [ ] =
+ {
+ { "IMG", CFG_STRING, const_cast<char*> (_image_format.c_str ()) },
+ { "PIPS", CFG_STRING, pips_path }
+ };
+
+ ::strcpy ( pref_path, ::getenv ( "HOME" ) );
+ ::strcat ( pref_path, "/" );
+ ::strcat ( pref_path, PREFERENCE );
+
+ ::strcpy ( pips_path, m_config_cls->m_cmd );
+
+ ::set_cfg ( pref_path, cfg, sizeof ( cfg ) / sizeof ( cfg [ 0 ] ) );
+}
+
+
+int
+view_manager::init_img_info (void)
+{
+ int i;
+ marquee * marq;
+ double max_width, max_height;
+ double coef [ 9 ];
+ float brightness, contrast;
+ long focus;
+
+
+ m_scanmanager_cls->set_brightness_method(br_iscan);
+ m_scanmanager_cls->get_value(SANE_NAME_BRIGHTNESS, &brightness);
+ m_scanmanager_cls->get_value(SANE_NAME_CONTRAST, &contrast);
+
+ if (m_scanmanager_cls->using_tpu ())
+ focus = 25;
+ else
+ focus = 0;
+
+ // get max area
+ m_scanmanager_cls->get_current_max_size ( & max_width, & max_height );
+ m_scanmanager_cls->get_color_profile ( coef );
+
+ // initialize marquee
+ marq = new marquee ( max_width, max_height, focus, brightness, contrast );
+ m_set.init ( & marq );
+
+ m_set.max_area [ 0 ] = max_width;
+ m_set.max_area [ 1 ] = max_height;
+
+ for ( i = 0; i < 9; i++ )
+ m_set.coef [ i ] = coef [ i ];
+
+ return PISA_ERR_SUCCESS;
+}
+
+int
+view_manager::init_scan_param (void)
+{
+ marquee marq = get_marquee (m_set.get_marquee_size ( ) - 1);
+
+ pisa_error_id err = m_scanmanager_cls->set_scan_parameters (m_set, marq);
+
+ if (PISA_ERR_SUCCESS != err)
+ {
+ aleart_dialog dlg;
+
+ dlg.message_box (m_main_cls->get_widget (),
+ pisa_error (err).get_error_string ());
+ }
+ return err;
+}
+
+// A pile of status flags used to track what's going on in the scan
+// loop. There are probably a few more than really needed, but at
+// least these cover the individual situations we can encounter.
+
+#define SCAN_BUTTON 0x10
+#define SCAN_ADF 0x20
+#define SCAN_NEXT 0x40
+#define SCAN_FINISH 0x80
+#define SCAN_ERROR 0x01
+#define SCAN_CANCEL 0x02
+#define SCAN_SINGLE 0x04
+#define SCAN_DATA 0x08
+
+bool
+view_manager::do_scan (iscan::imgstream& is, const iscan::file_opener& fo,
+ bool first_time_around)
+{
+ static int status;
+
+ if (first_time_around) status = 0;
+ if (!m_filsel_cls->multi_page_mode ()) status |= SCAN_SINGLE;
+ if (wait_for_button ()) status |= SCAN_BUTTON | SCAN_NEXT;
+ if (m_scanmanager_cls->using_adf ()) status |= SCAN_ADF | SCAN_NEXT;
+
+ try
+ {
+ do
+ {
+ is.next ();
+ status &= ~SCAN_DATA;
+ scan_file (is, &status, first_time_around);
+ first_time_around = false;
+ if (status & SCAN_ERROR ) status &= ~SCAN_NEXT;
+ if (status & SCAN_CANCEL) status &= ~SCAN_NEXT;
+ if (status & SCAN_FINISH) status &= ~SCAN_NEXT;
+ if ((status & SCAN_DATA)
+ && !(status & (SCAN_ERROR | SCAN_CANCEL)))
+ {
+ is.flush ();
+ print (fo.name ());
+ }
+ }
+ while (status & SCAN_NEXT);
+ }
+ catch (std::exception& oops)
+ {
+ return false;
+ }
+
+ // Signal whether the file should be removed. Note that ADF and
+ // scan button based scanning both open a temporary file ahead of
+ // scanning. Here we signal whether that temporary file needs to
+ // be removed. Also note that ADF scans ignore the scan button
+ // setting, so we should check for ADF stuff first.
+
+ if (status & (SCAN_ERROR | SCAN_CANCEL)) return true;
+
+ if (status & SCAN_ADF)
+ if (status & SCAN_FINISH) return (status & SCAN_SINGLE);
+
+ if (status & SCAN_BUTTON)
+ if (status & SCAN_FINISH) return (status & SCAN_SINGLE);
+
+ return false;
+}
+
+int
+view_manager::do_scan_gimp (bool first_time_around)
+{
+ int cancel = 0;
+
+ if (!m_scanmanager_cls->using_adf ()
+ && !m_set.enable_start_button)
+ return scan_gimp (&cancel);
+
+ bool eos = false;
+
+ while (!eos)
+ {
+ eos = scan_gimp (&cancel, first_time_around);
+ first_time_around = false;
+ }
+ if (!cancel)
+ do_scan_gimp (first_time_around);
+
+ return PISA_ERR_SUCCESS;
+}
+
+int
+view_manager::dialog_reply( const pisa_error& err ) const
+{
+ int reply = 0;
+
+ aleart_dialog dlg;
+
+ if ( PISA_STATUS_CANCELLED == err.get_error_id() )
+ {
+ // suppress message box when user cancelled from scanner side
+ reply = SCAN_ERROR;
+ }
+ else if ( ( PISA_STATUS_GOOD < err.get_error_id() ) &&
+ (m_scanmanager_cls->using_adf ()) )
+ {
+ int i = dlg.message_box( m_main_cls->get_widget(),
+ err.get_error_string(),
+ _(" Continue "), _(" Cancel ") );
+ if (2 == i)
+ reply = SCAN_FINISH;
+ else
+ reply = SCAN_NEXT;
+ }
+ else if ( ( PISA_STATUS_GOOD == err.get_error_id() ) &&
+ m_set.enable_start_button )
+ {
+
+ int i = dlg.message_box( m_main_cls->get_widget(),
+ "Waiting for ...",
+ _(" Finish ") );
+
+ if ( 1 == i )
+ reply = SCAN_FINISH;
+ }
+ else
+ {
+ dlg.message_box( m_main_cls->get_widget(),
+ err.get_error_string() );
+
+ if (PISA_ERR_FILEOPEN != err.get_error_id())
+ reply = SCAN_ERROR;
+ }
+
+ return reply;
+}
+
+/*------------------------------------------------------------*/
+bool
+view_manager::scan_gimp (int *cancel, bool first_time_around)
+{
+
+#ifndef HAVE_ANY_GIMP
+
+ *cancel = 1; // can't scan to GIMP
+ return true;
+
+#else
+ bool error = false; // be optimistic
+ try
+ {
+ *cancel = 0;
+
+ bool reset_params = false;
+ if (wait_for_button ()
+ && m_scanmanager_cls->is_button_pressed ()) {
+ // We have an impatient user here who pressed the scanner's
+ // button *before* we even got a chance to show our WAITING
+ // message.
+ m_scanmanager_cls->clear_button_status ();
+ reset_params = true;
+ }
+
+ _feedback->set_text (wait_for_button ()
+ ? progress_window::WAITING
+ : progress_window::WARMING_UP);
+ _feedback->set_progress (0, 1);
+ _feedback->show ();
+
+ if (wait_for_button ()) {
+ long usec = m_scanmanager_cls->get_polling_time ();
+ while (!m_scanmanager_cls->is_button_pressed ()
+ && !_feedback->is_cancelled ()) {
+ microsleep (usec);
+ while (gtk_events_pending ()) {
+ gtk_main_iteration ();
+ }
+ }
+ if (_feedback->is_cancelled ()) {
+ *cancel = 1;
+ return true;
+ }
+ if (m_scanmanager_cls->push_button_needs_polling ()) {
+ m_scanmanager_cls->disable_wait_for_button();
+ }
+ }
+
+ _feedback->set_text (progress_window::WARMING_UP);
+
+ int width, height;
+ m_scanmanager_cls->init_scan (&width, &height,
+ first_time_around || reset_params);
+
+ while ( ::gtk_events_pending ( ) )
+ ::gtk_main_iteration ( );
+
+ char depth = 8;
+ int rowbytes;
+ switch (m_set.imgtype.pixeltype)
+ {
+ case PISA_PT_RGB:
+ rowbytes = width * 3;
+ break;
+ case PISA_PT_GRAY:
+ rowbytes = width;
+ break;
+ case PISA_PT_BW:
+ depth = 1;
+ rowbytes = (width + 7) / 8;
+ break;
+ default:
+ rowbytes = 0;
+ }
+
+ gimp_scan gimp_cls;
+ if (PISA_ERR_SUCCESS !=
+ gimp_cls.create_gimp_image (width, height,
+ m_set.imgtype.pixeltype, depth))
+ {
+ m_scanmanager_cls->acquire_image ( 0, 0, 1, 1 );
+
+ throw pisa_error ( PISA_ERR_OUTOFMEMORY );
+ }
+
+ for (int i = 0; i < height; i++)
+ {
+ m_scanmanager_cls->acquire_image ( gimp_cls.get_next_buf ( ),
+ rowbytes,
+ 1,
+ *cancel );
+
+ if ( i == 0 )
+ _feedback->set_text (progress_window::SCANNING);
+
+ if (*cancel)
+ {
+ error = true;
+ break;
+ }
+
+ _feedback->set_progress (i, height);
+ *cancel = _feedback->is_cancelled ();
+
+ gimp_cls.set_image_rect ( );
+
+ while ( ::gtk_events_pending ( ) )
+ ::gtk_main_iteration ( );
+ }
+ m_scanmanager_cls->acquire_image (0, 1, 1, *cancel);
+ _feedback->set_progress (height, height);
+
+ gimp_cls.finish_scan ( *cancel );
+ }
+ catch ( pisa_error & err )
+ {
+ error = true;
+ *cancel = dialog_reply( err );
+ }
+
+ m_scanmanager_cls->release_memory ();
+
+ return error;
+#endif // HAVE_ANY_GIMP
+}
+
+void
+view_manager::scan_file (iscan::imgstream& is, int *status,
+ bool first_time_around)
+{
+ try
+ {
+ bool reset_params = false;
+ if (wait_for_button ()
+ && m_scanmanager_cls->is_button_pressed ()) {
+ // We have an impatient user here who pressed the scanner's
+ // button *before* we even got a chance to show our WAITING
+ // message.
+ m_scanmanager_cls->clear_button_status ();
+ reset_params = true;
+ }
+
+ _feedback->set_text (wait_for_button ()
+ ? progress_window::WAITING
+ : progress_window::WARMING_UP);
+ _feedback->set_progress (0, 1);
+ _feedback->show ();
+
+ while (::gtk_events_pending())
+ ::gtk_main_iteration();
+
+ if (wait_for_button ()) {
+ long usec = m_scanmanager_cls->get_polling_time ();
+ while (!m_scanmanager_cls->is_button_pressed ()
+ && !_feedback->is_cancelled ()) {
+ microsleep (usec);
+ while (gtk_events_pending ()) {
+ gtk_main_iteration ();
+ }
+ }
+ if (_feedback->is_cancelled ()) {
+ // user clicked the Finish button!
+ *status |= SCAN_FINISH;
+ *status &= ~SCAN_NEXT;
+ return;
+ }
+ if (m_scanmanager_cls->push_button_needs_polling ()) {
+ m_scanmanager_cls->disable_wait_for_button();
+ }
+ }
+
+ _feedback->set_text (progress_window::WARMING_UP);
+
+ int width, height;
+ m_scanmanager_cls->init_scan (&width, &height,
+ first_time_around || reset_params);
+
+ while (::gtk_events_pending())
+ ::gtk_main_iteration();
+
+ int rowbytes;
+ iscan::colour_space cs;
+
+ switch (m_set.imgtype.pixeltype)
+ {
+ case PISA_PT_RGB:
+ rowbytes = width * 3;
+ cs = iscan::RGB;
+ break;
+ case PISA_PT_GRAY:
+ rowbytes = width;
+ cs = iscan::gray;
+ break;
+ case PISA_PT_BW:
+ rowbytes = (width + 7) / 8;
+ cs = iscan::mono;
+ break;
+ default:
+ rowbytes = 0;
+ throw pisa_error (PISA_ERR_PARAMETER);
+ }
+
+ try
+ {
+ is.size (width, height);
+ is.depth (PISA_PT_BW == m_set.imgtype.pixeltype ? 1 : 8);
+ is.colour (cs);
+ is.resolution (m_set.resolution, m_set.resolution);
+ }
+ catch (pisa_error& oops)
+ {
+ m_scanmanager_cls->acquire_image( 0, 0, 1, 1 );
+ throw oops;
+ }
+
+ unsigned char *img = new unsigned char[rowbytes];
+ for (int i = 0; i < height; ++i)
+ {
+ m_scanmanager_cls->acquire_image (img, rowbytes, 1,
+ *status & SCAN_CANCEL);
+
+ if (0 == i)
+ _feedback->set_text (progress_window::SCANNING);
+
+ if (*status & SCAN_CANCEL)
+ {
+ //error = true;
+ break;
+ }
+
+ _feedback->set_progress (i, height);
+ if (_feedback->is_cancelled ())
+ *status |= SCAN_CANCEL;
+
+ try
+ {
+ try
+ {
+ is.write ((const char *)img, rowbytes);
+ *status |= SCAN_DATA;
+ }
+ catch (std::exception& oops)
+ { // map to old API and rethrow
+ throw (pisa_error (PISA_ERR_OUTOFMEMORY));
+ }
+ }
+ catch (pisa_error& oops)
+ {
+ *status |= SCAN_CANCEL;
+
+ if (i < height)
+ m_scanmanager_cls->acquire_image (img, rowbytes, 1,
+ *status & SCAN_CANCEL);
+ aleart_dialog aleart_dlg;
+ aleart_dlg.message_box( m_main_cls->get_widget(),
+ oops.get_error_string() );
+ break;
+ }
+ while (::gtk_events_pending())
+ ::gtk_main_iteration();
+ }
+ delete[] img;
+
+ m_scanmanager_cls->acquire_image (0, 1, 1, *status & SCAN_CANCEL);
+ _feedback->set_progress (height, height);
+ }
+ catch (pisa_error& oops)
+ {
+ //error = true;
+ *status |= dialog_reply (oops);
+ }
+
+ m_scanmanager_cls->release_memory ();
+
+ while (::gtk_events_pending())
+ ::gtk_main_iteration();
+
+ return;
+}
+
+void
+view_manager::print (const std::string& filename) const
+{
+ if (PISA_DE_PRINTER == m_set.destination)
+ {
+ char cmd[1024]; // FIXME: buffer overflow!
+ sprintf (cmd, "%s %s", m_config_cls->m_cmd, filename.c_str ());
+ system (cmd); // FIXME: check cmd exit status
+
+ while (gtk_events_pending ()) gtk_main_iteration ();
+
+ remove (filename.c_str ());
+ }
+}
+
+bool
+view_manager::is_multi_image (void) const
+{
+ return (m_scanmanager_cls->using_adf () || m_set.enable_start_button);
+}
+
+bool
+view_manager::wait_for_button (void) const
+{
+ if (m_scanmanager_cls->push_button_needs_polling ())
+ {
+ return (m_set.enable_start_button);
+ }
+ else
+ {
+ return (m_set.enable_start_button && !m_scanmanager_cls->using_adf ());
+ }
+}
+
+void
+view_manager::set_resolution (long resolution)
+{
+ long adjust_res[2] = {resolution, resolution};
+
+ m_set.resolution = resolution;
+
+ /* set resolution to backend */
+ m_scanmanager_cls->get_valid_resolution (&adjust_res[0], &adjust_res[1], true);
+ m_scanmanager_cls->set_scan_resolution (adjust_res[0], adjust_res[1]);
+}
+
+void
+view_manager::set_image_type (const imagetype *type)
+{
+ memcpy (&m_set.imgtype, type, sizeof (imagetype));
+ m_scanmanager_cls->set_color_mode(type->pixeltype, type->bitdepth);
+}
+
+int
+view_manager::microsleep (size_t usec)
+{
+ struct timespec ts;
+ ts.tv_sec = usec / 1000000;
+ ts.tv_nsec = (usec % 1000000) * 1000;
+
+ return nanosleep (&ts, NULL);
+}
diff --git a/frontend/pisa_view_manager.h b/frontend/pisa_view_manager.h
new file mode 100644
index 0000000..5650c38
--- /dev/null
+++ b/frontend/pisa_view_manager.h
@@ -0,0 +1,227 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2005, 2008 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___PISA_VIEW_MANAGER_H
+#define ___PISA_VIEW_MANAGER_H
+
+#include <string>
+
+#include "pisa_enums.h"
+
+#include "pisa_settings.h"
+
+#include "pisa_scan_manager.h"
+
+#include "pisa_main_window.h"
+#include "pisa_preview_window.h"
+#include "pisa_progress_window.h"
+#include "pisa_image_controls.h"
+#include "pisa_gamma_correction.h"
+#include "pisa_configuration.h"
+#include "pisa_error.h"
+#include "pisa_scan_selector.h"
+
+#include <cstring>
+
+#include "file-selector.h"
+#include "imgstream.hh"
+
+class view_manager
+{
+ public:
+
+ // operation
+ static int create_view_manager ( int argc, char * argv [ ] );
+
+ void release_view_manager ( void );
+
+ int main ( void );
+
+ void init ( void );
+ void destroy ( void );
+
+ GtkWidget * create_window ( pisa_window_id id );
+ int close_window ( pisa_window_id id, int destroy_flag );
+
+ image_controls *get_image_controls ();
+ gamma_correction *get_gamma_correction ();
+ void * get_window_cls ( pisa_window_id id );
+
+ scan_manager * get_scan_manager ( void ) { return m_scanmanager_cls; }
+
+ void sensitive ( void );
+
+ int is_gimp ( void );
+
+ void start_scan ( void );
+
+ int update_lut (long index = -1);
+ int update_lut (marquee& m);
+
+ bool change_document_source (const imagetype *);
+ void set_device ( char * name );
+ char * get_device_name() const;
+
+ const settings& get_settings (void) const;
+ const marquee& get_marquee (long index = -1) const;
+ marquee& get_marquee (long index = -1);
+
+ void add_marquee (marquee **m);
+ void del_marquee (long index = -1);
+
+ long get_marquee_size () const;
+
+ void set_destination (long destination);
+ void set_resolution (long resolution);
+ void set_image_type (const imagetype *type);
+ void set_unit (long unit);
+ void enable_start_button (bool value);
+ void enable_draft_mode (bool value);
+ void enable_unsharp_mask (bool value);
+
+ int microsleep (size_t usec);
+
+ private:
+
+ // operation
+ void open_device ( void );
+ void close_device ( void );
+
+ void load_preference ( void );
+ void save_preference ( void );
+
+ int init_img_info (void);
+
+ int init_scan_param (void);
+
+ bool do_scan (iscan::imgstream& is, const iscan::file_opener& fo,
+ bool first_time_around = true);
+ int do_scan_gimp (bool first_time_around = true);
+
+ void scan_file (iscan::imgstream& is, int *status,
+ bool first_time_around = true);
+ bool scan_gimp (int *cancel,
+ bool first_time_around = true);
+ int dialog_reply( const pisa_error& err ) const;
+
+ void print (const std::string& filename) const;
+ bool is_multi_image (void) const;
+ bool wait_for_button (void) const;
+ bool needs_duplex_rotation (void) const;
+
+ // attribute
+ settings m_set;
+
+ scan_manager * m_scanmanager_cls;
+
+ main_window * m_main_cls;
+ preview_window * m_prev_cls;
+ image_controls * m_imgctrl_cls;
+ gamma_correction * m_gamma_cls;
+ config_window * m_config_cls;
+ file_selector * m_filsel_cls;
+ scan_selector * m_scansel_cls;
+
+ progress_window *_feedback;
+
+ std::string _image_format;
+};
+
+extern view_manager * g_view_manager;
+
+inline const settings&
+view_manager::get_settings (void) const
+{
+ return m_set;
+}
+
+inline const marquee&
+view_manager::get_marquee (long index) const
+{
+ return m_set.get_marquee (index);
+}
+
+inline marquee&
+view_manager::get_marquee (long index)
+{
+ return m_set.get_marquee (index);
+}
+
+inline long
+view_manager::get_marquee_size (void) const
+{
+ return m_set.get_marquee_size ();
+}
+
+inline void
+view_manager::add_marquee (marquee **m)
+{
+ m_set.add_marquee (m);
+}
+
+inline void
+view_manager::del_marquee (long index)
+{
+ m_set.del_marquee (index);
+}
+
+inline void
+view_manager::set_destination (long destination)
+{
+ m_set.destination = destination;
+}
+
+inline void
+view_manager::set_unit (long unit)
+{
+ m_set.unit = unit;
+}
+
+inline void
+view_manager::enable_start_button (bool value)
+{
+ m_set.enable_start_button = value;
+}
+
+inline void
+view_manager::enable_draft_mode (bool value)
+{
+ m_set.enable_draft_mode = value;
+}
+
+inline void
+view_manager::enable_unsharp_mask (bool value)
+{
+ m_set.usm = (value ? 1 : 0);
+}
+
+
+#endif // ___PISA_VIEW_MANAGER_H
+
diff --git a/frontend/xpm_data.cc b/frontend/xpm_data.cc
new file mode 100644
index 0000000..26001a2
--- /dev/null
+++ b/frontend/xpm_data.cc
@@ -0,0 +1,1740 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001, 2008 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+// scan button
+const char * scan_xpm[] = {
+"36 28 257 2",
+" c None",
+". c #0C0C0C",
+"+ c #252426",
+"@ c #252526",
+"# c #262527",
+"$ c #262628",
+"% c #292929",
+"& c #2C2B2D",
+"* c #2E2E30",
+"= c #402810",
+"- c #373638",
+"; c #373737",
+"> c #3C3B3E",
+", c #3D3C3E",
+"' c #403F42",
+") c #414042",
+"! c #424043",
+"~ c #424144",
+"{ c #444444",
+"] c #49484B",
+"^ c #4A494C",
+"/ c #4E4C50",
+"( c #525154",
+"_ c #58565B",
+": c #5A585C",
+"< c #5A5A5A",
+"[ c #5B595D",
+"} c #5C5A5E",
+"| c #5D5B5F",
+"1 c #5E5C60",
+"2 c #5E5E5E",
+"3 c #5F5D61",
+"4 c #5F5F5F",
+"5 c #636166",
+"6 c #656367",
+"7 c #656368",
+"8 c #656468",
+"9 c #686868",
+"0 c #69676C",
+"a c #6A6A6A",
+"b c #6C6A6E",
+"c c #6E6C71",
+"d c #408080",
+"e c #717171",
+"f c #727075",
+"g c #737177",
+"h c #737373",
+"i c #747278",
+"j c #757575",
+"k c #767676",
+"l c #77757A",
+"m c #7A777D",
+"n c #7A787E",
+"o c #7B7B7B",
+"p c #7C7A80",
+"q c #7D7B80",
+"r c #7F7D82",
+"s c #7F7F7F",
+"t c #817E84",
+"u c #808080",
+"v c #817F85",
+"w c #827F85",
+"x c #818181",
+"y c #828086",
+"z c #848288",
+"A c #E63A3A",
+"B c #89868D",
+"C c #89878D",
+"D c None",
+"E c #8A888E",
+"F c #8B898F",
+"G c #8C8990",
+"H c #8D8A91",
+"I c #8D8B91",
+"J c #8E8B91",
+"K c #8F8D93",
+"L c #908E94",
+"M c #F63E3E",
+"N c #928F96",
+"O c #939097",
+"P c #3E9AF6",
+"Q c #949198",
+"R c #959299",
+"S c #949494",
+"T c #96949A",
+"U c #FF4040",
+"V c #97949B",
+"W c #97959B",
+"X c #809F9F",
+"Y c #979797",
+"Z c #40A0FF",
+"` c #9A979E",
+" . c #999999",
+".. c #9B989F",
+"+. c #9C99A0",
+"@. c #9D9AA1",
+"#. c #9D9BA2",
+"$. c #9E9BA3",
+"%. c #A09DA4",
+"&. c #A29FA6",
+"*. c #A5A2AA",
+"=. c #A6A3AB",
+"-. c #A8A5AC",
+";. c #AAA7AE",
+">. c #ABA7AF",
+",. c #60C0C0",
+"'. c #ABA8B0",
+"). c #ACA9B1",
+"!. c #ADAAB2",
+"~. c #AEABB3",
+"{. c #B0ADB5",
+"]. c #B1ADB5",
+"^. c #B0B0B0",
+"/. c #B2AEB6",
+"(. c #B2AFB7",
+"_. c #B3B0B8",
+":. c #B5B2BA",
+"<. c #B6B3BB",
+"[. c #B7B4BC",
+"}. c #B8B4BD",
+"|. c #B7B7B7",
+"1. c #B9B5BD",
+"2. c #B9B6BE",
+"3. c #BBB7BF",
+"4. c #BBB8C0",
+"5. c #BCB9C1",
+"6. c #BDBAC2",
+"7. c #FFA040",
+"8. c #BEBAC3",
+"9. c #BDBDBD",
+"0. c #BFBBC4",
+"a. c #C0BCC5",
+"b. c #C0BDC6",
+"c. c #C1BEC6",
+"d. c #C0C0C0",
+"e. c #C2BFC7",
+"f. c #C3C0C8",
+"g. c #C5C1CA",
+"h. c #90E63A",
+"i. c #C6C2CB",
+"j. c #C7C3CC",
+"k. c #C7C4CD",
+"l. c #C8C5CD",
+"m. c #C8C5CE",
+"n. c #C9C6CF",
+"o. c #CAC6CF",
+"p. c #CBC7D0",
+"q. c #CCC8D1",
+"r. c #CCCCCC",
+"s. c #CECAD3",
+"t. c #CECBD4",
+"u. c #CFCCD5",
+"v. c #D0CCD6",
+"w. c #D1CDD7",
+"x. c #D2CED8",
+"y. c #D3CFD8",
+"z. c #9AF63E",
+"A. c #D4D0DA",
+"B. c #D3D3D3",
+"C. c #D5D1DA",
+"D. c #D6D2DC",
+"E. c #D7D3DD",
+"F. c #D8D4DE",
+"G. c #D9D5DF",
+"H. c #DAD6E0",
+"I. c #A0FF40",
+"J. c #DBD7E1",
+"K. c #DCD8E1",
+"L. c #DCD8E2",
+"M. c #DDD9E3",
+"N. c #DEDAE4",
+"O. c #DFDBE5",
+"P. c #80FFFF",
+"Q. c #E3E3E3",
+"R. c #E6E6E6",
+"S. c #F6F6F6",
+"T. c #FCFCFC",
+"U. c #FFFFFF",
+"V. c #000000",
+"W. c #000000",
+"X. c #000000",
+"Y. c #000000",
+"Z. c #000000",
+"`. c #000000",
+" + c #000000",
+".+ c #000000",
+"++ c #000000",
+"@+ c #000000",
+"#+ c #000000",
+"$+ c #000000",
+"%+ c #000000",
+"&+ c #000000",
+"*+ c #000000",
+"=+ c #000000",
+"-+ c #000000",
+";+ c #000000",
+">+ c #000000",
+",+ c #000000",
+"'+ c #000000",
+")+ c #000000",
+"!+ c #000000",
+"~+ c #000000",
+"{+ c #000000",
+"]+ c #000000",
+"^+ c #000000",
+"/+ c #000000",
+"(+ c #000000",
+"_+ c #000000",
+":+ c #000000",
+"<+ c #000000",
+"[+ c #000000",
+"}+ c #000000",
+"|+ c #000000",
+"1+ c #000000",
+"2+ c #000000",
+"3+ c #000000",
+"4+ c #000000",
+"5+ c #000000",
+"6+ c #000000",
+"7+ c #000000",
+"8+ c #000000",
+"9+ c #000000",
+"0+ c #000000",
+"a+ c #000000",
+"b+ c #000000",
+"c+ c #000000",
+"d+ c #000000",
+"e+ c #000000",
+"f+ c #000000",
+"g+ c #000000",
+"h+ c #000000",
+"i+ c #000000",
+"j+ c #000000",
+"k+ c #000000",
+"l+ c #000000",
+"m+ c #000000",
+"n+ c #000000",
+"o+ c #000000",
+"p+ c #000000",
+"q+ c #000000",
+"r+ c #000000",
+"s+ c #000000",
+"t+ c #000000",
+"u+ c #000000",
+"v+ c #000000",
+"w+ c #000000",
+"x+ c #000000",
+"y+ c #000000",
+"z+ c #000000",
+"A+ c #000000",
+"B+ c #000000",
+"C+ c #000000",
+"D+ c #000000",
+"E+ c #000000",
+"F+ c #000000",
+"G+ c #000000",
+"H+ c #000000",
+"D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D ",
+"D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D ",
+"D D D D D &.Q K N +.>.[.4.0.D D D D D D D D D D D D D D D D D D D D D D ",
+"D D D D D . . . . .n t C L +.>.[.D D D D D D D D D D D D D D D D D D D ",
+"D D D D D !.r.d.d.d. . . . . .n t C J K N +.D D D D D D D D D D D D D D ",
+"D D D D D D -.r.r.r.d.d.d.d.d. . . . . .u u u H *.D D D D D D D D D D D ",
+"D D D D D D D D T y r.r.r.d.d.d.d.d.d.d.d.u u u u F *.D D D D D D D D D ",
+"D D D D D D D D D _.#.t 6 r.r.r.d.d.d.d.d.d.d.u u u u F *.D D D D D D D ",
+"D D D D D D D D D 5.*.E f } d.d.r.r.d.d.d.d.d.d.d.u u u u F =.D D D D D ",
+"D D D D D D D D /.V l m d.d.d.d.u u r.u d.d.d.d.d.d.d.u u u u O }.D D D ",
+"D D D D D D /...w 7 d.d.U.U.u { U.d d d u u u u u u d.d.d.u u u u D D D ",
+"D D D D /...w 7 d.d.U.U.u u U.,.U.,.,.d d d d d d d { u { { { { { R D D ",
+"D D /...w 7 d.d.U.U.u u U.U.U.,.U.,.,.,.,.d d d d d u u d.d.{ { u { !.D ",
+"D ;.z 7 d.d.U.U.u X ,.,.U.U.U.,.U.,.,.,.,.,.,.d u u d.d.{ { u u u { +.D ",
+"D T d.d.U.U.u u ,.,.,.,.U.U.U.,.U.,.,.,.,.,.d.u d.d.{ { u u u u u { O D ",
+"D L d.d.d.U.U.U.u X ,.,.U.U.U.,.U.,.,.,.d.d.U.U.{ { u u u u u u u { R D ",
+"D K d.U.U.d.d.U.U.U.u u U.U.U.,.U.,.d.d.U.U.{ { u u u u u u u u { t &.D ",
+"D N d.U.U.U.U.d.d.U.U.U.u u U.,.d.d.U.U.{ { u u u u u u u u { { t D D D ",
+"D $.d.U.u U.U.U.U.d.d.U.U.U.u u U.U.{ { u u u u u u u u { { p G D D D D ",
+"D :.d.U.U.u U.U.U.U.U.d.d.U.U.U.{ { u u u u u u u u { { p G D D D D D D ",
+"D D !.u u U.U.U.U.U.U.U.U.d.{ { u u u u u u u u { { p G D D D D D D D D ",
+"D D D ~.H u u U.U.U.U.U.U.U.{ u u u u u u u { { p G $.D D D D D D D D D ",
+"D D D D D =.E u u U.U.U.U.U.{ u u u u u { { p G $.D D D D D D D D D D D ",
+"D D D D D D D =.E u u U.U.U.{ u u u { { p G $.D D D D D D D D D D D D D ",
+"D D D D D D D D D =.E u u U.{ u { { p G D D D D D D D D D D D D D D D D ",
+"D D D D D D D D D D D =.G u { { p G D D D D D D D D D D D D D D D D D D ",
+"D D D D D D D D D D D D D !.#.W %.D D D D D D D D D D D D D D D D D D D ",
+"D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D "};
+
+// preview button
+const char * preview_xpm[] = {
+"18 18 257 2",
+" c None",
+". c #0C0C0C",
+"+ c #252426",
+"@ c #252526",
+"# c #262527",
+"$ c #262628",
+"% c #292929",
+"& c #2C2B2D",
+"* c #2E2E30",
+"= c #402810",
+"- c #373638",
+"; c #373737",
+"> c #3C3B3E",
+", c #3D3C3E",
+"' c #403F42",
+") c #414042",
+"! c #424043",
+"~ c #424144",
+"{ c #444444",
+"] c #49484B",
+"^ c #4A494C",
+"/ c #4E4C50",
+"( c #525154",
+"_ c #58565B",
+": c #5A585C",
+"< c #5A5A5A",
+"[ c #5B595D",
+"} c #5C5A5E",
+"| c #5D5B5F",
+"1 c #5E5C60",
+"2 c #5E5E5E",
+"3 c #5F5D61",
+"4 c #5F5F5F",
+"5 c #636166",
+"6 c #656367",
+"7 c #656368",
+"8 c #656468",
+"9 c #686868",
+"0 c #69676C",
+"a c #6A6A6A",
+"b c #6C6A6E",
+"c c #6E6C71",
+"d c #408080",
+"e c #717171",
+"f c #727075",
+"g c #737177",
+"h c #737373",
+"i c #747278",
+"j c #757575",
+"k c #767676",
+"l c #77757A",
+"m c #7A777D",
+"n c #7A787E",
+"o c #7B7B7B",
+"p c #7C7A80",
+"q c #7D7B80",
+"r c #7F7D82",
+"s c #7F7F7F",
+"t c #817E84",
+"u c #808080",
+"v c #817F85",
+"w c #827F85",
+"x c #818181",
+"y c #828086",
+"z c #848288",
+"A c #E63A3A",
+"B c #89868D",
+"C c #89878D",
+"D c None",
+"E c #8A888E",
+"F c #8B898F",
+"G c #8C8990",
+"H c #8D8A91",
+"I c #8D8B91",
+"J c #8E8B91",
+"K c #8F8D93",
+"L c #908E94",
+"M c #F63E3E",
+"N c #928F96",
+"O c #939097",
+"P c #3E9AF6",
+"Q c #949198",
+"R c #959299",
+"S c #949494",
+"T c #96949A",
+"U c #FF4040",
+"V c #97949B",
+"W c #97959B",
+"X c #809F9F",
+"Y c #979797",
+"Z c #40A0FF",
+"` c #9A979E",
+" . c #999999",
+".. c #9B989F",
+"+. c #9C99A0",
+"@. c #9D9AA1",
+"#. c #9D9BA2",
+"$. c #9E9BA3",
+"%. c #A09DA4",
+"&. c #A29FA6",
+"*. c #A5A2AA",
+"=. c #A6A3AB",
+"-. c #A8A5AC",
+";. c #AAA7AE",
+">. c #ABA7AF",
+",. c #60C0C0",
+"'. c #ABA8B0",
+"). c #ACA9B1",
+"!. c #ADAAB2",
+"~. c #AEABB3",
+"{. c #B0ADB5",
+"]. c #B1ADB5",
+"^. c #B0B0B0",
+"/. c #B2AEB6",
+"(. c #B2AFB7",
+"_. c #B3B0B8",
+":. c #B5B2BA",
+"<. c #B6B3BB",
+"[. c #B7B4BC",
+"}. c #B8B4BD",
+"|. c #B7B7B7",
+"1. c #B9B5BD",
+"2. c #B9B6BE",
+"3. c #BBB7BF",
+"4. c #BBB8C0",
+"5. c #BCB9C1",
+"6. c #BDBAC2",
+"7. c #FFA040",
+"8. c #BEBAC3",
+"9. c #BDBDBD",
+"0. c #BFBBC4",
+"a. c #C0BCC5",
+"b. c #C0BDC6",
+"c. c #C1BEC6",
+"d. c #C0C0C0",
+"e. c #C2BFC7",
+"f. c #C3C0C8",
+"g. c #C5C1CA",
+"h. c #90E63A",
+"i. c #C6C2CB",
+"j. c #C7C3CC",
+"k. c #C7C4CD",
+"l. c #C8C5CD",
+"m. c #C8C5CE",
+"n. c #C9C6CF",
+"o. c #CAC6CF",
+"p. c #CBC7D0",
+"q. c #CCC8D1",
+"r. c #CCCCCC",
+"s. c #CECAD3",
+"t. c #CECBD4",
+"u. c #CFCCD5",
+"v. c #D0CCD6",
+"w. c #D1CDD7",
+"x. c #D2CED8",
+"y. c #D3CFD8",
+"z. c #9AF63E",
+"A. c #D4D0DA",
+"B. c #D3D3D3",
+"C. c #D5D1DA",
+"D. c #D6D2DC",
+"E. c #D7D3DD",
+"F. c #D8D4DE",
+"G. c #D9D5DF",
+"H. c #DAD6E0",
+"I. c #A0FF40",
+"J. c #DBD7E1",
+"K. c #DCD8E1",
+"L. c #DCD8E2",
+"M. c #DDD9E3",
+"N. c #DEDAE4",
+"O. c #DFDBE5",
+"P. c #80FFFF",
+"Q. c #E3E3E3",
+"R. c #E6E6E6",
+"S. c #F6F6F6",
+"T. c #FCFCFC",
+"U. c #FFFFFF",
+"V. c #000000",
+"W. c #000000",
+"X. c #000000",
+"Y. c #000000",
+"Z. c #000000",
+"`. c #000000",
+" + c #000000",
+".+ c #000000",
+"++ c #000000",
+"@+ c #000000",
+"#+ c #000000",
+"$+ c #000000",
+"%+ c #000000",
+"&+ c #000000",
+"*+ c #000000",
+"=+ c #000000",
+"-+ c #000000",
+";+ c #000000",
+">+ c #000000",
+",+ c #000000",
+"'+ c #000000",
+")+ c #000000",
+"!+ c #000000",
+"~+ c #000000",
+"{+ c #000000",
+"]+ c #000000",
+"^+ c #000000",
+"/+ c #000000",
+"(+ c #000000",
+"_+ c #000000",
+":+ c #000000",
+"<+ c #000000",
+"[+ c #000000",
+"}+ c #000000",
+"|+ c #000000",
+"1+ c #000000",
+"2+ c #000000",
+"3+ c #000000",
+"4+ c #000000",
+"5+ c #000000",
+"6+ c #000000",
+"7+ c #000000",
+"8+ c #000000",
+"9+ c #000000",
+"0+ c #000000",
+"a+ c #000000",
+"b+ c #000000",
+"c+ c #000000",
+"d+ c #000000",
+"e+ c #000000",
+"f+ c #000000",
+"g+ c #000000",
+"h+ c #000000",
+"i+ c #000000",
+"j+ c #000000",
+"k+ c #000000",
+"l+ c #000000",
+"m+ c #000000",
+"n+ c #000000",
+"o+ c #000000",
+"p+ c #000000",
+"q+ c #000000",
+"r+ c #000000",
+"s+ c #000000",
+"t+ c #000000",
+"u+ c #000000",
+"v+ c #000000",
+"w+ c #000000",
+"x+ c #000000",
+"y+ c #000000",
+"z+ c #000000",
+"A+ c #000000",
+"B+ c #000000",
+"C+ c #000000",
+"D+ c #000000",
+"E+ c #000000",
+"F+ c #000000",
+"G+ c #000000",
+"H+ c #000000",
+"D r.r.r.r.r.r.r.r.r.r.r.-.D D D D D ",
+"D r.U.U.U.U.U.U.U.U.r.U.u K D D D D ",
+"D r.U.U.U.U.U.U.U.U.r.U.U.u K D D D ",
+"D r.U.U.U.U.U.U.U.U.u u u u 0 D D D ",
+"D r.U.U.U.U.U.U.U.U.U.U.U.u ( ` D D ",
+"D r.U.U.U.U.U.U.U.U.U.U.U.u ^ R D D ",
+"D r.U.U.U.U.U.U.U.U.U.U.U.u ^ R D D ",
+"D r.U.U.U.U.U.U.U.U.U.U.U.u ^ R D D ",
+"D r.U.U.U.U.U.U.U.U.U.U.U.u ^ R D D ",
+"D r.U.U.U.U.U.U.U.U.U.U.U.u ^ R D D ",
+"D r.U.U.U.U.U.U.U.U.U.U.U.u ^ R D D ",
+"D r.U.U.U.U.U.U.U.U.U.U.U.u ^ R D D ",
+"D r.U.U.U.U.U.U.U.U.U.U.U.u ^ R D D ",
+"D r.U.U.U.U.U.U.U.U.U.U.U.u ^ R D D ",
+"D r.U.U.U.U.U.U.U.U.U.U.U.u ^ R D D ",
+"D r.u u u u u u u u u u u u [ @.D D ",
+"D !.p [ ^ ^ ^ ^ ^ ^ ^ ^ ^ [ p D D D ",
+"D D d.@.R R R R R R R R R @.D D D D "};
+
+// zoom
+const char * zoom_xpm[] = {
+"18 18 257 2",
+" c None",
+". c #0C0C0C",
+"+ c #252426",
+"@ c #252526",
+"# c #262527",
+"$ c #262628",
+"% c #292929",
+"& c #2C2B2D",
+"* c #2E2E30",
+"= c #402810",
+"- c #373638",
+"; c #373737",
+"> c #3C3B3E",
+", c #3D3C3E",
+"' c #403F42",
+") c #414042",
+"! c #424043",
+"~ c #424144",
+"{ c #444444",
+"] c #49484B",
+"^ c #4A494C",
+"/ c #4E4C50",
+"( c #525154",
+"_ c #58565B",
+": c #5A585C",
+"< c #5A5A5A",
+"[ c #5B595D",
+"} c #5C5A5E",
+"| c #5D5B5F",
+"1 c #5E5C60",
+"2 c #5E5E5E",
+"3 c #5F5D61",
+"4 c #5F5F5F",
+"5 c #636166",
+"6 c #656367",
+"7 c #656368",
+"8 c #656468",
+"9 c #686868",
+"0 c #69676C",
+"a c #6A6A6A",
+"b c #6C6A6E",
+"c c #6E6C71",
+"d c #408080",
+"e c #717171",
+"f c #727075",
+"g c #737177",
+"h c #737373",
+"i c #747278",
+"j c #757575",
+"k c #767676",
+"l c #77757A",
+"m c #7A777D",
+"n c #7A787E",
+"o c #7B7B7B",
+"p c #7C7A80",
+"q c #7D7B80",
+"r c #7F7D82",
+"s c #7F7F7F",
+"t c #817E84",
+"u c #808080",
+"v c #817F85",
+"w c #827F85",
+"x c #818181",
+"y c #828086",
+"z c #848288",
+"A c #E63A3A",
+"B c #89868D",
+"C c #89878D",
+"D c None",
+"E c #8A888E",
+"F c #8B898F",
+"G c #8C8990",
+"H c #8D8A91",
+"I c #8D8B91",
+"J c #8E8B91",
+"K c #8F8D93",
+"L c #908E94",
+"M c #F63E3E",
+"N c #928F96",
+"O c #939097",
+"P c #3E9AF6",
+"Q c #949198",
+"R c #959299",
+"S c #949494",
+"T c #96949A",
+"U c #FF4040",
+"V c #97949B",
+"W c #97959B",
+"X c #809F9F",
+"Y c #979797",
+"Z c #40A0FF",
+"` c #9A979E",
+" . c #999999",
+".. c #9B989F",
+"+. c #9C99A0",
+"@. c #9D9AA1",
+"#. c #9D9BA2",
+"$. c #9E9BA3",
+"%. c #A09DA4",
+"&. c #A29FA6",
+"*. c #A5A2AA",
+"=. c #A6A3AB",
+"-. c #A8A5AC",
+";. c #AAA7AE",
+">. c #ABA7AF",
+",. c #60C0C0",
+"'. c #ABA8B0",
+"). c #ACA9B1",
+"!. c #ADAAB2",
+"~. c #AEABB3",
+"{. c #B0ADB5",
+"]. c #B1ADB5",
+"^. c #B0B0B0",
+"/. c #B2AEB6",
+"(. c #B2AFB7",
+"_. c #B3B0B8",
+":. c #B5B2BA",
+"<. c #B6B3BB",
+"[. c #B7B4BC",
+"}. c #B8B4BD",
+"|. c #B7B7B7",
+"1. c #B9B5BD",
+"2. c #B9B6BE",
+"3. c #BBB7BF",
+"4. c #BBB8C0",
+"5. c #BCB9C1",
+"6. c #BDBAC2",
+"7. c #FFA040",
+"8. c #BEBAC3",
+"9. c #BDBDBD",
+"0. c #BFBBC4",
+"a. c #C0BCC5",
+"b. c #C0BDC6",
+"c. c #C1BEC6",
+"d. c #C0C0C0",
+"e. c #C2BFC7",
+"f. c #C3C0C8",
+"g. c #C5C1CA",
+"h. c #90E63A",
+"i. c #C6C2CB",
+"j. c #C7C3CC",
+"k. c #C7C4CD",
+"l. c #C8C5CD",
+"m. c #C8C5CE",
+"n. c #C9C6CF",
+"o. c #CAC6CF",
+"p. c #CBC7D0",
+"q. c #CCC8D1",
+"r. c #CCCCCC",
+"s. c #CECAD3",
+"t. c #CECBD4",
+"u. c #CFCCD5",
+"v. c #D0CCD6",
+"w. c #D1CDD7",
+"x. c #D2CED8",
+"y. c #D3CFD8",
+"z. c #9AF63E",
+"A. c #D4D0DA",
+"B. c #D3D3D3",
+"C. c #D5D1DA",
+"D. c #D6D2DC",
+"E. c #D7D3DD",
+"F. c #D8D4DE",
+"G. c #D9D5DF",
+"H. c #DAD6E0",
+"I. c #A0FF40",
+"J. c #DBD7E1",
+"K. c #DCD8E1",
+"L. c #DCD8E2",
+"M. c #DDD9E3",
+"N. c #DEDAE4",
+"O. c #DFDBE5",
+"P. c #80FFFF",
+"Q. c #E3E3E3",
+"R. c #E6E6E6",
+"S. c #F6F6F6",
+"T. c #FCFCFC",
+"U. c #FFFFFF",
+"V. c #000000",
+"W. c #000000",
+"X. c #000000",
+"Y. c #000000",
+"Z. c #000000",
+"`. c #000000",
+" + c #000000",
+".+ c #000000",
+"++ c #000000",
+"@+ c #000000",
+"#+ c #000000",
+"$+ c #000000",
+"%+ c #000000",
+"&+ c #000000",
+"*+ c #000000",
+"=+ c #000000",
+"-+ c #000000",
+";+ c #000000",
+">+ c #000000",
+",+ c #000000",
+"'+ c #000000",
+")+ c #000000",
+"!+ c #000000",
+"~+ c #000000",
+"{+ c #000000",
+"]+ c #000000",
+"^+ c #000000",
+"/+ c #000000",
+"(+ c #000000",
+"_+ c #000000",
+":+ c #000000",
+"<+ c #000000",
+"[+ c #000000",
+"}+ c #000000",
+"|+ c #000000",
+"1+ c #000000",
+"2+ c #000000",
+"3+ c #000000",
+"4+ c #000000",
+"5+ c #000000",
+"6+ c #000000",
+"7+ c #000000",
+"8+ c #000000",
+"9+ c #000000",
+"0+ c #000000",
+"a+ c #000000",
+"b+ c #000000",
+"c+ c #000000",
+"d+ c #000000",
+"e+ c #000000",
+"f+ c #000000",
+"g+ c #000000",
+"h+ c #000000",
+"i+ c #000000",
+"j+ c #000000",
+"k+ c #000000",
+"l+ c #000000",
+"m+ c #000000",
+"n+ c #000000",
+"o+ c #000000",
+"p+ c #000000",
+"q+ c #000000",
+"r+ c #000000",
+"s+ c #000000",
+"t+ c #000000",
+"u+ c #000000",
+"v+ c #000000",
+"w+ c #000000",
+"x+ c #000000",
+"y+ c #000000",
+"z+ c #000000",
+"A+ c #000000",
+"B+ c #000000",
+"C+ c #000000",
+"D+ c #000000",
+"E+ c #000000",
+"F+ c #000000",
+"G+ c #000000",
+"H+ c #000000",
+"r.r.r.r.r.r.r.r.r.r.r.-.D D D D D D ",
+"r.U.U.U.U.U.U.U.U.r.U.u K D D D D D ",
+"r.U.U.U.U.U.U.U.U.r.U.U.u K D D D D ",
+"r.U.U.U.T.S.R.d.d.d.d.4 a 3 D D D D ",
+"r.U.U.U.S.d.d.P.P.P.P.d.d., B D D D ",
+"r.U.U.U.R.d.P.P.U.P.P.P.u @ c D D D ",
+"r.U.U.U.d.P.P.U.U.U.U.P.P.u _ D D D ",
+"r.U.U.U.d.P.U.U.U.P.P.P.P.u ~ D D D ",
+"r.U.U.U.d.P.P.U.P.P.P.P.P.u - D D D ",
+"r.U.U.U.d.P.P.U.P.P.P.P.P.u - ` D D ",
+"r.U.U.U.9.d.P.P.P.P.P.P.u . , %.D D ",
+"r.U.U.U.B.d.u P.P.P.P.u u 7.' #.D D ",
+"r.U.U.U.R.9.x u u u u % = = 7.z D D ",
+"r.U.U.U.S.Q.9.Y e 2 2 9 ; = = 7.H D ",
+"r.U.U.U.T.S.R.B.9.^.^.|.< & = = 7.N ",
+"r.u u u u u s o k h h j h ] 5 = = r ",
+"D p [ ^ ^ ^ ^ ^ ^ ^ ^ ^ : i D N r G ",
+"D D D D D D D D D D D D D D D D D D "};
+
+// auto
+const char * auto_xpm[] = {
+"18 18 257 2",
+" c None",
+". c #0C0C0C",
+"+ c #252426",
+"@ c #252526",
+"# c #262527",
+"$ c #262628",
+"% c #292929",
+"& c #2C2B2D",
+"* c #2E2E30",
+"= c #402810",
+"- c #373638",
+"; c #373737",
+"> c #3C3B3E",
+", c #3D3C3E",
+"' c #403F42",
+") c #414042",
+"! c #424043",
+"~ c #424144",
+"{ c #444444",
+"] c #49484B",
+"^ c #4A494C",
+"/ c #4E4C50",
+"( c #525154",
+"_ c #58565B",
+": c #5A585C",
+"< c #5A5A5A",
+"[ c #5B595D",
+"} c #5C5A5E",
+"| c #5D5B5F",
+"1 c #5E5C60",
+"2 c #5E5E5E",
+"3 c #5F5D61",
+"4 c #5F5F5F",
+"5 c #636166",
+"6 c #656367",
+"7 c #656368",
+"8 c #656468",
+"9 c #686868",
+"0 c #69676C",
+"a c #6A6A6A",
+"b c #6C6A6E",
+"c c #6E6C71",
+"d c #408080",
+"e c #717171",
+"f c #727075",
+"g c #737177",
+"h c #737373",
+"i c #747278",
+"j c #757575",
+"k c #767676",
+"l c #77757A",
+"m c #7A777D",
+"n c #7A787E",
+"o c #7B7B7B",
+"p c #7C7A80",
+"q c #7D7B80",
+"r c #7F7D82",
+"s c #7F7F7F",
+"t c #817E84",
+"u c #808080",
+"v c #817F85",
+"w c #827F85",
+"x c #818181",
+"y c #828086",
+"z c #848288",
+"A c #E63A3A",
+"B c #89868D",
+"C c #89878D",
+"D c #3A90E6",
+"E c #8A888E",
+"F c #8B898F",
+"G c #8C8990",
+"H c #8D8A91",
+"I c #8D8B91",
+"J c #8E8B91",
+"K c #8F8D93",
+"L c #908E94",
+"M c #F63E3E",
+"N c #928F96",
+"O c #939097",
+"P c #3E9AF6",
+"Q c #949198",
+"R c #959299",
+"S c #949494",
+"T c #96949A",
+"U c #FF4040",
+"V c #97949B",
+"W c #97959B",
+"X c #809F9F",
+"Y c #979797",
+"Z c #40A0FF",
+"` c #9A979E",
+" . c #999999",
+".. c #9B989F",
+"+. c #9C99A0",
+"@. c #9D9AA1",
+"#. c #9D9BA2",
+"$. c #9E9BA3",
+"%. c #A09DA4",
+"&. c #A29FA6",
+"*. c #A5A2AA",
+"=. c #A6A3AB",
+"-. c #A8A5AC",
+";. c #AAA7AE",
+">. c #ABA7AF",
+",. c #60C0C0",
+"'. c #ABA8B0",
+"). c #ACA9B1",
+"!. c #ADAAB2",
+"~. c #AEABB3",
+"{. c #B0ADB5",
+"]. c #B1ADB5",
+"^. c #B0B0B0",
+"/. c #B2AEB6",
+"(. c #B2AFB7",
+"_. c #B3B0B8",
+":. c #B5B2BA",
+"<. c #B6B3BB",
+"[. c #B7B4BC",
+"}. c #B8B4BD",
+"|. c #B7B7B7",
+"1. c #B9B5BD",
+"2. c #B9B6BE",
+"3. c #BBB7BF",
+"4. c #BBB8C0",
+"5. c #BCB9C1",
+"6. c #BDBAC2",
+"7. c #FFA040",
+"8. c #BEBAC3",
+"9. c #BDBDBD",
+"0. c #BFBBC4",
+"a. c #C0BCC5",
+"b. c #C0BDC6",
+"c. c #C1BEC6",
+"d. c #C0C0C0",
+"e. c #C2BFC7",
+"f. c #C3C0C8",
+"g. c #C5C1CA",
+"h. c #90E63A",
+"i. c #C6C2CB",
+"j. c #C7C3CC",
+"k. c #C7C4CD",
+"l. c #C8C5CD",
+"m. c #C8C5CE",
+"n. c #C9C6CF",
+"o. c #CAC6CF",
+"p. c #CBC7D0",
+"q. c #CCC8D1",
+"r. c #CCCCCC",
+"s. c #CECAD3",
+"t. c #CECBD4",
+"u. c #CFCCD5",
+"v. c #D0CCD6",
+"w. c #D1CDD7",
+"x. c #D2CED8",
+"y. c #D3CFD8",
+"z. c #9AF63E",
+"A. c #D4D0DA",
+"B. c #D3D3D3",
+"C. c #D5D1DA",
+"D. c #D6D2DC",
+"E. c #D7D3DD",
+"F. c #D8D4DE",
+"G. c #D9D5DF",
+"H. c #DAD6E0",
+"I. c #A0FF40",
+"J. c #DBD7E1",
+"K. c #DCD8E1",
+"L. c #DCD8E2",
+"M. c #DDD9E3",
+"N. c #DEDAE4",
+"O. c #DFDBE5",
+"P. c #80FFFF",
+"Q. c #E3E3E3",
+"R. c #E6E6E6",
+"S. c #F6F6F6",
+"T. c #FCFCFC",
+"U. c None",
+"V. c #000000",
+"W. c #000000",
+"X. c #000000",
+"Y. c #000000",
+"Z. c #000000",
+"`. c #000000",
+" + c #000000",
+".+ c #000000",
+"++ c #000000",
+"@+ c #000000",
+"#+ c #000000",
+"$+ c #000000",
+"%+ c #000000",
+"&+ c #000000",
+"*+ c #000000",
+"=+ c #000000",
+"-+ c #000000",
+";+ c #000000",
+">+ c #000000",
+",+ c #000000",
+"'+ c #000000",
+")+ c #000000",
+"!+ c #000000",
+"~+ c #000000",
+"{+ c #000000",
+"]+ c #000000",
+"^+ c #000000",
+"/+ c #000000",
+"(+ c #000000",
+"_+ c #000000",
+":+ c #000000",
+"<+ c #000000",
+"[+ c #000000",
+"}+ c #000000",
+"|+ c #000000",
+"1+ c #000000",
+"2+ c #000000",
+"3+ c #000000",
+"4+ c #000000",
+"5+ c #000000",
+"6+ c #000000",
+"7+ c #000000",
+"8+ c #000000",
+"9+ c #000000",
+"0+ c #000000",
+"a+ c #000000",
+"b+ c #000000",
+"c+ c #000000",
+"d+ c #000000",
+"e+ c #000000",
+"f+ c #000000",
+"g+ c #000000",
+"h+ c #000000",
+"i+ c #000000",
+"j+ c #000000",
+"k+ c #000000",
+"l+ c #000000",
+"m+ c #000000",
+"n+ c #000000",
+"o+ c #000000",
+"p+ c #000000",
+"q+ c #000000",
+"r+ c #000000",
+"s+ c #000000",
+"t+ c #000000",
+"u+ c #000000",
+"v+ c #000000",
+"w+ c #000000",
+"x+ c #000000",
+"y+ c #000000",
+"z+ c #000000",
+"A+ c #000000",
+"B+ c #000000",
+"C+ c #000000",
+"D+ c #000000",
+"E+ c #000000",
+"F+ c #000000",
+"G+ c #000000",
+"H+ c #000000",
+"U.U.U.U.U. . .U.U.U.U.U.U.U.U.U.U.U.",
+"U.U.U. . .O.Z .R U.U.U.U.U.U.U.U.U.",
+"U. . .O.O.Z O. .8 U.U.U.U.U.U.U.U.U.",
+" .O.O.U O.I.O.Z Y m . .U.U.U.U.U.U.",
+" .O.U O.I.O.Z O.S .U U . .#.U.U.U.",
+">. .O.O.U O.I.L.D .U U I.I. . .%.U.",
+"U. .O.U O.I.O.P .U U I.I.I.Z Z .R ",
+"U.#. .O.O.U L.h. .U U I.I.Z Z Z .g ",
+"U.U. .O.U O.z. .U U I.I.I.Z Z .* b ",
+"U.U.#. .O.L.A .U U I.I.Z Z Z .! U.",
+"U.U.U. .O.M .U U I.I.I.Z Z .$ 1 U.",
+"U.U.U.#.Y n. .U U I.I.Z Z Z .! v U.",
+"U.U.U.U.S .U U I.I.I.Z Z .+ 1 U.U.",
+"U.U.U.U.T .U U I.I.Z Z Z .) U.U.U.",
+"U.U.U.U.U.g . .I.I.Z Z .# 1 U.U.U.",
+"U.U.U.U.U.&.g / . .Z Z .! U.U.U.U.",
+"U.U.U.U.U.U.U.T q | . .> U.U.U.U.U.",
+"U.U.U.U.U.U.U.U.U.U.v b U.U.U.U.U.U."};
+
+// gamma correction mono
+const char * gamma_m_xpm[] = {
+"30 20 31 1",
+" c None",
+". c #000000",
+"+ c #080808",
+"@ c #111111",
+"# c #1A1A1A",
+"$ c #232323",
+"% c #2B2B2B",
+"& c #343434",
+"* c #3D3D3D",
+"= c #464646",
+"- c #4F4F4F",
+"; c #575757",
+"> c #606060",
+", c #696969",
+"' c #727272",
+") c #7B7B7B",
+"! c #838383",
+"~ c #8C8C8C",
+"{ c #959595",
+"] c #9E9E9E",
+"^ c #A7A7A7",
+"/ c #AFAFAF",
+"( c #B8B8B8",
+"_ c #C1C1C1",
+": c #CACACA",
+"< c #D3D3D3",
+"[ c #DBDBDB",
+"} c #E4E4E4",
+"| c #EDEDED",
+"1 c #F6F6F6",
+"2 c #FFFFFF",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12"};
+
+
+// gamma correction red
+const char * gamma_r_xpm[] = {
+"30 20 31 1",
+" c None",
+". c #FF0000",
+"+ c #FF0808",
+"@ c #FF1111",
+"# c #FF1A1A",
+"$ c #FF2323",
+"% c #FF2B2B",
+"& c #FF3434",
+"* c #FF3D3D",
+"= c #FF4646",
+"- c #FF4F4F",
+"; c #FF5757",
+"> c #FF6060",
+", c #FF6969",
+"' c #FF7272",
+") c #FF7B7B",
+"! c #FF8383",
+"~ c #FF8C8C",
+"{ c #FF9595",
+"] c #FF9E9E",
+"^ c #FFA7A7",
+"/ c #FFAFAF",
+"( c #FFB8B8",
+"_ c #FFC1C1",
+": c #FFCACA",
+"< c #FFD3D3",
+"[ c #FFDBDB",
+"} c #FFE4E4",
+"| c #FFEDED",
+"1 c #FFF6F6",
+"2 c #FFFFFF",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12"};
+
+
+// gamma correction green
+const char * gamma_g_xpm[] = {
+"30 20 31 1",
+" c None",
+". c #00FF00",
+"+ c #08FF08",
+"@ c #11FF11",
+"# c #1AFF1A",
+"$ c #23FF23",
+"% c #2BFF2B",
+"& c #34FF34",
+"* c #3DFF3D",
+"= c #46FF46",
+"- c #4FFF4F",
+"; c #57FF57",
+"> c #60FF60",
+", c #69FF69",
+"' c #72FF72",
+") c #7BFF7B",
+"! c #83FF83",
+"~ c #8CFF8C",
+"{ c #95FF95",
+"] c #9EFF9E",
+"^ c #A7FFA7",
+"/ c #AFFFAF",
+"( c #B8FFB8",
+"_ c #C1FFC1",
+": c #CAFFCA",
+"< c #D3FFD3",
+"[ c #DBFFDB",
+"} c #E4FFE4",
+"| c #EDFFED",
+"1 c #F6FFF6",
+"2 c #FFFFFF",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12"};
+
+
+// gamma correction blue
+const char * gamma_b_xpm[] = {
+"30 20 31 1",
+" c None",
+". c #0000FF",
+"+ c #0808FF",
+"@ c #1111FF",
+"# c #1A1AFF",
+"$ c #2323FF",
+"% c #2B2BFF",
+"& c #3434FF",
+"* c #3D3DFF",
+"= c #4646FF",
+"- c #4F4FFF",
+"; c #5757FF",
+"> c #6060FF",
+", c #6969FF",
+"' c #7272FF",
+") c #7B7BFF",
+"! c #8383FF",
+"~ c #8C8CFF",
+"{ c #9595FF",
+"] c #9E9EFF",
+"^ c #A7A7FF",
+"/ c #AFAFFF",
+"( c #B8B8FF",
+"_ c #C1C1FF",
+": c #CACAFF",
+"< c #D3D3FF",
+"[ c #DBDBFF",
+"} c #E4E4FF",
+"| c #EDEDFF",
+"1 c #F6F6FF",
+"2 c #FFFFFF",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12",
+".+@#$%&*=-;>,')!~{]^/(_:<[}|12"};
+
+// penguin
+const char * penguin_xpm[] = {
+"250 101 257 2",
+" c None",
+". c #000000",
+"+ c #010101",
+"@ c #020202",
+"# c #040404",
+"$ c #050505",
+"% c #060600",
+"& c #070707",
+"* c #080808",
+"= c #0A0A0A",
+"- c #0B0B0B",
+"; c #0D0D0D",
+"> c #0F0F0F",
+", c #101010",
+"' c #121212",
+") c #131313",
+"! c #151515",
+"~ c #171717",
+"{ c #181818",
+"] c #191918",
+"^ c #1C1C1C",
+"/ c #1D1D1D",
+"( c #212118",
+"_ c #21211C",
+": c #232307",
+"< c #222221",
+"[ c #232323",
+"} c #252525",
+"| c #272727",
+"1 c #292929",
+"2 c #2B2B2A",
+"3 c #2E2E22",
+"4 c #2D2D2D",
+"5 c #303004",
+"6 c #303005",
+"7 c #313107",
+"8 c #2F2F2D",
+"9 c #313131",
+"0 c #333333",
+"a c #383836",
+"b c #383838",
+"c c #3A3A3A",
+"d c #3F3F02",
+"e c #414108",
+"f c #3E3E3E",
+"g c #3F3F3F",
+"h c #424242",
+"i c #454545",
+"j c #464646",
+"k c #484848",
+"l c #4C4C10",
+"m c #4A4A4A",
+"n c #4E4E04",
+"o c #4D4D4D",
+"p c #535303",
+"q c #545409",
+"r c #525252",
+"s c #535353",
+"t c #57571E",
+"u c #585823",
+"v c #555555",
+"w c #565656",
+"x c #595958",
+"y c #595959",
+"z c #5A5A5A",
+"A c #5C5C5C",
+"B c #5D5D5D",
+"C c #62621A",
+"D c #5E5E5D",
+"E c #5F5F5F",
+"F c #616161",
+"G c #67671A",
+"H c #64645F",
+"I c #696908",
+"J c #696925",
+"K c #6A6A11",
+"L c #656565",
+"M c #68684A",
+"N c #666666",
+"O c #6A6A50",
+"P c #686868",
+"Q c #6D6D2F",
+"R c #696969",
+"S c #6B6B6B",
+"T c #6D6D6C",
+"U c #6D6D6D",
+"V c #6E6E6E",
+"W c #757500",
+"X c #6F6F6F",
+"Y c #787805",
+"Z c #727272",
+"` c #797906",
+" . c #747474",
+".. c #7B7B23",
+"+. c #787860",
+"@. c #767676",
+"#. c #787878",
+"$. c #7B7B65",
+"%. c #7A7A7A",
+"&. c #7B7B7B",
+"*. c #7D7D7D",
+"=. c #7E7E7E",
+"-. c #7F7F7F",
+";. c #818181",
+">. c #828282",
+",. c #8A8A02",
+"'. c #848484",
+"). c #858584",
+"!. c #868686",
+"~. c #8E8E01",
+"{. c #8D8C5A",
+"]. c #898989",
+"^. c #919101",
+"/. c #8B8B8B",
+"(. c #949401",
+"_. c #94941F",
+":. c #8D8D8D",
+"<. c #959500",
+"[. c #909076",
+"}. c #959535",
+"|. c #939450",
+"1. c #8F8F8F",
+"2. c #979718",
+"3. c #909090",
+"4. c #919191",
+"5. c #939393",
+"6. c #9B9B01",
+"7. c #9B9B0B",
+"8. c #949494",
+"9. c #9D9D01",
+"0. c #9E9E01",
+"a. c #969696",
+"b. c #9D9E3F",
+"c. c #989898",
+"d. c #A1A100",
+"e. c #999999",
+"f. c #A2A200",
+"g. c #9A9A9A",
+"h. c #9C9C92",
+"i. c #9C9C9C",
+"j. c #A5A501",
+"k. c #A2A264",
+"l. c #A1A172",
+"m. c #9D9D9D",
+"n. c #A6A600",
+"o. c #9F9F9F",
+"p. c #A8A800",
+"q. c #A8A925",
+"r. c #A1A1A1",
+"s. c #A2A2A2",
+"t. c #A5A682",
+"u. c #A5A5A4",
+"v. c #AEAE01",
+"w. c #A7A7A7",
+"x. c #AFAF68",
+"y. c #AAAAAA",
+"z. c #B5B503",
+"A. c #ADADAD",
+"B. c #B7B703",
+"C. c #B2B282",
+"D. c #B7B72A",
+"E. c #B0B0B0",
+"F. c #B1B1B1",
+"G. c #B2B2B2",
+"H. c #B6B68B",
+"I. c #B3B3B3",
+"J. c #BDBD01",
+"K. c #BEBE00",
+"L. c #B6B6B6",
+"M. c #BABA9D",
+"N. c #B8B8B8",
+"O. c #C2C20C",
+"P. c #BABABA",
+"Q. c #BBBBB3",
+"R. c #C5C502",
+"S. c #BCBCBC",
+"T. c #BDBDBD",
+"U. c #C4C47B",
+"V. c #CACA33",
+"W. c #C2C2C2",
+"X. c #CFCF01",
+"Y. c #C6C6C6",
+"Z. c #C9C9C9",
+"`. c #CBCBCB",
+" + c #CECECE",
+".+ c #DBDB02",
+"++ c #D0D0D0",
+"@+ c #D6D6B7",
+"#+ c #D4D4D4",
+"$+ c #E2E226",
+"%+ c #D7D7D7",
+"&+ c #E5E501",
+"*+ c #E2E271",
+"=+ c #DADADA",
+"-+ c #DCDCDC",
+";+ c #DDDDDD",
+">+ c #EEEE03",
+",+ c #E3E3E3",
+"'+ c #ECEC8D",
+")+ c #F2F20A",
+"!+ c #F3F314",
+"~+ c #F6F604",
+"{+ c #EBEBEA",
+"]+ c #F0F0E9",
+"^+ c #FEFE09",
+"/+ c #FFFF22",
+"(+ c #FFFF46",
+"_+ c #F9F9BA",
+":+ c #F4F4F4",
+"<+ c #FFFF7D",
+"[+ c #FAFAF9",
+"}+ c #FFFFFF",
+"|+ c #000000",
+"1+ c #000000",
+"2+ c #000000",
+"3+ c #000000",
+"4+ c #000000",
+"5+ c #000000",
+"6+ c #000000",
+"7+ c #000000",
+"8+ c #000000",
+"9+ c #000000",
+"0+ c #000000",
+"a+ c #000000",
+"b+ c #000000",
+"c+ c #000000",
+"d+ c #000000",
+"e+ c #000000",
+"f+ c #000000",
+"g+ c #000000",
+"h+ c #000000",
+"i+ c #000000",
+"j+ c #000000",
+"k+ c #000000",
+"l+ c #000000",
+"m+ c #000000",
+"n+ c #000000",
+"o+ c #000000",
+"p+ c #000000",
+"q+ c #000000",
+"r+ c #000000",
+"s+ c #000000",
+"t+ c #000000",
+"u+ c #000000",
+"v+ c #000000",
+"w+ c #000000",
+"x+ c #000000",
+"y+ c #000000",
+"z+ c #000000",
+"A+ c #000000",
+"B+ c #000000",
+"C+ c #000000",
+"D+ c #000000",
+"E+ c #000000",
+"F+ c #000000",
+"G+ c #000000",
+"H+ c #000000",
+"N . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .N ",
+" .Y.%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+Y. .",
+" .%+;+`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.;+%+ .",
+" .%+`.&. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .&.`.%+ .",
+" .%+`. .e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e. .`.%+ .",
+" .%+`. .g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g. .`.%+ .",
+" .%+`. .i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.y.y.A.A.A.A.A.A.A.E.E.E.E.F.F.F.G.G.I.Y.%+=+=+=+-+-+;+;+;+Z.m.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i. .`.%+ .",
+" .%+`. .m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.o.o.o.o.o.o.r.r.A.A.E.E.E.G.G.I.L.L.L.L.N.N.P.P.S.T.T.Z.Z.`.`.`. + + +++++++++#+#+#+%+%+%+%+%+%+%+=+=+=+=+-+;+;+`.o.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m. .`.%+ .",
+" .%+`. .o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.m.A.A.E.E.F.G.I.I.L.L.P.S.T.T.T.T.W.W.W.W.W.W.W.Y.Y.Y.Y.Y.Z.Z.Z.Z.`.`.`. + + + +++++++++#+#+#+#+%+%+%+%+%+=+=+=+=+-+;+`.m.m.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o. .`.%+ .",
+" .%+`. .r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.o.F.I.L.L.L.L.N.N.N.P.P.P.S.T.T.T.T.W.W.W.W.W.W.W.W.Y.Y.Y.Z.Z.Z.Z.Z.`.`.`. + + + +++++++++#+#+#+#+%+%+%+%+=+=+=+=+=+;+`.m.o.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r. .`.%+ .",
+" .%+`. .s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.o.F.I.L.L.L.L.N.N.N.N.P.P.S.T.T.T.T.T.W.W.W.W.W.W.W.Y.Y.Y.Y.Z.Z.Z.Z.`.`.`. + + + +++++++++++#+#+#+#+%+%+%+%+=+=+=+=+;+`.i.o.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s. .`.%+ .",
+" .%+`.@.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.o.E.I.I.L.L.L.L.N.N.N.P.P.P.S.S.T.T.T.W.W.W.W.W.W.W.W.Y.Y.Y.Y.Z.Z.Z.Z.`.`.`. + + + +I.y.A.A.A.A.A.E.E.E.E.F.F.=+=+=+;+Z.g.s.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.@.`.%+ .",
+" .%+`.@.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.o.E.I.I.I.I.A.y.w.u.u.u.w.w.w.w.w.e.E E F F F F F F F F F F F F H H H H H H L L L L | ! ! ~ h @.a.e.e.e.e.e.&.%+=+=+=+Z.e.u.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.@.`.%+ .",
+" .%+`.@.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.r.E.G.I.I.F.u.m.e.8.5.4.*.i < < < ] . . . . + . . . . . . . . + + + + + + + + @ + + + . . . . > j !.w.w.w.w.>.%+%+=+=+Y.a.u.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.@.`.%+ .",
+" .%+`.@.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.r.A.F.G.I.P.A.y.y.y.w.N ) . . . . . + = 9 o F o 1 $ + . + + + + @ @ @ @ ' 0 x P r 4 - @ @ + + . . $ s o.y.y.'.%+%+%+=+Y.a.w.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.Y.Y.Y.Y.W.T.T.T.T.G.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.@.`.%+ .",
+" .%+`.@.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.r.A.F.G.I.Y.E.y.y.].< + . . . . $ i /.Z.;+{+{+]+]+Z.X ; + + + @ @ @ ^ ].=+]+]+]+{+,+Z.1.h $ @ + . . . { Z y.'.%+%+%+=+W.8.w.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.P.W.W.W.T.S.P.P.P.N.N.N.A.A.A.A.y.y.Z.Z.P.P.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.@.`.%+ .",
+" .%+`.@.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.r.A.E.F.G. +I.w.R - . . . . $ h o.#+{+:+[+[+}+}+}+}+[+P.4 @ @ # * i ++[+}+}+}+}+}+[+[+]+;+o.0 & @ + + . * o =.%+%+%+=+W.8.w.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.L.T.Y.Y.W.E.o.m.a.5.4.4.1.:./.].].!.!.'.>.>.!.).'.g.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.@.`.%+ .",
+" .%+`.@.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.s.A.E.E.F. +y.j * . . . . ! V `.,+:+[+}+}+}+}+}+}+}+}+}+Y./ & = h =+}+}+}+}+}+}+}+}+}+}+[+]+++Z , # @ @ + # i #+%+%+%+W.4.y.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.N.T.++Z.w.w.i.c.a.8.8.4.3.1.:././.].!.!.).>.;.-.-.*.&.%.#.&.y.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.@.`.%+ .",
+" .%+`.@.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.s.y.E.E.F.`.m @ . . . . _ 8.%+{+[+}+}+}+}+}+}+}+}+}+}+}+[+1.> _ W.}+}+}+}+}+}+}+}+}+}+}+}+}+[+,+1._ & $ @ + b #+#+%+%+W.4.A.E.P.++%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+++Y.I.G.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.F.L.W.`.Y.A.A.e.e.c.a.8.5.4.3.1.:./.].].!.!.'.>.;.-.=.&.&.%.#.@. .5.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.@.`.%+ .",
+" .%+`.@.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.s.y.A.E.E.`.] . . . . ^ m.-+]+[+}+}+}+}+}+}+}+}+}+}+}+}+}+:+| F :+}+}+}+}+}+}+}+}+}+}+}+}+}+}+[+{+y.~ = & # c #+#+#+%+T.1.A.G.P.Z.F.r.g.g.g.g.e.`.-+-+-+;+;+;+;+#+G.L.T.I.F.A.F.F.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.I.Z. +%+L.y.e.e.e.c.a.8.5.4.3.1.:./.].].].!.'.>.;.-.-.*.&.%.#.@.@. .&.T.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.@.`.%+ .",
+" .%+`.@.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.s.y.A.E.E.`.] . . + * a.=+]+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+U L.}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+[+]+].! - * c #+#+#+%+T.:.A.I.I.W.%+T.w.u.W.;+L.W.=+=+=+-+-+;+;+#+G.G.E.y.s.m.a.1.a.4.E.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.L.Y.;+W.L.e.e.e.e.c.a.8.8.4.3.1.:././.].!.!.).>.;.-.=.*.&.%.#.@.@. .Z .L.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.@.`.%+ .",
+" .%+`.@.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.u.y.A.A.E.`.] + + + X ++{+[+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+m.=+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+[+,+N , - f #+#+#+%+T.:.E.L.A.Y.#+#+#+Y.w.=+I.W.=+=+=+=+=+=+-+#+G.G.A.w.s.m.a.4.].'.8.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.N.#+;+I.g.e.e.e.c.a.8.5.4.3.1.:./.].].!.!.'.>.;.-.=.&.&.%.#.@. .Z Z Z X c.W.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.@.`.%+ .",
+" .%+`.#.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.u.y.A.A.A.`.] + + 9 W.,+[+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+Z.{+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+:+Z.0 ; f ++#+#+#+P./.E.L.w.Z.%+=+=+ +u.%+G.T.%+%+=+=+=+=+=+#+G.F.A.w.s.i.a.4./.!.5.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.N.++ +w.e.e.e.c.a.8.5.4.3.:.:./.].].!.!.).>.;.-.-.*.&.%.#.@.@. .Z Z X V ;.W.N.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.#.`.%+ .",
+" .%+`.#.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.u.w.A.A.A.Z.^ + - ].%+]+}+}+}+}+}+[+[+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+[+[+}+}+}+}+}+}+[+{+]., f ++++#+#+P.].G.N.s. +=+%+%+`.u.#+E.T.%+%+%+%+%+%+=+#+G.F.A.w.r.g.a.4.].).5.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.Y.#+o.e.e.c.a.8.5.4.3.:.:./.].].!.).'.>.;.-.=.&.&.%.#.@. . .Z Z X U T X I.W.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.#.`.%+ .",
+" .%+`.#.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.u.w.y.A.A.Z.^ + 8 Z.,+:+}+}+}+}+[+5.E ++}+}+}+}+}+}+}+}+}+}+%+:+}+}+}+}+}+}+}+}+}+}+s.x Y.}+}+}+}+}+}+:+Z.9 f ++++++#+N.].I.P.i.++;+%+#+Z.s.#+E.S.#+%+%+%+%+%+%+#+G.E.y.u.o.g.8.3.].'.5.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.=+N.c.a.a.8.5.4.3.1.:./.].].].!.'.>.;.-.=.*.&.#.#.@. . .Z Z X V T S R i.Y.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.#.`.%+ .",
+" .%+`.#.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.u.w.y.A.A.Z.^ @ V ++{+[+}+}+}+}+8.# . Z }+}+}+}+}+}+}+}+}+}+4.=+}+}+}+}+}+}+}+}+}+:+c . ! {+}+}+}+}+}+[+,+P g ++++++#+N.!.I.P.a.#+-+#+++Y.r.++A.S.#+#+#+%+%+%+%+#+G.E.y.u.o.e.8.1.].'.5.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.Z.++o.a.8.5.4.3.:.:./.].].!.).).>.;.-.=.*.&.%.#.@.@. .Z Z X V T S R P %.`.T.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.#.`.%+ .",
+" .%+`.#.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.w.w.y.y.A.Z.^ ) s.%+{+[+}+}+}+}+Z . * w.}+}+}+}+}+}+}+}+}+}+ .W.}+}+}+}+}+}+}+}+}+}+ .& @ Z.}+}+}+}+}+[+{+i.g ++++++#+L.!.L.T.4.=+=+++ +W.o. +A.P.#+#+#+#+#+#+#+#+G.E.y.s.m.c.5.:.].>.5.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.-+L.8.5.4.3.:.:./.].].!.).'.>.;.-.=.&.&.%.#.@. .Z Z Z X U T S R N N R S.W.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.#.`.%+ .",
+" .%+`.#.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.w.u.y.y.y.Z.^ 1 W.=+{+[+}+}+}+}+=+>.s.:+}+}+}+}+}+}+}+}+}+[+z '.[+}+}+}+}+}+}+}+}+}+,+5.].[+}+}+}+}+}+}+{+W.k +++++++L.'.N.T.1.;+=+++`.W.m.`.y.N.++++++#+#+#+#+#+G.A.y.s.m.a.4.:.!.>.5.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T. +#+e.4.3.:.:./.].].!.!.'.>.;.-.=.*.&.#.#.@. . .Z X X V T S R P N L L i.Z.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.#.`.%+ .",
+" .%+`.#.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.w.u.y.y.y.Z.^ 0 `.;+{+[+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+Z.r s ,+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+]+++z + +++++I.>.N.W.3.;+%+ +Z.T.m.Z.w.N.++++++++++++#+#+F.A.w.s.m.a.4./.!.;.5.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.=+L.3.:.:./.].].!.).>.>.;.-.=.&.&.#.#.@. . .Z X X U T S R N N L H F =.Y.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.#.`.%+ .",
+" .%+`.#.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.w.u.w.y.y.Z.^ a ++;+{+[+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+[+/.w o L.}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+]+++N + + +++G.;.P.W.3.;+%+Z.Y.S.g.Y.w.N. + +++++++++++#+F.A.w.r.i.a.3./.).-.5.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W. +-+1.:./.].].!.!.'.>.;.-.=.*.&.#.#.@. .Z Z Z X V T R R P N L H F E S P.Y.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.#.`.%+ .",
+" .%+`.#.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.w.s.w.y.y.Z.^ a ++;+{+[+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+++s A E x {+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+[+{+++L + + +++G.;.P.W.3.;+#+Z.W.P.g.W.u.L. + + + +++++++#+E.y.w.o.g.8.3./.).-.5.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.Z.-+F./.].].!.).'.>.;.-.=.*.&.%.#.@. . .Z X X V T S R N N L H F E E B r.Z.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.W.#.`.%+ .",
+" .%+`.#.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.w.s.w.w.y.Z.^ 0 ++;+{+:+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+]+#.y E N D o.}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+[+{+Z.s + + + +E.-.S.Y.4.;+#+Y.W.L.e.W.s.L. + + + + + + +#+E.y.u.o.g.8.1.].'.-.5.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.%+#+/.].!.).>.>.;.-.=.&.%.#.#.@. .Z Z X X U S S R N N L H F E D A A !.Z.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.#.`.%+ .",
+" .%+`.#.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.w.s.w.w.w.Z.^ | W.;+{+:+[+}+}+}+}+}+}+}+}+}+}+}+}+}+[+m.x B L R U N Z.}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+[+{+E.h `. + + +E.=.T.Y.4.;+++W.T.I.a.T.r.L.`.`.`.`. + + +#+E.y.u.m.e.5.:.].>.=.5.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.`.=+A.!.!.'.>.;.-.=.*.&.#.#.@. .Z Z X X V T R R P N L H F E E B z z L W.`.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.#.`.%+ .",
+" .%+`.%.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.y.r.w.w.w.Y./ , g.;+,+{+[+}+}+}+}+}+}+}+}+}+}+}+}+}+Z.O O ..,.{.{.$.$.{+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+:+,+&.h `.`. + +A.&.W.Z.4.;+++W.P.G.a.S.o.I.Z.Z.`.`.`.`.`.#+A.y.s.m.c.4.:.].>.*.5.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.#+ +:.>.>.;.-.*.&.&.#.#.@. .Z Z X V U T R R N L L H F E D B z z x w y.`.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.%.`.%+ .",
+" .%+`.%.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.y.r.u.w.w.Y./ # k =+,+{+:+}+}+}+}+}+}+}+}+}+}+[+_+*+z.J.X.X..+.+.+X.O.V.'+]+[+}+}+}+}+}+}+}+}+}+}+}+[+{+Y.b h `.`.`. +A.&.W.`.4.-+ +T.N.E.a.N.m.G.Z.Z.Z.Z.Z.Z.`.#+A.w.s.i.a.4./.!.;.&.5.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`. +-+y.>.-.-.=.&.&.#.#.@. .Z Z Z X U T R R P N L H F E D B z z y w w %.++`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.%.`.%+ .",
+" .%+`.%.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.y.o.u.w.w.Y./ # ; /.,+{+]+[+}+}+}+}+}+}+_+'+$+.+.+.+.+$+$+&+>+>+>+>+&+&+$+$+$+*+'+[+}+}+}+}+}+}+}+}+:+,+R ! h Z.`.`. +y.%.W.`.5.-+ +P.L.A.e.L.m.G.Z.Z.Z.Z.Z.Z.Z.++A.w.s.i.a.4./.!.;.&.8.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.%+ +/.-.*.*.&.#.#.@. .Z Z X V V T R R N N L H F E E B z z x w w v E Y.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.%.`.%+ .",
+" .%+`.%. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +y.o.u.u.w.Y./ # $ ] s.,+{+:+[+}+[+_+*+.+.+.+$+&+&+&+>+>+)+~+~+^+^+~+~+~+!+)+>+>+&+&+&+'+_+[+}+}+}+[+,+=._ ! h Z.Z.`.`.y.#.W. +5.=+ +N.G.A.i.I.g.F.Y.Y.Y.Z.Z.Z.Z.++y.w.r.g.a.3./.!.-.&.5. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +++;+y.*.&.%.#.#.@. .Z Z X V U S R R N D s m m f c h k j r s y v v A y.++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%.`.%+ .",
+" .%+`.%. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +y.o.u.u.u.Y./ $ $ * ~ L P.;+]+'+.+.+$+&+&+>+>+)+!+~+~+^+^+^+/+/+/+/+/+^+^+^+^+^+~+~+!+>+>+&+'+]+,+L.r [ ] ) g Z.Z.`.`.w.@.W. +5.=+`.L.E.y.o.G.e.E.Y.Y.Y.Y.Y.Y.Y.++y.u.o.g.8.1.].).-.%.5. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%+;+!.&.#.@.@. .Z Z X X U T R P N w 8 ) = @ . . . . + $ - ' / c m =.`. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%.`.%+ .",
+" .%+`.&.++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++y.m.s.u.u.Y./ $ & * - > } M X..+&+>+>+!+~+~+^+^+^+^+/+/+/+/+/+/+/+/+/+/+/+/+/+/+/+/+^+^+^+~+>+R.M 0 } / ] ) g Z.Z.Z.Z.w. .Y.++8.=+Z.I.A.y.s.E.c.E.W.W.W.Y.Y.Y.Y.++y.u.o.e.8.:.].'.=.#.5.++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++;+T.&.@.@. .Z Z X V U T R P N A 4 ; + . . . . . . . . . . . . . - 2 L w.`.++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++&.`.%+ .",
+" .%+`.&.#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+y.m.s.s.u.Y./ $ & * - > l K.&+>+~+~+^+^+/+/+/+/+/+/+/+/+/+/+/+(+(+(+(+/+/+/+/+/+/+/+/+/+/+/+^+~+O.e } / ] ) g Z.Z.Z.Z.w. .Z.#+8.=+Z.F.y.w.u.A.e.E.W.W.W.W.W.W.Y.++w.s.m.c.5.:.].>.*.#.5.#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+%+;+:.@. .Z Z X X U T R R N N w ' . + + + + + + + + . . . . . . . . . . - w y.++#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+&.`.%+ .",
+" .%+`.&.#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+A.m.s.s.u.Y./ $ & * - ( v.&+)+~+^+/+/+/+/+/+/+/+/+/+/+/+(+(+<+_+_+<+(+(+(+(+/+/+/+/+/+/+/+/+/+/+~+2.} / ] ) g Y.Z.Z.Z.u.Z Z.#+8.=+Y.A.w.w.w.y.g.E.W.W.W.W.W.W.W.++w.s.m.c.5.:.].>.*.@.5.#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+=+W. .Z Z X V U T R P N N H i > . . + + @ @ @ @ + + + + . . . . . . . . . . * F W.#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+&.`.%+ .",
+" .%+`.&.%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+A.i.r.s.s.W./ $ & = ; 6 R.&+>+~+~+^+^+/+/+/+/+/+/+/+/+(+(+<+<+<+(+(+/+/+/+/+/+/+/+/+/+/+/+/+^+^+!+v.[ / ] ) g Y.Y.Z.Z.u.Z Z.%+8.%+Y.y.u.w.y.w.g.F.W.W.W.W.W.W.W.++w.s.m.a.4./.!.>.&.@.8.%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+=+%+1.Z X V U S R P N L H H g & . + + @ @ @ @ # # @ @ @ @ + + + . . . . . . . . @ 1 m.%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+&.`.%+ .",
+" .%+`.&.%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+A.g.r.r.s.W./ $ * = ; ~ ~..+&+&+>+>+)+!+~+~+^+^+^+^+^+/+/+(+(+/+/+/+/+/+^+^+^+^+^+^+^+~+~+!+>+&+X.G [ / { ) g Y.Y.Z.Z.s.X `.%+8.%+W.y.r.u.A.u.i.I.T.T.W.W.W.W.W.++w.r.i.a.4./.!.;.&.@.5.%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+-+W.X X U T R P N N L F F v > . + @ @ @ # $ $ $ $ $ $ # # @ @ @ + + + . . . . . . . ; @. +%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+&.`.%+ .",
+" .%+`.&.=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+A.g.o.r.s.W./ $ * - ; ' 7 2.X..+.+$+$+$+&+&+&+&+>+>+>+)+~+~+^+^+~+~+~+!+)+)+>+>+>+&+&+$+$+.+X.B.Y 8 [ / { ' g Y.Y.Y.Y.r.V `.=+a.%+W.w.m.s.E.s.m.I.T.T.T.T.T.T.W. +u.o.g.8.3.].).-.%. .5.=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+:.T S R P N L L F F E o , . + @ @ @ # $ $ * * * * * & & $ $ @ @ @ + + . . . . . . . * E W.%+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+&.`.%+ .",
+" .%+`.&.=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+A.g.o.o.r.W./ $ * - > ) { } K v.V.X.X..+.+.+.+.+.+.+.+.+.+$+&+&+&+$+$+.+.+.+.+X.X.X.X.V.O.z.(.u 0 4 [ / { ' g Y.Y.Y.Y.o.V +=+a.%+W.u.g.r.I.o.m.I.S.T.T.T.T.T.T. +u.o.g.8.1.].'.-.#. .5.=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+;+W.S R R N L L F F E D w ! . + @ @ @ # $ & * = = - - - ; - = * $ # @ @ + + . . . . . . . @ b F.=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+&.`.%+ .",
+" .%+`.&.-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+A.g.o.o.r.W./ $ * - > ) ] / } 8 G q.V.V.V.X.X.X.X.X.X.X.X.X.X.X.X.X.V.R.R.R.O.K.J.B.D.D.~.u h c 0 2 [ / { ' g W.Y.Y.Y.m.U +-+a.%+W.Z.W.Y.L.m.m.I.P.S.S.S.T.T.T. +u.o.g.8.1.].>.=.#.Z 5.-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+;+'.P N N L F F E D B w 2 + . + @ @ # # $ * = ; ; , ' ) ) ) , ; = $ # @ @ + . . . . . . . . . ) u.-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&.`.%+ .",
+" .%+`.&.;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+A.e.m.o.o.W./ $ * ; , ! ^ < 1 v y.,+{+,+%+@+U.U.U.R.R.R.R.R.O.J.J.B.D.D.x.x.x.H.Y.%+{+[+,+y.r b 9 2 [ / ~ , g W.W.Y.Y.m.U ++;+a.%+S.T.T.W.T.G.y.L.P.P.P.P.P.S.S. +s.m.e.5.:.].>.=.@.Z 5.;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+N.P L H F F E B A z y 0 + + + + @ # $ $ * - ; ' ! { ^ / / / ] ! , - * $ @ @ + + . . . . . . . . , P.;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+;+&.`.%+ .",
+" .%+`.%.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.o.c.m.m.o.W./ & = ; ) ] / 0 1.;+:+:+:+:+[+[+:+{+-+++Y.Q.M.H.H.C.C.y.A.I.W.#+,+:+}+}+}+}+}+}+%+#.b 2 [ ^ ~ , j W.W.Y.W.g.T P.Y.1.P.T.T.W.W.W.S.N.N.N.N.N.P.P.P.P. +s.m.c.4.:.!.>.*.@.Z ].Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.++;+*.L F F E D A z y x h * . + + @ @ $ $ * - > ) ] / < } | | | [ / { , - * $ @ @ + + . . . . . . . . h T.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.%.`.%+ .",
+" .%+`.#.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.g.a.m.m.o.W./ & - > ) ^ f L.:+:+:+:+:+[+[+}+}+}+}+[+:+]+,+ +`.Z.Z.;+{+:+[+}+}+}+}+}+}+}+}+}+}+:+4.9 < ^ ! , r W.W.W.W.g.S L.T./.I.I.L.W.1.!.).>.y.L.N.N.N.N.N.N. +r.i.a.4./.!.;.&.@.Z ].T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.W.=+L.N E E B A z y w w v ] . + + @ @ # $ & = > ! ^ < } 1 4 8 9 9 4 | < ] ' - * $ @ @ + . . . . . . . . # -.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.#.`.%+ .",
+" .%+`.#.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.g.a.i.m.m.W./ & - > ) | L.}+}+}+[+[+:+[+}+}+}+}+}+}+}+}+[+[+[+[+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+[+>./ ] ) > D W.W.W.W.c.R I.T./.I.I.I.%+S.u.e.].u.L.L.L.L.L.N.N. +o.g.a.3./.).-.&. .X !.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.`.;+&.E D A z z x w v v 0 @ . + + @ @ $ $ * ; ! ^ < | 4 9 0 b b b a 9 2 < ] ' - * $ # @ + . . . . . . . . | w.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.#.`.%+ .",
+" .%+`.#.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.e.a.i.m.m.W./ & = ; ) -.[+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+}+:+f ! , - R W.W.W.W.c.R G.S./.G.G.I.=+W.y.e.].s.L.L.L.L.L.L.L. +o.g.a.3.].'.-.%. .X !.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.W.%+P.D A z y w w w @.-.&.) . + + @ @ # $ * - ) ] < | 4 0 b c g h h g c 0 4 < ] ' ; * $ @ @ + . . . . . . . + o L.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.#.`.%+ .",
+" .%+`.#.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.a.8.g.i.m.W.X F H L R -+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+P.R N F g.W.W.W.W.a.R F.P.].F.F.F.=+Y.A.g.].s.I.I.I.L.L.L.L. +o.g.8.1.].>.=.#.Z V '.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.Z.%+=.z y Z #.5.w.w.y.y.y . . + + @ @ # $ * > ~ / [ 2 9 a f h i k k k i f a 2 < { ' - * $ @ + + . . . . . . . , 4.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.#.`.%+ .",
+" .%+`.#.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.a.8.g.g.i.s.y.y.A.A.A.A.A.E.E.E.E.F.G.G.I.I.I.L.L.L.L.L.N.N.N.N.P.P.P.S.S.T.T.T.T.T.W.W.W.W.W.W.W.W.W.Y.Y.Y.W.W.W.W.W.8.P E.N.].E.E.E.=+Z.I.m.].r.G.I.I.I.I.I.I.`.m.e.8.:.].>.*.#.Z U >.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.S.#+Y.:.s.s.A.E.E.A.A.A.o._ . . + + @ @ # & - ' ] _ | 4 0 c h j m o r o o j f 0 2 _ { , - * $ @ + + . . . . . . + | -.m.L.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.#.`.%+ .",
+" .%+`.#.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.8.8.g.g.i.i.m.m.m.o.o.o.r.s.s.s.u.u.u.w.w.w.w.y.y.y.y.A.A.A.A.A.E.E.F.F.G.I.I.I.L.L.L.L.N.N.N.P.P.P.S.T.T.T.T.T.W.W.W.8.N A.L.].A.A.A.=+ +N.o.].o.F.F.G.G.G.I.I.`.m.c.5.:.].>.*.@.Z T >.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.G.N.I.I.I.G.G.F.F.E.E.E.).$ . + + + @ @ $ * ; ) ^ < | 4 0 f i m o s w w s o j c 9 | / ~ , - & # @ + + . . . . . . . $ | o &.y.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.L.#.`.%+ .",
+" .%+`.@.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.4.8.e.g.g.g.i.m.m.m.o.o.o.r.s.s.s.u.u.u.w.w.w.w.y.y.y.y.A.A.A.A.E.E.E.E.F.G.I.I.I.L.L.L.L.N.N.N.P.P.P.S.T.T.T.T.T.W.W.4.N y.I.!.A.A.A.-+#+S.s.].o.E.E.E.F.F.F.G.`.i.a.4./.!.;.&.@.Z S -.I.I.I.I.I.I.I.I.I.I.I.I.I.L.L.I.I.I.I.I.I.I.I.I.y.i.I.L.L.I.I.I.I.G.G.E.o . . + + @ @ @ $ * ; ) ^ < 1 8 a g j o r w z A z v o h b 4 } ^ ! ; = & # @ + . . . . . . . . / } , y I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.@.`.%+ .",
+" .%+`.@.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.3.5.e.e.g.g.g.i.m.m.m.o.o.o.r.s.s.s.u.u.u.w.w.w.w.y.y.y.y.A.A.A.A.E.E.E.F.F.G.I.I.I.L.L.L.L.N.N.N.P.P.P.S.T.T.T.T.W.T.4.N y.F.!.y.y.A.-+%+W.w.].m.A.E.E.E.E.E.E.`.g.a.4./.!.-.&.@.X R -.F.F.F.F.F.F.F.F.F.F.F.F.Y.++ +++F.F.F.F.F.F.F.F.y.e.w.N.L.L.L.L.L.I.I.w.1 . + + + @ @ @ $ * ; ) ] < | 8 a g j o v A E F E z s k f 0 2 < ] ) ; * $ # @ + . . . . . . . * 0 m :.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.@.`.%+ .",
+" .%+`.@.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.1.;.!.!.!.!.].]././.8.o.o.o.r.r.s.s.u.u.u.w.w.w.w.w.y.y.y.A.A.A.A.A.E.E.E.F.G.I.I.I.L.L.L.L.L.N.N.P.P.P.S.S.T.T.T.T.T.3.N w.E.!.y.y.y.=+=+Y.A.].m.A.A.A.A.E.E.E.`.g.8.3.].).-.%. .X R -.E.E.E.E.E.E.E.E.E.E.L.++w.!.!.c.++++Z.Z.Z.Z.Z.Z.N.e.e.w.P.L.L.L.L.L.I.o._ + + + + @ @ @ $ = ; ) ] _ | 4 a g j o w B F L L E x o h a 4 } / ~ ' ; * $ @ + + . . . . . . = 1.A.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.@.`.%+ .",
+" .%+`.@.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.y.s.s.s.s.s.s.s.u.u.c.]././.:.:.1.3.4.4.4.5.3.>.>.>.>.'.).!.s.s.u.u.w.w.w.w.y.y.y.A.A.A.E.E.F.F.G.L.N.P.P.S.T.T.T.T.T.1.N w.A.).y.y.y.=+-+`.F./.i.A.A.A.A.A.A.A.Z.g.8.3.].'.=.#. .V R =.A.A.A.A.A.A.A.A.A.A.s.5.S.G.y.g.>.>.a.a.a.a.a.a.a.e.g.i.F.P.N.N.N.F.A.4.! + + @ @ @ @ @ $ * ; ) { / } 4 0 f j o w D L N N H z r h b 4 | _ ] ) > = $ # @ + . . . . . . . z A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.@.`.%+ .",
+" .%+`.@.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.m.#.@.@. .Z Z X #.#.#.@.@.@. . . . . . .@.@.@.@.@.m.y.y.A.A.A.E.E.F.G.I.I.5.>.y.A.).w.w.w.=+-+ +L.1.g.y.y.y.A.A.A.A.Z.e.8.1.].>.*.#.Z U P *.A.A.A.A.A.A.A.A.A.A.w.!.8.L.Y.L.A.A.e.e.e.e.e.e.e.g.m.m.s.P.P.P.P.N.A.5.] @ @ @ @ @ @ @ $ * - , ! / [ 2 0 c i o w E L R R N D r i b 4 } _ ] ) , - * $ @ + + . . . . . . } m.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.@.`.%+ .",
+" .%+`.@.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.w.w.w.w.w.w.w.w.y.y.y.y.y.y.y.y.y.y.y.y.y.y.e.4.4.3.:././.].].!.).>.>.;.-.*.&.%.#.@.@. .Z Z X m.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.'.w.w.w.=+;+#+P.4.g.y.y.y.y.y.y.y.Z.c.5.:.].>.&.@.Z T N *.y.y.y.y.y.y.y.y.y.y.y.y.i.!.1.P.N.y.y.y.y.y.y.y.u.m.o.o.r.w.S.S.S.P.G.g.[ # # # # @ @ @ $ * - > ) ^ < | 9 c h o v D L R S N E r h a 2 [ / { ) > - * $ # @ + + . . . . . @ Z y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.@.`.%+ .",
+" .%+`.@.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.u.].&.&.&.&.&.&.&.>.].].].].].].].].].].].].].].r.w.w.w.w.u.u.u.s.s.r.o.m.m.g.e.c.a.8.8.4.3.1.:./.s.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.>.u.u.u.=+;+%+W.a.e.w.w.w.y.y.y.y.Z.a.4.:.!.>.&.@.Z S N ].`.`.`.`.`.`.Z.w.w.w.w.w.w.w.m.).w.w.w.w.w.w.w.w.:.1.r.s.u.u.E.W.T.T.T.A.a & & $ $ $ $ # # $ = ; ' { / } 4 a g m s B L R R N B o h 9 } / { ) , ; = & $ # @ + + . . . . . . i w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.@.`.%+ .",
+" .%+`.@.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.1.*.:.3.].T @.3.3.>.P ;.#.Z /.:.>.*.:.:.:./.*.*./.-.&.&.&.&.&.&.&.&.&.&.&.&.&.&.].1.1.1.1.:./././.].:.:.:.:.a.w.w.w.w.w.w.w.w.w.w.w.>.u.u.u.=+;+=+W.e.e.w.w.w.w.y.y.y.Z.a.4./.!.;.&.@.Z S N Z !.!.!.!.!.!.:.Y.Y.Y.Y.Y.W.W.Y.G.1.Y.w.w.w.w.w.w.].h s.u.u.w.y.N.W.T.T.T.R - - * & $ $ $ # $ * - , ! ^ < 2 0 f j r z H P R L z o c 4 < { ' > - * & $ $ # @ + + . . . . . . ] 8.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.@.`.%+ .",
+" .%+`.@.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.5.-.].3.3.-. .X .&.Z Z @.:.!.S >.]./.].3.3./.#.Z *.1.!.).#.U /.!.'.].3.3.!.'.].3./.>.].].].].].]./././././././.!.-.-.-.-.-.-.-.m.u.u.u.>.s.s.s.=+;+-+Z.g.a.w.w.w.w.w.w.w.Z.8.3./.).-.%. .X R N #.g.g.g.g.g.g.c.-.-.-.-.>.8.8.:.).r.y.u.u.u.u.u.u.u.o L w.w.y.y.A.S.W.W.T.s.^ , ; - * * * & $ & = ; ) ] _ | 9 c i o x F L N F x k a | ^ ' - * $ # # @ @ @ @ + + . . . . . . - =.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.@.`.%+ .",
+" .%+`. .s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.i.].).1.3.3.3.&.!.].-.&.].].1.4.>.@.#.!.:././.].&.&.%.].4.'.Z >.].4.!. .1.8.8.%.Z ).8.!.S ).5.a.8.#.Z !.e.e.e.-.>.3.e.4.a.a.4.g.i.c.E s.s.s.;.r.s.s.=+;+;+ +o.a.u.u.u.u.w.w.w.Z.8.1.].'.-.#. .X R N %.s.s.s.s.s.s.s.s.s.s.s.r.5.5.c.s.s.s.s.s.s.s.s.s.s.g.b @.y.A.A.E.F.Z.W.W.N.j ] ) > ; - = * * & * - , ! ^ [ 4 a g m s A F H D s h 9 < ) - $ @ @ @ @ + + + + + + . . . . . . @ X s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s. .`.%+ .",
+" .%+`. .r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.o.!.>./.5.3.:.4.5.:.5.5.8.8.8.8.8.a.4.4.a.c.!.-.!.3.>.1.1.g.8.'.&.&.i.i.a.).).4.m.i.o.o.o.g.1.=.:.r.g.1.1.g.s.s.s.>.X =.s.#.4.g.3.s.r.e.z r.r.r.-.o.o.r.=+;+;+++s.8.s.s.w.r.o.s.u.Z.8.1.].>.=.#.Z V P N %.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r. .0 w.A.E.F.I.N.Z.W.W.o.} ^ ) , ; - - = * * = ; ) ] _ 1 9 c i o x B B w o c 1 ] - @ @ . . . . . . . + + + . , : @ . . * #.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r. .`.%+ .",
+" .%+`. .o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.5.>.8.i.m.e.5.g.m.4.a.4.1.a.g.5.:.1.m.s.s.s.o.:.3.a.s.r.a.a.i.o.a.r.w.w.w./.3.a.w.i.'.8.8.y.s.8.5.8.A.u.5.5.r.A.A.A.A.E.E.E.g.w.E.F.y.o./.s o.o.o.-.o.o.o.=+;+;+%+w.8.s.s.W./.&.1.u.Y.5.:.].>.*.@.Z U N N ].o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.#.0 w.G.I.L.L.S.Z.Y.W.s [ ^ ! ) , ; - * $ * - , ! ^ [ 4 a g k r w w o h 9 / ; # . . . . . . . . . . . . @ I v.n @ . , ;.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o. .`.%+ .",
+" .%+`. .m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.g.].4.s.u.u.w.u.u.w.y.y.y.s.w.y.u.r.y.y.y.c.c.g.e.a.o.u.w.8.g.m.w.A.w.w.u.y.G.F.G.I.I.A.i.i.y.L.A.g.g.y.L.w./.m.L.s.!.!.a.A.).'.E.P.P.I.u.4.&.o m.m.m.-.m.m.m.=+;+;+=+w.8.r.r.W.s.:.1.s.Y.4.:.].>.*.@.Z T &.g.i.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.9 &.I.L.L.N.N.Z.Z.Y.w.c [ ^ { ! , * @ # $ * ; , ~ / | 9 c h m o o j b } ) $ + . . . . . . . . . . . . 5 K.K.6.d @ [ a.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m.m. .`.%+ .",
+" .%+`. .g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.:.].w.F.A.A.E.y.A.F.F.y.w.y.w.w.w.E.F.F.L.L.I.I.L.L.L.N.N.N.L.N.P.P.I.y.A.u.E.s.m.s.T.T.I.s.I.I.W.L.T.W.W.I.F.A.I.S.I.w.S.W.w.:.N.W.Y.S.y.a.>.V m g.g.g.=.i.u.u. +%+#+++y.a.o.o.N.`.Z.A.s.W.4./.!.;.&.Z &.8.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.4.c &.L.N.P.S.T.`.Z.Y.).9 < / ~ * @ @ @ $ & - ; ) ^ < 2 9 c h i h c 4 ^ = + . . . . . . . . . . . . . W X.z.6.,.: f i.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g.g. .`.%+ .",
+" .%+`. .e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.3.!.s.P.P.P.S.S.S.N.T.T.T.P.P.T.F.N.P.T.E.E.N.E.E.E.F.P.T.T.W.W.T.T.W.T.T.W.Y.Y.Y.A.P.T.Z.Z.T.y.y.L.Z.u.W.`.`.m.o.w.w.I.w.w.L. +T.W. + +Y.F.m.].Z A S e.e.g.m.E.T.T.N.E.u.a.1.a.m.o.o.o.o.o.o.P.3./.!.=. .].a.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.:.9 3.P.S.T.W.W.++Z.W.R | ^ & . . + $ > * * - , ! ^ [ 2 0 b c b 9 < , # . . . . . . . . . . . . . # n.R.v.6.6.W +.A.g.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e. .`.%+ .",
+" .%+`. .a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.!.o.S.W.W.W.W.W.W.Y.Y.Y.Y.Z.Z.Z.Z.Z.Z.Z.Z.Z.`.Y.A.N.W.Y.I.A.A.Y.W.A.E.Z.y.A.L. +L.L.L.T. +L.L.W.#+++++#+Y.++#+%+Y.`.%+%+T.L.Y.#+#+w.=+=+ +I.u.1.@.B @.a.8.c.u.G.T.Z.`.W.N.y.g.:.].m.m.m.m.m.m.o.L.1.].%.*.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.E k N.T.W.W.W.`.++`.F.c & $ + . $ ) ! ' ; = - , ~ / [ 2 8 9 4 [ ! & + . . . . . . . . . . . . . ( O.J.j.6.6.<.b.L.E.G.c.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a. .`.%+ .",
+" .%+`. .8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.&./.:.1.4.8.4.5.g.G.I.I.L.L.L.N.Y.#+#+#+#+#+#+%+%+%+%+%+%+%+%+%+=+=+=+=+Y.%+=+=+Y.Y.Y.%+P.P.Y. +%+G.y.;+;+Z.W.W.++%+W.Y.,+W.W.-+ +W.,+#+L.y.8.-.o >.8.;.].e.w.L.W.#+%+Z.P.A.m.1.].g.i.i.i.m.m.m.A.&.=.:.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.H g W.W.W.Y.Z. +#+`.w & @ @ # ^ < ^ { ! , * - , ~ ^ < } } < { ; @ . . . . . . . . . . . . . . d R.z.d.6.<.6.7.t.N.N.s.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8. .`.%+ .",
+" .%+`. .8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.*.U X Z Z .#.&.=.;.'.]./.:.4.8.e.m.o.r.u.w.w.y.A.E.F.`. +++++++++#+#+#+%+%+,+,+,+,+{+{+,+{+{+{+=+y.`.{+{+ +Y.Y.{+G.W.=+++Z.++ +A.=+-+L.A.i. .o h >.&.#.].a.u.I.T.`. +Y.N.y.g.:.-.%.%.%.%.&.!.g.8.1.5.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.5.k g.W.Y.Z.Z.Z.++y.] # . * ] 0 4 } _ ^ ) $ * - , ! ] ^ ^ ~ > & @ . . . . . . . . . . . . . . K R.p.0.9.<.9.0.7.N.N.N.u.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8. .`.%+ .",
+" .%+`. .4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.Z w w w w w w w x x y y z A P X Z .@.#.&.-.>.).]./.1.5.a.e.i.o.s.u.w.y.A.I.W.W.Y.Y.Z.`.`. + + +=+;+,+{+{+{+{+{+{+,+{+{+,+{+]+,+,+,+L.E.r.&.z z z =.U @.).5.o.A.L.T.T.P.E.u.a.].>.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.].9 a.Z.Z.`.`. +'.! # , < f h c 0 4 } / ' * * - > ' ) ) > ; q 5 @ . . . . . + . + . . . . . 6.O.f.9.d.2.6.d.6.k.m.3.].s.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4. .`.%+ .",
+" .%+`.Z 3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.>.#.#.#.#.#.#.#.#.E r r r r r r s s B E F F H L P S U Z *.-.>.!.]./.1.4.a.e.i.o.r.s.u.E.L.L.N.P.P.S.T.T.T.Y.;+,+,+,+,+;+N.G.w.-.Z 3.3.3.!.P Z -./.c.s.y.E.F.A.w.i.3.=.H 3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.>.o W.`. + +++X a X :.h j o m i f 0 4 } / ; & * - - - * 7 R.6.d $ . . . . + . . + . . . > R.B.6.6.j.2.6.d.0.7.[.1.e.e.a.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.Z `.%+ .",
+" .%+`.Z :.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:. .U U U U U U U U F o o o o r r s s E U X Z @.#.&.-.>.!./.1.5.a.e.i.o.s.u.w.w.y.A.F.G.y.P Z :.:.:.:.).N S #.>.:.a.m.s.s.o.g.4.>.z !.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.o E Z.++++#+W.Y.;+++4.h m w v o j f 0 2 ! $ $ & & & ; ,.X.j.,.d @ . . . . . . . . . . 5 X.z.<.6.j.6.<.0.0.9.b.g.e.i.o.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.Z `.%+ .",
+" .%+`.Z /././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././.%.F F F F F F F F w o r r s w w x z Z -.>.!.].u.E.N ]./././././.].U N X #.>.].4.8.8.4.:.R s !./././././././././././././././././././././././././././././././././././././././././././././././././././././././.r x #+#+#+%+=+;+=+++8.g o D E z r j c 4 { $ @ @ @ 7 X.K.f.(.~.5 . . . . . . . . . . p V.p.^.6.n.6.(.6.0.0.7.l.g.e.e./././././././././././././././././././././.Z `.%+ .",
+" .%+`.Z /./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././.].].].].].].].].].R y y y y z z ]./././././././.].B m r v y A D D z r X ]././././././././././././././././././././././././././././././././././././././././././././././././././././././././.].i E.#+%+%+=+-+,+-+#+G.z o E N L z o g 2 * + @ + q .+z.f.(.6.,.: . . . . . . . . @ ..O.d.^.6.n.0.(.<.6.9.6.b.h.e.e.:././././././././././././././././././././.Z `.%+ .",
+" .%+`.Z ].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].;.%.%.%.%.%.%.%.*.].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].].@.9 L.=+=+-+;+;+,+=+%+W.#.o A N N z o c [ = . . ` X.p.d.<.6.0.,.! . . . . . . . % ~.J.2.~.0.n.d.(.^.2.7.6.7.l.e.e.4.].].].].].].].].].].].].].].].].].].].].Z `.%+ .",
+" .%+`.Z !.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.@.L %+;+;+;+;+;+,+=+%+++4.s s E F w i 8 ! $ @ ~.V.f.0.2.6.f.6.G - . . . . . . > n.v.^.~.d.n.d.(.^.t.l.k.b.l.g.e.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.Z `.%+ .",
+" .%+`.Z '.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.v #.=+;+;+;+;+;+,+=+=+%+I.L i r r k b [ ' : K.O.9.d.6.6.d.0.6.e @ . . . . ^ O R.j.~.~.d.n.f.(.}.s.r.o.m.i.e.3.>.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.'.Z `.%+ .",
+" .%+`.Z >.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.g Z ;+;+;+;+;+;+{+=+=+=+#+&.h b c a 1 { d X.B.<.d.0.<.6.0.0.~.: . . * g r.M.V.2.,.k.q.n.j.(.b.s.r.o.o.4.!.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.Z `.%+ .",
+" .%+`.Z ;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.-.h W.;+;+;+;+;+,+,+=+=+=+T.u.].j | / ~ q X.z.<.f.d.<.6.0.0.9.` : f g.L.L.U.K.~.|.y.t.v.j.(.l.s.s.8.).@.x ;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.Z `.%+ .",
+" .%+`.Z -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.N c W.;+;+;+;+;+,+,+;+-+-+-+=+`.A.s | C X.q.(.j.f.2.<.9.9.9.6.}.I.P.u.w.U.v.}.y.y.A.C.p.2.t.a.'.X j [ B -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.Z `.%+ .",
+" .%+`.Z *.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.L .;+;+;+;+;+;+{+;+;+;+;+;+,+;+++I.D.X.j.^.j.j.6.^.2.6.6.9.7.|.P.w.G.U.d.y.E.E.A.y.t.}.;.P h / { z *.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.Z `.%+ .",
+" .%+`.Z &.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.j /.=+;+;+;+;+,+{+,+]+{+,+=+%+#+#+*+V.9.^.j.j.6.^.^.6.6.6.6.2.Q.N.L.H.C.I.F.y.a.].Z T c ^ , ^ A &.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.&.Z `.%+ .",
+" .%+`.Z #.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.9 ].;+;+;+;+;+,+]+-+-+=+%+%+#+#+*+O.<.~.j.n.6.^.~.k.b.7.6.<.k.N.N.L.I.u.3.@.P E 0 ! , ~ k #.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.Z `.%+ .",
+" .%+`.Z #.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.@.A Z.;+;+;+;+;+{+;+-+=+%+%+#+#+.+B.^.,.j.n.0.^._.W.W.W.Q.M.M.N.G.a.=.N B h / ' , _ k @.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.Z `.%+ .",
+" .%+`.X @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.E i Y.;+;+;+;+;+;+;+=+=+%+#+#+X.v.~.~.j.n.d.^.b.W.W.W.T.T.s.#.S A m c / ) } 9 R U @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.X `.%+ .",
+" .%+`.X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .v ].;+;+;+;+;+=+;+=+=+%+%+#+X.j.~.x.v.n.f.^.k.L.F.u.&. .z k c _ ~ } b A T . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .X `.%+ .",
+" .%+`.X Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z f a.;+;+;+;+#+-+-+=+%+%+%+V.6.b.Y.M.v.f.<.[.S F w k | } ! ] f r Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z X `.%+ .",
+" .%+`.X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X R | o.;+;+;+;+`.;+=+=+T.y.J._.!.-.&.l.q._.F h 4 ~ , , i s X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X `.%+ .",
+" .%+`.X V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V U z #+;+;+;+'.'.'.'.>.>.|.;.X N D o C u ) , / b z U V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V X `.%+ .",
+" .%+`.X T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T r r +;+;+>.>.;.=.@.X D k g ] ] ! ~ 4 m F L T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T X `.%+ .",
+" .%+`.X R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R j i.++A.Z H a | } [ ^ , ) [ [ r z R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R X `.%+ .",
+" .%+`.X P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P w i 0 [ , , , , ] ] g o P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P X `.%+ .",
+" .%+`.X N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 4 , , , ^ g g N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N X `.%+ .",
+" .%+`.X N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N L L L N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N X `.%+ .",
+" .%+`.%.X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X %.`.%+ .",
+" .%+;+`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.;+%+ .",
+" .Y.%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+%+Y. .",
+"N . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .N "};
diff --git a/frontend/xpm_data.h b/frontend/xpm_data.h
new file mode 100644
index 0000000..ac7186c
--- /dev/null
+++ b/frontend/xpm_data.h
@@ -0,0 +1,48 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2001 SEIKO EPSON CORPORATION
+
+ Date Author Reason
+ 06/01/2001 N.Sasaki New
+
+ This file is part of the `iscan' program.
+
+ 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.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#ifndef ___XPM_DATA_H
+#define ___XPM_DATA_H
+
+extern char * scan_xpm [ ];
+extern char * preview_xpm [ ];
+extern char * zoom_xpm [ ];
+
+extern char * auto_xpm [ ];
+
+extern char * gamma_m_xpm [ ];
+extern char * gamma_r_xpm [ ];
+extern char * gamma_g_xpm [ ];
+extern char * gamma_b_xpm [ ];
+
+extern char * penguin_xpm [ ];
+
+#endif // ___XPM_DATA_H
+