diff options
Diffstat (limited to 'frontend/file-selector.cc')
-rw-r--r-- | frontend/file-selector.cc | 1137 |
1 files changed, 1137 insertions, 0 deletions
diff --git a/frontend/file-selector.cc b/frontend/file-selector.cc new file mode 100644 index 0000000..4848168 --- /dev/null +++ b/frontend/file-selector.cc @@ -0,0 +1,1137 @@ +/* file-selector.cc -- customized file selection dialog + Copyright (C) 2003, 2005, 2008 SEIKO EPSON CORPORATION + + This file is part of the `iscan' program. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + As a special exception, the copyright holders give permission + to link the code of this program with the esmod library and + distribute linked combinations including the two. You must obey + the GNU General Public License in all respects for all of the + code used other then esmod. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "file-selector.h" +#include "gettext.h" +#define _(msg_id) gettext (msg_id) + +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + + +#include "pisa_aleart_dialog.h" +#include "pisa_enums.h" +#include "pisa_error.h" + +#include "../lib/pcxstream.hh" +#include "../lib/pnmstream.hh" +#include "../lib/pngstream.hh" +#include "../lib/jpegstream.hh" +#include "../lib/tiffstream.hh" +#include "../lib/pdfstream.hh" + +#ifndef DEBUG +#define g_print(...) +#endif + + +#ifdef DONT_HAVE_GTK_2 +#undef HAVE_GTK_2 +#endif + +#ifndef HAVE_GTK_2 +#define G_CALLBACK GTK_SIGNAL_FUNC +#define g_signal_stop_emission_by_name gtk_signal_emit_stop_by_name +#define g_signal_connect_swapped gtk_signal_connect_object +#define g_signal_connect gtk_signal_connect +#define GTK1_OBJ(obj) GTK_OBJECT (obj) +GtkWidget * // "stolen" from GTK+2.0 +gtk_widget_get_parent (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + return widget->parent; +} +#else +#define GTK1_OBJ(obj) obj +#endif + +struct menu_info // helper struct for callbacks +{ + file_selector *fs; + GtkWidget *widget[4]; // holding "ADF" spinboxes and labels + int index; +}; + + +// callback implementations + +static void +_destroy (file_selector *fs) +{ + g_print ("%s\n", __func__); + + fs->destroy (true); +} + +#if 0 +static void +_click_ng( GtkWidget *, file_selector * ) +{ + DBG_FS fprintf( stderr, "_click_ng\n" ); + + gtk_main_quit(); +} +#endif + +static void +_click_ok (file_selector *fs) +{ + g_print ("%s\n", __func__); + + if (fs->save_pathname ()) + { + fs->destroy (); + gtk_main_quit (); + } +} + +// FIXME: this really should be using image stream capability API to +// determine what is and what is not supported. +static void +_change_format (GtkWidget *, struct menu_info *id) +{ + id->fs->change_format( id->index ); // fs->get_type() below, requires this + + // adjust "file with all pages" check box + bool make_active = (iscan::PDF == id->fs->get_type () || + iscan::TIF == id->fs->get_type ()); + gtk_widget_set_sensitive (id->widget[0], make_active); + if (!make_active) // disable multi-page mode when changing to a format that + // does not support it, do not do the opposite! + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (id->widget[0]), false); + } + + // adjust "binding edge" selector sensitivity + // We only support this for file formats (i.e. `imgstream` classes) + // that support "easy" rotation. Yes, this is utterly flawed. The + // "binding edge" selector _should_ be in the main part of the GUI. + // It has no dependency on saving to file whatsoever, it is an issue + // that depends on device capabilities and document characteristics + // only. + make_active = (iscan::PDF == id->fs->get_type ()); + gtk_widget_set_sensitive (id->widget[1], make_active); + gtk_widget_set_sensitive (id->widget[2], make_active); + gtk_widget_set_sensitive (id->widget[3], make_active); +} + +static void +_delete_text (GtkEditable *ed, gint start, gint end, file_selector *fs) +{ + g_print ("%s\n", __func__); + +#ifndef HAVE_GTK_2 + gtk_signal_handler_block_by_func (GTK_OBJECT (ed), + GTK_SIGNAL_FUNC (_delete_text), fs); + fs->delete_text (start, end); + gtk_signal_handler_unblock_by_func (GTK_OBJECT (ed), + GTK_SIGNAL_FUNC (_delete_text), fs); +#else + g_signal_handlers_block_by_func (ed, (gpointer) _delete_text, fs); + fs->delete_text (start, end); + g_signal_handlers_unblock_by_func (ed, (gpointer) _delete_text, fs); +#endif + + g_signal_stop_emission_by_name (GTK1_OBJ(ed), "delete-text"); +} + +static void +_insert_text (GtkEditable *ed, gchar *text, gint len, gint *pos, + file_selector *fs) +{ + g_print ("%s\n", __func__); + +#ifndef HAVE_GTK_2 + gtk_signal_handler_block_by_func (GTK_OBJECT (ed), + GTK_SIGNAL_FUNC ( _insert_text), fs); + fs->insert_text (text, len, pos); + gtk_signal_handler_unblock_by_func (GTK_OBJECT (ed), + GTK_SIGNAL_FUNC (_insert_text), fs); +#else + g_signal_handlers_block_by_func (ed, (gpointer) _insert_text, fs); + fs->insert_text (text, len, pos); + g_signal_handlers_unblock_by_func (ed, (gpointer) _insert_text, fs); +#endif + + g_signal_stop_emission_by_name (GTK1_OBJ(ed), "insert-text"); +} + +static void +_change_seqnum (GtkAdjustment *, file_selector *fs) +{ + fs->change_seqnum (); +} + +static void +_change_digits (GtkAdjustment *, file_selector *fs) +{ + fs->change_digits (); +} + +static void +_toggled_multi_page_mode_fs (GtkWidget *widget, file_selector *fs) +{ + fs->change_page_mode (gtk_toggle_button_get_active ( + GTK_TOGGLE_BUTTON (widget))); +} + +static void +_changed_binding_edge (GtkWidget *widget, file_selector *fs) +{ + fs->change_binding_edge (gtk_toggle_button_get_active ( + GTK_TOGGLE_BUTTON (widget))); +} +static void +_toggled_multi_page_mode_widget (GtkWidget *widget, GtkWidget *p) +{ + bool on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + gtk_widget_set_sensitive (p, !on); +} + +// class implementation + +using std::string; +using std::stringstream; +using iscan::file_opener; + + // define static variables +const file_selector::format +file_selector::_format[] = { + { "PCX", 0, "pcx", "pcx", iscan::PCX }, + { "PNM", 0, "pnm", "pnm", iscan::PNM }, + { "PNG", 0, "png", "png", iscan::PNG }, + { "JPEG", 0, "jpg", "jpg|jpeg", iscan::JPG }, + { "TIFF", 0, "tiff", "tif|tiff", iscan::TIF }, + { "PDF", 0, "pdf", "pdf", iscan::PDF }, + { 0, 0, 0, 0, iscan::PNM } // array terminator +}; + +void +file_selector::init () +{ + g_print ("%s\n", __func__); + + _widget = NULL; + _filename = NULL; + _pathname = NULL; + + _using_adf = false; + _format_index = 0; + _deleted_all = false; + _multi_page_mode = false; + _has_left_edge_binding = true; + + _number = -1; + _seqnum = NULL; + _digits = NULL; + + int size = 1; // terminating { 0 } element + for (int i = 0; 0 != _format[i].name; ++i) + ++size; + _fmt = new const format * [size]; + // okay, so we may waste a few + + // Looks like the idea is to only add supported formats to _fmt and + // leave unsupported formats out. + int cnt = 0; + int len = 0; + for (int i = 0; i < size - 1; ++i) + { + _fmt[cnt] = &_format[i]; // FIXME: check support + len += strlen (_fmt[cnt]->rgx); + ++len; // for the '|' + ++cnt; + } + _fmt[cnt] = 0; // array terminator + + _ext_regex = new char[len]; // overwrite last '|' with '\0' + char *pos = _ext_regex; + for (int i = 0; _fmt[i]; ++i) + { + strcpy (pos, _fmt[i]->rgx); + pos += strlen (_fmt[i]->rgx); + *pos++ = '|'; // overwrite '\0', then advance + } + *--pos = '\0'; // go back one, then terminate + + g_print ("%s: ext_regex =`%s'\n", __func__, _ext_regex); +} + +void +file_selector::create_window (GtkWindow *window, GtkWidget *parent, + bool do_consecutive_scanning, + bool do_duplex, + const char* default_format) +{ + g_print ("%s: enter\n", __func__); + + _parent = parent; + _using_adf = do_consecutive_scanning; + _do_duplex = do_duplex; + + _widget = GTK_FILE_SELECTION (gtk_file_selection_new (PACKAGE)); + + if (!_widget) + throw pisa_error( PISA_STATUS_NO_MEM ); + + gtk_window_set_transient_for (GTK_WINDOW (_widget), window); + gtk_file_selection_hide_fileop_buttons (_widget); + + gint index = 0; + if (is_supported (default_format)) + index = get_index (default_format); + else if (is_supported ("PNG")) + index = get_index ("PNG"); + + add_dialog_extensions (index); + // needs to be called before we canonize + + if (!_filename) + _filename = canonize ("default"); + + free (_pathname); + _pathname = 0; + // FIXME? 0 == _filename + gtk_file_selection_set_filename (_widget, _filename); + + g_signal_connect_swapped (GTK1_OBJ (_widget->ok_button), "clicked", + G_CALLBACK (_click_ok), GTK1_OBJ (this)); + g_signal_connect_swapped (GTK1_OBJ (_widget->cancel_button), "clicked", + G_CALLBACK (_destroy), GTK1_OBJ (this)); + g_signal_connect_swapped (GTK1_OBJ (_widget), "delete_event", + G_CALLBACK (_destroy), GTK1_OBJ (this)); + g_signal_connect_swapped (GTK1_OBJ (_widget), "destroy", + G_CALLBACK (gtk_main_quit), GTK1_OBJ (_widget)); + + // We need to interfere with users selecting and editing filenames + // to guarantee a correct extension and the appropriate templating + // bit when _using_adf. + g_signal_connect (GTK1_OBJ (_widget->selection_entry), "delete-text", + G_CALLBACK (_delete_text), this); + g_signal_connect (GTK1_OBJ (_widget->selection_entry), "insert-text", + G_CALLBACK (_insert_text), this); + + gtk_widget_show (GTK_WIDGET (_widget)); + gtk_grab_add (GTK_WIDGET (_widget)); + gtk_main (); + if (_widget) + { + gtk_grab_remove (GTK_WIDGET (_widget)); + } + g_print ("%s: exit\n", __func__); +} + +void +file_selector::destroy () +{ + g_print ("%s\n", __func__); + + if (_using_adf) + { + hide (); + } + else + { + destroy (true); + } +} + +void +file_selector::destroy (bool really) +{ + g_print ("%s (%i)\n", __func__, really); + + free (_filename); + _filename = NULL; + + if (_widget) + { + gtk_widget_destroy (GTK_WIDGET (_widget)); + } + _widget = NULL; + + if (_parent) + { + gtk_widget_set_sensitive (_parent, true); + } + _parent = NULL; + + _number = -1; + _seqnum = NULL; // gtk_widget_destroy cleans these up + _digits = NULL; + + _using_adf = false; + _do_duplex = false; + _deleted_all = false; + + delete [] _fmt; + _fmt = NULL; + delete [] _ext_regex; + _ext_regex = NULL; +} + +char * +file_selector::get_pathname () const +{ + return (_pathname ? strdup (_pathname) : NULL); +} + +int +file_selector::get_sequence_number () const +{ + return _number; +} + +bool +file_selector::valid_pathname (const string& path) const +{ + string::size_type slash = path.rfind (file_opener::dir_sep); + string name = path.substr (slash + 1); + string::size_type dot = name.rfind (file_opener::ext_sep); + + string::size_type cmp_pos = dot; + + if (_using_adf && !_multi_page_mode) + { + string::size_type dash = name.find_last_of ("-", dot - 1); + cmp_pos = dash; + } + + if (128 < path.size () // blame the spec for this one + || !permission (path.c_str ()) + || 0 >= cmp_pos) // 0 length filename + { + return false; + } + + return true; +} + +bool +file_selector::save_pathname() +{ + g_print ("%s\n", __func__); + + if (!_widget) + { + g_print ("%s: widget's gone!\n", __func__); + free (_pathname); + _pathname = NULL; + return true; + } + + const char *current = gtk_file_selection_get_filename (_widget); + + if (!valid_pathname (current)) + { + show_message (pisa_error (PISA_ERR_FILENAME)); + return false; + } + if (possible_overwrite (current)) + { + bool ok = show_message (pisa_error (PISA_ERR_OVERWRITE), + _("Overwrite"), _("Cancel")); + if (!ok) + return false; + } + + char *new_name = (char *) malloc ((strlen (current) + 1) + * sizeof (char)); + if (!new_name) + throw pisa_error (PISA_STATUS_NO_MEM); + + strcpy (new_name, current); + free (_pathname); + _pathname = new_name; + free (_filename); + _filename = 0; + + g_print ("%s: pathname = `%s'\n", __func__, _pathname); + + return true; +} + +void +file_selector::hide () const +{ + g_print ("%s\n", __func__); + + if (_using_adf) + { + g_print ("%s: calling gtk_widget_hide\n", __func__); + if (_widget) + { + gtk_widget_hide (GTK_WIDGET (_widget)); + } + if (_parent) + { + gtk_widget_set_sensitive (_parent, true); + } + } +} + +void +file_selector::set_entry (const char *text) +{ + g_print ("%s (%s)\n", __func__, text); + + if (!_filename) + return; // logical error but return for now + + if (!text + || !*text // empty string + || text == _filename + || 0 == strcmp (text, _filename)) + return; // nothing to change + + char *new_name = (canonize (text)); + if (new_name) + { + free (_filename); + _filename = new_name; +#ifndef HAVE_GTK_2 + gtk_signal_handler_block_by_func (GTK_OBJECT (_widget->selection_entry), + GTK_SIGNAL_FUNC (_delete_text), this); + gtk_signal_handler_block_by_func (GTK_OBJECT (_widget->selection_entry), + GTK_SIGNAL_FUNC (_insert_text), this); + gtk_entry_set_text (GTK_ENTRY (_widget->selection_entry), _filename); + gtk_signal_handler_unblock_by_func (GTK_OBJECT (_widget->selection_entry), + GTK_SIGNAL_FUNC (_insert_text), this); + gtk_signal_handler_unblock_by_func (GTK_OBJECT (_widget->selection_entry), + GTK_SIGNAL_FUNC (_delete_text), this); +#else + g_signal_handlers_block_by_func (GTK_EDITABLE (_widget->selection_entry), + (gpointer) _delete_text, this); + g_signal_handlers_block_by_func (GTK_EDITABLE (_widget->selection_entry), + (gpointer) _insert_text, this); + gtk_entry_set_text (GTK_ENTRY (_widget->selection_entry), _filename); + g_signal_handlers_unblock_by_func (GTK_EDITABLE (_widget->selection_entry), + (gpointer) _insert_text, this); + g_signal_handlers_unblock_by_func (GTK_EDITABLE (_widget->selection_entry), + (gpointer) _delete_text, this); +#endif + } +} + +void +file_selector::delete_text (gint start, gint end) +{ + g_print ("%s (%d,%d)\n", __func__, start, end); + g_print ("%s orig _filename '%s'\n", __func__, _filename); + + _deleted_all = ((end - start) == (gint) strlen (_filename)); + + if (0 > end) end = strlen (_filename) + 1; + int dot = strrchr (_filename, ((_using_adf && !_multi_page_mode) + ? '-' : '.')) - _filename; + if (end > dot) end = dot; + + if (start < end) + { + GtkWidget *ed = _widget->selection_entry; + gtk_editable_delete_text (GTK_EDITABLE (ed), start, end); + _filename = strdup (gtk_entry_get_text (GTK_ENTRY (ed))); + + g_print ("%s new _filename '%s'\n", __func__, _filename); + } +} + +void +file_selector::insert_text (gchar *text, gint len, gint *pos) +{ + g_print ("%s (%s,%d,%d)\n", __func__, text, len, (pos ? *pos : -1)); + g_print ("%s orig _filename '%s'\n", __func__, _filename); + + if (_deleted_all && pos && (0 == *pos)) + { + _deleted_all = false; + text = canonize (text); + if (!text) return; + len = strlen (text); + + GtkEditable *ed = GTK_EDITABLE (_widget->selection_entry); +#ifndef HAVE_GTK_2 + gtk_signal_handler_block_by_func (GTK_OBJECT (ed), + GTK_SIGNAL_FUNC (_delete_text), this); + gtk_editable_delete_text (ed, 0, -1); + gtk_signal_handler_unblock_by_func (GTK_OBJECT (ed), + GTK_SIGNAL_FUNC (_delete_text), this); +#else + g_signal_handlers_block_by_func (ed, (gpointer) _delete_text, this); + gtk_editable_delete_text (ed, 0, -1); + g_signal_handlers_unblock_by_func (ed, (gpointer) _delete_text, this); +#endif + } + + int dot = strrchr (_filename, ((_using_adf && !_multi_page_mode) + ? '-' : '.')) - _filename; + + if (pos && (*pos <= dot)) + { + GtkWidget *ed = _widget->selection_entry; + gtk_editable_insert_text (GTK_EDITABLE (ed), text, len, pos); +#ifndef HAVE_GTK_2 + { // not getting delete-event's for some + // reason + char *name = canonize (gtk_entry_get_text (GTK_ENTRY (ed))); + gtk_entry_set_text (GTK_ENTRY (ed), name); + free (name); + } +#endif + free (_filename); + _filename = strdup (gtk_entry_get_text (GTK_ENTRY (ed))); + + g_print ("%s new _filename '%s'\n", __func__, _filename); + } +} + +void +file_selector::change_format (int index) +{ + g_print ("%s (%d)\n", __func__, index); + + _format_index = index; + // reflect changes in filename + char *canon = canonize (_filename); + set_entry (canon); + free (canon); +} + +void +file_selector::change_seqnum () +{ + g_print ("%s\n", __func__); + + _number = int (_seqnum->value); +} + +void +file_selector::change_digits () +{ + g_print ("%s\n", __func__); + + int max = 9; // there's at least one digit + for (int i = 2; i <= int (_digits->value); ++i) + { + max *= 10; + max += 9; + } + + if (_seqnum) + { + _seqnum->upper = max; + if (max < _seqnum->value) // also updates the GUI + gtk_adjustment_set_value (_seqnum, max); + + gtk_adjustment_changed (_seqnum); + } + + if (_filename) // reflect changes in filename + { + char *canon = canonize (_filename); + set_entry (canon); + free (canon); + } +} + +#include <sstream> +#include <iostream> +void +file_selector::change_page_mode (bool mode) +{ + _multi_page_mode = mode; + + string name = _filename; + stringstream ss; + + string::size_type dot = name.rfind (file_opener::ext_sep); + string::size_type dash = name.find_last_of ("-", dot - 1); + + if (mode) + { + ss << name.substr (0, dash) + << name.substr (dot); + } + else + { + ss << name.substr (0, dot) << "-"; + for (int i = 0; i < int (_digits->value); ++i) + ss << file_opener::hash_mark; + ss << name.substr (dot); + } + + set_entry (ss.str ().c_str ()); +} + +void +file_selector::change_binding_edge (bool is_left_edge) +{ + g_print ("%s(%d)\n", __func__, is_left_edge); + + _has_left_edge_binding = is_left_edge; +} + +// caller needs to free returned char * +char * +file_selector::canonize (const char *text) const +{ + if (!text || 0 == *text) + return 0; + + g_print ("%s (%s)\n", __func__, text); + + string name = text; + string ext = _fmt[_format_index]->ext; + stringstream ss; + + string::size_type dot = name.rfind (file_opener::ext_sep); + + if (_using_adf && !_multi_page_mode) + { + string::size_type dash = name.find_last_of ("-", dot - 1); + ss << name.substr (0, dash) + << "-"; + + for (int i = 0; i < int (_digits->value); ++i) + ss << file_opener::hash_mark; + } + else + { + ss << name.substr (0, dot); + } + + ss << file_opener::ext_sep + << ext; + + return strdup (ss.str ().c_str ()); +} + +void +file_selector::add_dialog_extensions (gint index) +{ + g_print ("%s: enter\n", __func__); + + GtkWidget *frame = gtk_frame_new (_("Save Options")); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + + GtkWidget *align = gtk_alignment_new (1.0, 0.5, 0.0, 0.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (align), 5, 5, 5, 5); + gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (align)); + gtk_widget_show (align); + + unsigned short rows = 1; + if (_using_adf) rows = 2; + if (_do_duplex) rows = 3; + GtkTable *opts + = (GtkTable *) gtk_table_new (rows + (_using_adf ? 2 : 0), 3, FALSE); + gtk_table_set_col_spacings (opts, 5); + gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET (opts)); + gtk_widget_show (GTK_WIDGET (opts)); + + GtkWidget *mi = NULL; + GtkWidget *multi = + gtk_check_button_new_with_label ( _("Create file with all pages")); + + GtkWidget *bind_edge = gtk_label_new + (_("Binding Position (for Double-Sided Scanning)")); + GtkWidget *left_edge = gtk_radio_button_new_with_label (NULL, _("Left")); + GtkWidget *top_edge = gtk_radio_button_new_with_label + (gtk_radio_button_get_group (GTK_RADIO_BUTTON (left_edge)), _("Top")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (left_edge), true); + + GtkWidget *w = 0; + { // file type selector + w = gtk_label_new (_("Determine File Type:")); + gtk_table_attach_defaults (opts, w, 0, 1, 0, 1); + gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); + gtk_widget_show (w); + + w = gtk_option_menu_new (); + { + GtkWidget *m = gtk_menu_new (); + + for (int i = 0; _fmt[i]; ++i) + { + mi = gtk_menu_item_new_with_label (_fmt[i]->name); + gtk_menu_append (GTK_MENU (m), mi); + gtk_widget_show (mi); + + struct menu_info *id = new struct menu_info; + id->fs = this; + id->widget[0] = multi; + id->widget[1] = bind_edge; + id->widget[2] = left_edge; + id->widget[3] = top_edge; + id->index = i; + + g_signal_connect (GTK1_OBJ (mi), "activate", + G_CALLBACK (_change_format), (void *) id); + + gtk_widget_set_sensitive (mi, is_supported (_fmt[i]->name)); + } + gtk_option_menu_set_menu( GTK_OPTION_MENU( w ), m ); + } + gtk_table_attach_defaults (opts, w, 1, 3, 0, 1); + gtk_option_menu_set_history (GTK_OPTION_MENU (w), index); + change_format (index); + gtk_widget_show (w); + } + + if (_using_adf) + { + g_print ("%s: using adf\n", __func__); + + gtk_signal_connect (GTK_OBJECT (multi), "toggled", + GTK_SIGNAL_FUNC (_toggled_multi_page_mode_fs), this); + gtk_table_attach_defaults (opts, multi, 1, 3, 1, 2); + gtk_widget_show (multi); + + if (_do_duplex) + { + gtk_signal_connect (GTK_OBJECT (left_edge), "toggled", + GTK_SIGNAL_FUNC (_changed_binding_edge), this); + gtk_table_attach_defaults (opts, bind_edge, 0, 1, 2, 3); + gtk_misc_set_alignment (GTK_MISC (bind_edge), 1.0, 0.5); + gtk_table_attach_defaults (opts, left_edge, 1, 2, 2, 3); + gtk_table_attach_defaults (opts, top_edge, 2, 3, 2, 3); + gtk_widget_show (bind_edge); + gtk_widget_show (left_edge); + gtk_widget_show (top_edge); + } + + // sequence number selector + w = gtk_label_new (_("Start filing at:")); + gtk_table_attach_defaults (opts, w, 0, 1, rows + 0, rows + 1); + gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); + gtk_signal_connect (GTK_OBJECT (multi), "toggled", + GTK_SIGNAL_FUNC (_toggled_multi_page_mode_widget), w); + gtk_widget_show (w); + + if (!_seqnum) + _seqnum = (GtkAdjustment *) gtk_adjustment_new (1, 0, 9, 1, 10, 0); + if (_seqnum) + _number = int (_seqnum->value); + else // play it safe and make sure + _number = -1; + + w = gtk_spin_button_new (_seqnum, 0, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (w), true); + gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (w), true); + gtk_table_attach_defaults (opts, w, 1, 3, rows + 0, rows + 1); + gtk_signal_connect (GTK_OBJECT (multi), "toggled", + GTK_SIGNAL_FUNC (_toggled_multi_page_mode_widget), w); + gtk_widget_show (w); + + // number of digits selector + w = gtk_label_new (_("Number of digits:")); + gtk_table_attach_defaults (opts, w, 0, 1, rows + 1, rows + 2); + gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); + gtk_signal_connect (GTK_OBJECT (multi), "toggled", + GTK_SIGNAL_FUNC (_toggled_multi_page_mode_widget), w); + gtk_widget_show (w); + + if (!_digits) + _digits = (GtkAdjustment *) gtk_adjustment_new (3, 1, 6, 1, 1, 0); + if (_digits) + change_digits(); + + w = gtk_spin_button_new (_digits, 0, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (w), true); + gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (w), true); + gtk_table_attach_defaults (opts, w, 1, 3, rows + 1, rows + 2); + gtk_signal_connect (GTK_OBJECT (multi), "toggled", + GTK_SIGNAL_FUNC (_toggled_multi_page_mode_widget), w); + gtk_widget_show (w); + + g_signal_connect (GTK1_OBJ (_seqnum), "value_changed", + G_CALLBACK (_change_seqnum), this); + g_signal_connect (GTK1_OBJ (_digits), "value_changed", + G_CALLBACK (_change_digits), this); + } + + gtk_box_pack_end (GTK_BOX (_widget->main_vbox), frame, false, false, 0); + + struct menu_info id; + id.fs = this; + id.widget[0] = multi; + id.widget[1] = bind_edge; + id.widget[2] = left_edge; + id.widget[3] = top_edge; + id.index = index; + _change_format (mi, &id); + gtk_widget_show (frame); + gtk_widget_show (GTK_WIDGET (opts)); + + g_print ("%s: exit\n", __func__); +} + +bool +file_selector::permission( const char *file_in ) const +{ + if (!file_in || 0 == strlen (file_in)) + return false; + + char* file = strdup (file_in); + + if (!file) + return false; + + if (0 == access( file, F_OK )) // file exists + { + bool rv = (0 == access( file, W_OK )); // whether we can write to it + free (file); + return rv; + } + + // check write access to the directory (note that we need execute + // privileges as well) + + char *slash = strrchr( file, '/'); + *slash = '\0'; // temporarily truncate to dirname + const char *dir = (file == slash + ? "/" // whoops!, file in root directory + : file); + + bool w_ok = false; // assume the worst + if (0 == access( dir, F_OK )) + w_ok = (0 == access( dir, W_OK | X_OK )); + + *slash = '/'; // restore filename + + free (file); + return w_ok; +} + +bool +file_selector::show_message( const pisa_error& oops, + const char *yes, + const char *no ) const +{ + aleart_dialog dlg; + + if (yes && no) // binary question + return (1 == dlg.message_box( GTK_WIDGET( _widget ), + oops.get_error_string(), + yes, no )); + + dlg.message_box( GTK_WIDGET( _widget ), oops.get_error_string() ); + return true; +} + +void +file_selector::regerror (int code, regex_t *regex) const +{ + size_t length = ::regerror (code, regex, 0, 0); + char *message = new char[length]; + + ::regerror (code, regex, message, length); + fprintf (stderr, "%s\n", message); + + delete[] message; +} + +std::string +file_selector::get_format_name () const +{ + return _format[_format_index].name; +} + +iscan::file_format +file_selector::get_type () const +{ + return _format[_format_index].type; +} + +bool +file_selector::multi_page_mode () +{ + if (iscan::PDF == get_type () || iscan::TIF == get_type ()) + return _multi_page_mode; + + return false; +} + +bool +file_selector::has_left_edge_binding () +{ + return _has_left_edge_binding; +} + +gint +file_selector::get_index (const char* name) const +{ + if (!name) return 0; + + int i = 0; + while (_format[i].name && (0 != strcmp (name, _format[i].name))) + { + ++i; + } + return (_format[i].name ? i : 0); +} + +bool +file_selector::is_supported (const char* format) const +{ + // FIXME! ugly kludge to "check" for support + if (!format) + { + return false; + } + if (0 == strcmp ("PCX", format)) + { + return iscan::pcxstream::is_usable (); + } + if (0 == strcmp ("PNM", format)) + { + return iscan::pnmstream::is_usable (); + } + if (0 == strcmp ("PNG", format)) + { + return iscan::pngstream::is_usable (); + } + if (0 == strcmp ("JPEG", format)) + { + return iscan::jpegstream::is_usable (); + } + if (0 == strcmp ("PDF", format)) + { + return iscan::pdfstream::is_usable (); + } + if (0 == strcmp ("TIFF", format)) + { + return iscan::tiffstream::is_usable (); + } + + return false; +} + +#include <sstream> +#include <iostream> +bool +file_selector::possible_overwrite (const std::string& current) const +{ + if (!_using_adf || _multi_page_mode) + return (0 == access (current.c_str (), F_OK)); + + string::size_type dot = current.rfind (file_opener::ext_sep); + string::size_type hash = current.find_last_not_of (file_opener::hash_mark, + dot - 1); + int digits = dot - ++hash; + + stringstream ss; + ss << current.substr (0, hash) + << "([1-9][0-9]{" << digits << ",}|[0-9]{" << digits << "})\\" + << current.substr (dot); + + return check_overwrite (ss.str ().c_str ()); +} + +int +file_selector::check_overwrite (const char *regexp_in) const +{ + bool found = false; + + if (!regexp_in) + return false; + + char* regexp = strdup (regexp_in); + if (!regexp) + return true; + + char *slash = strrchr( regexp, '/' ); + + if (!slash) + { + free (regexp); + return true; + } + + *slash = '\0'; // regexp now holds the directory name + char dirname[ strlen( regexp )]; + strcpy( dirname, regexp ); + + *slash = '^'; // re-anchor the regexp + + regex_t *comp_regex = new regex_t; + int comp = regcomp( comp_regex, slash, REG_EXTENDED ); + + if (0 == comp) + { + size_t nsub = comp_regex->re_nsub + 1; + regmatch_t match[nsub]; + + DIR *dir = opendir( dirname ); + if (!dir) + { + regfree( comp_regex ); + delete comp_regex; + free (regexp); + return 0; // file creation failure handles this + } + + struct dirent *file = 0; + while (!found && (file = readdir( dir ))) + { + int result = regexec( comp_regex, file->d_name, nsub, match, 0 ); + if (0 == result) + { + size_t digits = match[1].rm_eo - match[1].rm_so; + char num[digits + 1]; + char *c = num; + { + char *p = file->d_name + match[1].rm_so; + while (0 < digits--) + *c++ = *p++; + } + int seq_num = atoi( num ); + + found = (seq_num >= get_sequence_number()); + } + else + if (REG_NOMATCH != result) + regerror( comp, comp_regex ); + } + closedir( dir ); + } + else + regerror( comp, comp_regex ); + + regfree( comp_regex ); + delete comp_regex; + free (regexp); + + return found; +} |