summaryrefslogtreecommitdiffstats
path: root/libkscan/kscandevice.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libkscan/kscandevice.cpp')
-rw-r--r--libkscan/kscandevice.cpp1541
1 files changed, 1541 insertions, 0 deletions
diff --git a/libkscan/kscandevice.cpp b/libkscan/kscandevice.cpp
new file mode 100644
index 00000000..ca678eac
--- /dev/null
+++ b/libkscan/kscandevice.cpp
@@ -0,0 +1,1541 @@
+/* This file is part of the KDE Project
+ Copyright (C) 1999 Klaas Freitag <freitag@suse.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <stdlib.h>
+#include <qwidget.h>
+#include <qobject.h>
+#include <qasciidict.h>
+#include <qcombobox.h>
+#include <qslider.h>
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qimage.h>
+#include <qfileinfo.h>
+#include <qapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+
+#include <unistd.h>
+#include "kgammatable.h"
+#include "kscandevice.h"
+#include "kscanslider.h"
+#include "kscanoption.h"
+#include "kscanoptset.h"
+#include "devselector.h"
+#include "imgscaninfo.h"
+
+#include <ksimpleconfig.h>
+
+#define MIN_PREVIEW_DPI 75
+#define UNDEF_SCANNERNAME I18N_NOOP( "undefined" )
+#define MAX_PROGRESS 100
+
+/* ---------------------------------------------------------------------------
+ Private class for KScanDevice
+ ------------------------------------------------------------------------- */
+class KScanDevice::KScanDevicePrivate
+
+{
+public:
+ KScanDevicePrivate()
+ : currScanResolutionX(0),
+ currScanResolutionY(0)
+ {
+
+ }
+
+ int currScanResolutionX, currScanResolutionY;
+
+};
+
+
+/* ---------------------------------------------------------------------------
+
+ ------------------------------------------------------------------------- */
+void KScanDevice::guiSetEnabled( const QCString& name, bool state )
+{
+ KScanOption *so = getExistingGuiElement( name );
+
+ if( so )
+ {
+ QWidget *w = so->widget();
+
+ if( w )
+ w->setEnabled( state );
+ }
+}
+
+
+/* ---------------------------------------------------------------------------
+
+ ------------------------------------------------------------------------- */
+KScanOption *KScanDevice::getExistingGuiElement( const QCString& name )
+{
+ KScanOption *ret = 0L;
+
+ QCString alias = aliasName( name );
+
+ /* gui_elements is a QList<KScanOption> */
+ for( ret = gui_elements.first(); ret != 0; ret = gui_elements.next())
+ {
+ if( ret->getName() == alias ) break;
+ }
+
+ return( ret );
+}
+/* ---------------------------------------------------------------------------
+
+ ------------------------------------------------------------------------- */
+
+KScanOption *KScanDevice::getGuiElement( const QCString& name, QWidget *parent,
+ const QString& desc,
+ const QString& tooltip )
+{
+ if( name.isEmpty() ) return(0);
+ QWidget *w = 0;
+ KScanOption *so = 0;
+
+ QCString alias = aliasName( name );
+
+ /* Check if already exists */
+ so = getExistingGuiElement( name );
+
+ if( so ) return( so );
+
+ /* ...else create a new one */
+ so = new KScanOption( alias );
+
+ if( so->valid() && so->softwareSetable())
+ {
+ /** store new gui-elem in list of all gui-elements */
+ gui_elements.append( so );
+
+ w = so->createWidget( parent, desc, tooltip );
+ if( w )
+ {
+ connect( so, SIGNAL( optionChanged( KScanOption* ) ),
+ this, SLOT( slOptChanged( KScanOption* )));
+ w->setEnabled( so->active() );
+ }
+ else
+ {
+ kdDebug(29000) << "ERROR: No widget created for " << name << endl;
+ }
+ }
+ else
+ {
+ if( !so->valid())
+ kdDebug(29000) << "getGuiElem: no option <" << alias << ">" << endl;
+ else
+ if( !so->softwareSetable())
+ kdDebug(29000) << "getGuiElem: option <" << alias << "> is not software Setable" << endl;
+
+ delete so;
+ so = 0;
+ }
+
+ return( so );
+}
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+
+
+KScanDevice::KScanDevice( QObject *parent )
+ : QObject( parent )
+{
+ SANE_Status sane_stat = sane_init(NULL, NULL );
+
+ d = new KScanDevicePrivate();
+
+ option_dic = new QAsciiDict<int>;
+ option_dic->setAutoDelete( true );
+ gui_elements.setAutoDelete( true );
+
+ scanner_initialised = false; /* stays false until openDevice. */
+ scanStatus = SSTAT_SILENT;
+
+ data = 0; /* temporary image data buffer while scanning */
+ sn = 0; /* socket notifier for async scanning */
+ img = 0; /* temporary image to scan in */
+ storeOptions = 0; /* list of options to store during preview */
+ overall_bytes = 0;
+ rest_bytes = 0;
+ pixel_x = 0;
+ pixel_y = 0;
+ scanner_name = 0L;
+
+ KConfig *konf = KGlobal::config ();
+ konf->setGroup( GROUP_STARTUP );
+ bool netaccess = konf->readBoolEntry( STARTUP_ONLY_LOCAL, false );
+ kdDebug(29000) << "Query for network scanners " << (netaccess ? "Not enabled" : "Enabled") << endl;
+ if( sane_stat == SANE_STATUS_GOOD )
+ {
+ sane_stat = sane_get_devices( &dev_list, netaccess ? SANE_TRUE : SANE_FALSE );
+
+ // NO network devices yet
+
+ // Store all available Scanner to Stringlist
+ for( int devno = 0; sane_stat == SANE_STATUS_GOOD &&
+ dev_list[ devno ]; ++devno )
+ {
+ if( dev_list[devno] )
+ {
+ scanner_avail.append( dev_list[devno]->name );
+ scannerDevices.insert( dev_list[devno]->name, dev_list[devno] );
+ kdDebug(29000) << "Found Scanner: " << dev_list[devno]->name << endl;
+ }
+ }
+#if 0
+ connect( this, SIGNAL(sigOptionsChanged()), SLOT(slReloadAll()));
+#endif
+ gammaTables = new KScanOptSet( "GammaTables" );
+ }
+ else
+ {
+ kdDebug(29000) << "ERROR: sane_init failed -> SANE installed ?" << endl;
+ }
+
+ connect( this, SIGNAL( sigScanFinished( KScanStat )), SLOT( slScanFinished( KScanStat )));
+
+}
+
+
+KScanDevice::~KScanDevice()
+{
+ if( storeOptions ) delete (storeOptions );
+ kdDebug(29000) << "Calling sane_exit to finish sane!" << endl;
+ sane_exit();
+ delete d;
+}
+
+
+KScanStat KScanDevice::openDevice( const QCString& backend )
+{
+ KScanStat stat = KSCAN_OK;
+ SANE_Status sane_stat = SANE_STATUS_GOOD;
+
+ if( backend.isEmpty() ) return KSCAN_ERR_PARAM;
+
+ // search for scanner
+ int indx = scanner_avail.find( backend );
+
+ if( indx < 0 ) {
+ stat = KSCAN_ERR_NO_DEVICE;
+ }
+
+ // if available, build lists of properties
+ if( stat == KSCAN_OK )
+ {
+ sane_stat = sane_open( backend, &scanner_handle );
+
+
+ if( sane_stat == SANE_STATUS_GOOD )
+ {
+ // fill description dic with names and numbers
+ stat = find_options();
+ scanner_name = backend;
+
+ } else {
+ stat = KSCAN_ERR_OPEN_DEV;
+ scanner_name = UNDEF_SCANNERNAME;
+ }
+ }
+
+ if( stat == KSCAN_OK )
+ scanner_initialised = true;
+
+ return( stat );
+}
+
+void KScanDevice::slCloseDevice( )
+{
+ /* First of all, send a signal to close down the scanner dev. */
+ emit( sigCloseDevice( ));
+
+ kdDebug(29000) << "Saving scan settings" << endl;
+ slSaveScanConfigSet( DEFAULT_OPTIONSET, i18n("the default startup setup"));
+
+ /* After return, delete all related stuff */
+ scanner_name = UNDEF_SCANNERNAME;
+ if( scanner_handle )
+ {
+ if( scanStatus != SSTAT_SILENT )
+ {
+ kdDebug(29000) << "Scanner is still active, calling cancel !" << endl;
+ sane_cancel( scanner_handle );
+ }
+ sane_close( scanner_handle );
+ scanner_handle = 0;
+ }
+
+ gui_elements.clear();
+
+ option_dic->clear();
+ scanner_initialised = false;
+
+}
+
+
+QString KScanDevice::getScannerName(const QCString& name) const
+{
+ QString ret = i18n("No scanner selected");
+ SANE_Device *scanner = 0L;
+
+ if( scanner_name && scanner_initialised && name.isEmpty())
+ {
+ scanner = scannerDevices[ scanner_name ];
+ }
+ else if ( ! name.isEmpty() )
+ {
+ scanner = scannerDevices[ name ];
+ ret = QString::null;
+ }
+
+ if( scanner ) {
+ // ret.sprintf( "%s %s %s", scanner->vendor, scanner->model, scanner->type );
+ ret.sprintf( "%s %s", scanner->vendor, scanner->model );
+ }
+
+ kdDebug(29000) << "getScannerName returns <" << ret << ">" << endl;
+ return ( ret );
+}
+
+
+QSize KScanDevice::getMaxScanSize( void ) const
+{
+ QSize s;
+ double min, max, q;
+
+ KScanOption so_w( SANE_NAME_SCAN_BR_X );
+ so_w.getRange( &min, &max, &q );
+
+ s.setWidth( (int) max );
+
+ KScanOption so_h( SANE_NAME_SCAN_BR_Y );
+ so_h.getRange( &min, &max, &q );
+
+ s.setHeight( (int) max );
+
+ return( s );
+}
+
+
+KScanStat KScanDevice::find_options()
+{
+ KScanStat stat = KSCAN_OK;
+ SANE_Int n;
+ SANE_Int opt;
+ int *new_opt;
+
+ SANE_Option_Descriptor *d;
+
+ if( sane_control_option(scanner_handle, 0,SANE_ACTION_GET_VALUE, &n, &opt)
+ != SANE_STATUS_GOOD )
+ stat = KSCAN_ERR_CONTROL;
+
+ // printf("find_options(): Found %d options\n", n );
+
+ // resize the Array which hold the descriptions
+ if( stat == KSCAN_OK )
+ {
+
+ option_dic->clear();
+
+ for(int i = 1; i<n; i++)
+ {
+ d = (SANE_Option_Descriptor*)
+ sane_get_option_descriptor( scanner_handle, i);
+
+ if( d )
+ {
+ // logOptions( d );
+ if(d->name )
+ {
+ // Die Option anhand des Namen in den Dict
+
+ if( strlen( d->name ) > 0 )
+ {
+ new_opt = new int;
+ *new_opt = i;
+ kdDebug(29000) << "Inserting <" << d->name << "> as " << *new_opt << endl;
+ /* create a new option in the set. */
+ option_dic->insert ( (const char*)d->name, new_opt );
+ option_list.append( (const char*) d->name );
+#if 0
+ KScanOption *newOpt = new KScanOption( d->name );
+ const QString qq = newOpt->get();
+ qDebug( "INIT: <%s> = <%s>", d->name, qq );
+ allOptionSet->insert( d->name, newOpt );
+#endif
+ }
+ else if( d->type == SANE_TYPE_GROUP )
+ {
+ // qDebug( "######### Group found: %s ########", d->title );
+ }
+ else
+ kdDebug(29000) << "Unable to detect option " << endl;
+ }
+ }
+ }
+ }
+ return stat;
+}
+
+
+QStrList KScanDevice::getAllOptions()
+{
+ return( option_list );
+}
+
+QStrList KScanDevice::getCommonOptions()
+{
+ QStrList com_opt;
+
+ QCString s = option_list.first();
+
+ while( !s.isEmpty() )
+ {
+ KScanOption opt( s );
+ if( opt.commonOption() )
+ com_opt.append( s );
+ s = option_list.next();
+ }
+ return( com_opt );
+}
+
+QStrList KScanDevice::getAdvancedOptions()
+{
+ QStrList com_opt;
+
+ QCString s = option_list.first();
+
+ while( !s.isEmpty() )
+ {
+ KScanOption opt( s );
+ if( !opt.commonOption() )
+ com_opt.append( s );
+ s = option_list.next();
+ }
+ return( com_opt );
+}
+
+KScanStat KScanDevice::apply( KScanOption *opt, bool isGammaTable )
+{
+ KScanStat stat = KSCAN_OK;
+ if( !opt ) return( KSCAN_ERR_PARAM );
+ int sane_result = 0;
+
+ int *num = (*option_dic)[ opt->getName() ];
+ SANE_Status sane_stat = SANE_STATUS_GOOD;
+ const QCString& oname = opt->getName();
+
+ if ( oname == "preview" || oname == "mode" ) {
+ sane_stat = sane_control_option( scanner_handle, *num,
+ SANE_ACTION_SET_AUTO, 0,
+ &sane_result );
+ /* No return here, please ! Carsten, does it still work than for you ? */
+ }
+
+
+ if( ! opt->initialised() || opt->getBuffer() == 0 )
+ {
+ kdDebug(29000) << "Attempt to set Zero buffer of " << oname << " -> skipping !" << endl;
+
+ if( opt->autoSetable() )
+ {
+ kdDebug(29000) << "Setting option automatic !" << endl;
+ sane_stat = sane_control_option( scanner_handle, *num,
+ SANE_ACTION_SET_AUTO, 0,
+ &sane_result );
+ }
+ else
+ {
+ sane_stat = SANE_STATUS_INVAL;
+ }
+ stat = KSCAN_ERR_PARAM;
+ }
+ else
+ {
+ if( ! opt->active() )
+ {
+ kdDebug(29000) << "Option " << oname << " is not active now!" << endl;
+ stat = KSCAN_OPT_NOT_ACTIVE;
+ }
+ else if( ! opt->softwareSetable() )
+ {
+ kdDebug(29000) << "Option " << oname << " is not software Setable!" << endl;
+ stat = KSCAN_OPT_NOT_ACTIVE;
+ }
+ else
+ {
+
+ sane_stat = sane_control_option( scanner_handle, *num,
+ SANE_ACTION_SET_VALUE,
+ opt->getBuffer(),
+ &sane_result );
+ }
+ }
+
+ if( stat == KSCAN_OK )
+ {
+ if( sane_stat == SANE_STATUS_GOOD )
+ {
+ kdDebug(29000) << "Applied <" << oname << "> successfully" << endl;
+
+ if( sane_result & SANE_INFO_RELOAD_OPTIONS )
+ {
+ kdDebug(29000) << "* Setting status to reload options" << endl;
+ stat = KSCAN_RELOAD;
+#if 0
+ qDebug( "Emitting sigOptionChanged()" );
+ emit( sigOptionsChanged() );
+#endif
+ }
+
+#if 0
+ if( sane_result & SANE_INFO_RELOAD_PARAMS )
+ emit( sigScanParamsChanged() );
+#endif
+ if( sane_result & SANE_INFO_INEXACT )
+ {
+ kdDebug(29000) << "Option <" << oname << "> was set inexact !" << endl;
+ }
+
+ /* if it is a gamma table, the gamma values must be stored */
+ if( isGammaTable )
+ {
+ gammaTables->backupOption( *opt );
+ kdDebug(29000) << "GammaTable stored: " << opt->getName() << endl;
+ }
+ }
+ else
+ {
+ kdDebug(29000) << "Status of sane is bad: " << sane_strstatus( sane_stat )
+ << " for option " << oname << endl;
+
+ }
+ }
+ else
+ {
+ kdDebug(29000) << "Setting of <" << oname << "> failed -> kscanerror." << endl;
+ }
+
+ if( stat == KSCAN_OK )
+ {
+ slSetDirty( oname );
+ }
+
+ return( stat );
+}
+
+bool KScanDevice::optionExists( const QCString& name )
+{
+ if( name.isEmpty() ) return false;
+ int *i = 0L;
+
+ QCString altname = aliasName( name );
+
+ if( ! altname.isNull() )
+ i = (*option_dic)[ altname ];
+
+ if( !i )
+ return( false );
+ return( *i > -1 );
+}
+
+void KScanDevice::slSetDirty( const QCString& name )
+{
+ if( optionExists( name ) )
+ {
+ if( dirtyList.find( name ) == -1 )
+ {
+ kdDebug(29000)<< "Setting dirty <" << name << ">" << endl;
+ /* item not found */
+ dirtyList.append( name );
+ }
+ }
+}
+
+
+/* This function tries to find name aliases which appear from backend to backend.
+ * Example: Custom-Gamma is for epson backends 'gamma-correction' - not a really
+ * cool thing :-|
+ * Maybe this helps us out ?
+ */
+QCString KScanDevice::aliasName( const QCString& name )
+{
+ int *i = (*option_dic)[ name ];
+ QCString ret;
+
+ if( i ) return( name );
+ ret = name;
+
+ if( name == SANE_NAME_CUSTOM_GAMMA )
+ {
+ if((*option_dic)["gamma-correction"])
+ ret = "gamma-correction";
+
+ }
+
+ if( ret != name )
+ kdDebug( 29000) << "Found alias for <" << name << "> which is <" << ret << ">" << endl;
+
+ return( ret );
+}
+
+
+
+/* Nothing to do yet. This Slot may get active to do same user Widget changes */
+void KScanDevice::slOptChanged( KScanOption *opt )
+{
+ kdDebug(29000) << "Slot Option Changed for Option " << opt->getName() << endl;
+ apply( opt );
+}
+
+/* This might result in a endless recursion ! */
+void KScanDevice::slReloadAllBut( KScanOption *not_opt )
+{
+ if( ! not_opt )
+ {
+ kdDebug(29000) << "ReloadAllBut called with invalid argument" << endl;
+ return;
+ }
+
+ /* Make sure its applied */
+ apply( not_opt );
+
+ kdDebug(29000) << "*** Reload of all except <" << not_opt->getName() << "> forced ! ***" << endl;
+
+ for( KScanOption *so = gui_elements.first(); so; so = gui_elements.next())
+ {
+ if( so != not_opt )
+ {
+ kdDebug(29000) << "Reloading <" << so->getName() << ">" << endl;
+ so->slReload();
+ so->slRedrawWidget(so);
+ }
+ }
+ kdDebug(29000) << "*** Reload of all finished ! ***" << endl;
+
+}
+
+
+/* This might result in a endless recursion ! */
+void KScanDevice::slReloadAll( )
+{
+ kdDebug(29000) << "*** Reload of all forced ! ***" << endl;
+
+ for( KScanOption *so = gui_elements.first(); so; so = gui_elements.next())
+ {
+ so->slReload();
+ so->slRedrawWidget(so);
+ }
+}
+
+
+void KScanDevice::slStopScanning( void )
+{
+ kdDebug(29000) << "Attempt to stop scanning" << endl;
+ if( scanStatus == SSTAT_IN_PROGRESS )
+ {
+ emit( sigScanFinished( KSCAN_CANCELLED ));
+ }
+ scanStatus = SSTAT_STOP_NOW;
+}
+
+
+const QString KScanDevice::previewFile()
+{
+ QString dir = (KGlobal::dirs())->saveLocation( "data", "ScanImages", true );
+ if( !dir.endsWith("/") )
+ dir += "/";
+
+ QString fname = dir + QString::fromLatin1(".previews/");
+ QString sname( getScannerName(shortScannerName()) );
+ sname.replace( '/', "_");
+
+ return fname+sname;
+}
+
+QImage KScanDevice::loadPreviewImage()
+{
+ const QString prevFile = previewFile();
+ kdDebug(29000) << "Loading preview from file " << prevFile << endl;
+
+ QImage image;
+ image.load( prevFile );
+
+ return image;
+}
+
+bool KScanDevice::savePreviewImage( const QImage &image )
+{
+ if( image.isNull() )
+ return false;
+
+ const QString prevFile = previewFile();
+ kdDebug(29000) << "Saving preview to file " << prevFile << endl;
+
+ return image.save( prevFile, "BMP" );
+}
+
+
+KScanStat KScanDevice::acquirePreview( bool forceGray, int dpi )
+{
+ if( ! scanner_handle )
+ return KSCAN_ERR_NO_DEVICE;
+
+ double min, max, q;
+
+ (void) forceGray;
+
+ if( storeOptions )
+ storeOptions->clear();
+ else
+ storeOptions = new KScanOptSet( "TempStore" );
+
+
+ /* set Preview = ON if exists */
+ if( optionExists( SANE_NAME_PREVIEW ) )
+ {
+ KScanOption prev( aliasName(SANE_NAME_PREVIEW) );
+
+ prev.set( true );
+ apply( &prev );
+
+ /* after having applied, save set to false to switch preview mode off after
+ scanning */
+ prev.set( false );
+ storeOptions->backupOption( prev );
+ }
+
+ /* Gray-Preview only done by widget ? */
+ if( optionExists( SANE_NAME_GRAY_PREVIEW ))
+ {
+ KScanOption *so = getExistingGuiElement( SANE_NAME_GRAY_PREVIEW );
+ if( so )
+ {
+ if( so->get() == "true" )
+ {
+ /* Gray preview on */
+ so->set( true );
+ kdDebug(29000) << "Setting GrayPreview ON" << endl;
+ }
+ else
+ {
+ so->set(false );
+ kdDebug(29000) << "Setting GrayPreview OFF" << endl;
+ }
+ }
+ apply( so );
+ }
+
+
+ if( optionExists( SANE_NAME_SCAN_MODE ) )
+ {
+ KScanOption mode( SANE_NAME_SCAN_MODE );
+ const QString kk = mode.get();
+ kdDebug(29000) << "Mode is <" << kk << ">" << endl;
+ storeOptions->backupOption( mode );
+ /* apply if it has a widget, or apply always ? */
+ if( mode.widget() ) apply( &mode );
+ }
+
+ /** Scan Resolution should always exist. **/
+ KScanOption res ( SANE_NAME_SCAN_RESOLUTION );
+ const QString p = res.get();
+
+ kdDebug(29000) << "Scan Resolution pre Preview is " << p << endl;
+ storeOptions->backupOption( res );
+
+ int set_dpi = dpi;
+
+ if( dpi == 0 )
+ {
+ /* No resolution argument */
+ if( ! res.getRange( &min, &max, &q ) )
+ {
+ if( ! res.getRangeFromList ( &min, &max, &q ) )
+ {
+ kdDebug(29000) << "Could not retrieve resolution range!" << endl;
+ min = 75.0; // Hope that every scanner can 75
+ }
+ }
+ kdDebug(29000) << "Minimum Range: " << min << ", Maximum Range: " << max << endl;
+
+ if( min > MIN_PREVIEW_DPI )
+ set_dpi = (int) min;
+ else
+ set_dpi = MIN_PREVIEW_DPI;
+ }
+
+ /* Set scan resolution for preview. */
+ if( !optionExists( SANE_NAME_SCAN_Y_RESOLUTION ) )
+ d->currScanResolutionY = 0;
+ else
+ {
+ KScanOption yres ( SANE_NAME_SCAN_Y_RESOLUTION );
+ /* if active ? */
+ storeOptions->backupOption( yres );
+ yres.set( set_dpi );
+ apply( &yres );
+ yres.get( &d->currScanResolutionY );
+
+ /* Resolution bind switch ? */
+ if( optionExists( SANE_NAME_RESOLUTION_BIND ) )
+ {
+ KScanOption bind_so( SANE_NAME_RESOLUTION_BIND );
+ /* Switch binding on if available */
+ storeOptions->backupOption( bind_so );
+ bind_so.set( true );
+ apply( &bind_so );
+ }
+ }
+
+ res.set( set_dpi );
+ apply( &res );
+
+ /* Store the resulting preview for additional image information */
+ res.get( &d->currScanResolutionX );
+
+ if ( d->currScanResolutionY == 0 )
+ d->currScanResolutionY = d->currScanResolutionX;
+
+ /* Start scanning */
+ KScanStat stat = acquire_data( true );
+
+ /* Restauration of the previous values must take place in the scanfinished slot,
+ * because scanning works asynchron now.
+ */
+
+ return( stat );
+}
+
+
+
+/**
+ * prepareScan tries to set as much as parameters as possible.
+ *
+ **/
+#define NOTIFIER(X) optionNotifyString(X)
+
+QString KScanDevice::optionNotifyString( int i ) const
+{
+ const QString sOff = " |";
+ const QString sOn = " X |";
+ if( i > 0 )
+ {
+ return sOn;
+ }
+ return sOff;
+}
+
+
+void KScanDevice::prepareScan( void )
+{
+ QAsciiDictIterator<int> it( *option_dic ); // iterator for dict
+
+ kdDebug(29000) << "########################################################################################################" << endl;
+ kdDebug(29000) << "Scanner: " << scanner_name << endl;
+ kdDebug(29000) << " " << getScannerName() << endl;
+ kdDebug(29000) << "----------------------------------+--------+--------+--------+--------+--------+--------+--------+" << endl;
+ kdDebug(29000) << " Option-Name |SOFT_SEL|HARD_SEL|SOFT_DET|EMULATED|AUTOMATI|INACTIVE|ADVANCED|" << endl;
+ kdDebug(29000) << "----------------------------------+--------+--------+--------+--------+--------+--------+--------+" << endl;
+
+ while ( it.current() )
+ {
+ // qDebug( "%s -> %d", it.currentKey().latin1(), *it.current() );
+ int descriptor = *it.current();
+
+ const SANE_Option_Descriptor *d = sane_get_option_descriptor( scanner_handle, descriptor );
+
+ if( d )
+ {
+ int cap = d->cap;
+
+ QString s = QString(it.currentKey()).leftJustify(32, ' ');
+ kdDebug(29000) << " " << s << " |" <<
+ NOTIFIER( ((cap) & SANE_CAP_SOFT_SELECT)) <<
+ NOTIFIER( ((cap) & SANE_CAP_HARD_SELECT)) <<
+ NOTIFIER( ((cap) & SANE_CAP_SOFT_DETECT)) <<
+ NOTIFIER( ((cap) & SANE_CAP_EMULATED) ) <<
+ NOTIFIER( ((cap) & SANE_CAP_AUTOMATIC) ) <<
+ NOTIFIER( ((cap) & SANE_CAP_INACTIVE) ) <<
+ NOTIFIER( ((cap) & SANE_CAP_ADVANCED ) ) << endl;
+
+ }
+ ++it;
+ }
+ kdDebug(29000) << "----------------------------------+--------+--------+--------+--------+--------+--------+--------+" << endl;
+
+ KScanOption pso( SANE_NAME_PREVIEW );
+ const QString q = pso.get();
+
+ kdDebug(29000) << "Preview-Switch is at the moment: " << q << endl;
+
+
+}
+
+/** Starts scanning
+ * depending on if a filename is given or not, the function tries to open
+ * the file using the Qt-Image-IO or really scans the image.
+ **/
+KScanStat KScanDevice::acquire( const QString& filename )
+{
+ if( ! scanner_handle )
+ return KSCAN_ERR_NO_DEVICE;
+
+ KScanOption *so = 0;
+
+ if( filename.isEmpty() )
+ {
+ /* *real* scanning - apply all Options and go for it */
+ prepareScan();
+
+ for( so = gui_elements.first(); so; so = gui_elements.next() )
+ {
+ if( so->active() )
+ {
+ kdDebug(29000) << "apply <" << so->getName() << ">" << endl;
+ apply( so );
+ }
+ else
+ {
+ kdDebug(29000) << "Option <" << so->getName() << "> is not active !" << endl;
+ }
+ }
+
+ /** Scan Resolution should always exist. **/
+ KScanOption res( SANE_NAME_SCAN_RESOLUTION );
+ res.get( &d->currScanResolutionX );
+ if ( !optionExists( SANE_NAME_SCAN_Y_RESOLUTION ) )
+ d->currScanResolutionY = d->currScanResolutionX;
+ else
+ {
+ KScanOption yres( SANE_NAME_SCAN_Y_RESOLUTION );
+ yres.get( &d->currScanResolutionY );
+ }
+
+ return( acquire_data( false ));
+ }
+ else
+ {
+ /* a filename is in the parameter */
+ QFileInfo file( filename );
+ if( file.exists() )
+ {
+ QImage i;
+ ImgScanInfo info;
+ if( i.load( filename ))
+ {
+ info.setXResolution(i.dotsPerMeterX()); // TODO: *2.54/100
+ info.setYResolution(i.dotsPerMeterY()); // TODO: *2.54/100
+ info.setScannerName(filename);
+ emit( sigNewImage( &i, &info ));
+ }
+ }
+ }
+
+ return KSCAN_OK;
+}
+
+
+/**
+ * Creates a new QImage from the retrieved Image Options
+ **/
+KScanStat KScanDevice::createNewImage( SANE_Parameters *p )
+{
+ if( !p ) return( KSCAN_ERR_PARAM );
+ KScanStat stat = KSCAN_OK;
+
+ if( img ) {
+ delete( img );
+ img = 0;
+ }
+
+ if( p->depth == 1 ) // Lineart !!
+ {
+ img = new QImage( p->pixels_per_line, p->lines, 8 );
+ if( img )
+ {
+ img->setNumColors( 2 );
+ img->setColor( 0, qRgb( 0,0,0));
+ img->setColor( 1, qRgb( 255,255,255) );
+ }
+ }
+ else if( p->depth == 8 )
+ {
+ // 8 bit rgb-Picture
+ if( p->format == SANE_FRAME_GRAY )
+ {
+ /* Grayscale */
+ img = new QImage( p->pixels_per_line, p->lines, 8 );
+ if( img )
+ {
+ img->setNumColors( 256 );
+
+ for(int i = 0; i<256; i++)
+ img->setColor(i, qRgb(i,i,i));
+ }
+ }
+ else
+ {
+ /* true color image */
+ img = new QImage( p->pixels_per_line, p->lines, 32 );
+ if( img )
+ img->setAlphaBuffer( false );
+ }
+ }
+ else
+ {
+ /* ERROR: NO OTHER DEPTHS supported */
+ kdDebug(29000) << "KScan supports only bit dephts 1 and 8 yet!" << endl;
+ }
+
+ if( ! img ) stat = KSCAN_ERR_MEMORY;
+ return( stat );
+}
+
+
+KScanStat KScanDevice::acquire_data( bool isPreview )
+{
+ SANE_Status sane_stat = SANE_STATUS_GOOD;
+ KScanStat stat = KSCAN_OK;
+
+ scanningPreview = isPreview;
+
+ emit sigScanStart();
+
+ sane_stat = sane_start( scanner_handle );
+ if( sane_stat == SANE_STATUS_GOOD )
+ {
+ sane_stat = sane_get_parameters( scanner_handle, &sane_scan_param );
+
+ if( sane_stat == SANE_STATUS_GOOD )
+ {
+ kdDebug(29000) << "--Pre-Loop" << endl;
+ kdDebug(29000) << "format : " << sane_scan_param.format << endl;
+ kdDebug(29000) << "last_frame : " << sane_scan_param.last_frame << endl;
+ kdDebug(29000) << "lines : " << sane_scan_param.lines << endl;
+ kdDebug(29000) << "depth : " << sane_scan_param.depth << endl;
+ kdDebug(29000) << "pixels_per_line : " << sane_scan_param.pixels_per_line << endl;
+ kdDebug(29000) << "bytes_per_line : " << sane_scan_param.bytes_per_line << endl;
+ }
+ else
+ {
+ stat = KSCAN_ERR_OPEN_DEV;
+ kdDebug(29000) << "sane-get-parameters-Error: " << sane_strstatus( sane_stat ) << endl;
+ }
+ }
+ else
+ {
+ stat = KSCAN_ERR_OPEN_DEV;
+ kdDebug(29000) << "sane-start-Error: " << sane_strstatus( sane_stat ) << endl;
+ }
+
+
+ if( sane_scan_param.pixels_per_line == 0 || sane_scan_param.lines < 1 )
+ {
+ kdDebug(29000) << "ERROR: Acquiring empty picture !" << endl;
+ stat = KSCAN_ERR_EMPTY_PIC;
+ }
+
+ /* Create new Image from SANE-Parameters */
+ if( stat == KSCAN_OK )
+ {
+ stat = createNewImage( &sane_scan_param );
+ }
+
+ if( stat == KSCAN_OK )
+ {
+ /* new buffer for scanning one line */
+ if(data) delete [] data;
+ data = new SANE_Byte[ sane_scan_param.bytes_per_line +4 ];
+ if( ! data ) stat = KSCAN_ERR_MEMORY;
+ }
+
+ /* Signal for a progress dialog */
+ emit( sigScanProgress( 0 ) );
+ emit( sigAcquireStart() );
+
+ if( stat == KSCAN_OK )
+ {
+ /* initiates Redraw of the Progress-Window */
+ qApp->processEvents();
+ }
+
+ if( stat == KSCAN_OK )
+ {
+ overall_bytes = 0;
+ scanStatus = SSTAT_IN_PROGRESS;
+ pixel_x = 0;
+ pixel_y = 0;
+ overall_bytes = 0;
+ rest_bytes = 0;
+
+ /* internal status to indicate Scanning in progress
+ * this status might be changed by pressing Stop on a GUI-Dialog displayed during scan */
+ if( sane_set_io_mode( scanner_handle, SANE_TRUE ) == SANE_STATUS_GOOD )
+ {
+
+ int fd = 0;
+ if( sane_get_select_fd( scanner_handle, &fd ) == SANE_STATUS_GOOD )
+ {
+ sn = new QSocketNotifier( fd, QSocketNotifier::Read, this );
+ QObject::connect( sn, SIGNAL(activated(int)),
+ this, SLOT( doProcessABlock() ) );
+
+ }
+ }
+ else
+ {
+ do
+ {
+ doProcessABlock();
+ if( scanStatus != SSTAT_SILENT )
+ {
+ sane_stat = sane_get_parameters( scanner_handle, &sane_scan_param );
+ kdDebug(29000) << "--ProcessABlock-Loop" << endl;
+ kdDebug(29000) << "format : " << sane_scan_param.format << endl;
+ kdDebug(29000) << "last_frame : " << sane_scan_param.last_frame << endl;
+ kdDebug(29000) << "lines : " << sane_scan_param.lines << endl;
+ kdDebug(29000) << "depth : " << sane_scan_param.depth << endl;
+ kdDebug(29000) << "pixels_per_line : " << sane_scan_param.pixels_per_line << endl;
+ kdDebug(29000) << "bytes_per_line : " << sane_scan_param.bytes_per_line << endl;
+ }
+ } while ( scanStatus != SSTAT_SILENT );
+ }
+ }
+
+ if( stat != KSCAN_OK )
+ {
+ /* Scanning was disturbed in any way - end it */
+ kdDebug(29000) << "Scanning was disturbed - clean up" << endl;
+ emit( sigScanFinished( stat ));
+ }
+ return( stat );
+}
+
+void KScanDevice::loadOptionSet( KScanOptSet *optSet )
+{
+ if( !optSet )
+ {
+ return;
+ }
+
+ // kdDebug(29000) << "Loading Option set: " << optSet->optSetName() << endl;
+ QAsciiDictIterator<KScanOption> it(*optSet);
+
+ kdDebug(29000) << "Postinstalling " << optSet->count() << " options" << endl;
+ while( it.current() )
+ {
+ KScanOption *so = it.current();
+ if( ! so->initialised() )
+ kdDebug(29000) << so->getName() << " is not initialised" << endl;
+
+ if( ! so->active() )
+ kdDebug(29000) << so->getName() << " is not active" << endl;
+
+ if( so && so->active() && so->initialised())
+ {
+ const QString qq = so->get();
+
+ kdDebug(29000) << "Post-Scan-apply <" << it.currentKey() << ">: " << qq << endl;
+ apply( so );
+ }
+ ++it;
+ }
+
+}
+
+
+void KScanDevice::slScanFinished( KScanStat status )
+{
+ // clean up
+ if( sn ) {
+ sn->setEnabled( false );
+ delete sn;
+ sn = 0;
+ }
+
+ emit( sigScanProgress( MAX_PROGRESS ));
+
+ kdDebug(29000) << "Slot ScanFinished hit with status " << status << endl;
+
+ if( data )
+ {
+ delete[] data;
+ data = 0;
+ }
+
+ if( status == KSCAN_OK && img )
+ {
+ ImgScanInfo info;
+ info.setXResolution(d->currScanResolutionX);
+ info.setYResolution(d->currScanResolutionY);
+ info.setScannerName(shortScannerName());
+
+ // put the resolution also into the image itself
+ img->setDotsPerMeterX(static_cast<int>(d->currScanResolutionX / 0.0254 + 0.5));
+ img->setDotsPerMeterY(static_cast<int>(d->currScanResolutionY / 0.0254 + 0.5));
+ kdDebug(29000) << "resolutionX:" << d->currScanResolutionX << " resolutionY:" << d->currScanResolutionY << endl;
+
+ if( scanningPreview )
+ {
+ kdDebug(29000) << "Scanning a preview !" << endl;
+ savePreviewImage(*img);
+ emit( sigNewPreview( img, &info ));
+
+ /* The old settings need to be redefined */
+ loadOptionSet( storeOptions );
+ }
+ else
+ {
+ emit( sigNewImage( img, &info ));
+ }
+ }
+
+
+ sane_cancel(scanner_handle);
+
+ /* This follows after sending the signal */
+ if( img )
+ {
+ delete img;
+ img = 0;
+ }
+
+ /* delete the socket notifier */
+ if( sn ) {
+ delete( sn );
+ sn = 0;
+ }
+
+}
+
+
+/* This function calls at least sane_read and converts the read data from the scanner
+ * to the qimage.
+ * The function needs:
+ * QImage img valid
+ * the data-buffer set to a appropriate size
+ **/
+
+void KScanDevice::doProcessABlock( void )
+{
+ int val,i;
+ QRgb col, newCol;
+
+ SANE_Byte *rptr = 0;
+ SANE_Int bytes_written = 0;
+ int chan = 0;
+ SANE_Status sane_stat = SANE_STATUS_GOOD;
+ uchar eight_pix = 0;
+ bool goOn = true;
+
+ // int rest_bytes = 0;
+ while( goOn && data )
+ {
+ sane_stat =
+ sane_read( scanner_handle, data + rest_bytes, sane_scan_param.bytes_per_line, &bytes_written);
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+
+ if( sane_stat != SANE_STATUS_GOOD )
+ {
+ /** any other error **/
+ kdDebug(29000) << "sane_read returned with error <" << sane_strstatus( sane_stat ) << ">: " << bytes_written << " bytes left" << endl;
+ goOn = false;
+ }
+
+ if( goOn && bytes_written < 1 )
+ {
+ // qDebug( "No bytes written -> leaving out !" );
+ goOn = false; /** No more data -> leave ! **/
+ }
+
+ if( goOn )
+ {
+ overall_bytes += bytes_written;
+ // qDebug( "Bytes read: %d, bytes written: %d", bytes_written, rest_bytes );
+
+ rptr = data; // + rest_bytes;
+
+ switch( sane_scan_param.format )
+ {
+ case SANE_FRAME_RGB:
+ if( sane_scan_param.lines < 1 ) break;
+ bytes_written += rest_bytes; // die übergebliebenen Bytes dazu
+ rest_bytes = bytes_written % 3;
+
+ for( val = 0; val < ((bytes_written-rest_bytes) / 3); val++ )
+ {
+ red = *rptr++;
+ green = *rptr++;
+ blue = *rptr++;
+
+ // kdDebug(29000) << "RGB: %d, %d, %d\n", red, green, blue" << endl;
+ if( pixel_x == sane_scan_param.pixels_per_line )
+ { pixel_x = 0; pixel_y++; }
+ if( pixel_y < img->height())
+ img->setPixel( pixel_x, pixel_y, qRgb( red, green,blue ));
+
+ pixel_x++;
+ }
+ /* copy the remaining bytes to the beginning of the block :-/ */
+ for( val = 0; val < rest_bytes; val++ )
+ {
+ *(data+val) = *rptr++;
+ }
+
+ break;
+ case SANE_FRAME_GRAY:
+ for( val = 0; val < bytes_written ; val++ )
+ {
+ if( pixel_y >= sane_scan_param.lines ) break;
+ if( sane_scan_param.depth == 8 )
+ {
+ if( pixel_x == sane_scan_param.pixels_per_line ) { pixel_x = 0; pixel_y++; }
+ img->setPixel( pixel_x, pixel_y, *rptr++ );
+ pixel_x++;
+ }
+ else
+ { // Lineart
+ /* Lineart needs to be converted to byte */
+ eight_pix = *rptr++;
+ for( i = 0; i < 8; i++ )
+ {
+ if( pixel_y < sane_scan_param.lines )
+ {
+ chan = (eight_pix & 0x80)> 0 ? 0:1;
+ eight_pix = eight_pix << 1;
+ //qDebug( "Setting on %d, %d: %d", pixel_x, pixel_y, chan );
+ img->setPixel( pixel_x, pixel_y, chan );
+ pixel_x++;
+ if( pixel_x >= sane_scan_param.pixels_per_line )
+ {
+ pixel_x = 0; pixel_y++;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ kdDebug(29000) << "Scanning Single color Frame: " << bytes_written << " Bytes!" << endl;
+ for( val = 0; val < bytes_written ; val++ )
+ {
+ if( pixel_x >= sane_scan_param.pixels_per_line )
+ {
+ pixel_y++; pixel_x = 0;
+ }
+
+ if( pixel_y < sane_scan_param.lines )
+ {
+
+ col = img->pixel( pixel_x, pixel_y );
+
+ red = qRed( col );
+ green = qGreen( col );
+ blue = qBlue( col );
+ chan = *rptr++;
+
+ switch( sane_scan_param.format )
+ {
+ case SANE_FRAME_RED :
+ newCol = qRgba( chan, green, blue, 0xFF );
+ break;
+ case SANE_FRAME_GREEN :
+ newCol = qRgba( red, chan, blue, 0xFF );
+ break;
+ case SANE_FRAME_BLUE :
+ newCol = qRgba( red , green, chan, 0xFF );
+ break;
+ default:
+ kdDebug(29000) << "Undefined format !" << endl;
+ newCol = qRgba( 0xFF, 0xFF, 0xFF, 0xFF );
+ break;
+ }
+ img->setPixel( pixel_x, pixel_y, newCol );
+ pixel_x++;
+ }
+ }
+ break;
+ default:
+ kdDebug(29000) << "Unexpected ERROR: No Format type" << endl;
+ break;
+ } /* switch */
+
+
+ if( (sane_scan_param.lines > 0) && (sane_scan_param.lines * pixel_y> 0) )
+ {
+ int progress = (int)(((double)MAX_PROGRESS) / sane_scan_param.lines *
+ pixel_y);
+ if( progress < MAX_PROGRESS)
+ emit( sigScanProgress( progress));
+ }
+
+ if( bytes_written == 0 || sane_stat == SANE_STATUS_EOF )
+ {
+ kdDebug(29000) << "Down under sane_stat not OK" << endl;
+ goOn = false;
+ }
+ }
+
+ if( goOn && scanStatus == SSTAT_STOP_NOW )
+ {
+ /* scanStatus is set to SSTAT_STOP_NOW due to hitting slStopScanning */
+ /* Mostly that one is fired by the STOP-Button in the progress dialog. */
+
+ /* This is also hit after the normal finish of the scan. Most probably,
+ * the QSocketnotifier fires for a few times after the scan has been
+ * cancelled. Does it matter ? To see it, just uncomment the qDebug msg.
+ */
+ kdDebug(29000) << "Stopping the scan progress !" << endl;
+ goOn = false;
+ scanStatus = SSTAT_SILENT;
+ emit( sigScanFinished( KSCAN_OK ));
+ }
+
+ } /* while( 1 ) */
+
+
+ /** Comes here if scanning is finished or has an error **/
+ if( sane_stat == SANE_STATUS_EOF)
+ {
+ if ( sane_scan_param.last_frame)
+ {
+ /** Everythings okay, the picture is ready **/
+ kdDebug(29000) << "last frame reached - scan successful" << endl;
+ scanStatus = SSTAT_SILENT;
+ emit( sigScanFinished( KSCAN_OK ));
+ }
+ else
+ {
+ /** EOF und nicht letzter Frame -> Parameter neu belegen und neu starten **/
+ scanStatus = SSTAT_NEXT_FRAME;
+ kdDebug(29000) << "EOF, but another frame to scan" << endl;
+
+ }
+ }
+
+ if( sane_stat == SANE_STATUS_CANCELLED )
+ {
+ scanStatus = SSTAT_STOP_NOW;
+ kdDebug(29000) << "Scan was cancelled" << endl;
+
+ // stat = KSCAN_CANCELLED;
+ // emit( sigScanFinished( stat ));
+
+ /* hmmm - how could this happen ? */
+ }
+} /* end of fkt */
+
+
+void KScanDevice::slSaveScanConfigSet( const QString& setName, const QString& descr )
+{
+ if( setName.isEmpty() || setName.isNull()) return;
+
+ kdDebug(29000) << "Saving Scan Configuration" << setName << endl;
+
+ KScanOptSet optSet( DEFAULT_OPTIONSET );
+ getCurrentOptions( &optSet );
+
+ optSet.saveConfig( scanner_name , setName, descr );
+
+}
+
+
+void KScanDevice::getCurrentOptions( KScanOptSet *optSet )
+{
+ if( ! optSet ) return;
+
+ for( KScanOption *so = gui_elements.first(); so; so = gui_elements.next())
+ {
+ kdDebug(29000) << "Storing <" << so->getName() << ">" << endl;
+ if( so && so->active())
+ {
+ apply(so);
+ optSet->backupOption( *so );
+ }
+
+ /* drop the thing from the dirty-list */
+ dirtyList.removeRef( so->getName());
+ }
+
+ QStrListIterator it( dirtyList );
+ while( it.current())
+ {
+ KScanOption so( it.current() );
+ optSet->backupOption( so );
+ ++it;
+ }
+}
+
+QString KScanDevice::getConfig( const QString& key, const QString& def ) const
+{
+ QString confFile = SCANNER_DB_FILE;
+
+ KSimpleConfig scanConfig( confFile, true );
+ scanConfig.setGroup( shortScannerName() );
+
+ return scanConfig.readEntry( key, def );
+}
+
+void KScanDevice::slStoreConfig( const QString& key, const QString& val )
+{
+ QString confFile = SCANNER_DB_FILE;
+ QString scannerName = shortScannerName();
+
+ if( scannerName.isEmpty() || scannerName == UNDEF_SCANNERNAME )
+ {
+ kdDebug(29000) << "Skipping config write, scanner name is empty!" << endl;
+ }
+ else
+ {
+ kdDebug(29000) << "Storing config " << key << " in Group " << scannerName << endl;
+
+ KSimpleConfig scanConfig( confFile );
+ scanConfig.setGroup( scannerName );
+ scanConfig.writeEntry( key, val );
+ scanConfig.sync();
+ }
+}
+
+
+bool KScanDevice::scanner_initialised = false;
+SANE_Handle KScanDevice::scanner_handle = 0L;
+SANE_Device const **KScanDevice::dev_list = 0L;
+QAsciiDict<int> *KScanDevice::option_dic = 0L;
+KScanOptSet *KScanDevice::gammaTables = 0L;
+
+
+#include "kscandevice.moc"