You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/libkscan/kscandevice.cpp

1542 lines
39 KiB

/* 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 <tqwidget.h>
#include <tqobject.h>
#include <tqasciidict.h>
#include <tqcombobox.h>
#include <tqslider.h>
#include <tqcheckbox.h>
#include <tqlineedit.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqtooltip.h>
#include <tqimage.h>
#include <tqfileinfo.h>
#include <tqapplication.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <tdeglobal.h>
#include <tdeconfig.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 TQCString& name, bool state )
{
KScanOption *so = getExistingGuiElement( name );
if( so )
{
TQWidget *w = so->widget();
if( w )
w->setEnabled( state );
}
}
/* ---------------------------------------------------------------------------
------------------------------------------------------------------------- */
KScanOption *KScanDevice::getExistingGuiElement( const TQCString& name )
{
KScanOption *ret = 0L;
TQCString alias = aliasName( name );
/* gui_elements is a TQPtrList<KScanOption> */
for( ret = gui_elements.first(); ret != 0; ret = gui_elements.next())
{
if( ret->getName() == alias ) break;
}
return( ret );
}
/* ---------------------------------------------------------------------------
------------------------------------------------------------------------- */
KScanOption *KScanDevice::getGuiElement( const TQCString& name, TQWidget *parent,
const TQString& desc,
const TQString& tooltip )
{
if( name.isEmpty() ) return(0);
TQWidget *w = 0;
KScanOption *so = 0;
TQCString 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, TQ_SIGNAL( optionChanged( KScanOption* ) ),
this, TQ_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( TQObject *parent )
: TQObject( parent )
{
SANE_Status sane_stat = sane_init(NULL, NULL );
d = new KScanDevicePrivate();
option_dic = new TQAsciiDict<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;
TDEConfig *konf = TDEGlobal::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, TQ_SIGNAL(sigOptionsChanged()), TQ_SLOT(slReloadAll()));
#endif
gammaTables = new KScanOptSet( "GammaTables" );
}
else
{
kdDebug(29000) << "ERROR: sane_init failed -> SANE installed ?" << endl;
}
connect( this, TQ_SIGNAL( sigScanFinished( KScanStat )), TQ_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 TQCString& 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;
}
TQString KScanDevice::getScannerName(const TQCString& name) const
{
TQString ret = i18n("No scanner selected");
SANE_Device *scanner = 0L;
if( !scanner_name.isNull() && scanner_initialised && name.isEmpty())
{
scanner = scannerDevices[ scanner_name ];
}
else if ( ! name.isEmpty() )
{
scanner = scannerDevices[ name ];
ret = TQString();
}
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 );
}
TQSize KScanDevice::getMaxScanSize( void ) const
{
TQSize 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 TQString qq = newOpt->get();
tqDebug( "INIT: <%s> = <%s>", d->name, qq );
allOptionSet->insert( d->name, newOpt );
#endif
}
else if( d->type == SANE_TYPE_GROUP )
{
// tqDebug( "######### Group found: %s ########", d->title );
}
else
kdDebug(29000) << "Unable to detect option " << endl;
}
}
}
}
return stat;
}
TQStrList KScanDevice::getAllOptions()
{
return( option_list );
}
TQStrList KScanDevice::getCommonOptions()
{
TQStrList com_opt;
TQCString s = option_list.first();
while( !s.isEmpty() )
{
KScanOption opt( s );
if( opt.commonOption() )
com_opt.append( s );
s = option_list.next();
}
return( com_opt );
}
TQStrList KScanDevice::getAdvancedOptions()
{
TQStrList com_opt;
TQCString 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 TQCString& 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
tqDebug( "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 TQCString& name )
{
if( name.isEmpty() ) return false;
int *i = 0L;
TQCString altname = aliasName( name );
if( ! altname.isNull() )
i = (*option_dic)[ altname ];
if( !i )
return( false );
return( *i > -1 );
}
void KScanDevice::slSetDirty( const TQCString& 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 ?
*/
TQCString KScanDevice::aliasName( const TQCString& name )
{
int *i = (*option_dic)[ name ];
TQCString 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 TQString KScanDevice::previewFile()
{
TQString dir = (TDEGlobal::dirs())->saveLocation( "data", "ScanImages", true );
if( !dir.endsWith("/") )
dir += "/";
TQString fname = dir + TQString::fromLatin1(".previews/");
TQString sname( getScannerName(shortScannerName()) );
sname.replace( '/', "_");
return fname+sname;
}
TQImage KScanDevice::loadPreviewImage()
{
const TQString prevFile = previewFile();
kdDebug(29000) << "Loading preview from file " << prevFile << endl;
TQImage image;
image.load( prevFile );
return image;
}
bool KScanDevice::savePreviewImage( const TQImage &image )
{
if( image.isNull() )
return false;
const TQString 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 TQString 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 TQString 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)
TQString KScanDevice::optionNotifyString( int i ) const
{
const TQString sOff = " |";
const TQString sOn = " X |";
if( i > 0 )
{
return sOn;
}
return sOff;
}
void KScanDevice::prepareScan( void )
{
TQAsciiDictIterator<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() )
{
// tqDebug( "%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;
TQString s = TQString(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 TQString 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 TQt-Image-IO or really scans the image.
**/
KScanStat KScanDevice::acquire( const TQString& 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 */
TQFileInfo file( filename );
if( file.exists() )
{
TQImage 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 TQImage 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 TQImage( p->pixels_per_line, p->lines, 8 );
if( img )
{
img->setNumColors( 2 );
img->setColor( 0, tqRgb( 0,0,0));
img->setColor( 1, tqRgb( 255,255,255) );
}
}
else if( p->depth == 8 )
{
// 8 bit rgb-Picture
if( p->format == SANE_FRAME_GRAY )
{
/* Grayscale */
img = new TQImage( p->pixels_per_line, p->lines, 8 );
if( img )
{
img->setNumColors( 256 );
for(int i = 0; i<256; i++)
img->setColor(i, tqRgb(i,i,i));
}
}
else
{
/* true color image */
img = new TQImage( 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 */
tqApp->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 TQSocketNotifier( fd, TQSocketNotifier::Read, this );
TQObject::connect( sn, TQ_SIGNAL(activated(int)),
this, TQ_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;
TQAsciiDictIterator<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 TQString 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:
* TQImage img valid
* the data-buffer set to a appropriate size
**/
void KScanDevice::doProcessABlock( void )
{
int val,i;
TQRgb 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 )
{
// tqDebug( "No bytes written -> leaving out !" );
goOn = false; /** No more data -> leave ! **/
}
if( goOn )
{
overall_bytes += bytes_written;
// tqDebug( "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 <20>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, tqRgb( 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;
//tqDebug( "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 = tqRed( col );
green = tqGreen( col );
blue = tqBlue( col );
chan = *rptr++;
switch( sane_scan_param.format )
{
case SANE_FRAME_RED :
newCol = tqRgba( chan, green, blue, 0xFF );
break;
case SANE_FRAME_GREEN :
newCol = tqRgba( red, chan, blue, 0xFF );
break;
case SANE_FRAME_BLUE :
newCol = tqRgba( red , green, chan, 0xFF );
break;
default:
kdDebug(29000) << "Undefined format !" << endl;
newCol = tqRgba( 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 TQSocketnotifier fires for a few times after the scan has been
* cancelled. Does it matter ? To see it, just uncomment the tqDebug 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 TQString& setName, const TQString& 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());
}
TQStrListIterator it( dirtyList );
while( it.current())
{
KScanOption so( it.current() );
optSet->backupOption( so );
++it;
}
}
TQString KScanDevice::getConfig( const TQString& key, const TQString& def ) const
{
TQString confFile = SCANNER_DB_FILE;
KSimpleConfig scanConfig( confFile, true );
scanConfig.setGroup( shortScannerName() );
return scanConfig.readEntry( key, def );
}
void KScanDevice::slStoreConfig( const TQString& key, const TQString& val )
{
TQString confFile = SCANNER_DB_FILE;
TQString 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;
TQAsciiDict<int> *KScanDevice::option_dic = 0L;
KScanOptSet *KScanDevice::gammaTables = 0L;
#include "kscandevice.moc"