From 1145733c29db0a678537ce99ff60e21613f622a8 Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Fri, 6 Jan 2023 10:02:49 +0200 Subject: Import iscan 2.30.4-2 --- frontend/Makefile.am | 102 ++ frontend/esmod-wrapper.hh | 282 ++++++ frontend/file-selector.cc | 1137 +++++++++++++++++++++++ frontend/file-selector.h | 135 +++ frontend/gimp-plugin.h | 186 ++++ frontend/pisa_aleart_dialog.cc | 144 +++ frontend/pisa_aleart_dialog.h | 52 ++ frontend/pisa_change_unit.cc | 101 ++ frontend/pisa_change_unit.h | 50 + frontend/pisa_configuration.cc | 223 +++++ frontend/pisa_configuration.h | 63 ++ frontend/pisa_default_val.h | 89 ++ frontend/pisa_enums.h | 248 +++++ frontend/pisa_error.cc | 178 ++++ frontend/pisa_error.h | 104 +++ frontend/pisa_esmod_structs.h | 75 ++ frontend/pisa_gamma_correction.cc | 718 +++++++++++++++ frontend/pisa_gamma_correction.h | 85 ++ frontend/pisa_gimp.cc | 665 +++++++++++++ frontend/pisa_gimp.h | 109 +++ frontend/pisa_gimp_1_0_patch.h | 58 ++ frontend/pisa_image_controls.cc | 381 ++++++++ frontend/pisa_image_controls.h | 50 + frontend/pisa_img_converter.cc | 99 ++ frontend/pisa_img_converter.h | 45 + frontend/pisa_main.cc | 116 +++ frontend/pisa_main.h | 39 + frontend/pisa_main_window.cc | 1746 +++++++++++++++++++++++++++++++++++ frontend/pisa_main_window.h | 112 +++ frontend/pisa_marquee.cc | 117 +++ frontend/pisa_marquee.h | 83 ++ frontend/pisa_preference.cc | 351 +++++++ frontend/pisa_preference.h | 68 ++ frontend/pisa_preview_window.cc | 1844 +++++++++++++++++++++++++++++++++++++ frontend/pisa_preview_window.h | 171 ++++ frontend/pisa_progress_window.cc | 263 ++++++ frontend/pisa_progress_window.h | 118 +++ frontend/pisa_sane_scan.cc | 1214 ++++++++++++++++++++++++ frontend/pisa_sane_scan.h | 172 ++++ frontend/pisa_scan_manager.cc | 1003 ++++++++++++++++++++ frontend/pisa_scan_manager.h | 151 +++ frontend/pisa_scan_selector.cc | 503 ++++++++++ frontend/pisa_scan_selector.h | 72 ++ frontend/pisa_scan_tool.cc | 591 ++++++++++++ frontend/pisa_scan_tool.h | 63 ++ frontend/pisa_settings.cc | 149 +++ frontend/pisa_settings.h | 75 ++ frontend/pisa_structs.h | 226 +++++ frontend/pisa_tool.cc | 184 ++++ frontend/pisa_tool.h | 79 ++ frontend/pisa_view_manager.cc | 1134 +++++++++++++++++++++++ frontend/pisa_view_manager.h | 227 +++++ frontend/xpm_data.cc | 1740 ++++++++++++++++++++++++++++++++++ frontend/xpm_data.h | 48 + 54 files changed, 18038 insertions(+) create mode 100644 frontend/Makefile.am create mode 100644 frontend/esmod-wrapper.hh create mode 100644 frontend/file-selector.cc create mode 100644 frontend/file-selector.h create mode 100644 frontend/gimp-plugin.h create mode 100644 frontend/pisa_aleart_dialog.cc create mode 100644 frontend/pisa_aleart_dialog.h create mode 100644 frontend/pisa_change_unit.cc create mode 100644 frontend/pisa_change_unit.h create mode 100644 frontend/pisa_configuration.cc create mode 100644 frontend/pisa_configuration.h create mode 100644 frontend/pisa_default_val.h create mode 100644 frontend/pisa_enums.h create mode 100644 frontend/pisa_error.cc create mode 100644 frontend/pisa_error.h create mode 100644 frontend/pisa_esmod_structs.h create mode 100644 frontend/pisa_gamma_correction.cc create mode 100644 frontend/pisa_gamma_correction.h create mode 100644 frontend/pisa_gimp.cc create mode 100644 frontend/pisa_gimp.h create mode 100644 frontend/pisa_gimp_1_0_patch.h create mode 100644 frontend/pisa_image_controls.cc create mode 100644 frontend/pisa_image_controls.h create mode 100644 frontend/pisa_img_converter.cc create mode 100644 frontend/pisa_img_converter.h create mode 100644 frontend/pisa_main.cc create mode 100644 frontend/pisa_main.h create mode 100644 frontend/pisa_main_window.cc create mode 100644 frontend/pisa_main_window.h create mode 100644 frontend/pisa_marquee.cc create mode 100644 frontend/pisa_marquee.h create mode 100644 frontend/pisa_preference.cc create mode 100644 frontend/pisa_preference.h create mode 100644 frontend/pisa_preview_window.cc create mode 100644 frontend/pisa_preview_window.h create mode 100644 frontend/pisa_progress_window.cc create mode 100644 frontend/pisa_progress_window.h create mode 100644 frontend/pisa_sane_scan.cc create mode 100644 frontend/pisa_sane_scan.h create mode 100644 frontend/pisa_scan_manager.cc create mode 100644 frontend/pisa_scan_manager.h create mode 100644 frontend/pisa_scan_selector.cc create mode 100644 frontend/pisa_scan_selector.h create mode 100644 frontend/pisa_scan_tool.cc create mode 100644 frontend/pisa_scan_tool.h create mode 100644 frontend/pisa_settings.cc create mode 100644 frontend/pisa_settings.h create mode 100644 frontend/pisa_structs.h create mode 100644 frontend/pisa_tool.cc create mode 100644 frontend/pisa_tool.h create mode 100644 frontend/pisa_view_manager.cc create mode 100644 frontend/pisa_view_manager.h create mode 100644 frontend/xpm_data.cc create mode 100644 frontend/xpm_data.h (limited to 'frontend') 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 +#endif + +#include "file-selector.h" +#include "gettext.h" +#define _(msg_id) gettext (msg_id) + +#include +#include +#include +#include +#include +#include + + +#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 +#include +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 +#include +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 +#endif + +#include +#include +#include + +#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 + +#include "gettext.h" +#define _(msg_id) gettext (msg_id) + +#include + +/*------------------------------------------------------------*/ +#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 + +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 + +#include "gettext.h" +#define _(msg_id) gettext (msg_id) + +/*------------------------------------------------------------*/ +#include +#include + +/*------------------------------------------------------------*/ +#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 +#include + +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 + +#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 (&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 (&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 + +#include "gettext.h" +#define _(msg_id) gettext (msg_id) + +/*------------------------------------------------------------*/ +#include +#include +#include +#include + +/*------------------------------------------------------------*/ +#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 +#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 +#include +#include +#include +#include +#include + +#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", + "/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 = "/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 = "/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 +#include + +#ifdef HAVE_ANY_GIMP +#include +#include "gimp-plugin.h" + +#ifndef HAVE_GIMP_2 +#ifdef HAVE_LIBGIMP_GIMPFEATURES_H +#include +#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 + +#include "gettext.h" +#define _(msg_id) gettext (msg_id) + +/*------------------------------------------------------------*/ +#include +#include +#include + +/*------------------------------------------------------------*/ +#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 + +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> 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 + +#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 +#include +#include +#include +#include + +#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 + +#include "gettext.h" +#define _(msg_id) gettext (msg_id) +#define N_(msg_id) (msg_id) + +/*------------------------------------------------------------*/ +#include +#include +#include + +/*------------------------------------------------------------*/ +#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 (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 + (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 +#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 +#include +#include +#include +#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 +#include +#include + +/*------------------------------------------------------------*/ +#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 (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 +#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 + +#include "pisa_progress_window.h" +#include "gettext.h" +#define _(msg_id) gettext (msg_id) + +#include +#include + +#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 +#endif + +#include + +//! 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 +#include +#include +#include + +#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 (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(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 +#include + +#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 +#include +#include +#include +#include +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 + (_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 + (_pixeltype)); + + info->bits_per_pixel = calc_bitperpix (static_cast + (_pixeltype), + static_cast + (_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 + (_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 + (_pixeltype)); + + info->bits_per_pixel = calc_bitperpix (static_cast + (_pixeltype), + static_cast + (_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 + (_pixeltype)); + + info->bits_per_pixel = calc_bitperpix (static_cast + (_pixeltype), + static_cast + (_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 (_pixeltype)); + m_moire_info.out_rowbytes = + calc_rowbytes (m_moire_info.out_width, + static_cast (_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 + +#include "gettext.h" +#define _(msg_id) gettext (msg_id) + +/*------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#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 +#include + +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 +#include + +/*------------------------------------------------------------*/ +#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 + +/*------------------------------------------------------------*/ +#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 (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 +#endif + +#ifdef HAVE_GTK_GTK_H +#include +#ifndef HAVE_GTK_2 +#include +#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 +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 _pointD; +typedef _point _pointL; + +/*--------------------------------------------------------------*/ +template +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 > _rectPL; +typedef _rect _rectD; +typedef _rect _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 + +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 ( 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 +#include +#include "pisa_structs.h" + +template +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 +int pt_in_rect ( const _rect & rect, const _point & 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 + +#include "gettext.h" +#define _(msg_id) gettext (msg_id) + +/*------------------------------------------------------------*/ +#include +#ifndef HAVE_GTK_2 +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*------------------------------------------------------------*/ +#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 (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 (_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 + +#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 + +#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 + -- cgit v1.2.3