aboutsummaryrefslogtreecommitdiff
path: root/frontend/pisa_scan_selector.cc
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/pisa_scan_selector.cc')
-rw-r--r--frontend/pisa_scan_selector.cc503
1 files changed, 503 insertions, 0 deletions
diff --git a/frontend/pisa_scan_selector.cc b/frontend/pisa_scan_selector.cc
new file mode 100644
index 0000000..8eea853
--- /dev/null
+++ b/frontend/pisa_scan_selector.cc
@@ -0,0 +1,503 @@
+/*
+ SANE EPSON backend
+ Copyright (C) 2003, 2008, 2009 SEIKO EPSON CORPORATION
+
+ This file is part of the `iscan' program.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ As a special exception, the copyright holders give permission
+ to link the code of this program with the esmod library and
+ distribute linked combinations including the two. You must obey
+ the GNU General Public License in all respects for all of the
+ code used other then esmod.
+*/
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msg_id) gettext (msg_id)
+
+/*------------------------------------------------------------*/
+#include <gtk/gtk.h>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <regex.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <sane/sane.h>
+#include <sane/saneopts.h>
+#ifdef __cplusplus
+}
+#endif
+#include "pisa_error.h"
+#include "pisa_main.h"
+#include "pisa_scan_selector.h"
+#include "pisa_view_manager.h"
+#include "pisa_gimp.h"
+/*------------------------------------------------------------*/
+
+static void
+_delete( GtkWidget *widget, scan_selector *ss )
+{
+ ss->cancel(); // same process as cancel
+}
+
+static void
+_select( GtkEditable *, scan_selector *ss )
+{
+ ss->select(); // just forward the call
+}
+
+static void
+_update( GtkButton *, scan_selector *ss )
+{
+ ss->update(); // just forward the call
+}
+
+static void
+_cancel( GtkButton *, scan_selector *ss )
+{
+ ss->cancel(); // just forward the call
+}
+
+
+scan_selector::~scan_selector()
+{
+ free( _current_gui );
+ free( _current_dev );
+ g_slist_free( _gui_names );
+ g_slist_free( _dev_names );
+ g_slist_free( _items );
+ ::gtk_widget_destroy( m_dbox );
+}
+
+scan_selector::scan_selector( bool is_dialog )
+ : m_local_only( SANE_FALSE ),
+ m_dbox( 0 ), m_hbox( 0 ), m_menu( 0 ), _items( 0 ),
+ _dev_names( 0 ), _gui_names( 0 ),
+ _current_dev( 0 ), _current_gui( 0 )
+{
+ GtkWidget *vbox = 0; // dialog container
+
+ if (is_dialog)
+ {
+ { // dialog box
+ GtkWidget *w = ::gtk_dialog_new ();
+ ::gtk_object_set_data( GTK_OBJECT( w ), "m_dbox", w );
+ ::gtk_window_set_title( GTK_WINDOW( w ),
+ _("Scan Selector Dialog") );
+
+#ifdef HAVE_GTK_2
+ GTK_WINDOW( w )->type = GTK_WINDOW_TOPLEVEL;
+#else
+ GTK_WINDOW( w )->type = GTK_WINDOW_DIALOG;
+#endif
+ ::gtk_window_set_modal( GTK_WINDOW( w ), TRUE );
+ ::gtk_window_set_policy( GTK_WINDOW( w ), FALSE, TRUE, FALSE );
+
+ ::gtk_signal_connect( GTK_OBJECT( w ), "delete_event",
+ GTK_SIGNAL_FUNC( ::_delete ), this );
+
+ m_dbox = w;
+ }
+
+ {
+ GtkWidget *w = GTK_DIALOG( m_dbox )->vbox;
+ ::gtk_object_set_data( GTK_OBJECT( m_dbox ), "vbox", w );
+ ::gtk_container_set_border_width( GTK_CONTAINER( w ), 5 );
+ ::gtk_widget_show( w );
+
+ vbox = w;
+ }
+ }
+
+ { // selector container
+ GtkWidget *w = ::gtk_hbox_new( FALSE, 5 );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( w ), "m_hbox", w,
+ (GtkDestroyNotify) ::gtk_widget_unref );
+ if (vbox) // only when in a dialog
+ ::gtk_box_pack_start( GTK_BOX( vbox ), w, TRUE, TRUE, 0 );
+ ::gtk_container_set_border_width( GTK_CONTAINER( w ), 5 );
+
+ m_hbox = w;
+ }
+
+ { // scanner label
+ GtkWidget *w = ::gtk_label_new (_("Scanner:"));
+ ::gtk_box_pack_start( GTK_BOX( m_hbox ), w, FALSE, FALSE, 0 );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT (m_hbox), "label", w,
+ (GtkDestroyNotify) ::gtk_widget_unref );
+ ::gtk_widget_show( w );
+ }
+
+ { // option menu
+ GtkWidget *w = ::gtk_option_menu_new ();
+ ::gtk_box_pack_start( GTK_BOX( m_hbox ), w, FALSE, FALSE, 0 );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( m_hbox ), "option", w,
+ (GtkDestroyNotify) ::gtk_widget_unref );
+ ::gtk_widget_show( w );
+
+ m_opts = w;
+ }
+
+ if (!is_dialog)
+ { // update button
+ GtkWidget *w = ::gtk_button_new_with_label( _(" Update ") );
+ ::gtk_container_set_border_width( GTK_CONTAINER( w ), 5 );
+ ::gtk_box_pack_start( GTK_BOX( m_hbox ), w, FALSE, FALSE, 0 );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( m_hbox ), "update", w,
+ (GtkDestroyNotify) ::gtk_widget_unref );
+ ::gtk_widget_show( w );
+
+ ::gtk_signal_connect( GTK_OBJECT( w ), "clicked",
+ GTK_SIGNAL_FUNC( ::_update ), this );
+ }
+ else
+ {
+ GtkWidget *action_area;
+ { // dialog action button area
+ GtkWidget *w = GTK_DIALOG (m_dbox)->action_area;
+ ::gtk_object_set_data( GTK_OBJECT( m_dbox ), "action_area", w );
+ ::gtk_container_set_border_width( GTK_CONTAINER( w ), 5 );
+ ::gtk_widget_show( w );
+
+ action_area = w;
+ }
+
+ GtkWidget *button_box;
+ {
+ GtkWidget *w = gtk_hbutton_box_new();
+ gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( m_dbox ), "button_box", w,
+ (GtkDestroyNotify) gtk_widget_unref );
+ ::gtk_box_pack_start( GTK_BOX( action_area ), w, TRUE, FALSE, 0 );
+ ::gtk_widget_show( w );
+
+ button_box = w;
+ }
+
+ { // okay button
+ GtkWidget *w = ::gtk_button_new_with_label( _(" OK ") );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( m_dbox ), "okay_button", w,
+ (GtkDestroyNotify) gtk_widget_unref );
+ ::gtk_box_pack_start( GTK_BOX( button_box ), w, TRUE, FALSE, 0 );
+ GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
+ ::gtk_widget_grab_default( w );
+ ::gtk_widget_show( w );
+
+ ::gtk_signal_connect( GTK_OBJECT( w ), "clicked",
+ GTK_SIGNAL_FUNC( ::_select ), this );
+ }
+
+ { // cancel button
+ GtkWidget *w = ::gtk_button_new_with_label( _(" Cancel ") );
+ ::gtk_widget_ref( w );
+ ::gtk_object_set_data_full( GTK_OBJECT( m_dbox ), "cancel_button", w,
+ (GtkDestroyNotify) gtk_widget_unref );
+ ::gtk_box_pack_start( GTK_BOX( button_box ), w, TRUE, FALSE, 0 );
+ ::gtk_widget_show( w );
+
+ ::gtk_signal_connect( GTK_OBJECT( w ), "clicked",
+ GTK_SIGNAL_FUNC( ::_cancel ), this );
+ }
+ }
+}
+
+void
+scan_selector::show() const
+{
+ gtk_widget_show( m_hbox );
+ if (m_dbox)
+ gtk_widget_show( m_dbox );
+}
+
+GtkWidget *
+scan_selector::widget() const
+{
+ return (m_dbox ? m_dbox : m_hbox);
+}
+
+void
+scan_selector::update()
+{
+ const SANE_Device **device;
+ SANE_Status status;
+ status = sane_get_devices ( &device, m_local_only );
+
+ if (SANE_STATUS_GOOD != status)
+ throw pisa_error( status );
+
+ // prepare a new menu
+ GtkWidget *menu = gtk_menu_new();
+ GSList *items = 0;
+ GSList *dev_names = 0;
+ GSList *gui_names = 0;
+
+ int cnt = 0;
+ while (*device)
+ {
+ char *name = rewrite_name( *device );
+ if (name)
+ {
+ GtkWidget *item = gtk_menu_item_new ();
+ GtkWidget *label = gtk_label_new( name );
+ gtk_label_set_line_wrap( GTK_LABEL( label ), TRUE );
+ gtk_container_add( GTK_CONTAINER(item), label );
+ ++cnt;
+
+ if (!m_dbox)
+ gtk_signal_connect( GTK_OBJECT( item ), "activate",
+ GTK_SIGNAL_FUNC( _select ), this );
+
+ items = g_slist_append( items, item );
+ dev_names = g_slist_append( dev_names,
+ (void *) ((*device)->name) );
+ gui_names = g_slist_append( gui_names, (void *) name );
+
+ gtk_menu_append( GTK_MENU( menu ), item );
+ gtk_widget_show( label );
+ gtk_widget_show( item );
+ }
+ ++device;
+ }
+
+ if (!cnt)
+ throw pisa_error( PISA_ERR_CONNECT );
+
+ if (m_menu) // get rid of the old menu
+ gtk_option_menu_remove_menu( GTK_OPTION_MENU( m_opts ) );
+
+ m_menu = menu;
+ gtk_option_menu_set_menu( GTK_OPTION_MENU( m_opts ), m_menu );
+
+ if (_items)
+ {
+ GSList *item;
+ if (!m_dbox)
+ for (item = _items; item; item = item->next )
+ gtk_signal_disconnect_by_func( GTK_OBJECT( item->data ),
+ GTK_SIGNAL_FUNC( _select ), this );
+ g_slist_free( _items );
+ }
+ if (_dev_names)
+ g_slist_free( _dev_names );
+ if (_gui_names)
+ g_slist_free( _gui_names );
+
+ _items = items;
+ _dev_names = dev_names;
+ _gui_names = gui_names;
+
+ if (!_current_dev)
+ {
+ char *name = (char *) _dev_names->data;
+ _current_dev = (char *) malloc( strlen( name ) + 1 );
+ if (!_current_dev)
+ throw pisa_error( PISA_STATUS_NO_MEM );
+ strcpy( _current_dev, name );
+ }
+ if (!_current_gui)
+ {
+ char *name = (char *) _gui_names->data;
+ _current_gui = (char *) malloc( strlen( name ) + 1 );
+ if (!_current_gui)
+ throw pisa_error( PISA_STATUS_NO_MEM );
+ strcpy( _current_gui, name );
+ }
+}
+
+void
+scan_selector::cancel()
+{
+ ::gtk_main_quit();
+ sane_exit ();
+ if( pisa_gimp_plugin() )
+ pisa_gimp_quit();
+ exit( 0 );
+}
+
+void
+scan_selector::select()
+{
+ GtkWidget *item = gtk_menu_get_active( GTK_MENU( m_menu ) );
+
+ gint pos = g_slist_position( _items, g_slist_find( _items, item ) );
+ if (-1 == pos)
+ {
+ fprintf( stderr, "scan_selector: internal inconsistency\n" );
+ abort();
+ }
+
+ char *dev_name = (char *) g_slist_nth_data( _dev_names, (guint) pos );
+ char *gui_name = (char *) g_slist_nth_data( _gui_names, (guint) pos );
+
+ if (0 != strcmp( _current_dev, dev_name ))
+ {
+ char *new_name = (char *) malloc( (strlen( dev_name ) + 1)
+ * sizeof( char ) );
+ if (!new_name)
+ throw pisa_error( PISA_STATUS_NO_MEM );
+
+ strcpy( new_name, dev_name );
+ free( _current_dev );
+ _current_dev = new_name;
+ }
+ if (0 != strcmp( _current_gui, gui_name ))
+ {
+ char *new_name = (char *) malloc( (strlen( gui_name ) + 1)
+ * sizeof( char ) );
+ if (!new_name)
+ throw pisa_error( PISA_STATUS_NO_MEM );
+
+ strcpy( new_name, gui_name );
+ free( _current_gui );
+ _current_gui = new_name;
+ }
+
+ if (m_dbox)
+ {
+ ::gtk_widget_hide( m_dbox );
+ ::gtk_main_quit();
+ }
+ else
+ g_view_manager->set_device( dev_name );
+}
+
+char *
+scan_selector::get_device( bool rewrite )
+{
+ if (m_dbox && !rewrite)
+ {
+ update();
+ if (1 < g_slist_length( _items ))
+ {
+ show();
+ ::gtk_grab_add( m_dbox );
+ ::gtk_main();
+ ::gtk_grab_remove( m_dbox );
+ }
+ }
+ return (rewrite
+ ? _current_gui // GUI device name
+ : _current_dev); // raw device name
+}
+
+char *
+scan_selector::rewrite_name( const SANE_Device *device )
+{
+ if (!device)
+ return 0;
+
+ if (0 != strcmp( device->vendor, "Epson" ))
+ return 0; // sorry 'bout that, everyone ;-}
+
+ char *name = const_cast< char * >( device->name );
+ if (!name)
+ return 0;
+
+ // disable host-base scanners accessed
+ // through the net backend (they crash
+ // way too often)
+ if (0 == strncmp( name, "net:", strlen( "net:" ) )
+ && ( 0 == strcmp( device->model, "GT-7200" )
+ || 0 == strcmp( device->model, "GT-7300" )
+ || 0 == strcmp( device->model, "Perfection1250" )
+ || 0 == strcmp( device->model, "Perfection1260" )
+ ))
+ return 0;
+
+ { // dropping other backend EPSON devices
+ regex_t *comp_regex = new regex_t;
+ const char *regex = "^(net:([^:]+:))?(epson|epson2|plustek|snapscan):(.*)$";
+ int comp = regcomp( comp_regex, regex, REG_EXTENDED );
+
+ if (0 == comp)
+ {
+ int result = regexec( comp_regex, device->name, 0, 0, 0 );
+ if (0 == result)
+ {
+ name = 0; // other backend controlled device
+ }
+ else
+ if (REG_NOMATCH != result)
+ regerror( comp, comp_regex );
+ }
+ else
+ regerror( comp, comp_regex );
+
+ regfree( comp_regex );
+ delete comp_regex;
+
+ if (!name)
+ return 0; // sorry 'bout that everyone ;-}
+ }
+
+ // if we are still here we have an epkowa backend supported EPSON
+ // scanner OR, heaven forbid, an aliased scanner
+
+ {
+ // rewrite things into a string of the following form:
+ // "device->vendor device->model [device->name]"
+
+ size_t length = (strlen (device->vendor) + 1 // " "
+ + strlen (device->model) + 2 // " ["
+ + strlen (device->name) + 1 + 1); // "]" + \0
+
+ name = (char *) malloc( length * sizeof( char ) );
+ if (!name)
+ throw pisa_error( PISA_STATUS_NO_MEM );
+
+ strncpy (name, device->vendor, strlen (device->vendor));
+
+ char *c = name + strlen (device->vendor);
+ *c++ = ' ';
+ {
+ const char *p = device->model;
+ while (*p)
+ *c++ = *p++;
+ }
+ *c++ = ' ';
+ *c++ = '[';
+ { // copy device name
+ const char *p = device->name;
+ while (*p)
+ *c++ = *p++;
+ }
+ *c++ = ']';
+ *c = '\0';
+ }
+
+ return name;
+}
+
+void
+scan_selector::regerror( int code, regex_t *regex )
+{
+ size_t length = ::regerror( code, regex, 0, 0 );
+ char *message = new char[length];
+
+ ::regerror( code, regex, message, length );
+ fprintf( stderr, "%s\n", message );
+
+ delete[] message;
+}