diff --git a/kooka/Makefile.am b/kooka/Makefile.am index 188c36b..e9479b3 100644 --- a/kooka/Makefile.am +++ b/kooka/Makefile.am @@ -1,45 +1,46 @@ ## Makefile.am for kooka SUBDIRS = pics bin_PROGRAMS = kooka METASOURCES = AUTO kooka_SOURCES = main.cpp kooka.cpp kookaview.cpp kookapref.cpp \ imgsaver.cpp formatdialog.cpp ksaneocr.cpp \ kookaimage.cpp kookaimagemeta.cpp scanpackager.cpp \ imgnamecombo.cpp imageselectline.cpp \ thumbview.cpp thumbviewitem.cpp \ dwmenuaction.cpp kocrbase.cpp \ kocrgocr.cpp kocrkadmos.cpp kadmosocr.cpp ocrword.cpp \ ocrresedit.cpp kookaprint.cpp imgprintdialog.cpp \ - kocrocrad.cpp + kocrocrad.cpp scanparamsdialog.cpp newscanparams.cpp kooka_CXXFLAGS = -DUSE_KIMAGEIO kooka_LDADD = $(LIB_KFILE) -lkdeprint -lktexteditor $(LIBTIFF) \ $(top_builddir)/libkscan/libkscan.la $(KADMOS_LIB) \ $(LIB_KSPELL) kooka_LDFLAGS = $(KDE_RPATH) $(all_libraries) INCLUDES = -I$(top_srcdir)/libkscan $(all_includes) \ $(LIBSANE_INCLUDES) $(KADMOS_INC) noinst_HEADERS = kookaview.h scanpackager.h \ imgsaver.h formatdialog.h ksaneocr.h \ kooka.h kookaiface.h thumbview.h thumbviewitem.h \ kookapref.h resource.h imgnamecombo.h \ imageselectline.h kookaimage.h kookaimagemeta.h \ kocrbase.h kocrgocr.h kocrkadmos.h \ kadmosocr.h ocrword.h ocrresedit.h kookaprint.h \ - imgprintdialog.h kocrocrad.h + imgprintdialog.h kocrocrad.h scanparamsdialog.h \ + newscanparams.h appdatadir = $(kde_datadir)/kooka appdata_DATA = kookaui.rc kde_conf_DATA = kookarc xdg_apps_DATA = kooka.desktop messages: rc.cpp $(XGETTEXT) *.cpp -o $(podir)/kooka.pot diff --git a/kooka/formatdialog.cpp b/kooka/formatdialog.cpp index 59adc0f..0a293aa 100644 --- a/kooka/formatdialog.cpp +++ b/kooka/formatdialog.cpp @@ -1,335 +1,336 @@ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include "imgsaver.h" #include "formatdialog.h" #include "formatdialog.moc" struct formatInfo { const char *format; QString helpString; int recForTypes; }; static struct formatInfo formats[] = { { "BMP", I18N_NOOP( "Bitmap Picture is a widely used format for images under MS Windows. \ It is suitable for color, grayscale and line art images. \

This format is widely supported but is not recommended, use an open format \ instead."), ImgSaver::ImgNone }, { "PBM", I18N_NOOP( "Portable Bitmap, as used by Netpbm, is an uncompressed format for line art \ (bitmap) images. Only 1 bit per pixel depth is supported."), ImgSaver::ImgBW }, { "PGM", I18N_NOOP( "Portable Greymap, as used by Netpbm, is an uncompressed format for grayscale \ images. Only 8 bit per pixel depth is supported."), ImgSaver::ImgGray }, { "PPM", I18N_NOOP( "Portable Pixmap, as used by Netpbm, is an uncompressed format for full colour \ images. Only 24 bit per pixel RGB is supported."), ImgSaver::ImgColor|ImgSaver::ImgHicolor }, { "PCX", I18N_NOOP( "This is a lossless compressed format which is often supported by PC imaging \ applications, although it is rather old and unsophisticated. It is suitable for \ color and grayscale images. \

This format is not recommended, use an open format instead."), ImgSaver::ImgNone }, { "XBM", I18N_NOOP( "X Bitmap is often used by the X Window System to store cursor and icon bitmaps. \

Unless required for this purpose, use a general purpose format instead."), ImgSaver::ImgNone }, { "XPM", I18N_NOOP( "X Pixmap is often used by the X Window System for color icons and other images. \

Unless required for this purpose, use a general purpose format instead."), ImgSaver::ImgNone }, { "PNG", I18N_NOOP( "Portable Network Graphics is a lossless compressed format designed to be \ portable and extensible. It is suitable for any type of color or grayscale images, \ indexed or true color. \

PNG is an open format which is widely supported."), ImgSaver::ImgBW|ImgSaver::ImgColor|ImgSaver::ImgGray|ImgSaver::ImgHicolor }, { "JPEG", I18N_NOOP( "JPEG is a compressed format suitable for true color or grayscale images. \ It is a lossy format, so it is not recommended for archiving or for repeated loading \ and saving. \

This is an open format which is widely supported."), ImgSaver::ImgHicolor|ImgSaver::ImgGray }, { "JP2", I18N_NOOP( "JPEG 2000 was intended as an update to the JPEG format, with the option of \ lossless compression, but so far is not widely supported. It is suitable for true \ color or grayscale images."), ImgSaver::ImgNone }, { "EPS", I18N_NOOP( "Encapsulated PostScript is derived from the PostScript™ \ page description language. Use this format for importing into other \ applications, or to use with (e.g.) TeX."), ImgSaver::ImgNone }, { "TGA", I18N_NOOP( "Truevision Targa can store full colour images with an alpha channel, and is \ used extensively by animation and video applications. \

This format is not recommended, use an open format instead."), ImgSaver::ImgNone }, { "GIF", I18N_NOOP( // writing may not be supported "Graphics Interchange Format is a popular but patent-encumbered format often \ used for web graphics. It uses lossless compression with up to 256 colors and optional \ transparency.\

For legal reasons this format is not recommended, use an open format instead."), ImgSaver::ImgNone }, { "TIFF", I18N_NOOP( // writing may not be supported "Tagged Image File Format is a versatile and extensible file format often \ supported by imaging and publishing applications. It supports indexed and true colour \ images with alpha transparency. Because there are many variations, there may sometimes \ be compatibility problems.\

Unless required for use with other applications, use an open format instead."), ImgSaver::ImgBW|ImgSaver::ImgColor|ImgSaver::ImgGray|ImgSaver::ImgHicolor }, { "XV", "", ImgSaver::ImgNone }, { "RGB", "", ImgSaver::ImgNone }, { NULL, "", ImgSaver::ImgNone } }; FormatDialog::FormatDialog( QWidget *parent, ImgSaver::ImageType type) : KDialogBase( parent, NULL, true, /* Tabbed,*/ i18n( "Save Assistant" ), KDialogBase::Ok|KDialogBase::Cancel) { QWidget *page = new QWidget(this); setMainWidget(page); QGridLayout *gl = new QGridLayout(page,10,3,0,KDialog::spacingHint()); // some nice words QLabel *l0 = new QLabel(i18n("Select a format to save the scanned image.
This is a %1.") .arg(ImgSaver::picTypeAsString(type)),page); gl->addMultiCellWidget(l0,0,0,0,2); KSeparator *sep = new KSeparator(KSeparator::HLine,page); gl->addMultiCellWidget(sep,1,1,0,2); // Insert scrolled list for formats QLabel *l1 = new QLabel(i18n("Image file format:"),page); gl->addWidget(l1,2,0,Qt::AlignLeft); lb_format = new QListBox(page); #ifdef USE_KIMAGEIO formatList = KImageIO::types(); #else formatList = QImage::outputFormatList(); #endif kdDebug(28000) << k_funcinfo << "have " << formatList.count() << " image types" << endl; imgType = type; // list is built later connect(lb_format,SIGNAL(highlighted(const QString &)),SLOT(showHelp(const QString &))); l1->setBuddy(lb_format); gl->addWidget(lb_format,3,0); // Insert label for help text l_help = new QLabel(page); l_help->setFrameStyle( QFrame::Panel|QFrame::Sunken ); l_help->setAlignment(Qt::AlignLeft|Qt::AlignTop); l_help->setMinimumSize(230,200); l_help->setMargin(4); gl->addMultiCellWidget(l_help,2,5,2,2); // Insert selection box for subformat l2 = new QLabel(i18n("Image sub-format:"),page); gl->addWidget(l2,4,0,Qt::AlignLeft); cb_subf = new QComboBox(page); gl->addWidget(cb_subf,5,0); l2->setBuddy(cb_subf); sep = new KSeparator(KSeparator::HLine,page); gl->addMultiCellWidget(sep,6,6,0,2); // Checkbox to store setting cbRecOnly = new QCheckBox(i18n("Only show recommended formats for this image type"),page); gl->addMultiCellWidget(cbRecOnly,7,7,0,2,Qt::AlignLeft); KConfig *conf = KGlobal::config(); conf->setGroup(OP_FILE_GROUP); cbRecOnly->setChecked(conf->readBoolEntry(OP_ONLY_REC_FMT,true)); connect(cbRecOnly,SIGNAL(toggled(bool)),SLOT(buildFormatList(bool))); buildFormatList(cbRecOnly->isChecked()); // now have this setting cbDontAsk = new QCheckBox(i18n("Always use this save format for this image type"),page); gl->addMultiCellWidget(cbDontAsk,8,8,0,2,Qt::AlignLeft); gl->addMultiCellWidget(new QLabel("",page),9,9,0,2); // need that for bottom margin gl->setRowSpacing(9,KDialog::marginHint()); gl->setColSpacing(1,KDialog::marginHint()); gl->setRowStretch(3,1); gl->setColStretch(2,1); } void FormatDialog::showHelp(const QString &item) { if (item.isNull()) // nothing is selected { l_help->setText(i18n("No format selected.")); enableButtonOK(false); return; } QString helptxt; for (formatInfo *ip = &formats[0]; ip->format!=NULL; ++ip) { if (ip->format==item) { helptxt = ip->helpString; break; } } if (!helptxt.isEmpty()) { l_help->setText(helptxt); // Set the hint check_subformat(helptxt); // and check subformats } else l_help->setText(i18n("No information is available for this format.")); enableButtonOK(true); } void FormatDialog::check_subformat( const QString & format ) { // not yet implemented //kdDebug(28000) << "This is format in check_subformat: " << format << endl; cb_subf->setEnabled(false); // l2 = Label "select subformat" ->bad name :-| l2->setEnabled(false); } void FormatDialog::setSelectedFormat(const QString &fo) { QListBoxItem *item = lb_format->findItem(fo); if (item!=NULL) lb_format->setSelected(lb_format->index(item),true); } QString FormatDialog::getFormat( ) const { int item = lb_format->currentItem(); if( item > -1 ) { const QString f = lb_format->text( item ); return( f ); } return( "BMP" ); } QCString FormatDialog::getSubFormat( ) const { // Not yet... return( "" ); } void FormatDialog::buildFormatList(bool recOnly) { kdDebug(29000) << k_funcinfo << "only=" << recOnly << " type=" << imgType << endl; lb_format->clear(); - for (QStringList::const_iterator it = formatList.begin(); it!=formatList.end(); ++it) + for (QStringList::const_iterator it = formatList.constBegin(); + it!=formatList.constEnd(); ++it) { if (recOnly) // only want recommended { bool formatOk = false; for (formatInfo *ip = &formats[0]; ip->format!=NULL; ++ip) { // search for this format if (ip->format!=(*it)) continue; if (ip->recForTypes & imgType) // recommended for this type? { formatOk = true; // this format to be shown break; // no more to do } } if (!formatOk) continue; // this format not to be shown } lb_format->insertItem((*it).upper()); // add format to list } showHelp(QString::null); // selection has been cleared } void FormatDialog::slotOk() { KConfig *conf = KGlobal::config(); conf->setGroup(OP_FILE_GROUP); // save state of this option conf->writeEntry(OP_ONLY_REC_FMT,cbRecOnly->isChecked()); KDialogBase::slotOk(); } void FormatDialog::forgetRemembered() { KConfig *conf = KGlobal::config(); conf->setGroup(OP_FILE_GROUP); // reset all options to default conf->deleteEntry(OP_ONLY_REC_FMT); conf->deleteEntry(OP_FILE_ASK_FORMAT); conf->deleteEntry(OP_ASK_FILENAME); conf->deleteEntry(OP_FORMAT_HICOLOR); conf->deleteEntry(OP_FORMAT_COLOR); conf->deleteEntry(OP_FORMAT_GRAY); conf->deleteEntry(OP_FORMAT_BW); } diff --git a/kooka/imgnamecombo.h b/kooka/imgnamecombo.h index a577929..42c76e8 100644 --- a/kooka/imgnamecombo.h +++ b/kooka/imgnamecombo.h @@ -1,57 +1,58 @@ /*************************************************************************** imgnamecombo.h - combobox for image names ------------------- begin : Tue Nov 13 2001 copyright : (C) 2001 by Klaas Freitag email : freitag@suse.de ***************************************************************************/ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ #ifndef IMGNAMECOMBO_H #define IMGNAMECOMBO_H #include /** *@author Klaas Freitag */ class QListViewItem; class KFileBranch; +class KFileTreeBranch; class ImageNameCombo: public KComboBox { Q_OBJECT public: ImageNameCombo( QWidget* ); ~ImageNameCombo(); public slots: void slotGalleryPathChanged( KFileTreeBranch* branch, const QString& relativPath ); void slotPathRemove( KFileTreeBranch *branch, const QString& relPath ); private: void rewriteList( KFileTreeBranch *, const QString& selText ); QStringList items; }; #endif diff --git a/kooka/imgsaver.cpp b/kooka/imgsaver.cpp index b9d9f9b..c4f9b33 100644 --- a/kooka/imgsaver.cpp +++ b/kooka/imgsaver.cpp @@ -1,707 +1,707 @@ /*************************************************************************** img_saver.cpp - description ------------------- begin : Mon Dec 27 1999 copyright : (C) 1999 by Klaas Freitag email : freitag@suse.de ***************************************************************************/ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "previewer.h" #include "kookaimage.h" #include "formatdialog.h" #include "imgsaver.h" #include "imgsaver.moc" ImgSaver::ImgSaver( QWidget *parent, const KURL dir_name ) : QObject( parent ) { if( dir_name.isEmpty() || dir_name.protocol() != "file" ) { kdDebug(28000) << "ImageServer initialised with wrong dir " << dir_name.url() << endl; directory = Previewer::galleryRoot(); } else { /* A path was given */ if( dir_name.protocol() != "file" ) { kdDebug(28000) << "ImgSaver: Can only save local image, sorry !" << endl; } else { directory = dir_name.directory(true, false); } } kdDebug(28000) << "ImageSaver uses dir <" << directory << endl; createDir( directory ); readConfig(); last_file = ""; last_format =""; } ImgSaver::ImgSaver( QWidget *parent ) :QObject( parent ) { directory = Previewer::galleryRoot(); createDir( directory ); readConfig(); last_file = ""; last_format =""; } /* Needs a full qualified directory name */ void ImgSaver::createDir( const QString& dir ) { KURL url( dir ); if( ! KIO::NetAccess::exists(url, false, 0) ) { kdDebug(28000) << "Wrn: Directory <" << dir << "> does not exist -> try to create !" << endl; // if( mkdir( QFile::encodeName( dir ), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) != 0 ) if( KIO::mkdir( KURL(dir))) { KMessageBox::sorry(0, i18n("The folder\n%1\n does not exist and could not be created;\n" "please check the permissions.").arg(dir)); } } #if 0 if( ! fi.isWritable() ) { KMessageBox::sorry(0, i18n("The directory\n%1\n is not writeable;\nplease check the permissions.") .arg(dir)); } #endif } /** * This function asks the user for a filename or creates * one by itself, depending on the settings **/ ImgSaveStat ImgSaver::saveImage( QImage *image ) { ImgSaveStat stat; ImgSaver::ImageType imgType; if( !image ) return( ISS_ERR_PARAM ); /* Find out what kind of image it is */ if( image->depth() > 8 ) { imgType = ImgSaver::ImgHicolor; } else { if( image->depth() == 1 || image->numColors() == 2 ) { kdDebug(28000) << "This is black And White!" << endl; imgType = ImgSaver::ImgBW; } else { imgType = ImgSaver::ImgColor; if( image->allGray() ) { imgType = ImgSaver::ImgGray; } } } QString format = findFormat( imgType ); QString subformat = findSubFormat( format ); // Call save-Function with this params if( format.isEmpty() ) { kdDebug(28000) << "Save canceled by user -> no save !" << endl; return( ISS_SAVE_CANCELED ); } kdDebug(28000) << "saveImage: Directory is " << directory << endl; QString filename = createFilename( format ); KConfig *konf = KGlobal::config (); konf->setGroup( OP_FILE_GROUP ); if( konf->readBoolEntry( OP_ASK_FILENAME, false ) ) { bool ok; QString text = KInputDialog::getText( i18n( "Filename" ), i18n("Enter filename:"), filename, &ok ); if(ok) { filename = text; } } QString fi = directory + "/" + filename; if( extension(fi).isEmpty() ) { if( ! fi.endsWith( "." ) ) { fi+= "."; } fi+=format.lower(); } kdDebug(28000) << "saveImage: saving file <" << fi << ">" << endl; stat = save( image, fi, format, subformat ); return( stat ); } /** * This member creates a filename for the image to save. * This is done by numbering all existing files and adding * one **/ QString ImgSaver::createFilename(QString format) { if (format.isEmpty()) return (QString::null); const QString suffix = KImageIO::suffix(format); // file extension for saving QString s = "kscan_*."+suffix; QDir files(directory,s); // list of existing files QString fname; for (unsigned long c = 1; c<=files.count()+1; ++c) // that must be the upper bound { fname = "kscan_"+QString::number(c).rightJustify(4,'0')+"."+suffix; if (!files.exists(fname)) break; } return (fname); } /** * This function gets a filename from the parent. The filename must not be relative. **/ ImgSaveStat ImgSaver::saveImage( QImage *image, const KURL& filename, const QString& imgFormat ) { QString format = imgFormat; /* Check if the filename is local */ if( !filename.isLocalFile()) { kdDebug(29000) << "ImgSaver: Can only save local image, sorry !" << endl; return( ISS_ERR_PROTOCOL ); } QString localFilename = filename.path(); //localFilename = filename.directory( false, true) + filename.fileName(); kdDebug(28000) << "saveImage: Saving "<< localFilename << " in format " << format << endl; if( format.isEmpty() ) format = "BMP"; return( save( image, localFilename, format, "" ) ); } /* * findFormat does all the stuff with the dialog. */ QString ImgSaver::findFormat( ImgSaver::ImageType type ) { if (type==ImgSaver::ImgThumbnail) return ("BMP"); // thumbnail always this format if (type==ImgSaver::ImgPreview) return ("BMP"); // preview always this format // real images from here on QString format; KConfig *konf = KGlobal::config (); konf->setGroup( OP_FILE_GROUP ); switch( type ) { case ImgSaver::ImgColor: format = konf->readEntry( OP_FORMAT_COLOR, "nothing" ); kdDebug( 28000 ) << "Format for Color: " << format << endl; break; case ImgSaver::ImgGray: format = konf->readEntry( OP_FORMAT_GRAY, "nothing" ); kdDebug( 28000 ) << "Format for Gray: " << format << endl; break; case ImgSaver::ImgBW: format = konf->readEntry( OP_FORMAT_BW, "nothing" ); kdDebug( 28000 ) << "Format for BlackAndWhite: " << format << endl; break; case ImgSaver::ImgHicolor: format = konf->readEntry( OP_FORMAT_HICOLOR, "nothing" ); kdDebug( 28000 ) << "Format for HiColorImage: " << format << endl; break; default: format = "nothing"; kdDebug( 28000 ) << "ERR: Could not find image type !" << endl; break; } if (format=="nothing" || ask_for_format) format = startFormatDialog(type); return (format); } QString ImgSaver::picTypeAsString(ImgSaver::ImageType type) { QString res; switch (type) { case ImgSaver::ImgColor: res = i18n("indexed color image (up to 8 bit depth)"); break; case ImgSaver::ImgGray: res = i18n("gray scale image (up to 8 bit depth)"); break; case ImgSaver::ImgBW: res = i18n("lineart image (black and white, 1 bit depth)"); break; case ImgSaver::ImgHicolor: res = i18n("high/true color image (more than 8 bit depth)"); break; default: res = i18n("unknown image type %1").arg(type); break; } return (res); } QString ImgSaver::startFormatDialog( ImgSaver::ImageType type) { FormatDialog fd(NULL,type); // set default values if( type != ImgSaver::ImgPreview ) { QString defFormat = getFormatForType( type ); fd.setSelectedFormat( defFormat ); } QString format; if( fd.exec() ) { format = fd.getFormat(); kdDebug(28000) << "Storing to format <" << format << ">" << endl; bool ask = fd.askForFormat(); kdDebug(28000)<< "Store askFor is " << ask << endl; storeFormatForType( type, format, ask ); subformat = fd.getSubFormat(); } return( format ); } /* * This method returns true if the image format given in format is remembered * for that image type. */ bool ImgSaver::isRememberedFormat( ImgSaver::ImageType type, QString format ) const { if( getFormatForType( type ) == format ) { return( true ); } else { return( false ); } } QString ImgSaver::getFormatForType( ImgSaver::ImageType type ) const { KConfig *konf = KGlobal::config (); Q_CHECK_PTR( konf ); konf->setGroup( OP_FILE_GROUP ); QString f; switch( type ) { case ImgSaver::ImgColor: f = konf->readEntry( OP_FORMAT_COLOR, "BMP" ); break; case ImgSaver::ImgGray: f = konf->readEntry( OP_FORMAT_GRAY, "BMP" ); break; case ImgSaver::ImgBW: f = konf->readEntry( OP_FORMAT_BW, "BMP" ); break; case ImgSaver::ImgHicolor: f = konf->readEntry( OP_FORMAT_HICOLOR, "BMP" ); break; default: f = "BMP"; break; } return( f ); } void ImgSaver::storeFormatForType( ImgSaver::ImageType type, QString format, bool ask ) { KConfig *konf = KGlobal::config (); Q_CHECK_PTR( konf ); konf->setGroup( OP_FILE_GROUP ); konf->writeEntry( OP_FILE_ASK_FORMAT, ask ); ask_for_format = ask; switch( type ) { case ImgSaver::ImgColor: konf->writeEntry( OP_FORMAT_COLOR, format ); break; case ImgSaver::ImgGray: konf->writeEntry( OP_FORMAT_GRAY, format ); break; case ImgSaver::ImgBW: konf->writeEntry( OP_FORMAT_BW, format ); break; case ImgSaver::ImgHicolor: konf->writeEntry( OP_FORMAT_HICOLOR, format ); break; default: kdDebug(28000) << "Wrong Type - cant store format setting" << endl; break; } konf->sync(); } QString ImgSaver::findSubFormat( QString format ) { kdDebug(28000) << "Searching Subformat for " << format << endl; return( subformat ); } /** private save() does the work to save the image. the filename must be complete and local. **/ ImgSaveStat ImgSaver::save( QImage *image, const QString &filename, const QString &format, const QString &subformat ) { bool result = false; kdDebug(28000) << "in ImgSaver::save: saving " << filename << endl; if( ! format || !image ) { kdDebug(28000) << "ImgSaver ERROR: Wrong parameter Format <" << format << "> or image" << endl; return( ISS_ERR_PARAM ); } if( image ) { // remember the last processed file - only the filename - no path QFileInfo fi( filename ); QString dirPath = fi.dirPath(); QDir dir = QDir( dirPath ); if( ! dir.exists() ) { /* The dir to save in always should exist, except in the first preview save */ kdDebug(28000) << "Creating dir " << dirPath << endl; if( !dir.mkdir( dirPath ) ) { kdDebug(28000) << "ERR: Could not create directory" << endl; } } if( fi.exists() && !fi.isWritable() ) { kdDebug(28000) << "Cant write to file <" << filename << ">, cant save !" << endl; result = false; return( ISS_ERR_PERM ); } /* Check the format, is it writable ? */ #ifdef USE_KIMAGEIO if( ! KImageIO::canWrite( format ) ) { kdDebug(28000) << "Cant write format <" << format << ">" << endl; result = false; return( ISS_ERR_FORMAT_NO_WRITE ); } #endif kdDebug(28000) << "ImgSaver: saving image to <" << filename << "> as <" << format << "/" << subformat <<">" << endl; result = image->save( filename, format.latin1() ); last_file = fi.absFilePath(); last_format = format.latin1(); } if( result ) return( ISS_OK ); else { last_file = ""; last_format = ""; return( ISS_ERR_UNKNOWN ); } } void ImgSaver::readConfig( void ) { KConfig *konf = KGlobal::config (); Q_CHECK_PTR( konf ); konf->setGroup( OP_FILE_GROUP ); ask_for_format = konf->readBoolEntry( OP_FILE_ASK_FORMAT, true ); QDir home = QDir::home(); } QString ImgSaver::errorString( ImgSaveStat stat ) { QString re; switch( stat ) { case ISS_OK: re = i18n( " image save OK " ); break; case ISS_ERR_PERM: re = i18n( " permission error " ); break; case ISS_ERR_FILENAME: re = i18n( " bad filename " ); break; case ISS_ERR_NO_SPACE: re = i18n( " no space on device " ); break; case ISS_ERR_FORMAT_NO_WRITE: re = i18n( " could not write image format " ); break; case ISS_ERR_PROTOCOL: re = i18n( " can not write file using that protocol "); break; case ISS_SAVE_CANCELED: re = i18n( " user canceled saving " ); break; case ISS_ERR_UNKNOWN: re = i18n( " unknown error " ); break; case ISS_ERR_PARAM: re = i18n( " parameter wrong " ); break; default: re = ""; } return( re ); } QString ImgSaver::extension( const KURL& url ) { QString extension = url.fileName(); int dotPos = extension.findRev( '.' ); if( dotPos > 0 ) { int len = extension.length(); extension = extension.right( len - dotPos -1 ); } else { /* No extension was supplied */ extension = QString(); } return extension; } -bool ImgSaver::renameImage( const KURL& fromUrl, KURL& toUrl, bool askExt, QWidget *overWidget ) +bool ImgSaver::renameImage( const KURL& fromUrl, const KURL& toUrl, bool askExt, QWidget *overWidget ) { /* Check if the provided filename has a extension */ QString extTo = extension( toUrl ); QString extFrom = extension( fromUrl ); KURL targetUrl( toUrl ); if( extTo.isEmpty() && !extFrom.isEmpty() ) { /* Ask if the extension should be added */ int result = KMessageBox::Yes; QString fName = toUrl.fileName(); if( ! fName.endsWith( "." ) ) { fName += "."; } fName += extFrom; if( askExt ) { QString s; s = i18n("The filename you supplied has no file extension.\nShould the correct one be added automatically? "); s += i18n( "That would result in the new filename: %1" ).arg( fName); result = KMessageBox::questionYesNo(overWidget, s, i18n( "Extension Missing"), i18n("Add Extension"), i18n("Do Not Add"), "AutoAddExtensions" ); } if( result == KMessageBox::Yes ) { targetUrl.setFileName( fName ); kdDebug(28000) << "Rename file to " << targetUrl.prettyURL() << endl; } } else if( !extFrom.isEmpty() && extFrom != extTo ) { if( ! ((extFrom.lower() == "jpeg" && extTo.lower() == "jpg") || (extFrom.lower() == "jpg" && extTo.lower() == "jpeg" ))) { /* extensions differ -> TODO */ KMessageBox::error( overWidget, i18n("Format changes of images are currently not supported."), i18n("Wrong Extension Found" )); return(false); } } bool success = false; if( KIO::NetAccess::exists( targetUrl, false,0 ) ) { kdDebug(28000)<< "Target already exists - can not copy" << endl; } else { if( KIO::file_move(fromUrl, targetUrl) ) { success = true; } } return( success ); } QString ImgSaver::tempSaveImage( KookaImage *img, const QString& format, int colors ) { KTempFile *tmpFile = new KTempFile( QString(), "."+format.lower()); tmpFile->setAutoDelete( false ); tmpFile->close(); KookaImage tmpImg; if( colors != -1 && img->numColors() != colors ) { // Need to convert image if( colors == 1 || colors == 8 || colors == 24 || colors == 32 ) { tmpImg = img->convertDepth( colors ); img = &tmpImg; } else { kdDebug(29000) << "ERROR: Wrong color depth requested: " << colors << endl; img = 0; } } QString name; if( img ) { name = tmpFile->name(); if( ! img->save( name, format.latin1() ) ) name = QString(); } delete tmpFile; return name; } bool ImgSaver::copyImage( const KURL& fromUrl, const KURL& toUrl, QWidget *overWidget ) { /* Check if the provided filename has a extension */ QString extTo = extension( toUrl ); QString extFrom = extension( fromUrl ); KURL targetUrl( toUrl ); if( extTo.isEmpty() && !extFrom.isEmpty()) { /* Ask if the extension should be added */ int result = KMessageBox::Yes; QString fName = toUrl.fileName(); if( ! fName.endsWith( "." )) fName += "."; fName += extFrom; QString s; s = i18n("The filename you supplied has no file extension.\nShould the correct one be added automatically? "); s += i18n( "That would result in the new filename: %1" ).arg( fName); result = KMessageBox::questionYesNo(overWidget, s, i18n( "Extension Missing"), i18n("Add Extension"), i18n("Do Not Add"), "AutoAddExtensions" ); if( result == KMessageBox::Yes ) { targetUrl.setFileName( fName ); } } else if( !extFrom.isEmpty() && extFrom != extTo ) { /* extensions differ -> TODO */ if( ! ((extFrom.lower() == "jpeg" && extTo.lower() == "jpg") || (extFrom.lower() == "jpg" && extTo.lower() == "jpeg" ))) { KMessageBox::error( overWidget, i18n("Format changes of images are currently not supported."), i18n("Wrong Extension Found" )); return(false); } } KIO::Job *copyjob = KIO::copy( fromUrl, targetUrl, false ); return( copyjob ? true : false ); } diff --git a/kooka/imgsaver.h b/kooka/imgsaver.h index 77a4779..783a66e 100644 --- a/kooka/imgsaver.h +++ b/kooka/imgsaver.h @@ -1,157 +1,157 @@ /***************************************************** -*- mode:c++; -*- *** img_saver.h - description ------------------- begin : Mon Dec 27 1999 copyright : (C) 1999 by Klaas Freitag email : freitag@suse.de ***************************************************************************/ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ #ifndef __IMGSAVER_H__ #define __IMGSAVER_H__ #include #include #define OP_FILE_GROUP "Files" #define OP_FILE_ASK_FORMAT "AskForSaveFormat" #define OP_ASK_FILENAME "AskForFilename" #define OP_FORMAT_HICOLOR "HiColorSaveFormat" #define OP_FORMAT_COLOR "ColorSaveFormat" #define OP_FORMAT_GRAY "GraySaveFormat" #define OP_FORMAT_BW "BWSaveFormat" #define OP_FORMAT_THUMBNAIL "ThumbnailFormat" #define OP_PREVIEW_GROUP "ScanPreview" #define OP_PREVIEW_FILE "PreviewFile" #define OP_PREVIEW_FORMAT "PreviewFormat" #define OP_ONLY_REC_FMT "OnlyRecommended" /** * enum ImgSaveStat: * Errorflags for the save. These enums are returned by the * all image save operations and the calling object my display * a human readable Error-Message on this information **/ typedef enum { ISS_OK, /* Image save OK */ ISS_ERR_PERM, /* permission Error */ ISS_ERR_FILENAME, /* bad filename */ ISS_ERR_NO_SPACE, /* no space on device */ ISS_ERR_FORMAT_NO_WRITE, /* Image format can not be written */ ISS_ERR_UNKNOWN, ISS_ERR_PARAM, /* Parameter wrong */ ISS_ERR_PROTOCOL, ISS_SAVE_CANCELED } ImgSaveStat; class KookaImage; /** * Class ImgSaver: * The main class of this module. It manages all saving of images * in kooka * It asks the user for the img-format if desired, creates thumbnails * and cares for database entries (later ;) **/ class ImgSaver : public QObject { Q_OBJECT public: /** * enum ImageType: * Specifies the type of the image to save. This is important for * getting the format. **/ enum ImageType { ImgNone = 0x00, ImgPreview = 0x01, ImgThumbnail = 0x02, ImgHicolor = 0x04, ImgColor = 0x08, ImgGray = 0x10, ImgBW = 0x20 }; /** * constructor of the image-saver object. * name is the name of a subdirectory of the save directory, * which can be given in dir. If no dir is given, an * dir ~/.ksane is created. * @param dir Name of the save root directory * @param name Name of a subdirectory in the saveroot. **/ ImgSaver( QWidget *parent, const KURL ); ImgSaver( QWidget *parent ); QString errorString( ImgSaveStat ); /** * returns the name of the last file that was saved by ImgSaver. */ QString lastFilename() const { return( last_file ); } KURL lastFileUrl() const { return( KURL(last_file )); } /** * returns the image format of the last saved image. */ QCString lastSaveFormat( void ) const { return( last_format ); } QString getFormatForType( ImgSaver::ImageType ) const; void storeFormatForType( ImgSaver::ImageType, QString, bool ); bool isRememberedFormat( ImgSaver::ImageType type, QString format ) const; /* static function that exports a file */ static bool copyImage( const KURL& fromUrl, const KURL& toUrl, QWidget *overWidget=0 ); - static bool renameImage( const KURL& fromUrl, KURL& toUrl, bool askExt=false, QWidget *overWidget=0 ); + static bool renameImage( const KURL& fromUrl, const KURL& toUrl, bool askExt=false, QWidget *overWidget=0 ); static QString tempSaveImage( KookaImage *img, const QString& format, int colors = -1 ); /* static function that returns the extension of an url */ static QString extension( const KURL& ); static QString picTypeAsString(ImgSaver::ImageType type); public slots: ImgSaveStat saveImage( QImage *image ); ImgSaveStat saveImage( QImage *image, const KURL& filename, const QString& imgFormat ); private: QString findFormat( ImgSaver::ImageType type ); QString findSubFormat( QString format ); void createDir( const QString& ); ImgSaveStat save( QImage *image, const QString &filename, const QString &format, const QString &subformat ); QString createFilename( QString format ); void readConfig( void ); QString startFormatDialog( ImgSaver::ImageType ); QString directory; // dir where the image should be saved QString last_file; QCString subformat; QCString last_format; bool ask_for_format; }; #endif diff --git a/kooka/kooka.cpp b/kooka/kooka.cpp index 9055178..87e478a 100644 --- a/kooka/kooka.cpp +++ b/kooka/kooka.cpp @@ -1,508 +1,502 @@ /************************************************************************** kooka.cpp - Main program class ------------------- begin : Sun Jan 16 2000 copyright : (C) 2000 by Klaas Freitag email : freitag@suse.de ***************************************************************************/ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kookapref.h" #include "formatdialog.h" #include "kookaview.h" #include "kooka.h" #include "kooka.moc" #define DOCK_SIZES "DockSizes" Kooka::Kooka( const QCString& deviceToUse) : KParts::DockMainWindow( 0, "Kooka" ), m_printer(0), m_prefDialogIndex(0) { /* Start to create the main view framework */ m_view = new KookaView( this, deviceToUse); /* Call createGUI on the ocr-result view */ setXMLFile( "kookaui.rc", true ); setAcceptDrops(false); // Waba: Not (yet?) supported KConfig *konf = KGlobal::config (); readDockConfig ( konf, DOCK_SIZES ); // then, setup our actions setupActions(); createGUI(0L); // m_view->ocrResultPart()); // and a status bar statusBar()->insertItem(i18n("Ready"),KookaView::StatusTemp,1); statusBar()->setItemAlignment(KookaView::StatusTemp,Qt::AlignLeft); statusBar()->show(); // allow the view to change the statusbar and caption connect(m_view, SIGNAL(signalChangeStatusbar(const QString&)), this, SLOT(changeStatusbar(const QString&))); connect(m_view, SIGNAL(signalCleanStatusbar(void)), this, SLOT(cleanStatusbar())); connect(m_view, SIGNAL(signalChangeCaption(const QString&)), this, SLOT(changeCaption(const QString&))); connect(m_view, SIGNAL(signalScannerChanged(bool)), this, SLOT(slotUpdateScannerActions(bool))); connect(m_view,SIGNAL(signalRectangleChanged(bool)), this, SLOT(slotUpdateRectangleActions(bool))); connect(m_view,SIGNAL(signalGallerySelectionChanged(bool,int)), this, SLOT(slotUpdateGalleryActions(bool,int))); connect(m_view,SIGNAL(signalLoadedImageChanged(bool,bool)), this, SLOT(slotUpdateLoadedActions(bool,bool))); changeCaption( i18n( "KDE Scanning" )); setAutoSaveSettings( QString::fromLatin1("General Options"), true ); slotUpdateScannerActions(m_view->scannerConnected()); slotUpdateRectangleActions(false); slotUpdateGalleryActions(true,0); } void Kooka::createMyGUI( KParts::Part *part ) { kdDebug(28000) << "Part changed, Creating gui" << endl; createGUI(part); } Kooka::~Kooka() { KConfig *konf = KGlobal::config (); m_view->slCloseScanDevice(); writeDockConfig ( konf, DOCK_SIZES ); delete m_printer; } void Kooka::startup( void ) { kdDebug(29000) << "Starting startup !" << endl; if( m_view ) m_view->loadStartupImage(); } void Kooka::setupActions() { printImageAction = KStdAction::print(this, SLOT(filePrint()), actionCollection()); KStdAction::quit(this,SLOT(close()),actionCollection()); KStdAction::keyBindings(guiFactory(),SLOT(configureShortcuts()),actionCollection()); KStdAction::configureToolbars(this,SLOT(optionsConfigureToolbars()),actionCollection()); KStdAction::preferences(this,SLOT(optionsPreferences()),actionCollection()); m_view->createDockMenu(actionCollection(), this, "settings_show_docks" ); /* Image Viewer action Toolbar - Scaling etc. */ scaleToWidthAction = new KAction(i18n("Scale to Width"), "scaletowidth", CTRL+Key_I, m_view, SLOT( slIVScaleToWidth()), actionCollection(), "scaleToWidth" ); m_view->connectViewerAction( scaleToWidthAction ); scaleToHeightAction = new KAction(i18n("Scale to Height"), "scaletoheight", CTRL+Key_H, m_view, SLOT( slIVScaleToHeight()), actionCollection(), "scaleToHeight" ); m_view->connectViewerAction( scaleToHeightAction ); scaleToOriginalAction = new KAction(i18n("Original Size"), "scaleorig", CTRL+Key_1, m_view, SLOT( slIVScaleOriginal()), actionCollection(), "scaleOriginal" ); m_view->connectViewerAction( scaleToOriginalAction ); #ifdef QICONSET_HONOUR_ON_OFF /* The Toggleaction does not seem to handle the on/off icon from QIconSet */ QIconSet lockSet; lockSet.setPixmap(BarIcon("lock") , QIconSet::Automatic, QIconSet::Normal, QIconSet::On ); lockSet.setPixmap(BarIcon("unlock"), QIconSet::Automatic, QIconSet::Normal, QIconSet::Off); KAction *act = new KToggleAction ( i18n("Keep Zoom Setting"), lockSet, CTRL+Key_Z, actionCollection(), "keepZoom" ); #else KAction *act = new KToggleAction( i18n("Keep Zoom Setting"), BarIcon("lockzoom"), CTRL+Key_Z, actionCollection(), "keepZoom" ); #endif connect( act, SIGNAL( toggled( bool ) ), m_view->getImageViewer(), SLOT(setKeepZoom(bool))); m_view->connectViewerAction( act ); /* thumbview and gallery actions */ act = new KAction(i18n("Set Zoom..."), "viewmag", 0, m_view, SLOT( slIVShowZoomDialog()), actionCollection(), "showZoomDialog" ); m_view->connectViewerAction( act ); newFromSelectionAction = new KAction(i18n("New Image From Selection"), "crop", CTRL+Key_N, m_view, SLOT( slCreateNewImgFromSelection() ), actionCollection(), "createFromSelection" ); mirrorVerticallyAction = new KAction(i18n("Mirror Vertically"), "mirror-vert", CTRL+Key_V, this, SLOT( slMirrorVertical() ), actionCollection(), "mirrorVertical" ); mirrorHorizontallyAction = new KAction(i18n("Mirror Horizontally"), "mirror-horiz", CTRL+Key_M, this, SLOT( slMirrorHorizontal() ), actionCollection(), "mirrorHorizontal" ); rotateCwAction = new KAction(i18n("Rotate Clockwise"), "rotate_cw", CTRL+Key_9, this, SLOT( slRotateClockWise() ), actionCollection(), "rotateClockwise" ); m_view->connectViewerAction( rotateCwAction ); rotateAcwAction = new KAction(i18n("Rotate Counter-Clockwise"), "rotate_ccw", CTRL+Key_7, this, SLOT( slRotateCounterClockWise() ), actionCollection(), "rotateCounterClockwise" ); m_view->connectViewerAction( rotateAcwAction ); rotate180Action = new KAction(i18n("Rotate 180 Degrees"), "rotate", CTRL+Key_8, this, SLOT( slRotate180() ), actionCollection(), "upsitedown" ); m_view->connectViewerAction( rotate180Action ); /* Gallery actions */ createFolderAction = new KAction(i18n("Create Folder..."), "folder_new", 0, m_view->gallery(), SLOT( slotCreateFolder() ), actionCollection(), "foldernew" ); m_view->connectGalleryAction( createFolderAction ); //openWithAction = new KAction(i18n("Open In Graphical Application..."), "fileopen", KStdAccel::open(), // m_view, SLOT( slOpenCurrInGraphApp() ), // actionCollection(), "openInGraphApp" ); openWithMenu = new KActionMenu(i18n("Open With"),"fileopen", actionCollection(),"openWith"); connect(openWithMenu->popupMenu(),SIGNAL(aboutToShow()),SLOT(slotOpenWithMenu())); m_view->connectGalleryAction( openWithMenu ); saveImageAction = new KAction(i18n("Save Image..."), "filesave", KStdAccel::save(), m_view->gallery(), SLOT( slotExportFile() ), actionCollection(), "saveImage" ); m_view->connectGalleryAction( saveImageAction ); importImageAction = new KAction(i18n("Import Image..."), "fileimport", 0, m_view->gallery(), SLOT( slotImportFile() ), actionCollection(), "importImage" ); //m_view->connectGalleryAction( importImageAction ); deleteImageAction = new KAction(i18n("Delete Image"), "editdelete", SHIFT+Key_Delete, m_view->gallery(), SLOT( slotDeleteItems() ), actionCollection(), "deleteImage" ); m_view->connectGalleryAction( deleteImageAction ); renameImageAction = new KAction(i18n("Rename Image"), "edittool", Key_F2, m_view->gallery(), SLOT( slotRenameItems() ), actionCollection(), "renameImage" ); m_view->connectGalleryAction( renameImageAction ); unloadImageAction = new KAction(i18n("Unload Image"), "fileclose", CTRL+SHIFT+Key_U, m_view->gallery(), SLOT( slotUnloadItems() ), actionCollection(), "unloadImage" ); m_view->connectGalleryAction( unloadImageAction ); -#if 0 - /* not yet supported actions - coming post 3.1 */ - (void) new KAction(i18n("Load Scan Parameters"), "bookmark_add", CTRL+Key_L, - m_view, SLOT(slLoadScanParams()), - actionCollection(), "loadscanparam" ); - - (void) new KAction(i18n("Save Scan Parameters"), "bookmark_add", CTRL+Key_S, - m_view, SLOT(slSaveScanParams()), - actionCollection(), "savescanparam" ); -#endif - // "Settings" menu (void) new KAction(i18n("Select Scan Device..."), "scanner", 0, m_view, SLOT( slSelectDevice()), actionCollection(), "selectsource" ); (void) new KAction(i18n("Add Scan Device..."), "add", 0, m_view, SLOT( slAddDevice()), actionCollection(), "addsource" ); (void) new KAction( i18n("Enable All Warnings && Messages"), 0, this, SLOT(slEnableWarnings()), actionCollection(), "enable_msgs"); // Scanning functions - scanAction = new KAction(i18n("Preview"), "preview", 0, + scanAction = new KAction(i18n("Preview"), "preview", Key_F3, m_view, SLOT( slStartPreview()), actionCollection(), "startPreview" ); - previewAction = new KAction(i18n("Start Scan"), "scanner", 0, + previewAction = new KAction(i18n("Start Scan"), "scanner", Key_F4, m_view, SLOT( slStartFinalScan()), actionCollection(), "startScan" ); + paramsAction = new KAction(i18n("Scan Parameters..."), "bookmark_add", CTRL+SHIFT+Key_S, + m_view, SLOT(slScanParams()), + actionCollection(), "scanparam" ); + // OCR functions ocrAction = new KAction(i18n("OCR Image..."), "ocr", 0, m_view, SLOT(doOCR()), actionCollection(), "ocrImage" ); ocrSelectAction = new KAction(i18n("OCR Selection..."), "ocr-select", 0, m_view, SLOT(doOCRonSelection()), actionCollection(), "ocrImageSelect" ); m_saveOCRTextAction = new KAction( i18n("Save OCR Result Text..."), "filesaveas", CTRL+Key_U, m_view, SLOT(slSaveOCRResult()), actionCollection(), "saveOCRResult"); } void Kooka::saveProperties(KConfig *config) { // the 'config' object points to the session managed // config file. anything you write here will be available // later when this app is restored //if (!m_view->currentURL().isNull()) // config->writePathEntry("lastURL", m_view->currentURL()); kdDebug(28000) << "In kooka's saveProperties !" << endl; config->setGroup( KOOKA_STATE_GROUP ); config->writeEntry( PREFERENCE_DIA_TAB, m_prefDialogIndex ); m_view->saveProperties( config ); } void Kooka::readProperties(KConfig *config) { (void) config; // the 'config' object points to the session managed // config file. this function is automatically called whenever // the app is being restored. read in here whatever you wrote // in 'saveProperties' config->setGroup( KOOKA_STATE_GROUP ); m_prefDialogIndex = config->readNumEntry( PREFERENCE_DIA_TAB, 0 ); // QString url = config->readPathEntry("lastURL"); } void Kooka::dragEnterEvent(QDragEnterEvent *event) { // accept uri drops only event->accept(KURLDrag::canDecode(event)); } void Kooka::filePrint() { // this slot is called whenever the File->Print menu is selected, // the Print shortcut is pressed (usually CTRL+P) or the Print toolbar // button is clicked m_view->print(); } void Kooka::optionsShowScanParams() { m_view->slSetScanParamsVisible( m_scanParamsAction->isChecked() ); } void Kooka::optionsShowPreviewer() { m_view->slSetTabWVisible( m_previewerAction->isChecked()); } void Kooka::optionsConfigureToolbars() { // use the standard toolbar editor saveMainWindowSettings(KGlobal::config(), autoSaveGroup()); KEditToolbar dlg(factory()); connect(&dlg, SIGNAL(newToolbarConfig()), SLOT(newToolbarConfig())); dlg.exec(); } void Kooka::newToolbarConfig() { // OK/Apply pressed in the toolbar editor applyMainWindowSettings(KGlobal::config(), autoSaveGroup()); } void Kooka::optionsPreferences() { // popup some sort of preference dialog, here KookaPref dlg; dlg.showPage(m_prefDialogIndex); connect(&dlg,SIGNAL(dataSaved()),m_view,SLOT(slFreshUpThumbView())); if (dlg.exec()) { // redo your settings m_prefDialogIndex = dlg.activePageIndex(); m_view->gallery()->setAllowRename(dlg.allowGalleryRename()); // m_view->slFreshUpThumbView(); } } void Kooka::changeStatusbar(const QString& text) { // display the text on the statusbar statusBar()->changeItem( text, KookaView::StatusTemp ); } void Kooka::changeCaption(const QString& text) { // display the text on the caption setCaption(text); } void Kooka::slMirrorVertical( void ) { m_view->slMirrorImage( KookaView::MirrorVertical ); } void Kooka::slMirrorHorizontal( void ) { m_view->slMirrorImage( KookaView::MirrorHorizontal ); } void Kooka::slRotateClockWise( void ) { m_view->slRotateImage( 90 ); } void Kooka::slRotateCounterClockWise( void ) { m_view->slRotateImage( -90 ); } void Kooka::slRotate180( void ) { m_view->slMirrorImage( KookaView::MirrorBoth ); //m_view->slRotateImage( 180 ); } void Kooka::slEnableWarnings( ) { KMessageBox::information(this,i18n("All messages and warnings will now be shown.")); KMessageBox::enableAllMessages(); FormatDialog::forgetRemembered(); kapp->config()->reparseConfiguration(); } void Kooka::slotUpdateScannerActions(bool haveConnection) { kdDebug(29000) << k_funcinfo << "hc=" << haveConnection << endl; scanAction->setEnabled(haveConnection); previewAction->setEnabled(haveConnection); + paramsAction->setEnabled(haveConnection); setCaption(m_view->scannerName()); } void Kooka::slotUpdateRectangleActions(bool haveSelection) { kdDebug(29000) << k_funcinfo << "hs=" << haveSelection << endl; ocrSelectAction->setEnabled(haveSelection); newFromSelectionAction->setEnabled(haveSelection); } void Kooka::slotUpdateGalleryActions(bool isDir,int howmanySelected) { kdDebug(29000) << k_funcinfo << "isdir=" << isDir << " howmany=" << howmanySelected << endl; const bool singleImage = howmanySelected==1 && !isDir; ocrAction->setEnabled(singleImage); scaleToWidthAction->setEnabled(singleImage); scaleToHeightAction->setEnabled(singleImage); scaleToOriginalAction->setEnabled(singleImage); mirrorVerticallyAction->setEnabled(singleImage); mirrorHorizontallyAction->setEnabled(singleImage); rotateCwAction->setEnabled(singleImage); rotateAcwAction->setEnabled(singleImage); rotate180Action->setEnabled(singleImage); if (howmanySelected==0) slotUpdateRectangleActions(false); createFolderAction->setEnabled(isDir); importImageAction->setEnabled(isDir); saveImageAction->setEnabled(singleImage); printImageAction->setEnabled(singleImage); if (isDir) { unloadImageAction->setText(i18n("Unload Folder")); unloadImageAction->setEnabled(true); deleteImageAction->setText(i18n("Delete Folder")); renameImageAction->setText(i18n("Rename Folder")); bool rs = m_view->galleryRootSelected(); deleteImageAction->setEnabled(!rs); renameImageAction->setEnabled(!rs); } else { unloadImageAction->setText(i18n("Unload Image")); unloadImageAction->setEnabled(singleImage && m_view->gallery()->getCurrImage()!=NULL); deleteImageAction->setText(i18n("Delete Image")); renameImageAction->setText(i18n("Rename Image")); deleteImageAction->setEnabled(singleImage); renameImageAction->setEnabled(singleImage); } } void Kooka::slotUpdateLoadedActions(bool isLoaded,bool isDir) { kdDebug(29000) << k_funcinfo << "loaded=" << isLoaded << " isDir=" << isDir << endl; unloadImageAction->setEnabled(isLoaded || isDir); } void Kooka::slotOpenWithMenu() { m_view->gallery()->showOpenWithMenu(openWithMenu); } diff --git a/kooka/kooka.h b/kooka/kooka.h index d25462c..59acdd2 100644 --- a/kooka/kooka.h +++ b/kooka/kooka.h @@ -1,169 +1,170 @@ /************************************************************************** kooka.h - Main program class ------------------- begin : Sun Jan 16 2000 copyright : (C) 2000 by Klaas Freitag email : freitag@suse.de ***************************************************************************/ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ #ifndef KOOKA_H #define KOOKA_H #ifdef HAVE_CONFIG_H #include #endif #include #define KOOKA_STATE_GROUP "State" #define PREFERENCE_DIA_TAB "PreferencesTab" class KPrinter; class KToggleAction; class KActionMenu; class KookaView; /** * This class serves as the main window for Kooka. It handles the * menus, toolbars, and status bars. * * @short Main window class * @author Klaas Freitag * @version 0.1 */ class Kooka : public KParts::DockMainWindow { Q_OBJECT public: /** * Default Constructor */ Kooka(const QCString& deviceToUse); /** * Default Destructor */ ~Kooka(); /** * Startup, loads (at the moment) only the last displayed image **/ void startup( void ); protected: /** * Overridden virtuals for Qt drag 'n drop (XDND) */ virtual void dragEnterEvent(QDragEnterEvent *event); // virtual void dropEvent(QDropEvent *event); /** * This function is called when it is time for the app to save its * properties for session management purposes. */ void saveProperties(KConfig *); /** * This function is called when this app is restored. The KConfig * object points to the session management config file that was saved * with @ref saveProperties */ void readProperties(KConfig *); protected slots: void slotUpdateScannerActions(bool haveConnection); void slotUpdateRectangleActions(bool haveSelection); void slotUpdateGalleryActions(bool isDir,int howmanySelected); void slotUpdateLoadedActions(bool isLoaded,bool isDir); void slotOpenWithMenu(); private slots: void createMyGUI( KParts::Part* ); void filePrint(); /* ImageViewer-Actions */ void optionsShowScanParams(); void optionsShowPreviewer(); void optionsConfigureToolbars(); void optionsPreferences(); void changeStatusbar(const QString& text); void cleanStatusbar(void) { changeStatusbar(""); } void changeCaption(const QString& text); void newToolbarConfig(); // void fileSaveAs(); void slMirrorVertical( void ); void slMirrorHorizontal( void ); //void slMirrorBoth( void ); void slRotateClockWise( void ); void slRotateCounterClockWise( void ); void slRotate180( void ); void slEnableWarnings(); private: void setupAccel(); void setupActions(); private: KookaView *m_view; KPrinter *m_printer; KToggleAction *m_scanParamsAction; KToggleAction *m_previewerAction; KActionMenu *m_settingsShowDocks; KAction *m_saveOCRTextAction; int m_prefDialogIndex; KAction *scanAction; KAction *previewAction; + KAction *paramsAction; KAction *ocrAction; KAction *ocrSelectAction; KAction *newFromSelectionAction; KAction *scaleToWidthAction; KAction *scaleToHeightAction; KAction *scaleToOriginalAction; KAction *mirrorVerticallyAction; KAction *mirrorHorizontallyAction; KAction *rotateCwAction; KAction *rotateAcwAction; KAction *rotate180Action; KAction *createFolderAction; KAction *saveImageAction; KAction *printImageAction; KAction *importImageAction; KAction *deleteImageAction; KAction *renameImageAction; KAction *unloadImageAction; KActionMenu *openWithMenu; }; #endif // KOOKA_H diff --git a/kooka/kookapref.cpp b/kooka/kookapref.cpp index 8fc4b1b..3421098 100644 --- a/kooka/kookapref.cpp +++ b/kooka/kookapref.cpp @@ -1,514 +1,514 @@ /*************************************************************************** kookapref.cpp - Kookas preferences dialog ------------------- begin : Wed Jan 5 2000 copyright : (C) 2000 by Klaas Freitag email : ***************************************************************************/ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "devselector.h" #include "imgsaver.h" #include "thumbview.h" #include "imageselectline.h" #include "ksaneocr.h" #include "kocrgocr.h" #include "kocrocrad.h" #include "kocrkadmos.h" #include "kookapref.h" #include "kookapref.moc" KookaPref::KookaPref() : KDialogBase(KDialogBase::IconList, i18n("Preferences"), KDialogBase::Help|KDialogBase::Default|KDialogBase::Ok|KDialogBase::Apply|KDialogBase::Cancel, KDialogBase::Ok ) { // this is the base class for your preferences dialog. it is now // a Treelist dialog.. but there are a number of other // possibilities (including Tab, Swallow, and just Plain) konf = KGlobal::config (); setupGeneralPage(); setupStartupPage(); setupSaveFormatPage(); setupThumbnailPage(); setupOCRPage(); } void KookaPref::setupOCRPage() { konf->setGroup( CFG_GROUP_OCR_DIA ); QFrame *page = addPage( i18n("OCR"), i18n("Optical Character Recognition" ), BarIcon("ocr", KIcon::SizeMedium ) ); QGridLayout *lay = new QGridLayout(page,7,2,KDialog::marginHint(), KDialog::spacingHint()); lay->setRowStretch(6,9); lay->setColStretch(1,9); engineCB = new KComboBox(page); engineCB->insertItem(KSaneOcr::engineName(KSaneOcr::OcrNone),KSaneOcr::OcrNone); engineCB->insertItem(KSaneOcr::engineName(KSaneOcr::OcrGocr),KSaneOcr::OcrGocr); engineCB->insertItem(KSaneOcr::engineName(KSaneOcr::OcrOcrad),KSaneOcr::OcrOcrad); engineCB->insertItem(KSaneOcr::engineName(KSaneOcr::OcrKadmos),KSaneOcr::OcrKadmos); connect(engineCB,SIGNAL(activated(int)),SLOT(slotEngineSelected(int))); lay->addWidget(engineCB,0,1); QLabel *lab = new QLabel(i18n("OCR Engine:"),page); lab->setBuddy(engineCB); lay->addWidget(lab,0,0,Qt::AlignRight); lay->setRowSpacing(1,KDialog::marginHint()); binaryReq = new KURLRequester(page); binaryReq->setMode(KFile::File|KFile::ExistingOnly|KFile::LocalOnly); lay->addWidget(binaryReq,2,1); lab = new QLabel(i18n("Engine executable:"),page); lab->setBuddy(binaryReq); lay->addWidget(lab,2,0,Qt::AlignRight); lay->setRowSpacing(3,KDialog::marginHint()); KSeparator *sep = new KSeparator(KSeparator::HLine,page); lay->addMultiCellWidget(sep,4,4,0,1); lay->setRowSpacing(5,KDialog::marginHint()); ocrDesc = new KActiveLabel("?",page); lay->addMultiCellWidget(ocrDesc,6,6,0,1); originalEngine = static_cast(konf->readNumEntry(CFG_OCR_ENGINE2,KSaneOcr::OcrNone)); engineCB->setCurrentItem(originalEngine); slotEngineSelected(originalEngine); } void KookaPref::slotEngineSelected(int i) { selectedEngine = static_cast(i); kdDebug(29000) << k_funcinfo << "engine=" << selectedEngine << endl; QString msg; switch (selectedEngine) { case KSaneOcr::OcrNone: binaryReq->setEnabled(false); binaryReq->clear(); msg = i18n("No OCR engine is selected. Select and configure one to perform OCR."); break; case KSaneOcr::OcrGocr: binaryReq->setEnabled(true); binaryReq->setURL(tryFindGocr()); msg = KGOCRDialog::engineDesc(); break; case KSaneOcr::OcrOcrad: binaryReq->setEnabled(true); binaryReq->setURL(tryFindOcrad()); msg = ocradDialog::engineDesc(); break; case KSaneOcr::OcrKadmos: binaryReq->setEnabled(false); binaryReq->clear(); msg = KadmosDialog::engineDesc(); break; default: binaryReq->setEnabled(false); binaryReq->clear(); msg = i18n("Unknown engine %1!").arg(selectedEngine); break; } ocrDesc->setText(msg); } QString KookaPref::tryFindGocr( void ) { return( tryFindBinary( "gocr", CFG_GOCR_BINARY ) ); } QString KookaPref::tryFindOcrad( void ) { return( tryFindBinary( "ocrad", CFG_OCRAD_BINARY ) ); } QString KookaPref::tryFindBinary(const QString &bin,const QString &configKey) { KConfig *cfg = KGlobal::config(); /* First check the config files for an entry */ cfg->setGroup(CFG_GROUP_OCR_DIA); QString exe = cfg->readPathEntry(configKey); // try from config file // Why do we do the second test here? checkOCRBin() does the same, why also? if (!exe.isEmpty() && exe.contains(bin)) { QFileInfo fi(exe); // check for valid executable if (fi.exists() && fi.isExecutable() && !fi.isDir()) return (exe); } /* Otherwise find the program on the user's search path */ return (KGlobal::dirs()->findExe(bin)); // search using $PATH } bool KookaPref::checkOCRBin(const QString &cmd,const QString &bin,bool show_msg) { // Why do we do this test? See above. if (!cmd.contains(bin)) return (false); QFileInfo fi(cmd); if (!fi.exists()) { if (show_msg) KMessageBox::sorry(this,i18n("" "The path %1 does not lead to a valid binary.\n" "Please check the path and and install the program if necessary.").arg(cmd), i18n("OCR Engine Not Found")); return (false); } else { /* File exists, check if not dir and executable */ if (fi.isDir() || (!fi.isExecutable())) { if (show_msg) KMessageBox::sorry(this,i18n("" "The program %1 exists, but is not executable.\n" "Please check the path and permissions, and/or reinstall the program if necessary.").arg(cmd), i18n("OCR Engine Not Executable")); return (false); } } return (true); } void KookaPref::setupGeneralPage() { konf->setGroup( GROUP_GENERAL ); QFrame *page = addPage( i18n("General"), i18n("General Options" ), BarIcon("configure", KIcon::SizeMedium ) ); QVBoxLayout *top = new QVBoxLayout( page, 0, spacingHint() ); - top->addSpacing(KDialogBase::spacingHint()); + /* Description-Label */ + top->addWidget(new QLabel(i18n("These options will take effect when Kooka is next started."),page)); + top->addSpacing(2*KDialogBase::spacingHint()); /* Allow renaming in gallery */ cbAllowRename = new QCheckBox( i18n("Click-to-rename in gallery"), page); QToolTip::add( cbAllowRename, i18n( "Check this if you want to be able to rename gallery items by clicking on them " "(otherwise, use the \"Rename\" menu option)")); cbAllowRename->setChecked(konf->readBoolEntry(GENERAL_ALLOW_RENAME,false)); top->addWidget(cbAllowRename); top->addStretch(10); } void KookaPref::setupStartupPage() { /* startup options */ konf->setGroup( GROUP_STARTUP ); QFrame *page = addPage( i18n("Startup"), i18n("Startup Options" ), BarIcon("gear", KIcon::SizeMedium ) ); QVBoxLayout *top = new QVBoxLayout( page, 0, spacingHint() ); /* Description-Label */ - top->addWidget( new QLabel( i18n("Note that changing these options will affect Kooka's next start!"), page )); - top->addSpacing(KDialogBase::spacingHint()); + top->addWidget(new QLabel(i18n("These options will take effect when Kooka is next started."),page)); + top->addSpacing(2*KDialogBase::spacingHint()); /* Query for network scanner (Checkbox) */ cbNetQuery = new QCheckBox( i18n("Query network for available scanners"), page); QToolTip::add( cbNetQuery, i18n( "Check this if you want a network query for available scanners.\nNote that this does not mean a query over the entire network but only the stations configured for SANE!" )); cbNetQuery->setChecked( ! (konf->readBoolEntry( STARTUP_ONLY_LOCAL, false )) ); /* Show scanner selection box on startup (Checkbox) */ - cbShowScannerSelection = new QCheckBox( i18n("Show the scanner selection box on next startup"), - page); + cbShowScannerSelection = new QCheckBox( i18n("Show the scanner selection dialog"),page); QToolTip::add( cbShowScannerSelection, i18n( "Check this if you once checked 'do not show the scanner selection on startup',\nbut you want to see it again." )); cbShowScannerSelection->setChecked( !konf->readBoolEntry( STARTUP_SKIP_ASK, false )); /* Read startup image on startup (Checkbox) */ - cbReadStartupImage = new QCheckBox( i18n("Load the last image into the viewer on startup"), - page); + cbReadStartupImage = new QCheckBox( i18n("Load the last selected image into the viewer"),page); QToolTip::add( cbReadStartupImage, i18n( "Check this if you want Kooka to load the last selected image into the viewer on startup.\nIf your images are large, that might slow down Kooka's start." )); cbReadStartupImage->setChecked( konf->readBoolEntry( STARTUP_READ_IMAGE, true)); /* -- */ top->addWidget( cbNetQuery ); top->addWidget( cbShowScannerSelection ); top->addWidget( cbReadStartupImage ); top->addStretch(10); } void KookaPref::setupSaveFormatPage( ) { konf->setGroup( OP_FILE_GROUP ); - QFrame *page = addPage( i18n("Image Saving"), i18n("Configure Image Saving" ), + QFrame *page = addPage( i18n("Image Saving"), i18n("Image Saving Options" ), BarIcon("filesave", KIcon::SizeMedium ) ); QVBoxLayout *top = new QVBoxLayout( page, 0, spacingHint() ); + top->addSpacing(KDialogBase::spacingHint()); /* Skip the format asking if a format entry exists */ - cbSkipFormatAsk = new QCheckBox( i18n("Always display image save assistant"), + cbSkipFormatAsk = new QCheckBox( i18n("Always use the Save Assistant"), page); cbSkipFormatAsk->setChecked( konf->readBoolEntry( OP_FILE_ASK_FORMAT, true )); QToolTip::add( cbSkipFormatAsk, i18n("Check this if you want to see the image save assistant even if there is a default format for the image type." )); top->addWidget( cbSkipFormatAsk ); - cbFilenameAsk = new QCheckBox( i18n("Ask for filename when saving file"), + cbFilenameAsk = new QCheckBox( i18n("Ask for filename when saving"), page); cbFilenameAsk->setChecked( konf->readBoolEntry( OP_ASK_FILENAME, false)); QToolTip::add( cbFilenameAsk, i18n("Check this if you want to enter a filename when an image has been scanned." )); top->addWidget( cbFilenameAsk ); - - top->addStretch(10); } void KookaPref::setupThumbnailPage() { konf->setGroup( THUMB_GROUP ); QFrame *page = addPage( i18n("Thumbnail View"), i18n("Thumbnail Gallery View" ), BarIcon("thumbnail", KIcon::SizeMedium ) ); QVBoxLayout *top = new QVBoxLayout( page, 0, spacingHint() ); top->addWidget( new QLabel( i18n("Here you can configure the appearance of the thumbnail view of your scan picture gallery."),page )); + top->addSpacing(2*KDialogBase::spacingHint()); /* Backgroundimage */ KStandardDirs stdDir; QString bgImg = konf->readPathEntry( BG_WALLPAPER ); if( bgImg.isEmpty() ) bgImg = stdDir.findResource( "data", STD_TILE_IMG ); /* image file selector */ QVGroupBox *hgb1 = new QVGroupBox( i18n("Thumbview Background" ), page ); m_tileSelector = new ImageSelectLine( hgb1, i18n("Select background image:")); kdDebug(28000) << "Setting tile url " << bgImg << endl; m_tileSelector->setURL( KURL(bgImg) ); top->addWidget( hgb1 ); /* Add the Boxes to configure size, framestyle and background */ QVGroupBox *hgb2 = new QVGroupBox( i18n("Thumbnail Size" ), page ); QVGroupBox *hgb3 = new QVGroupBox( i18n("Thumbnail Frame" ), page ); /* Thumbnail size */ int w = konf->readNumEntry( PIXMAP_WIDTH, 100); int h = konf->readNumEntry( PIXMAP_HEIGHT, 120 ); QGrid *lGrid = new QGrid( 2, hgb2 ); lGrid->setSpacing( 2 ); QLabel *l1 = new QLabel( i18n("Thumbnail maximum &width:"), lGrid ); m_thumbWidth = new KIntNumInput( w, lGrid ); m_thumbWidth->setMinValue(1); l1->setBuddy( m_thumbWidth ); lGrid->setSpacing( 4 ); l1 = new QLabel( i18n("Thumbnail maximum &height:"), lGrid ); m_thumbHeight = new KIntNumInput( m_thumbWidth, h, lGrid ); m_thumbHeight->setMinValue(1); l1->setBuddy( m_thumbHeight ); /* Frame Stuff */ int frameWidth = konf->readNumEntry( THUMB_MARGIN, 3 ); QColor col1 = konf->readColorEntry( MARGIN_COLOR1, &(colorGroup().base())); QColor col2 = konf->readColorEntry( MARGIN_COLOR2, &(colorGroup().foreground())); QGrid *fGrid = new QGrid( 2, hgb3 ); fGrid->setSpacing( 2 ); l1 = new QLabel(i18n("Thumbnail &frame width:"), fGrid ); m_frameWidth = new KIntNumInput( frameWidth, fGrid ); m_frameWidth->setMinValue(0); l1->setBuddy( m_frameWidth ); l1 = new QLabel(i18n("Frame color &1: "), fGrid ); m_colButt1 = new KColorButton( col1, fGrid ); l1->setBuddy( m_colButt1 ); l1 = new QLabel(i18n("Frame color &2: "), fGrid ); m_colButt2 = new KColorButton( col2, fGrid ); l1->setBuddy( m_colButt2 ); /* TODO: Gradient type */ top->addWidget( hgb2, 10); top->addWidget( hgb3, 10); top->addStretch(10); } void KookaPref::slotOk( void ) { slotApply(); accept(); } void KookaPref::slotApply( void ) { /* ** general options ** */ konf->setGroup(GROUP_GENERAL); konf->writeEntry(GENERAL_ALLOW_RENAME,allowGalleryRename()); /* ** startup options ** */ /** write the global one, to read from libkscan also */ konf->setGroup(GROUP_STARTUP); bool cbVal = !(cbShowScannerSelection->isChecked()); kdDebug(28000) << "Writing for " << STARTUP_SKIP_ASK << ": " << cbVal << endl; konf->writeEntry( STARTUP_SKIP_ASK, cbVal, true, true ); /* global flag goes to kdeglobals */ /* only search for local (=non-net) scanners ? */ konf->writeEntry( STARTUP_ONLY_LOCAL, !cbNetQuery->isChecked(), true, true ); /* global */ /* Should kooka open the last displayed image in the viewer ? */ if( cbReadStartupImage ) konf->writeEntry( STARTUP_READ_IMAGE, cbReadStartupImage->isChecked()); /* ** Image saver option(s) ** */ konf->setGroup( OP_FILE_GROUP ); bool showFormatAssist = cbSkipFormatAsk->isChecked(); konf->writeEntry( OP_FILE_ASK_FORMAT, showFormatAssist ); konf->writeEntry( OP_ASK_FILENAME, cbFilenameAsk->isChecked() ); /* ** Thumbnail options ** */ konf->setGroup( THUMB_GROUP ); konf->writeEntry( PIXMAP_WIDTH, m_thumbWidth->value() ); konf->writeEntry( PIXMAP_HEIGHT, m_thumbHeight->value() ); konf->writeEntry( THUMB_MARGIN, m_frameWidth->value() ); konf->writeEntry( MARGIN_COLOR1, m_colButt1->color()); konf->writeEntry( MARGIN_COLOR2, m_colButt2->color()); KURL bgUrl = m_tileSelector->selectedURL().url(); bgUrl.setProtocol(""); kdDebug(28000) << "Writing tile-pixmap " << bgUrl.prettyURL() << endl; konf->writePathEntry( BG_WALLPAPER, bgUrl.url() ); /* ** OCR Options ** */ konf->setGroup( CFG_GROUP_OCR_DIA ); konf->writeEntry(CFG_OCR_ENGINE2,selectedEngine); if (selectedEngine!=originalEngine) { // selection of the ocr engine has changed. Popup button. KMessageBox::information(this,i18n("The OCR engine has been changed.\n" "Kooka needs to be restarted for this " "change to take effect."), i18n("OCR Engine Changed")); } QString path = binaryReq->url(); if (!path.isEmpty()) { switch (selectedEngine) { case KSaneOcr::OcrGocr: if (checkOCRBin(path,"gocr",true)) konf->writePathEntry(CFG_GOCR_BINARY,path); break; case KSaneOcr::OcrOcrad: if (checkOCRBin(path,"ocrad",true)) konf->writePathEntry(CFG_OCRAD_BINARY,path); break; default: break; } } konf->sync(); emit dataSaved(); } void KookaPref::slotDefault( void ) { cbAllowRename->setChecked(false); cbNetQuery->setChecked( true ); cbShowScannerSelection->setChecked( true); cbReadStartupImage->setChecked( true); cbSkipFormatAsk->setChecked( true ); KStandardDirs stdDir; QString bgImg = stdDir.findResource( "data", STD_TILE_IMG ); m_tileSelector->setURL( KURL(bgImg) ); m_thumbWidth->setValue( 100 ); m_thumbHeight->setValue( 120 ); QColor col1 = QColor( colorGroup().base()); QColor col2 = QColor( colorGroup().foreground()); m_frameWidth->setValue( 3 ); m_colButt1->setColor( col1 ); m_colButt2->setColor( col2 ); slotEngineSelected(KSaneOcr::OcrNone); } bool KookaPref::allowGalleryRename() { return (cbAllowRename->isChecked()); } diff --git a/kooka/kookaui.rc b/kooka/kookaui.rc index 9762f9a..8d4df54 100644 --- a/kooka/kookaui.rc +++ b/kooka/kookaui.rc @@ -1,87 +1,88 @@

Gallery S&can + + + + &Image Settings - - - Image Viewer Toolbar Scanning Toolbar diff --git a/kooka/kookaview.cpp b/kooka/kookaview.cpp index 00e8330..5062e38 100644 --- a/kooka/kookaview.cpp +++ b/kooka/kookaview.cpp @@ -1,1189 +1,1185 @@ /************************************************************************** kookaview.cpp - kookas visible stuff ------------------- begin : ? copyright : (C) 1999 by Klaas Freitag email : freitag@suse.de $Id$ ***************************************************************************/ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ -#include "kookaview.h" - -#include "resource.h" -#include "kscandevice.h" -#include "imgscaninfo.h" -#include "devselector.h" -#include "ksaneocr.h" -#include "imgsaver.h" -#include "kookapref.h" -#include "imgnamecombo.h" -#include "thumbview.h" -#include "dwmenuaction.h" -#include "kookaimage.h" -#include "kookaimagemeta.h" -#include "ocrresedit.h" -#include "kookaprint.h" -#include "imgprintdialog.h" -#if 0 -#include "paramsetdialogs.h" -#endif -#include "adddevice.h" - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include "resource.h" +#include "kscandevice.h" +#include "imgscaninfo.h" +#include "devselector.h" +#include "ksaneocr.h" +#include "imgsaver.h" +#include "kookapref.h" +#include "imgnamecombo.h" +#include "thumbview.h" +#include "dwmenuaction.h" +#include "kookaimage.h" +#include "kookaimagemeta.h" +#include "ocrresedit.h" +#include "kookaprint.h" +#include "imgprintdialog.h" +#include "adddevice.h" +#include "scanparamsdialog.h" + +#include "kookaview.h" +#include "kookaview.moc" + + #define STARTUP_IMG_SELECTION "SelectedImageOnStartup" KookaView::KookaView( KParts::DockMainWindow *parent, const QCString& deviceToUse) : QObject(), m_ocrResultImg(0), ocrFabric(0), m_mainDock(0), m_dockScanParam(0), m_dockThumbs(0), m_dockPackager(0), m_dockRecent(0), m_dockPreview(0), m_dockOCRText(0), m_mainWindow(parent), m_ocrResEdit(0) { KIconLoader *loader = KGlobal::iconLoader(); scan_params = NULL; preview_canvas = NULL; m_parent = dynamic_cast(parent); // for dialogues, etc m_mainDock = parent->createDockWidget( "Kookas MainDock", loader->loadIcon( "folder_image", KIcon::Small ), 0L, i18n("Image Viewer")); m_mainDock->setEnableDocking(KDockWidget::DockNone ); m_mainDock->setDockSite( KDockWidget::DockFullSite ); parent->setView( m_mainDock); parent->setMainDockWidget( m_mainDock); img_canvas = new ImageCanvas( m_mainDock ); img_canvas->setMinimumSize(100,200); img_canvas->enableContextMenu(true); connect( img_canvas, SIGNAL( imageReadOnly(bool)), this, SLOT(slViewerReadOnly(bool))); connect( img_canvas, SIGNAL( newRect()), this, SLOT(slSelectionChanged())); connect( img_canvas, SIGNAL( noRect()), this, SLOT(slSelectionChanged())); KPopupMenu *ctxtmenu = static_cast(img_canvas->contextMenu()); if( ctxtmenu ) ctxtmenu->insertTitle(i18n("Image View")); m_mainDock->setWidget( img_canvas ); /** Thumbview **/ m_dockThumbs = parent->createDockWidget( "Thumbs", loader->loadIcon( "thumbnail", KIcon::Small ), 0L, i18n("Thumbnails")); m_dockThumbs->setDockSite(KDockWidget::DockFullSite ); /* thumbnail viewer widget */ m_thumbview = new ThumbView( m_dockThumbs); m_dockThumbs->setWidget( m_thumbview ); m_dockThumbs->manualDock( m_mainDock, // dock target KDockWidget::DockBottom, // dock site 20 ); // relation target/this (in percent) /** Packager Dock **/ /* A new packager to contain the already scanned images */ m_dockPackager = parent->createDockWidget( "Scanpackager", loader->loadIcon( "palette_color", KIcon::Small ), 0L, i18n("Gallery")); m_dockPackager->setDockSite(KDockWidget::DockFullSite); packager = new ScanPackager( m_dockPackager ); m_dockPackager->setWidget( packager ); m_dockPackager->manualDock( m_mainDock, // dock target KDockWidget::DockLeft, // dock site 30 ); // relation target/this (in percent) connect( packager, SIGNAL(showThumbnails( KFileTreeViewItem* )), this, SLOT( slShowThumbnails( KFileTreeViewItem* ))); connect( m_thumbview, SIGNAL( selectFromThumbnail( const KURL& )), packager, SLOT( slSelectImage(const KURL&))); connect(packager,SIGNAL(selectionChanged()), this,SLOT(slotGallerySelectionChanged())); - connect(packager,SIGNAL(showImage(KookaImage *,bool)), - this,SLOT(slotLoadedImageChanged(KookaImage *,bool))); + connect(packager,SIGNAL(showImage(const KookaImage *,bool)), + this,SLOT(slotLoadedImageChanged(const KookaImage *,bool))); /* * Create a Kombobox that holds the last folders visible even on the preview page */ m_dockRecent = parent->createDockWidget( "Recent", loader->loadIcon( "image", KIcon::Small ), 0L, i18n("Gallery Folders")); m_dockRecent->setDockSite(KDockWidget::DockFullSite); QHBox *recentBox = new QHBox( m_dockRecent ); recentBox->setMargin(KDialog::marginHint()); QLabel *lab = new QLabel( i18n("Gallery:"), recentBox ); lab->setSizePolicy( QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) ); recentFolder = new ImageNameCombo( recentBox ); m_dockRecent->setWidget( recentBox ); m_dockRecent->manualDock( m_dockPackager, // dock target KDockWidget::DockBottom, // dock site 5 ); // relation target/this (in percent) connect( packager, SIGNAL( galleryPathSelected( KFileTreeBranch*, const QString&)), recentFolder, SLOT( slotGalleryPathChanged( KFileTreeBranch*, const QString& ))); connect( packager, SIGNAL( directoryToRemove( KFileTreeBranch*, const QString&)), recentFolder, SLOT( slotPathRemove( KFileTreeBranch*, const QString& ))); connect( recentFolder, SIGNAL(activated( const QString& )), packager, SLOT(slotSelectDirectory( const QString& ))); /* the object from the kscan lib to handle low level scanning */ m_dockScanParam = parent->createDockWidget( "Scan Parameter", loader->loadIcon( "folder", KIcon::Small ), 0L, i18n("Scan Parameter")); // m_dockScanParam->setDockSite(KDockWidget::DockFullSite); m_dockScanParam->setWidget( 0 ); // later sane = new KScanDevice( m_dockScanParam ); Q_CHECK_PTR(sane); if (!deviceToUse.isEmpty() && deviceToUse!="gallery") sane->addUserSpecifiedDevice(deviceToUse,"on command line",true); m_dockScanParam->manualDock( m_dockRecent, // dock target KDockWidget::DockBottom, // dock site 20 ); // relation target/this (in percent) m_dockScanParam->hide(); /* select the scan device, either user or from config, this creates and assembles * the complete scanner options dialog * scan_params must be zero for that */ m_dockPreview = parent->createDockWidget( "Preview ", loader->loadIcon( "viewmag", KIcon::Small ), 0L, i18n("Scan Preview")); preview_canvas = new Previewer( m_dockPreview ); { preview_canvas->setMinimumSize( 100,100); /* since the scan_params will be created in slSelectDevice, do the * connections later */ } m_dockPreview->setWidget( preview_canvas ); m_dockPreview->manualDock( m_mainDock, // dock target KDockWidget::DockCenter, // dock site 100 ); // relation target/this (in percent) /* Create a text editor part for ocr results */ m_dockOCRText = parent->createDockWidget( "OCRResults", loader->loadIcon("edit", KIcon::Small ), 0L, i18n("OCR Result Text")); // m_textEdit m_ocrResEdit = new ocrResEdit( m_dockOCRText ); if( m_ocrResEdit ) { m_dockOCRText->setWidget( m_ocrResEdit ); // m_textEdit->widget() ); m_dockOCRText->manualDock( m_dockThumbs, // dock target KDockWidget::DockCenter, // dock site 100 ); // relation target/this (in percent) m_ocrResEdit->setTextFormat( Qt::PlainText ); m_ocrResEdit->setWordWrap( QTextEdit::NoWrap ); // m_dockOCRText->hide(); } if( slSelectDevice(deviceToUse,false)) { /* Load from config which tab page was selected last time */ } /* New image created after scanning */ connect(sane, SIGNAL(sigNewImage(QImage*,ImgScanInfo*)), this, SLOT(slNewImageScanned(QImage*,ImgScanInfo*))); /* New preview image */ connect(sane, SIGNAL(sigNewPreview(QImage*,ImgScanInfo *)), this, SLOT( slNewPreview(QImage*,ImgScanInfo *))); connect( sane, SIGNAL( sigScanStart() ), this, SLOT( slScanStart())); connect( sane, SIGNAL( sigScanFinished(KScanStat)), this, SLOT(slScanFinished(KScanStat))); connect( sane, SIGNAL( sigAcquireStart()), this, SLOT( slAcquireStart())); /* Image canvas should show a new document */ - connect( packager, SIGNAL( showImage( KookaImage*,bool )), - this, SLOT( slShowAImage( KookaImage*))); + connect( packager, SIGNAL( showImage( const KookaImage*,bool )), + this, SLOT( slShowAImage( const KookaImage*))); connect( packager, SIGNAL( aboutToShowImage(const KURL&)), this, SLOT( slStartLoading( const KURL& ))); /* Packager unloads the image */ connect( packager, SIGNAL( unloadImage( KookaImage* )), this, SLOT( slUnloadAImage( KookaImage*))); /* a image changed mostly through a image manipulation method like rotate */ connect( packager, SIGNAL( fileChanged( KFileItem* )), m_thumbview, SLOT( slImageChanged( KFileItem* ))); connect( packager, SIGNAL( fileRenamed( KFileItem*, const KURL& )), m_thumbview, SLOT( slImageRenamed( KFileItem*, const KURL& ))); connect( packager, SIGNAL( fileDeleted( KFileItem* )), m_thumbview, SLOT( slImageDeleted( KFileItem* ))); packager->openRoots(); /* Status Bar */ KStatusBar *statBar = m_mainWindow->statusBar(); // statBar->insertItem(QString("1"), SBAR_ZOOM, 0, true ); statBar->insertItem( QString("-"), StatusImage, 0, true ); /* Set a large enough size */ int w = statBar->fontMetrics(). - width(img_canvas->imageInfoString(2000, 2000, 48)); + width(img_canvas->imageInfoString(2000, 2000, 48)+"--"); kdDebug(28000) << "Fixed size for status bar: " << w << " from string " << img_canvas->imageInfoString(2000, 2000, 48) << endl; statBar->setItemFixed( StatusImage, w ); - } KookaView::~KookaView() { saveProperties( KGlobal::config () ); delete preview_canvas; kdDebug(28000)<< "Finished saving config data" << endl; } void KookaView::slViewerReadOnly( bool ) { /* retrieve actions that could change the image */ } bool KookaView::slSelectDevice(const QCString& useDevice,bool alwaysAsk) { kdDebug(28000) << k_funcinfo << "use device [" << useDevice << "] ask=" << alwaysAsk << endl; haveConnection = false; connectedDevice = ""; bool gallery_mode = (useDevice=="gallery"); QCString selDevice; /* in case useDevice is the term 'gallery', the user does not want to * connect to a scanner, but only work in gallery mode. Otherwise, try * to read the device to use from config or from a user dialog */ if (!gallery_mode) { selDevice = useDevice; if (selDevice.isEmpty()) selDevice = userDeviceSelection(alwaysAsk); - if (selDevice.isEmpty()) return (false); // dialogue cancelled + + if (selDevice.isEmpty()) // dialogue cancelled + { + if (scan_params!=NULL) return (false); // have setup, do nothing + gallery_mode = true; + } } // Allow reopening //if( connectedDevice == selDevice ) { // kdDebug( 28000) << "Device " << selDevice << " is already selected!" << endl; //return( true ); //} if (scan_params!=NULL) slCloseScanDevice(); // remove existing GUI object scan_params = new ScanParams(m_dockScanParam); // and create a new one Q_CHECK_PTR(scan_params); if (!selDevice.isEmpty()) // connect to the selected scanner { while (!haveConnection) { kdDebug(28000) << "Opening device " << selDevice << endl; if (sane->openDevice(selDevice)!=KSCAN_OK) { QString msg = i18n("

\ There was a problem opening the scanner device. \ Check that the scanner is connected and switched on, and that SANE support for it \ is correctly configured.\

\ Trying to use scanner device: %2\
\ The error reported was: %1").arg(sane->lastErrorMessage()).arg(selDevice); if (KMessageBox::warningContinueCancel(m_parent,msg,QString::null, KGuiItem("Retry"))==KMessageBox::Cancel) { break; } } else { connect( scan_params, SIGNAL( scanResolutionChanged( int, int )), preview_canvas, SLOT( slNewScanResolutions( int, int ))); if (!scan_params->connectDevice(sane)) { // This will never happen, connectDevice always returns TRUE kdDebug(28000) << "Connecting to the scanner failed :( ->TODO" << endl; } else { haveConnection = true; connectedDevice = selDevice; /* New Rectangle selection in the preview, now scanimge exists */ ImageCanvas *previewCanvas = preview_canvas->getImageCanvas(); connect( previewCanvas , SIGNAL( newRect(QRect)), scan_params, SLOT(slCustomScanSize(QRect))); connect( previewCanvas, SIGNAL( noRect()), scan_params, SLOT(slMaximalScanSize())); // connect( scan_params, SIGNAL( scanResolutionChanged( int, int )), // preview_canvas, SLOT( slNewScanResolutions( int, int ))); if (preview_canvas!=NULL) /* load the preview image */ { preview_canvas->setPreviewImage( sane->loadPreviewImage() ); /* Call this after the device is actually open */ preview_canvas->slConnectScanner( sane ); } } } } } m_dockScanParam->setWidget(scan_params); m_dockScanParam->show(); /* Show the widget again */ if (!haveConnection) { // No devices available, or starting in gallery mode if (scan_params!=NULL) scan_params->connectDevice(NULL,gallery_mode); } emit signalScannerChanged(haveConnection); return (haveConnection); } void KookaView::slAddDevice() { kdDebug(29000) << k_funcinfo << endl; AddDeviceDialog d(m_parent,i18n("Add Scan Device")); if (d.exec()) { QString dev = d.getDevice(); QString dsc = d.getDescription(); kdDebug(29000) << k_funcinfo << "dev=[" << dev << "] desc=[" << dsc << "]" << endl; sane->addUserSpecifiedDevice(dev,dsc); } } QCString KookaView::userDeviceSelection(bool alwaysAsk) { /* Human readable scanner descriptions */ QStringList hrbackends; /* a list of backends the scan backend knows */ QStrList backends = sane->getDevices(); if (backends.count()==0) { if (KMessageBox::warningContinueCancel(m_parent,i18n("\

No scanner devices are available.\ -

If your scanner is a type that should be auto-detected by SANE, check that it \ +

If your scanner is a type that can be auto-detected by SANE, check that it \ is connected, switched on and configured correctly.\

If the scanner cannot be auto-detected by SANE (this includes some network \ scanners), you need to specify the device to use. Use the \"Add Scan Device\" \ option to enter the backend name and parameters, or see that dialogue for more \ information."),QString::null,KGuiItem(i18n("Add Scan Device...")))!=KMessageBox::Continue) return (""); slAddDevice(); backends = sane->getDevices(); // refresh the list if (backends.count()==0) return (""); // give up this time } QCString selDevice; QStrListIterator it( backends ); while( it ) { kdDebug( 28000 ) << k_funcinfo << "Found backend [" << it.current() << "] = " << sane->getScannerName(it.current()) << endl; hrbackends.append( sane->getScannerName( it.current() )); ++it; } /* allow the user to select one */ DeviceSelector ds(m_parent, backends, hrbackends ); if (!alwaysAsk) selDevice = ds.getDeviceFromConfig( ); if( selDevice.isEmpty() || selDevice.isNull() ) { kdDebug(29000) << "selDevice not found - starting selector!" << selDevice << endl; if ( ds.exec() == QDialog::Accepted ) { selDevice = ds.getSelectedDevice(); } } return( selDevice ); } QString KookaView::scannerName() const { if (connectedDevice=="") return (i18n("Gallery")); if (!haveConnection) return (i18n("No scanner connected")); return (sane->getScannerName(connectedDevice)); } void KookaView::slSelectionChanged() { kdDebug( 28000) << k_funcinfo << endl; emit signalRectangleChanged(img_canvas->selectedImage(NULL)); } void KookaView::slotGallerySelectionChanged() { KFileTreeViewItem *fti = packager->currentKFileTreeViewItem(); if (fti==NULL) { emit signalChangeStatusbar(i18n("No selection")); emit signalGallerySelectionChanged(false,0); } else { - if (fti->isDir()) emit signalChangeStatusbar(i18n("Gallery folder %1").arg(fti->url().pathOrURL())); + emit signalChangeStatusbar(i18n("Gallery %1 %2") + .arg(fti->isDir() ? i18n("folder") : i18n("image")) + .arg(fti->url().pathOrURL())); emit signalGallerySelectionChanged(fti->isDir(),packager->selectedItems().count()); } } -void KookaView::slotLoadedImageChanged(KookaImage *img,bool isDir) +void KookaView::slotLoadedImageChanged(const KookaImage *img,bool isDir) { kdDebug( 28000) << k_funcinfo << "img=" << ((void*)img) << " isDir=" << isDir << endl; if (!isDir && img==NULL) emit signalChangeStatusbar(i18n("Image unloaded")); emit signalLoadedImageChanged(img!=NULL,isDir); } bool KookaView::galleryRootSelected() const { if (packager==NULL) return (false); KFileTreeViewItem *tvi = packager->currentKFileTreeViewItem(); if (tvi==NULL) return (true); return (tvi==packager->branches().first()->root()); } void KookaView::loadStartupImage( void ) { kdDebug( 28000) << "Starting to load startup image" << endl; /* Now set the configured stuff */ KConfig *konf = KGlobal::config (); if( konf ) { konf->setGroup(GROUP_STARTUP); bool wantReadOnStart = konf->readBoolEntry( STARTUP_READ_IMAGE, true ); if( wantReadOnStart ) { QString startup = konf->readPathEntry( STARTUP_IMG_SELECTION ); if( !startup.isEmpty() ) { kdDebug(28000) << "Loading startup image !" << endl; packager->slSelectImage( KURL(startup) ); } } else { kdDebug(28000) << "Do not load startup image due to config value" << endl; } } } void KookaView::print() { /* For now, print a single file. Later, print multiple images to one page */ KookaImage *img = packager->getCurrImage(true); // load image if necessary if (img==NULL) return; KPrinter printer; // ( true, pMode ); printer.setUsePrinterResolution(true); printer.addDialogPage( new ImgPrintDialog( img )); if( printer.setup( m_mainWindow, i18n("Print %1").arg(img->localFileName().section('/', -1)) )) { KookaPrint kookaprint( &printer ); kookaprint.printImage(img); } } void KookaView::slNewPreview( QImage *new_img,ImgScanInfo *info) { if (!new_img) return; kdDebug(29000) << k_funcinfo << "new preview image, size=" << new_img->size() << " res=[" << info->getXResolution() << "x" << info->getYResolution() << "]" << endl; // flip preview to front if (!new_img->isNull()) m_dockPreview->makeDockVisible(); preview_canvas->newImage(new_img); // set new image and size } bool KookaView::ToggleVisibility( int item ) { QWidget *w = 0; bool ret = false; switch( item ) { case ID_VIEW_SCANPARAMS: w = scan_params; break; case ID_VIEW_POOL: w = preview_canvas; break; default: w = 0; } if( w ) { if( w->isVisible() ) { w->hide(); ret = false; } else { w->show(); ret = true; } } return ret; } void KookaView::doOCRonSelection( void ) { emit( signalChangeStatusbar( i18n("Starting OCR on selection" ))); KookaImage img; if( img_canvas->selectedImage(&img) ) { startOCR( &img ); } emit( signalCleanStatusbar() ); } /* Does OCR on the entire picture */ void KookaView::doOCR( void ) { emit( signalChangeStatusbar( i18n("Starting OCR on the entire image" ))); KookaImage *img = packager->getCurrImage(true); startOCR( img ); emit( signalCleanStatusbar( )); } void KookaView::startOCR(KookaImage *img) { if (img==NULL || img->isNull()) return; if (ocrFabric==NULL) { ocrFabric = new KSaneOcr( m_mainDock, KGlobal::config() ); ocrFabric->setImageCanvas( img_canvas ); connect( ocrFabric, SIGNAL( newOCRResultText( const QString& )), m_ocrResEdit, SLOT(setText( const QString& ))); connect( ocrFabric, SIGNAL( newOCRResultText( const QString& )), m_dockOCRText, SLOT( show() )); connect( ocrFabric, SIGNAL( repaintOCRResImage( )), img_canvas, SLOT(repaint())); connect( ocrFabric, SIGNAL( clearOCRResultText()), m_ocrResEdit, SLOT(clear())); connect( ocrFabric, SIGNAL( updateWord(int, const QString&, const QString& )), m_ocrResEdit, SLOT( slUpdateOCRResult( int, const QString&, const QString& ))); connect( ocrFabric, SIGNAL( ignoreWord(int, const ocrWord&)), m_ocrResEdit, SLOT( slIgnoreWrongWord( int, const ocrWord& ))); connect( ocrFabric, SIGNAL( markWordWrong(int, const ocrWord& )), m_ocrResEdit, SLOT( slMarkWordWrong( int, const ocrWord& ))); connect( ocrFabric, SIGNAL( readOnlyEditor( bool )), m_ocrResEdit, SLOT( setReadOnly( bool ))); connect( ocrFabric, SIGNAL( selectWord( int, const ocrWord& )), m_ocrResEdit, SLOT( slSelectWord( int, const ocrWord& ))); } ocrFabric->slSetImage(img); ocrFabric->startOCRVisible(m_mainDock); } void KookaView::slOCRResultImage( const QPixmap& pix ) { kdDebug(28000) << "Showing OCR Result Image" << endl; if( ! img_canvas ) return; if( m_ocrResultImg ) { img_canvas->newImage(0L); delete m_ocrResultImg; } m_ocrResultImg = new QImage(); *m_ocrResultImg = pix; img_canvas->newImage( m_ocrResultImg ); img_canvas->setReadOnly(true); // ocr result images should be read only. } void KookaView::slScanStart( ) { kdDebug(28000) << "Scan starts " << endl; if( scan_params ) { scan_params->setEnabled( false ); KLed *led = scan_params->operationLED(); if( led ) { led->setColor( Qt::red ); led->setState( KLed::On ); } } } void KookaView::slAcquireStart( ) { kdDebug(28000) << "Acquire starts " << endl; if( scan_params ) { KLed *led = scan_params->operationLED(); if( led ) { led->setColor( Qt::green ); } } } void KookaView::slNewImageScanned( QImage* img, ImgScanInfo* si ) { KookaImageMeta *meta = new KookaImageMeta; meta->setScanResolution(si->getXResolution(), si->getYResolution()); packager->slAddImage(img, meta); } void KookaView::slScanFinished( KScanStat stat ) { kdDebug(28000) << "Scan finished with status " << stat << endl; if (stat!=KSCAN_OK && stat!=KSCAN_CANCELLED) { QString msg = i18n("

\ There was a problem during preview or scanning. \
\ Check that the scanner is still connected and switched on, \ and that media is loaded if required.\

\ Trying to use scanner device: %2\
\ The error reported was: %1").arg(sane->lastErrorMessage()).arg(connectedDevice); KMessageBox::error(m_parent,msg); } if( scan_params ) { scan_params->setEnabled( true ); KLed *led = scan_params->operationLED(); if( led ) { led->setColor( Qt::green ); led->setState( KLed::Off ); } } } void KookaView::slCloseScanDevice( ) { kdDebug(28000) << "Scanner Device closes down !" << endl; if( scan_params ) { delete scan_params; scan_params = NULL; m_dockScanParam->setWidget(NULL); m_dockScanParam->hide(); } sane->slCloseDevice(); } void KookaView::slCreateNewImgFromSelection() { if( img_canvas->rootImage() ) { emit( signalChangeStatusbar( i18n("Create new image from selection" ))); QImage img; if( img_canvas->selectedImage( &img ) ) { packager->slAddImage( &img ); } emit( signalCleanStatusbar( )); } } void KookaView::slRotateImage(int angle) { KookaImage *img = packager->getCurrImage(); bool doUpdate = true; if( img ) { QImage resImg; QApplication::setOverrideCursor(waitCursor); switch( angle ) { case 90: emit( signalChangeStatusbar( i18n("Rotate image 90 degrees" ))); resImg = rotateRight( img ); break; // case 180: // emit( signalChangeStatusbar( i18n("Rotate image 180 degrees" ))); // resImg = rotate180( img ); // break; case 270: case -90: emit( signalChangeStatusbar( i18n("Rotate image -90 degrees" ))); resImg = rotateLeft( img ); break; default: kdDebug(28000) << k_funcinfo << "Angle " << angle << " not supported!" << endl; doUpdate = false; break; } QApplication::restoreOverrideCursor(); /* updateCurrImage does the status-bar cleanup */ if( doUpdate ) updateCurrImage( resImg ); else emit(signalCleanStatusbar()); } } void KookaView::slMirrorImage( MirrorType m ) { const QImage *img = img_canvas->rootImage(); bool doUpdate = true; if( img ) { QImage resImg; QApplication::setOverrideCursor(waitCursor); switch( m ) { case MirrorVertical: emit( signalChangeStatusbar( i18n("Mirroring image vertically" ))); resImg = img->mirror(); break; case MirrorHorizontal: emit( signalChangeStatusbar( i18n("Mirroring image horizontally" ))); resImg = img->mirror( true, false ); break; case MirrorBoth: emit( signalChangeStatusbar( i18n("Mirroring image in both directions" ))); resImg = img->mirror( true, true ); break; default: kdDebug(28000) << k_funcinfo << "Mirror type " << m << " not supported!" << endl; doUpdate = false; } QApplication::restoreOverrideCursor(); /* updateCurrImage does the status-bar cleanup */ if( doUpdate ) updateCurrImage( resImg ); else emit(signalCleanStatusbar()); // img_canvas->newImage( ); } } void KookaView::slSaveOCRResult() { if( ! m_ocrResEdit ) return; m_ocrResEdit->slSaveText(); } - +#if 0 void KookaView::slLoadScanParams( ) { - if( ! sane ) return; + if (sane==NULL) return; // no scanner device + kdDebug(29000) << k_funcinfo << "NYI" << endl; + #if 0 /* not yet cooked */ LoadSetDialog loadDialog( m_mainDock, sane->shortScannerName(), sane ); if( loadDialog.exec()) { kdDebug(28000)<< "Executed successfully" << endl; } #endif } +#endif -void KookaView::slSaveScanParams( ) -{ - if( !sane ) return; - /* not yet cooked */ -#if 0 - KScanOptSet optSet( "SaveSet" ); - - sane->getCurrentOptions( &optSet ); - SaveSetDialog dialog( m_mainDock /* this */ , &optSet ); - if( dialog.exec()) - { - kdDebug(28000)<< "Executed successfully" << endl; - QString name = dialog.paramSetName(); - QString desc = dialog.paramSetDescription(); - sane->slSaveScanConfigSet( name, desc ); - } -#endif +void KookaView::slScanParams() +{ + if (sane==NULL) return; // no scanner device + ScanParamsDialog d(m_mainDock,sane); + d.exec(); } -void KookaView::slShowAImage(KookaImage *img) +void KookaView::slShowAImage(const KookaImage *img) { if (img_canvas) { img_canvas->newImage(img); img_canvas->setReadOnly(false); } if (ocrFabric) ocrFabric->slSetImage(img); /* tell ocr about it */ KStatusBar *statBar = m_mainWindow->statusBar(); /* Status Bar */ if (img_canvas) statBar->changeItem(img_canvas->imageInfoString(),StatusImage); if (img) emit signalChangeStatusbar(i18n("Showing image %1").arg(img->url().pathOrURL())); } void KookaView::slUnloadAImage( KookaImage * ) { kdDebug(28000) << "Unloading Image" << endl; if( img_canvas ) { img_canvas->newImage( 0L ); } } void KookaView::slShowThumbnails(KFileTreeViewItem *dirKfi, bool forceRedraw ) { /* If no item is specified, use the current one */ if( ! dirKfi ) { /* do on the current visible dir */ KFileTreeViewItem *kftvi = packager->currentKFileTreeViewItem(); if ( !kftvi ) { return; } if( kftvi->isDir()) { dirKfi = kftvi; } else { kftvi = static_cast(static_cast(kftvi)->parent()); dirKfi = kftvi; forceRedraw = true; packager->setSelected( static_cast(dirKfi), true ); } } kdDebug(28000) << "Showing thumbs for " << dirKfi->url().prettyURL() << endl; /* Only do the new thumbview if the old is on another dir */ if( m_thumbview && (forceRedraw || m_thumbview->currentDir() != dirKfi->url()) ) { m_thumbview->clear(); /* Find a list of child KFileItems */ if( forceRedraw ) m_thumbview->readSettings(); KFileItemList fileItemsList; QListViewItem * myChild = dirKfi->firstChild(); while( myChild ) { fileItemsList.append( static_cast(myChild)->fileItem()); myChild = myChild->nextSibling(); } m_thumbview->slNewFileItems( fileItemsList ); m_thumbview->setCurrentDir( dirKfi->url()); // m_thumbview->arrangeItemsInGrid(); } } /* this slot is called when the user clicks on an image in the packager * and loading of the image starts */ void KookaView::slStartLoading( const KURL& url ) { emit( signalChangeStatusbar( i18n("Loading %1" ).arg( url.prettyURL() ) )); // if( m_stack->visibleWidget() != img_canvas ) // { // m_stack->raiseWidget( img_canvas ); // } } void KookaView::updateCurrImage( QImage& img ) { if( ! img_canvas->readOnly() ) { emit( signalChangeStatusbar( i18n("Storing image changes" ))); packager->slotCurrentImageChanged( &img ); emit( signalCleanStatusbar()); } else { emit( signalChangeStatusbar( i18n("Can not save image, it is write protected!"))); kdDebug(28000) << "Image is write protected, no saving!" << endl; } } void KookaView::saveProperties(KConfig *config) { kdDebug(28000) << "Saving Properties for KookaView !" << endl; config->setGroup( GROUP_STARTUP ); /* Get with path */ config->writePathEntry( STARTUP_IMG_SELECTION, packager->getCurrImageFileName(true)); } void KookaView::slOpenCurrInGraphApp() { if (packager==NULL) return; KFileTreeViewItem *ftvi = packager->currentKFileTreeViewItem(); if (ftvi==NULL) return; KURL::List urllist(ftvi->url()); KRun::displayOpenWithDialog(urllist); } QImage KookaView::rotateLeft( QImage *m_img ) { QImage rot; if( m_img ) { QWMatrix m; m.rotate(-90); rot = m_img->xForm(m); } return( rot ); } QImage KookaView::rotateRight( QImage *m_img ) { QImage rot; if( m_img ) { QWMatrix m; m.rotate(+90); rot = m_img->xForm(m); } return( rot ); } //QImage KookaView::rotate180( QImage *m_img ) //{ // QImage rot; // // if( m_img ) // { // QWMatrix m; // // m.rotate(+180); // rot = m_img->xForm(m); // } // return( rot ); //} void KookaView::connectViewerAction( KAction *action ) { QPopupMenu *popup = img_canvas->contextMenu(); //kdDebug(29000) << "This is the popup: " << popup << endl; if( popup && action ) { action->plug( popup ); } } void KookaView::connectGalleryAction( KAction *action ) { QPopupMenu *popup = packager->contextMenu(); if( popup && action ) { action->plug( popup ); } } void KookaView::slFreshUpThumbView() { if( m_thumbview ) { /* readSettings returns true if something changes */ if( m_thumbview->readSettings() ) { kdDebug(28000) << "Thumbview-Settings changed, readraw thumbs" << endl; /* new settings */ slShowThumbnails(0, true); } } } void KookaView::createDockMenu( KActionCollection *col, KDockMainWindow *mainWin, const char * name ) { KActionMenu *actionMenu = new KActionMenu( i18n("Tool Views"), "view_icon", col, name ); actionMenu->insert( new dwMenuAction( i18n("Show Image Viewer"), KShortcut(), m_mainDock, col, mainWin, "dock_viewer" )); actionMenu->insert( new dwMenuAction( i18n("Show Preview"), KShortcut(), m_dockPreview, col, mainWin, "dock_preview" )); actionMenu->insert( new dwMenuAction( i18n("Show Recent Gallery Folders"), KShortcut(), m_dockRecent, col, mainWin, "dock_recent" )); actionMenu->insert( new dwMenuAction( i18n("Show Gallery"), KShortcut(), m_dockPackager, col, mainWin, "dock_gallery" )); actionMenu->insert( new dwMenuAction( i18n("Show Thumbnail Window"), KShortcut(), m_dockThumbs, col, mainWin, "dock_thumbs" )); actionMenu->insert( new dwMenuAction( i18n("Show Scan Parameters"), KShortcut(), m_dockScanParam, col, mainWin, "dock_scanparams" )); actionMenu->insert( new dwMenuAction( i18n("Show OCR Results"), KShortcut(), m_dockOCRText, col, mainWin, "dock_ocrResults" )); } -#include "kookaview.moc" diff --git a/kooka/kookaview.h b/kooka/kookaview.h index c8e1bd8..655d3ce 100644 --- a/kooka/kookaview.h +++ b/kooka/kookaview.h @@ -1,261 +1,261 @@ /*************************************************************************** kookaview.h - Main view ------------------- begin : Sun Jan 16 2000 copyright : (C) 2000 by Klaas Freitag email : freitag@suse.de ***************************************************************************/ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ #ifndef KOOKAVIEW_H #define KOOKAVIEW_H #include #include #include "kookaiface.h" #include #include #include #include #include #include #include // application specific includes #include "kscandevice.h" #include "previewer.h" #include "scanpackager.h" #include "scanparams.h" #include "img_canvas.h" class KDockWidget; class QPainter; class KSaneOcr; class KConfig; class KPrinter; class KComboBox; class KAction; class KActionCollection; class ThumbView; class KookaImage; class QPixmap; class ocrResEdit; /** * This is the main view class for Kooka. Most of the non-menu, * non-toolbar, and non-statusbar (e.g., non frame) GUI code should go * here. * * @short Main view * @author Klaas Freitag * @version 0.1 */ class KookaView : public QObject { Q_OBJECT public: typedef enum { MirrorVertical, MirrorHorizontal, MirrorBoth } MirrorType; typedef enum { StatusTemp, StatusImage } StatusBarIDs; /** * Default constructor */ KookaView(KParts::DockMainWindow *parent, const QCString& deviceToUse); /** * Destructor */ virtual ~KookaView(); /** * Print this view to any medium -- paper or not */ void print( ); bool ToggleVisibility( int ); void loadStartupImage( void ); KDockWidget *mainDockWidget( ) { return m_mainDock; } void createDockMenu( KActionCollection*, KDockMainWindow *, const char *); ScanPackager *gallery() { return packager; } // KParts::Part* ocrResultPart() { return m_textEdit; } ImageCanvas *getImageViewer() { return img_canvas; } bool scannerConnected() const { return (haveConnection); } QString scannerName() const; bool galleryRootSelected() const; public slots: void slShowPreview() { } void slShowPackager() { } void slNewPreview( QImage *, ImgScanInfo * ); void slSetScanParamsVisible( bool v ) { if( v ) scan_params->show(); else scan_params->hide(); } void slSetTabWVisible( bool v ) { if( v ) preview_canvas->show(); else preview_canvas->hide(); } void doOCR( void ); void doOCRonSelection( void ); void slStartPreview() { if( scan_params ) scan_params->slAcquirePreview(); } void slStartFinalScan() { if( scan_params ) scan_params->slStartScan(); } void slCreateNewImgFromSelection( void ); void slRotateImage( int ); void slMirrorImage( MirrorType ); void slIVScaleToWidth( void ) { if( img_canvas ) img_canvas->handle_popup(ImageCanvas::ID_FIT_WIDTH );} void slIVScaleToHeight( void ) { if( img_canvas ) img_canvas->handle_popup(ImageCanvas::ID_FIT_HEIGHT );} void slIVScaleOriginal( void ) { if( img_canvas ) img_canvas->handle_popup(ImageCanvas::ID_ORIG_SIZE ); } void slIVShowZoomDialog( ) { if( img_canvas ) img_canvas->handle_popup(ImageCanvas::ID_POP_ZOOM ); } void slOpenCurrInGraphApp( void ); void slSaveOCRResult(); - void slLoadScanParams( ); - void slSaveScanParams( ); + //void slLoadScanParams( ); + void slScanParams(); void slOCRResultImage( const QPixmap& ); void slShowThumbnails( KFileTreeViewItem *dirKfi = 0, bool forceRedraw=false); void slFreshUpThumbView(); /** * slot that show the image viewer */ void slStartLoading( const KURL& url ); /** * starts ocr on the image the parameter is pointing to **/ void startOCR( KookaImage* ); void slCloseScanDevice(); void saveProperties( KConfig* ); /** * slot to select the scanner device. Does all the work with selection * of scanner, disconnection of the old device and connecting the new. */ bool slSelectDevice(const QCString& useDevice = QCString(), bool alwaysAsk = true); void slAddDevice(); void connectViewerAction( KAction *action ); void connectGalleryAction( KAction *action ); void slScanStart(); void slScanFinished( KScanStat stat ); void slAcquireStart(); protected slots: - void slShowAImage( KookaImage* ); + void slShowAImage( const KookaImage* ); void slUnloadAImage( KookaImage* ); /** * called from the scandevice if a new Image was successfully scanned. * Needs to convert the one-page-QImage to a KookaImage */ void slNewImageScanned(QImage*, ImgScanInfo*); /** * called if an viewer image was set to read only or back to read write state. */ void slViewerReadOnly( bool ro ); void slSelectionChanged(); void slotGallerySelectionChanged(); - void slotLoadedImageChanged(KookaImage *img,bool isDir); + void slotLoadedImageChanged(const KookaImage *img,bool isDir); signals: /** * Use this signal to change the content of the statusbar */ void signalChangeStatusbar(const QString& text); /** * Use this signal to clean up the statusbar */ void signalCleanStatusbar( void ); /** * Use this signal to change the content of the caption */ void signalChangeCaption(const QString& text); void signalScannerChanged(bool haveConnection); void signalRectangleChanged(bool haveSelection); void signalGallerySelectionChanged(bool isDir,int howmanySelected); void signalLoadedImageChanged(bool isLoaded,bool isDir); private: QImage rotateRight( QImage* ); QImage rotateLeft ( QImage* ); //QImage rotate180 ( QImage* ); QCString userDeviceSelection(bool alwaysAsk); void updateCurrImage( QImage& ) ; QWidget *m_parent; ImageCanvas *img_canvas; ThumbView *m_thumbview; Previewer *preview_canvas; ScanPackager *packager; ScanParams *scan_params; bool haveConnection; KScanDevice *sane; KComboBox *recentFolder; QCString connectedDevice; QImage *m_ocrResultImg; int image_pool_id; int preview_id; KSaneOcr *ocrFabric; KDockWidget *m_mainDock; KDockWidget *m_dockScanParam; KDockWidget *m_dockThumbs; KDockWidget *m_dockPackager; KDockWidget *m_dockRecent; KDockWidget *m_dockPreview; KDockWidget *m_dockOCRText; KMainWindow *m_mainWindow; ocrResEdit *m_ocrResEdit; }; #endif // KOOKAVIEW_H diff --git a/kooka/ksaneocr.cpp b/kooka/ksaneocr.cpp index 7e18590..2cafb8b 100644 --- a/kooka/ksaneocr.cpp +++ b/kooka/ksaneocr.cpp @@ -1,1502 +1,1502 @@ /*************************************************************************** ksaneocr.cpp - generic ocr ------------------- begin : Fri Jun 30 2000 copyright : (C) 2000 by Klaas Freitag email : freitag@suse.de ***************************************************************************/ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ /* $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "imgsaver.h" #include "kadmosocr.h" #include "kocrbase.h" #include "kocrkadmos.h" #include "kocrocrad.h" #include "config.h" #include "ksaneocr.h" #include "kocrgocr.h" #include "kookaimage.h" #include "kookapref.h" #include "ocrword.h" #include #include #include #include #include #include #include #include /* * Thread support is disabled here because the kadmos lib seems not to be * thread save unfortunately. See slotKadmosResult-comments for more information */ KSaneOcr::KSaneOcr( QWidget*, KConfig *cfg ): m_ocrProcessDia(0L), daemon(0L), visibleOCRRunning(false), m_resultImage(0), m_imgCanvas(0L), m_spell(0L), m_wantKSpell(true), m_kspellVisible(true), m_hideDiaWhileSpellcheck(true), m_spellInitialConfig(0L), m_parent(0L), m_ocrCurrLine(0), m_currHighlight(-1), m_applyFilter(false), m_unlinkORF(true) { KConfig *konf = KGlobal::config (); m_ocrEngine = KSaneOcr::OcrNone; m_img = 0L; m_tmpFile = 0L; if( cfg ) m_hideDiaWhileSpellcheck = cfg->readBoolEntry( HIDE_BASE_DIALOG, true ); /* * a initial config is needed as a starting point for the config dialog * but also for ocr without visible dialog. */ m_spellInitialConfig = new KSpellConfig( 0L, 0L ,0L, false ); if( konf ) { /* -- ocr dialog information -- */ konf->setGroup( CFG_GROUP_OCR_DIA ); m_ocrEngine = static_cast(konf->readNumEntry(CFG_OCR_ENGINE2,KSaneOcr::OcrNone)); kdDebug(28000) << k_funcinfo << "OCR engine is " << m_ocrEngine << " = " << engineName(m_ocrEngine) << endl; m_unlinkORF = konf->readBoolEntry( CFG_OCR_CLEANUP, true ); } /* resize m_blocks to size 1 since there is at least one block */ m_blocks.resize(1); } KSaneOcr::~KSaneOcr() { if( daemon ) { delete( daemon ); daemon = 0; } if ( m_tmpFile ) { m_tmpFile->setAutoDelete( true ); delete m_tmpFile; } if( m_resultImage ) { delete m_resultImage; m_resultImage = 0; } if( m_img ) delete m_img; if( m_spellInitialConfig ) delete m_spellInitialConfig; } /* * This slot is called to introduce a new image, usually if the user clicks on a * new image either in the gallery or on the thumbnailview. */ -void KSaneOcr::slSetImage(KookaImage *img ) +void KSaneOcr::slSetImage(const KookaImage *img ) { if( ! img ) return ; if( m_img ) delete m_img; // FIXME: copy all the image is bad. m_img = new KookaImage(*img); if( m_ocrProcessDia ) { m_ocrProcessDia->introduceImage( m_img ); } m_applyFilter = false; } /* * Request to visualise a line-box in the source image, KADMOS Engine */ void KSaneOcr::slLineBox( const QRect& ) { if( ! m_img ) return; } /* * starts visual ocr process. Depending on the ocr engine, this function creates * a new dialog, and shows it. */ bool KSaneOcr::startOCRVisible(QWidget *parent) { if (visibleOCRRunning) { KMessageBox::sorry(NULL,i18n("An OCR process is already running.")); return (false); } m_ocrProcessDia = NULL; m_parent = parent; if (m_ocrEngine == KSaneOcr::OcrGocr) { m_ocrProcessDia = new KGOCRDialog( parent, m_spellInitialConfig ); } else if (m_ocrEngine == KSaneOcr::OcrOcrad) { m_ocrProcessDia = new ocradDialog( parent, m_spellInitialConfig ); } else if (m_ocrEngine == KSaneOcr::OcrKadmos) { #ifdef HAVE_KADMOS m_ocrProcessDia = new KadmosDialog(parent, m_spellInitialConfig); #else KMessageBox::sorry(NULL,i18n("This version of Kooka is not compiled with KADMOS support.\n" "Please select another OCR engine.")); #endif /* HAVE_KADMOS */ } else if (m_ocrEngine == KSaneOcr::OcrNone) { KMessageBox::sorry(NULL,i18n("No OCR engine is configured.\n" "Please select and configure one (in the 'Configure Kooka...' dialog) to perform OCR.")); } else { kdDebug(28000) << "Unknown OCR engine " << m_ocrEngine << endl; } /* * this part is independant from the engine again */ if (m_ocrProcessDia==NULL) return (false); m_ocrProcessDia->setupGui(); m_ocrProcessDia->introduceImage(m_img); visibleOCRRunning = true; connect( m_ocrProcessDia, SIGNAL( user1Clicked()), this, SLOT( startOCRProcess() )); connect( m_ocrProcessDia, SIGNAL( closeClicked()), this, SLOT( slotClose() )); connect( m_ocrProcessDia, SIGNAL( user2Clicked()), this, SLOT( slotStopOCR() )); m_ocrProcessDia->show(); return (true); } /** * This method should be called by the engine specific finish slots. * It does the not engine dependant cleanups like re-enabling buttons etc. */ void KSaneOcr::finishedOCRVisible( bool success ) { bool doSpellcheck = m_wantKSpell; if( m_ocrProcessDia ) { m_ocrProcessDia->stopOCR(); doSpellcheck = m_ocrProcessDia->wantSpellCheck(); } if( success ) { QString goof = ocrResultText(); emit newOCRResultText(goof); if( m_imgCanvas ) { if( m_resultImage != 0 ) delete m_resultImage; kdDebug(28000) << "Result image name: " << m_ocrResultImage << endl; m_resultImage = new QImage( m_ocrResultImage, "BMP" ); kdDebug(28000) << "New result image has dimensions: " << m_resultImage->width() << "x" << m_resultImage->height()<< endl; /* The image canvas is non-zero. Set it to our image */ m_imgCanvas->newImageHoldZoom( m_resultImage ); m_imgCanvas->setReadOnly(true); /* now handle double clicks to jump to the word */ m_applyFilter=true; } /** now it is time to invoke the dictionary if required **/ emit readOnlyEditor( false ); if( doSpellcheck ) { m_ocrCurrLine = 0; /* * create a new kspell object, based on the config of the base dialog */ connect( new KSpell( m_parent, i18n("Kooka OCR Dictionary Check"), this, SLOT( slSpellReady(KSpell*)), m_ocrProcessDia->spellConfig() ), SIGNAL( death()), this, SLOT(slSpellDead())); } delete m_ocrProcessDia; m_ocrProcessDia = 0L; } visibleOCRRunning = false; cleanUpFiles(); kdDebug(28000) << "# ocr finished #" << endl; } /* * starting the spell check on line m_ocrCurrLine if the line exists. * If not, the function returns. */ void KSaneOcr::startLineSpellCheck() { if( m_ocrCurrLine < m_ocrPage.size() ) { m_checkStrings = (m_ocrPage[m_ocrCurrLine]).stringList(); /* In case the checklist is empty, call the result slot immediately */ if( m_checkStrings.count() == 0 ) { slCheckListDone(false); return; } kdDebug(28000)<< "Wordlist (size " << m_ocrPage[m_ocrCurrLine].count() << ", line " << m_ocrCurrLine << "):" << m_checkStrings.join(", ") << endl; // if( list.count() > 0 ) m_spell->checkList( &m_checkStrings, m_kspellVisible ); kdDebug(28000)<< "Started!" << endl; /** * This call ends in three slots: * 1. slMisspelling: Hit _before_ the dialog (if any) appears. Time to * mark the wrong word. * 2. slSpellCorrected: Hit if the user decided which word to use. * 3. slCheckListDone: The line is finished. The global counter needs to be * increased and this function needs to be called again. **/ } else { kdDebug(28000) << k_funcinfo <<" -- no more lines !" << endl; m_spell->cleanUp(); } } /* User Cancel is called when the user does not really start the * ocr but uses the cancel-Button to come out of the Dialog */ void KSaneOcr::slotClose() { kdDebug(28000) << "closing ocr Dialog" << endl; if( daemon && daemon->isRunning() ) { kdDebug(28000) << "Still running - Killing daemon with Sig. 9" << endl; daemon->kill(9); } finishedOCRVisible(false); } void KSaneOcr::slotStopOCR() { kdDebug(28000) << "closing ocr Dialog" << endl; if( daemon && daemon->isRunning() ) { kdDebug(28000) << "Killing daemon with Sig. 9" << endl; daemon->kill(9); // that leads to the process being destroyed. KMessageBox::error(0, i18n("The OCR-process was stopped.") ); } } void KSaneOcr::startOCRAD( ) { ocradDialog *ocrDia = static_cast(m_ocrProcessDia); m_ocrResultImage = ocrDia->orfUrl(); const QString cmd = ocrDia->getOCRCmd(); // if( m_ocrResultImage.isEmpty() ) { /* The url is empty. Start the program to fill up a temp file */ m_ocrResultImage = ImgSaver::tempSaveImage( m_img, "BMP", 8 ); // m_tmpFile->name(); kdDebug(28000) << "The new image name is <" << m_ocrResultImage << ">" << endl; } m_ocrImagePBM = ImgSaver::tempSaveImage( m_img, "PBM", 1 ); /* temporar file for orf result */ KTempFile *tmpOrf = new KTempFile( QString(), ".orf" ); tmpOrf->setAutoDelete( false ); tmpOrf->close(); m_tmpOrfName = QFile::encodeName(tmpOrf->name()); if( daemon ) { delete( daemon ); daemon = 0; } daemon = new KProcess; Q_CHECK_PTR(daemon); *daemon << cmd; *daemon << QString("-x"); *daemon << m_tmpOrfName; // the orf result file *daemon << QFile::encodeName( m_ocrImagePBM ); // The name of the image *daemon << QString("-l"); *daemon << QString::number( ocrDia->layoutDetectionMode()); KConfig *konf = KGlobal::config (); KConfigGroupSaver( konf, CFG_GROUP_OCRAD ); QString format = konf->readEntry( CFG_OCRAD_FORMAT, "utf8"); *daemon << QString("-F"); *daemon << format; QString charset = konf->readEntry( CFG_OCRAD_CHARSET, "iso-8859-15"); *daemon << QString("-c"); *daemon << charset; QString addArgs = konf->readEntry( CFG_OCRAD_EXTRA_ARGUMENTS, QString() ); if( !addArgs.isEmpty() ) { kdDebug(28000) << "Setting additional args from config for ocrad: " << addArgs << endl; *daemon << addArgs; } m_ocrResultText = ""; connect(daemon, SIGNAL(processExited(KProcess *)), this, SLOT( ocradExited(KProcess*))); connect(daemon, SIGNAL(receivedStdout(KProcess *, char*, int)), this, SLOT( ocradStdIn(KProcess*, char*, int))); connect(daemon, SIGNAL(receivedStderr(KProcess *, char*, int)), this, SLOT( ocradStdErr(KProcess*, char*, int))); if (!daemon->start(KProcess::NotifyOnExit, KProcess::All)) { kdDebug(28000) << "Error starting ocrad-daemon!" << endl; } else { kdDebug(28000) << "Start OK" << endl; } delete tmpOrf; } void KSaneOcr::ocradExited(KProcess* ) { kdDebug(28000) << "ocrad exit " << endl; QString err; bool parseRes = true; if( ! readORF(m_tmpOrfName, err) ) { KMessageBox::error( m_parent, i18n("Parsing of the OCR Result File failed:") + err, i18n("Parse Problem")); parseRes = false; } finishedOCRVisible( parseRes ); } void KSaneOcr::ocradStdErr(KProcess*, char* buffer, int buflen) { QString errorBuffer = QString::fromLocal8Bit(buffer, buflen); kdDebug(28000) << "ocrad says on stderr: " << errorBuffer << endl; } void KSaneOcr::ocradStdIn(KProcess*, char* buffer, int buflen) { QString errorBuffer = QString::fromLocal8Bit(buffer, buflen); kdDebug(28000) << "ocrad says on stdin: " << errorBuffer << endl; } /* * This slot is fired if the user clicks on the 'Start' button of the GUI, no * difference which engine is active. */ void KSaneOcr::startOCRProcess( void ) { if( ! m_ocrProcessDia ) return; /* starting the animation, setting fields disabled */ m_ocrProcessDia->startOCR(); kapp->processEvents(); if( m_ocrEngine == KSaneOcr::OcrOcrad ) { startOCRAD(); } if( m_ocrEngine == KSaneOcr::OcrGocr ) { /* * Starting a gocr process */ KGOCRDialog *gocrDia = static_cast(m_ocrProcessDia); const QString cmd = gocrDia->getOCRCmd(); /* Save the image to a temp file */ /** * Save images formats: * Black&White: PBM * Gray: PGM * Bunt: PPM */ QString format; if( m_img->depth() == 1 ) format = "PBM"; else if( m_img->isGrayscale() ) format = "PGM"; else format = "PPM"; QString tmpFile = ImgSaver::tempSaveImage( m_img, format ); // m_tmpFile->name(); kdDebug(28000) << "Starting GOCR-Command: " << cmd << " on file " << tmpFile << ", format " << format << endl; if( daemon ) { delete( daemon ); daemon = 0; } daemon = new KProcess; Q_CHECK_PTR(daemon); m_ocrResultText = ""; connect(daemon, SIGNAL(processExited(KProcess *)), this, SLOT( gocrExited(KProcess*))); connect(daemon, SIGNAL(receivedStdout(KProcess *, char*, int)), this, SLOT( gocrStdIn(KProcess*, char*, int))); connect(daemon, SIGNAL(receivedStderr(KProcess *, char*, int)), this, SLOT( gocrStdErr(KProcess*, char*, int))); QString opt; *daemon << QFile::encodeName(cmd); *daemon << "-x"; *daemon << "-"; if( !( m_img->numColors() > 0 && m_img->numColors() <3 )) /* not a bw-image */ { *daemon << "-l"; opt.setNum(gocrDia->getGraylevel()); *daemon << opt; } *daemon << "-s"; opt.setNum(gocrDia->getSpaceWidth()); *daemon << opt; *daemon << "-d"; opt.setNum(gocrDia->getDustsize()); *daemon << opt; // Write an result image *daemon << "-v"; *daemon << "32"; // Unfortunately this is fixed by gocr. m_ocrResultImage = "out30.bmp"; *daemon << QFile::encodeName(tmpFile); m_ocrCurrLine = 0; // Important in gocrStdIn to store the results if (!daemon->start(KProcess::NotifyOnExit, KProcess::All)) { kdDebug(28000) << "Error starting daemon!" << endl; } else { kdDebug(28000) << "Start OK" << endl; } } #ifdef HAVE_KADMOS if( m_ocrEngine == KADMOS ) { KadmosDialog *kadDia = static_cast(m_ocrProcessDia); kdDebug(28000) << "Starting Kadmos OCR Engine" << endl; QString clasPath; /* target where the clasPath is written in */ if( ! kadDia->getSelClassifier( clasPath ) ) { KMessageBox::error( m_parent, i18n("The classifier file necessary for OCR cannot be loaded: %1;\n" "OCR with the KADMOS engine is not possible." ). arg(clasPath), i18n("KADMOS Installation Problem")); finishedOCRVisible(false); return; } QCString c = clasPath.latin1(); kdDebug(28000) << "Using classifier " << c << endl; m_rep.Init( c ); if( m_rep.kadmosError() ) /* check if kadmos initialised OK */ { KMessageBox::error( m_parent, i18n("The KADMOS OCR system could not be started:\n") + m_rep.getErrorText()+ i18n("\nPlease check the configuration." ), i18n("KADMOS Failure") ); } else { /** Since initialising succeeded, we start the ocr here **/ m_rep.SetNoiseReduction( kadDia->getNoiseReduction() ); m_rep.SetScaling( kadDia->getAutoScale() ); kdDebug(28000) << "Image size " << m_img->width() << " x " << m_img->height() << endl; kdDebug(28000) << "Image depth " << m_img->depth() << ", colors: " << m_img->numColors() << endl; #define USE_KADMOS_FILEOP /* use a save-file for OCR instead of filling the reImage struct manually */ #ifdef USE_KADMOS_FILEOP m_tmpFile = new KTempFile( QString(), QString("bmp")); m_tmpFile->setAutoDelete( false ); m_tmpFile->close(); QString tmpFile = m_tmpFile->name(); kdDebug() << "Saving to file " << tmpFile << endl; m_img->save( tmpFile, "BMP" ); m_rep.SetImage(tmpFile); #else m_rep.SetImage(m_img); #endif // rep.Recognize(); m_rep.run(); /* Dealing with threads or no threads (using QT_THREAD_SUPPORT to distinguish) * If threads are here, the recognition task is started in its own thread. The gui thread * needs to wait until the recognition thread is finished. Therefore, a timer is fired once * that calls slotKadmosResult and checks if the recognition task is finished. If it is not, * a new one-shot-timer is fired in slotKadmosResult. If it is, the OCR result can be * processed. * In case the system has no threads, the method start of the recognition engine does not * return until it is ready, the user has to live with a non responsive gui while * recognition is performed. The start()-method is implemented as a wrapper to the run() * method of CRep, which does the recognition job. Instead of pulling up a timer, simply * the result slot is called if start()=run() has finished. In the result slot, finished() * is only a dummy always returning true to avoid more preprocessor tags here. * Hope that works ... * It does not :( That is why it is not used here. Maybe some day... */ } #ifdef QT_THREAD_SUPPORT /* start a timer and wait until it fires. */ QTimer::singleShot( 500, this, SLOT( slotKadmosResult() )); #else slotKadmosResult(); #endif } #endif /* HAVE_KADMOS */ } /* * This method is called to check if the kadmos process was already finished, if * thread support is enabled (check for preprocessor variable QT_THREAD_SUPPORT) * The problem is that the kadmos library seems not to be thread stable so thread * support should not be enabled by default. In case threads are enabled, this slot * checks if the KADMOS engine is finished already and if not it fires a timer. */ void KSaneOcr::slotKadmosResult() { #ifdef HAVE_KADMOS kdDebug(28000) << "check for Recognition finished" << endl; if( m_rep.finished() ) { /* The recognition thread is finished. */ kdDebug(28000) << "kadmos is finished." << endl; m_ocrResultText = ""; if( ! m_rep.kadmosError() ) { int lines = m_rep.GetMaxLine(); kdDebug(28000) << "Count lines: " << lines << endl; m_ocrPage.clear(); m_ocrPage.resize( lines ); for( int line = 0; line < m_rep.GetMaxLine(); line++ ) { // ocrWordList wordList = m_rep.getLineWords(line); /* call an ocr engine independent method to use the spellbook */ ocrWordList words = m_rep.getLineWords(line); kdDebug(28000) << "Have " << words.count() << " entries in list" << endl; m_ocrPage[line]=words; } /* show results of ocr */ m_rep.End(); } finishedOCRVisible( !m_rep.kadmosError() ); } else { /* recognition thread is not yet finished. Wait another half a second. */ QTimer::singleShot( 500, this, SLOT( slotKadmosResult() )); /* Never comes here if no threads exist on the system */ } #endif /* HAVE_KADMOS */ } /* * */ void KSaneOcr::gocrExited(KProcess* d) { kdDebug(28000) << "daemonExited start !" << endl; /* Now all the text of gocr is in the member m_ocrResultText. This one must * be split up now to m_ocrPage. First break up the lines, resize m_ocrPage * accordingly and than go through every line and create ocrwords for every * word. */ QStringList lines = QStringList::split( '\n', m_ocrResultText, true ); m_ocrPage.clear(); m_ocrPage.resize( lines.count() ); kdDebug(28000) << "RESULT " << m_ocrResultText << " was splitted to lines " << lines.count() << endl; unsigned int lineCnt = 0; for ( QStringList::Iterator it = lines.begin(); it != lines.end(); ++it ) { kdDebug(28000) << "Splitting up line " << *it << endl; ocrWordList ocrLine; QStringList words = QStringList::split( QRegExp( "\\s+" ), *it, false ); for ( QStringList::Iterator itWord = words.begin(); itWord != words.end(); ++itWord ) { kdDebug(28000) << "Appending to results: " << *itWord << endl; ocrLine.append( ocrWord( *itWord )); } m_ocrPage[lineCnt] = ocrLine; lineCnt++; } kdDebug(28000) << "Finished to split!" << endl; /* set the result pixmap to the result pix of gocr */ if( ! m_resPixmap.load( m_ocrResultImage ) ) { kdDebug(28000) << "Can not load result image!" << endl; } /* load the gocr result image */ if( m_img ) delete m_img; m_img = new KookaImage(); m_img->load( "out30.bmp" ); finishedOCRVisible( d->normalExit() ); } /* * A sample orf snippet: * * # Ocr Results File. Created by GNU ocrad version 0.3pre1 * total blocks 2 * block 1 0 0 560 344 * lines 5 * line 1 chars 10 height 26 * 71 109 17 26;2,'0'1,'o'0 * 93 109 15 26;2,'1'1,'l'0 * 110 109 18 26;1,'2'0 * 131 109 18 26;1,'3'0 * 151 109 19 26;1,'4'0 * 172 109 17 26;1,'5'0 * 193 109 17 26;1,'6'0 * 213 108 17 27;1,'7'0 * 232 109 18 26;1,'8'0 * 253 109 17 26;1,'9'0 * line 2 chars 14 height 27 * */ bool KSaneOcr::readORF( const QString& fileName, QString& errStr ) { QFile file( fileName ); QRegExp rx; bool error = false; /* use a global line number counter here, not the one from the orf. The orf one * starts at 0 for every block, but we want line-no counting page global here. */ unsigned int lineNo = 0; int blockCnt = 0; int currBlock = -1; /* Fetch the numeric version of ocrad */ ocradDialog *ocrDia = static_cast(m_ocrProcessDia); int ocradVersion = 0; if( ocrDia ) { ocradVersion = ocrDia->getNumVersion(); } /* clear the ocr result page */ m_ocrPage.clear(); kdDebug(28000) << "***** starting to analyse orf at " << fileName << " *****" << endl; /* some checks on the orf */ QFileInfo fi( fileName ); if( ! fi.exists() ) { error = true; errStr = i18n("The orf %1 does not exist.").arg(fileName); } if( ! error && ! fi.isReadable() ) { error = true; errStr = i18n("Permission denied on file %1.").arg(fileName); } if ( !error && file.open( IO_ReadOnly ) ) { QTextStream stream( &file ); QString line; QString recLine; // recognised line while ( !stream.atEnd() ) { line = stream.readLine().stripWhiteSpace(); // line of text excluding '\n' int len = line.length(); if( ! line.startsWith( "#" )) // Comments { kdDebug(28000) << "# Line check |" << line << "|" << endl; if( line.startsWith( "total blocks " ) ) // total count fo blocks, must be first line { blockCnt = line.right( len - 13 /* QString("total blocks ").length() */ ).toInt(); kdDebug(28000) << "Amount of blocks: " << blockCnt << endl; m_blocks.resize(blockCnt); } else if( line.startsWith( "total text blocks " )) { blockCnt = line.right( len - 18 /* QString("total text blocks ").length() */ ).toInt(); kdDebug(28000) << "Amount of blocks (V. 10): " << blockCnt << endl; m_blocks.resize(blockCnt); } else if( line.startsWith( "block ") || line.startsWith( "text block ") ) { rx.setPattern("^.*block\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"); if( rx.search( line ) > -1) { currBlock = (rx.cap(1).toInt())-1; kdDebug(28000) << "Setting current block " << currBlock << endl; QRect r( rx.cap(2).toInt(), rx.cap(3).toInt(), rx.cap(4).toInt(), rx.cap(5).toInt()); m_blocks[currBlock] = r; } else { kdDebug(28000) << "WRN: Unknown block line: " << line << endl; // Not a killing bug } } else if( line.startsWith( "lines " )) { int lineCnt = line.right( len - 6 /* QString("lines ").length() */).toInt(); m_ocrPage.resize(m_ocrPage.size()+lineCnt); kdDebug(28000) << "Resized ocrPage to linecount " << lineCnt << endl; } else if( line.startsWith( "line" )) { // line 5 chars 13 height 20 rx.setPattern("^line\\s+(\\d+)\\s+chars\\s+(\\d+)\\s+height\\s+\\d+" ); if( rx.search( line )>-1 ) { kdDebug(28000) << "RegExp-Result: " << rx.cap(1) << " : " << rx.cap(2) << endl; int charCount = rx.cap(2).toInt(); ocrWord word; QRect brect; ocrWordList ocrLine; ocrLine.setBlock(currBlock); /* Loop over all characters in the line. Every char has it's own line * defined in the orf file */ kdDebug(28000) << "Found " << charCount << " chars for line " << lineNo << endl; for( int c=0; c < charCount && !stream.atEnd(); c++ ) { /* Read one line per character */ QString charLine = stream.readLine(); int semiPos = charLine.find(';'); if( semiPos == -1 ) { kdDebug(28000) << "invalid line: " << charLine << endl; } else { QString rectStr = charLine.left( semiPos ); QString results = charLine.remove(0, semiPos+1 ); bool lineErr = false; // rectStr contains the rectangle info of for the character // results contains the real result caracter // find the amount of alternatives. int altCount = 0; int h = results.find(','); // search the first comma if( h > -1 ) { // kdDebug(28000) << "Results of count search: " << results.left(h) << endl; altCount = results.left(h).toInt(); results = results.remove( 0, h+1 ).stripWhiteSpace(); } else { lineErr = true; } // kdDebug(28000) << "Results-line after cutting the alter: " << results << endl; QChar detectedChar = UndetectedChar; if( !lineErr ) { /* take the first alternative only FIXME */ if( altCount > 0 ) detectedChar = results[1]; // kdDebug(28000) << "Found " << altCount << " alternatives for " // << QString(detectedChar) << endl; } /* Analyse the rectangle */ if( ! lineErr && detectedChar != ' ' ) { // kdDebug(28000) << "STRING: " << rectStr << "<" << endl; rx.setPattern( "(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"); if( rx.search( rectStr ) != -1 ) { /* unite the rectangles */ QRect privRect( rx.cap(1).toInt(), rx.cap(2).toInt(), rx.cap(3).toInt(), rx.cap(4).toInt() ); word.setRect( word.rect() | privRect ); } else { kdDebug(28000) << "ERR: Unable to read rect info for char!" << endl; lineErr = true; } } if( ! lineErr ) { /* store word if finished by a space */ if( detectedChar == ' ' ) { /* add the block offset to the rect of the word */ QRect r = word.rect(); if( ocradVersion < 10 ) { QRect blockRect = m_blocks[currBlock]; r.moveBy( blockRect.x(), blockRect.y()); } word.setRect( r ); ocrLine.append( word ); word = ocrWord(); } else { word.append( detectedChar ); } } } } if( !word.isEmpty() ) { /* add the block offset to the rect of the word */ QRect r = word.rect(); if( ocradVersion < 10 ) { QRect blockRect = m_blocks[currBlock]; r.moveBy( blockRect.x(), blockRect.y()); } word.setRect( r ); ocrLine.append( word ); } if( lineNo < m_ocrPage.size() ) { kdDebug(29000) << "Store result line no " << lineNo << "=\"" << ocrLine.first() << "..." << endl; m_ocrPage[lineNo] = ocrLine; lineNo++; } else { kdDebug(28000) << "ERR: line index overflow: " << lineNo << endl; } } else { kdDebug(28000) << "ERR: Unknown line found: " << line << endl; } } else { kdDebug(29000) << "Unknown line: " << line << endl; } } /* is a comment? */ } file.close(); } return !error; } void KSaneOcr::cleanUpFiles( void ) { if( m_tmpFile ) { delete m_tmpFile; m_tmpFile = 0L; } if( ! m_ocrResultImage.isEmpty()) { kdDebug(28000) << "Unlinking OCR Result image file!" << endl; unlink(QFile::encodeName(m_ocrResultImage)); m_ocrResultImage = QString(); } if( ! m_ocrImagePBM.isEmpty()) { kdDebug(28000) << "Unlinking OCR PBM file!" << endl; unlink( QFile::encodeName(m_ocrImagePBM)); m_ocrImagePBM = QString(); } if( ! m_tmpOrfName.isEmpty() ) { if( m_unlinkORF ) { unlink(QFile::encodeName(m_tmpOrfName)); m_tmpOrfName = QString(); } else { kdDebug(28000) << "Do NOT unlink temp orf file " << m_tmpOrfName << endl; } } /* Delete the debug images of gocr ;) */ unlink( "out20.bmp" ); } void KSaneOcr::gocrStdErr(KProcess*, char* buffer, int buflen) { QString errorBuffer = QString::fromLocal8Bit(buffer, buflen); kdDebug(28000) << "gocr says: " << errorBuffer << endl; } void KSaneOcr::gocrStdIn(KProcess*, char* buffer, int buflen) { QString aux = QString::fromLocal8Bit(buffer, buflen); QRegExp rx( "^\\s*\\d+\\s+\\d+"); if( rx.search( aux ) > -1 ) { /* calculate ocr progress for gocr */ int progress = rx.capturedTexts()[0].toInt(); int subProgress = rx.capturedTexts()[1].toInt(); // kdDebug(28000) << "Emitting progress: " << progress << endl; emit ocrProgress( progress, subProgress ); } else { m_ocrResultText += aux; } // kdDebug(28000) << aux << endl; } /* * Assemble the result text */ QString KSaneOcr::ocrResultText() { QString res; const QString space(" "); /* start from the back and search the original word to replace it */ QValueVector::iterator pageIt; for( pageIt = m_ocrPage.begin(); pageIt != m_ocrPage.end(); ++pageIt ) { /* thats goes over all lines */ QValueList::iterator lineIt; for( lineIt = (*pageIt).begin(); lineIt != (*pageIt).end(); ++lineIt ) { res += space + *lineIt; } res += "\n"; } kdDebug(28000) << "Returning result String " << res << endl; return res; } /* -------------------------------------------------------------------------------- * event filter to filter the mouse events to the image viewer */ void KSaneOcr::setImageCanvas( ImageCanvas *canvas ) { m_imgCanvas = canvas; m_imgCanvas->installEventFilter( this ); } bool KSaneOcr::eventFilter( QObject *object, QEvent *event ) { QWidget *w = (QWidget*) object; if( m_applyFilter && m_imgCanvas && w == m_imgCanvas ) { if( event->type() == QEvent::MouseButtonDblClick ) { QMouseEvent *mev = static_cast(event); int x = mev->x(); int y = mev->y(); int scale = m_imgCanvas->getScaleFactor(); m_imgCanvas->viewportToContents( mev->x(), mev->y(), x, y ); kdDebug(28000) << "Clicked to " << x << "/" << y << ", scale " << scale << endl; if( scale != 100 ) { // Scale is e.g. 50 that means tha the image is only half of size. // thus the clicked coords must be multiplied with 2 y = int(double(y)*100/scale); x = int(double(x)*100/scale); } /* now search the word that was clicked on */ QValueVector::iterator pageIt; int line = 0; bool valid = false; ocrWord wordToFind; for( pageIt = m_ocrPage.begin(); pageIt != m_ocrPage.end(); ++pageIt ) { QRect r = (*pageIt).wordListRect(); if( y > r.top() && y < r.bottom() ) { kdDebug(28000)<< "It is in between " << r.top() << "/" << r.bottom() << ", line " << line << endl; valid = true; break; } line++; } /* * If valid, we have the line into which the user clicked. Now we * have to find out the actual word */ if( valid ) { valid = false; /* find the word in the line and mark it */ ocrWordList words = *pageIt; ocrWordList::iterator wordIt; for( wordIt = words.begin(); wordIt != words.end() && !valid; ++wordIt ) { QRect r = (*wordIt).rect(); if( x > r.left() && x < r.right() ) { wordToFind = *wordIt; valid = true; } } } /* * if valid, the wordToFind contains the correct word now. */ if( valid ) { kdDebug(28000) << "Found the clicked word " << wordToFind << endl; emit selectWord( line, wordToFind ); } return true; } } return false; } /* -------------------------------------------------------------------------------- * Spellbook support */ /* * This slot is hit when the checkWord method of KSpell thinks a word is wrong. * KSpell detects the correction by itself and delivers it in newword here. * To see all alternatives KSpell proposes, slMissspelling must be used. */ void KSaneOcr::slSpellCorrected( const QString& originalword, const QString& newword, unsigned int pos ) { kdDebug(28000) << "Corrected: Original Word " << originalword << " was corrected to " << newword << ", pos ist " << pos << endl; kdDebug(28000) << "Dialog state is " << m_spell->dlgResult() << endl; if( slUpdateWord( m_ocrCurrLine, pos, originalword, newword ) ) { if( m_imgCanvas && m_currHighlight > -1 ) { if( m_applyFilter ) m_imgCanvas->removeHighlight( m_currHighlight ); } else { kdDebug(28000) << "No highlighting to remove!" << endl; } } } void KSaneOcr::slSpellIgnoreWord( const QString& word ) { ocrWord ignoreOCRWord; ignoreOCRWord = ocrWordFromKSpellWord( m_ocrCurrLine, word ); if( ! ignoreOCRWord.isEmpty() ) { emit ignoreWord( m_ocrCurrLine, ignoreOCRWord ); if( m_imgCanvas && m_currHighlight > -1 ) { m_imgCanvas->removeHighlight( m_currHighlight ); /* create a new highlight. That will never be removed */ QBrush brush; QPen pen( gray, 1 ); QRect r = ignoreOCRWord.rect(); r.moveBy(0,2); // a bit offset to the top if( m_applyFilter ) m_imgCanvas->highlight( r, pen, brush ); } } } ocrWord KSaneOcr::ocrWordFromKSpellWord( int line, const QString& word ) { ocrWord resWord; if( lineValid(line) ) { ocrWordList words = m_ocrPage[line]; words.findFuzzyIndex( word, resWord ); } return resWord; } bool KSaneOcr::lineValid( int line ) { bool ret = false; if( line >= 0 && (uint)line < m_ocrPage.count() ) ret = true; return ret; } void KSaneOcr::slMisspelling( const QString& originalword, const QStringList& suggestions, unsigned int pos ) { /* for the first try, use the first suggestion */ ocrWord s( suggestions.first()); kdDebug(28000) << "Misspelled: " << originalword << " at position " << pos << endl; int line = m_ocrCurrLine; m_currHighlight = -1; // ocrWord resWord = ocrWordFromKSpellWord( line, originalword ); ocrWordList words = m_ocrPage[line]; ocrWord resWord; kdDebug(28000) << "Size of wordlist (line " << line << "): " << words.count() << endl; if( pos < words.count() ) { resWord = words[pos]; } if( ! resWord.isEmpty() ) { QBrush brush; brush.setColor( QColor(red)); // , "Dense4Pattern" ); brush.setStyle( Qt::Dense4Pattern ); QPen pen( red, 2 ); QRect r = resWord.rect(); r.moveBy(0,2); // a bit offset to the top if( m_applyFilter ) m_currHighlight = m_imgCanvas->highlight( r, pen, brush, true ); kdDebug(28000) << "Position ist " << r.x() << ", " << r.y() << ", width: " << r.width() << ", height: " << r.height() << endl; /* draw a line under the word to check */ /* copy the source */ emit repaintOCRResImage(); } else { kdDebug(28000) << "Could not find the ocrword for " << originalword << endl; } emit markWordWrong( line, resWord ); } /* * This is the global starting point for spell checking of the ocr result. * After the KSpell object was created in method finishedOCRVisible, this * slot is called if the KSpell-object feels itself ready for operation. * Coming into this slot, the spelling starts in a line by line manner */ void KSaneOcr::slSpellReady( KSpell *spell ) { m_spell = spell; connect ( m_spell, SIGNAL( misspelling( const QString&, const QStringList&, unsigned int )), this, SLOT( slMisspelling(const QString& , const QStringList& , unsigned int ))); connect( m_spell, SIGNAL( corrected ( const QString&, const QString&, unsigned int )), this, SLOT( slSpellCorrected( const QString&, const QString&, unsigned int ))); connect( m_spell, SIGNAL( ignoreword( const QString& )), this, SLOT( slSpellIgnoreWord( const QString& ))); connect( m_spell, SIGNAL( done(bool)), this, SLOT(slCheckListDone(bool))); kdDebug(28000) << "Spellcheck available" << endl; if( m_ocrProcessDia && m_hideDiaWhileSpellcheck ) m_ocrProcessDia->hide(); emit readOnlyEditor( true ); startLineSpellCheck(); } /** * slot called after either the spellcheck finished or the KSpell object found * out that it does not want to run because of whatever problems came up. * If it is an KSpell-init problem, the m_spell variable is still zero and * Kooka pops up a warning. */ void KSaneOcr::slSpellDead() { if( ! m_spell ) { kdDebug(28000) << "Spellcheck NOT available" << endl; /* Spellchecking has not yet been existing, thus there is a base problem with * spellcheck on this system. */ KMessageBox::error( m_parent, i18n("Spell-checking cannot be started on this system.\n" "Please check the configuration" ), i18n("Spell-Check") ); } else { if( m_spell->status() == KSpell::Cleaning ) { kdDebug(28000) << "KSpell cleans up" << endl; } else if( m_spell->status() == KSpell::Finished ) { kdDebug(28000) << "KSpell finished" << endl; } else if( m_spell->status() == KSpell::Error ) { kdDebug(28000) << "KSpell finished with Errors" << endl; } else if( m_spell->status() == KSpell::Crashed ) { kdDebug(28000) << "KSpell Chrashed" << endl; } else { kdDebug(28000) << "KSpell finished with unknown state!" << endl; } /* save the current config */ delete m_spell; m_spell = 0L; /* reset values */ m_checkStrings.clear(); m_ocrCurrLine = 0; if( m_imgCanvas && m_currHighlight > -1 ) m_imgCanvas->removeHighlight( m_currHighlight ); } if( m_ocrProcessDia ) m_ocrProcessDia->show(); emit readOnlyEditor( false ); } /** * This slot reads the current line from the member m_ocrCurrLine and * writes the corrected wordlist to the member page word lists */ void KSaneOcr::slCheckListDone(bool) { /* * nothing needs to be updated here in the texts, because it is already done * in the slSpellCorrected slot */ /* Check the dialog state here */ if( m_spell->dlgResult() == KS_CANCEL || m_spell->dlgResult() == KS_STOP ) { /* stop processing */ m_spell->cleanUp(); } else { m_ocrCurrLine++; kdDebug(28000) << "Starting spellcheck from CheckListDone" << endl; startLineSpellCheck(); } } /** * updates the word at position spellWordIndx in line line to the new word newWord. * The original word was origWord. This slot is called from slSpellCorrected * */ bool KSaneOcr::slUpdateWord( int line, int spellWordIndx, const QString& origWord, const QString& newWord ) { bool result = false; if( lineValid( line )) { ocrWordList words = m_ocrPage[line]; kdDebug(28000) << "Updating word " << origWord << " to " << newWord << endl; if( words.updateOCRWord( words[spellWordIndx] /* origWord */, newWord ) ) // searches for the word and updates { result = true; emit updateWord( line, origWord, newWord ); } else kdDebug(28000) << "WRN: Update from " << origWord << " to " << newWord << " failed" << endl; } else { kdDebug(28000) << "WRN: Line " << line << " no not valid!" << endl; } return result; } char KSaneOcr::UndetectedChar = '_'; /* -- */ const QString KSaneOcr::engineName(OcrEngine eng) { switch (eng) { case OcrNone: return (i18n("None")); case OcrGocr: return (i18n("GOCR")); case OcrOcrad: return (i18n("OCRAD")); case OcrKadmos: return (i18n("Kadmos")); default: return (i18n("Unknown")); } } #include "ksaneocr.moc" diff --git a/kooka/ksaneocr.h b/kooka/ksaneocr.h index e2ace4e..5a8ed02 100644 --- a/kooka/ksaneocr.h +++ b/kooka/ksaneocr.h @@ -1,304 +1,304 @@ /***************************************************** -*- mode:c++; -*- *** ksaneocr.h - ocr-engine class ------------------- begin : Fri Jun 30 2000 copyright : (C) 2000 by Klaas Freitag email : freitag@suse.de ***************************************************************************/ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ #ifndef KSANEOCR_H #define KSANEOCR_H #include "config.h" #include #include "ocrword.h" #define CFG_OCR_ENGINE "ocrEngine" #define CFG_OCR_ENGINE2 "ocrEngine2" #define CFG_OCR_CLEANUP "unlinkORF" /* delete orf file? */ #define CFG_OCR_KSPELL "ocrSpellSettings" #define CFG_WANT_KSPELL "ocrKSpellEnabled" #define CFG_KS_NOROOTAFFIX "KSpell_NoRootAffix" #define CFG_KS_RUNTOGETHER "KSpell_RunTogether" #define CFG_KS_DICTIONARY "KSpell_Dictionary" #define CFG_KS_DICTFROMLIST "KSpell_DictFromList" #define CFG_KS_ENCODING "KSpell_Encoding" #define CFG_KS_CLIENT "KSpell_Client" #define HIDE_BASE_DIALOG "hideOCRDialogWhileSpellCheck" /** *@author Klaas Freitag */ class KOCRBase; class KookaImage; class KTempFile; class KProcess; class QRect; class QPixmap; class QStringList; class KSpell; class KSpellConfig; class ImageCanvas; class KConfig; #ifdef HAVE_KADMOS #include "kadmosocr.h" #endif class KSaneOcr : public QObject { Q_OBJECT public: /* * Error Classifier the report errors on bad engine setup */ enum EngineError { ENG_ERROR, ENG_OK, ENG_DATA_MISSING, ENG_BAD_SETUP }; enum OcrEngine { OcrNone, OcrGocr, OcrOcrad, OcrKadmos }; KSaneOcr(QWidget *parent, KConfig *config); ~KSaneOcr(); bool startOCRVisible(QWidget* parent = NULL); void finishedOCRVisible(bool); /** * checks after a ocr run if the line number exists in the result */ bool lineValid( int line ); #ifdef HAVE_KADMOS bool startKadmosOCR(); #endif /** * return the final ocr result */ QString ocrResultText(); /** * @return the current spell config. */ KSpellConfig* ocrSpellConfig() const { return m_spellInitialConfig; } /** * Sets an image Canvas that displays the result image of ocr. If this * is set to zero (or never set) no result image is displayed. * The ocr fabric passes a new image to the canvas which is a copy of * the image to ocr. */ void setImageCanvas( ImageCanvas* canvas ); static const QString engineName(OcrEngine eng); signals: void newOCRResultText( const QString& ); void clearOCRResultText(); void newOCRResultPixmap( const QPixmap& ); /** * progress of the ocr process. The first integer is the main progress, * the second the sub progress. If there is only on progress, it is the * first parameter, the second is always -1 than. * Both have a range from 0..100. * Note that this signal may not be emitted if the engine does not support * progress. */ void ocrProgress(int, int); /** * select a word in the editor in line line. */ void selectWord( int line, const ocrWord& word ); /** * signal to indicate that a ocr text must be updated due to better results * retrieved from spell check. The internal ocr data structure is already * updated when this signal is fired. * * @param line the line in which the word must be changed (start at 0) * @param wordFrom the original word * @param wordTo the new word(s). */ void updateWord( int line, const QString& wordFrom, const QString& wordTo ); /** * signal to indicate that word word was ignored by the user. This should result * in a special coloring in the editor. */ void ignoreWord( int, const ocrWord& ); /** * signal that comes if a word is considered to be wrong in the editor. * The word should be marked in any way, e.g. with a signal color. **/ void markWordWrong( int, const ocrWord& ); /** * signal the tells that the result image was modified. */ void repaintOCRResImage( ); /** * indicates that the text editor holding the text that came through * newOCRResultText should be set to readonly or not. Can be connected * to QTextEdit::setReadOnly directly. */ void readOnlyEditor( bool ); public slots: - void slSetImage( KookaImage* ); + void slSetImage( const KookaImage* ); void slLineBox( const QRect& ); protected: /** * Start spell checking on a specific line that is stored in m_ocrCurrLine. * This method starts the spell checking. **/ void startLineSpellCheck(); ocrWord ocrWordFromKSpellWord( int line, const QString& word ); /** * Eventhandler to handle the mouse events to the image viewer showing the * ocr result image */ bool eventFilter( QObject *object, QEvent *event ); void startOCRAD(); protected slots: void slotClose (); void slotStopOCR(); void slSpellReady( KSpell* ); void slSpellDead( ); /** * a new list of ocr results of the current ocr process arrived and is available * in the member m_ocrPage[line] */ // void gotOCRLine( int line ); void slMisspelling( const QString& originalword, const QStringList& suggestions, unsigned int pos ); void slSpellCorrected( const QString& originalword, const QString& newword, unsigned int pos ); void slSpellIgnoreWord( const QString& word ); void slCheckListDone( bool ); bool slUpdateWord( int line, int spellWordIndx, const QString& origWord, const QString& newWord ); private slots: void slotKadmosResult(); void startOCRProcess( void ); void gocrStdIn(KProcess*, char* buffer, int buflen); void gocrStdErr(KProcess*, char* buffer, int buflen); void gocrExited(KProcess*); void ocradStdIn(KProcess*, char* buffer, int buflen); void ocradStdErr(KProcess*, char* buffer, int buflen); void ocradExited(KProcess*); /* * reads orf files from a file and fills the result structures * accordingly. */ bool readORF( const QString&, QString& ); private: void cleanUpFiles( void ); KOCRBase *m_ocrProcessDia; KProcess *daemon; bool visibleOCRRunning; KTempFile *m_tmpFile; KookaImage *m_img; QString m_ocrResultText; QString m_ocrResultImage; QString m_ocrImagePBM; QString m_tmpOrfName; QImage *m_resultImage; OcrEngine m_ocrEngine; QPixmap m_resPixmap; QPixmap m_storePixmap; ImageCanvas *m_imgCanvas; KSpell *m_spell; bool m_wantKSpell; bool m_kspellVisible; bool m_hideDiaWhileSpellcheck; KSpellConfig *m_spellInitialConfig; /* ValueVector of wordLists for every line of ocr results */ ocrBlock m_ocrPage; /* one block contains all lines of the page */ QWidget *m_parent; /* current processed line to speed kspell correction */ unsigned m_ocrCurrLine; QStringList m_checkStrings; int m_currHighlight; bool m_applyFilter; bool m_unlinkORF; rectList m_blocks; // dimensions of blocks static char UndetectedChar; #ifdef HAVE_KADMOS Kadmos::CRep m_rep; #endif }; #endif diff --git a/kooka/newscanparams.cpp b/kooka/newscanparams.cpp new file mode 100644 index 0000000..8bd9bf8 --- /dev/null +++ b/kooka/newscanparams.cpp @@ -0,0 +1,87 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Jonathan Marten + + 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 +#include +#include + +#include +#include +#include + +#include "newscanparams.h" +#include "newscanparams.moc" + + +NewScanParams::NewScanParams(QWidget *parent, + const QString &name,const QString &desc,bool renaming) + : KDialogBase(parent,NULL,true,QString::null, + KDialogBase::Ok|KDialogBase::Cancel,KDialogBase::Ok) +{ + enableButtonSeparator(true); + + QVBox *vb = makeVBoxMainWidget(); + vb->setMargin(KDialogBase::marginHint()); + vb->setSpacing(KDialogBase::spacingHint()); + + if (renaming) + { + setCaption(i18n("Edit Scan Parameters")); + new QLabel(i18n("Change the name and/or description of the scan parameter set."),vb); + } + else + { + setCaption(i18n("Save Scan Parameters")); + new QLabel(i18n("Enter a name and description for the new scan parameter set."),vb); + } + new QLabel("",vb); + + QLabel *l = new QLabel(i18n("Set name:"),vb); + mNameEdit = new QLineEdit(name,vb); + connect(mNameEdit,SIGNAL(textChanged(const QString &)),SLOT(slotTextChanged())); + l->setBuddy(mNameEdit); + + l = new QLabel(i18n("Description:"),vb); + mDescEdit = new QLineEdit(desc,vb); + connect(mDescEdit,SIGNAL(textChanged(const QString &)),SLOT(slotTextChanged())); + l->setBuddy(mDescEdit); + + slotTextChanged(); + mNameEdit->setFocus(); +} + + +void NewScanParams::slotTextChanged() +{ + bool ok = !mNameEdit->text().stripWhiteSpace().isEmpty() && + !mDescEdit->text().stripWhiteSpace().isEmpty(); + enableButtonOK(ok); +} + + +QString NewScanParams::getName() const +{ + return (mNameEdit->text()); +} + + +QString NewScanParams::getDescription() const +{ + return (mDescEdit->text()); +} diff --git a/kooka/newscanparams.h b/kooka/newscanparams.h new file mode 100644 index 0000000..1f05658 --- /dev/null +++ b/kooka/newscanparams.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE Project -*- mode:c++ -*- + Copyright (C) 2008 Jonathan Marten + + 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. +*/ + +#ifndef NEWSCANPARAMS_H +#define NEWSCANPARAMS_H + +#include + +class QLineEdit; + +/** + * A dialogue to allow the user to enter a name and description for + * a set of saved scan parameters. + */ + +class NewScanParams : public KDialogBase +{ + Q_OBJECT + +public: + NewScanParams(QWidget *parent,const QString &name,const QString &desc,bool renaming); + + QString getName() const; + QString getDescription() const; + +protected slots: + void slotTextChanged(); + +private: + QLineEdit *mNameEdit; + QLineEdit *mDescEdit; +}; + +#endif // NEWSCANPARAMS_H diff --git a/kooka/scanpackager.cpp b/kooka/scanpackager.cpp index 1d16f35..2f8b674 100644 --- a/kooka/scanpackager.cpp +++ b/kooka/scanpackager.cpp @@ -1,1392 +1,1380 @@ /*************************************************************************** scanpackager.cpp - description ------------------- begin : Fri Dec 17 1999 copyright : (C) 1999 by Klaas Freitag email : freitag@suse.de $Id$ ***************************************************************************/ /*************************************************************************** * * * This file may be distributed and/or modified under the terms of the * * GNU General Public License version 2 as published by the Free Software * * Foundation and appearing in the file COPYING included in the * * packaging of this file. * * * As a special exception, permission is given to link this program * * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * * Kreuzlingen and distribute the resulting executable without * * including the source code for KADMOS in the source distribution. * * * As a special exception, permission is given to link this program * * with any edition of Qt, and distribute the resulting executable, * * without including the source code for Qt in the source distribution. * * * ***************************************************************************/ #include "resource.h" #include "imgsaver.h" #include "kookaimage.h" #include "kookaimagemeta.h" #include "previewer.h" #include "devselector.h" #include "kookapref.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scanpackager.h" #include "scanpackager.moc" //#define STARTUP_FIRST_START "firstStart" /* ----------------------------------------------------------------------- */ /* Constructor Scan Packager */ ScanPackager::ScanPackager( QWidget *parent ) : KFileTreeView( parent ) { - setItemsRenameable(false); + setItemsRenameable(false); // set later from config setDefaultRenameAction( QListView::Reject ); + //header()->setStretchEnabled(true,0); // do we like this effect? + addColumn( i18n("Image Name" )); setColumnAlignment( 0, AlignLeft ); setRenameable ( 0, true ); addColumn( i18n("Size") ); setColumnAlignment( 1, AlignRight ); setRenameable ( 1, false ); addColumn( i18n("Format" )); setColumnAlignment( 2, AlignRight ); setRenameable ( 2, false ); /* Drag and Drop */ setDragEnabled( true ); setDropVisualizer(true); setAcceptDrops(true); connect( this, SIGNAL(dropped( QWidget*, QDropEvent*, KURL::List&, KURL& )), this, SLOT( slotUrlsDropped( QWidget*, QDropEvent*, KURL::List&, KURL& ))); KConfig *konf = KGlobal::config(); konf->setGroup(GROUP_GENERAL); setAllowRename(konf->readBoolEntry(GENERAL_ALLOW_RENAME,false)); setRootIsDecorated( false ); connect( this, SIGNAL( clicked( QListViewItem*)), SLOT( slClicked(QListViewItem*))); connect( this, SIGNAL( rightButtonPressed( QListViewItem *, const QPoint &, int )), SLOT( slShowContextMenu(QListViewItem *,const QPoint &))); connect( this, SIGNAL(itemRenamed (QListViewItem*, const QString &, int ) ), this, - SLOT(slFileRename( QListViewItem*, const QString&, int))); + SLOT(slFileRename(QListViewItem*,const QString&))); connect(this,SIGNAL(expanded(QListViewItem *)),SLOT(slotItemExpanded(QListViewItem *))); img_counter = 1; /* Set the current export dir to home */ m_currCopyDir = QDir::home().absPath(); m_currImportDir = m_currCopyDir; /* Preload frequently used icons */ KIconLoader *loader = KGlobal::iconLoader(); m_floppyPixmap = loader->loadIcon( "3floppy_unmount", KIcon::Small ); m_grayPixmap = loader->loadIcon( "palette_gray", KIcon::Small ); m_bwPixmap = loader->loadIcon( "palette_lineart", KIcon::Small ); m_colorPixmap = loader->loadIcon( "palette_color", KIcon::Small ); m_startup = true; /* create a context menu and set the title */ m_contextMenu = new KPopupMenu(); static_cast(m_contextMenu)->insertTitle( i18n( "Gallery" )); openWithMapper = NULL; } void ScanPackager::openRoots() { /* standard root always exists, ImgRoot creates it */ KURL rootUrl(Previewer::galleryRoot()); kdDebug(28000) << "Open standard root " << rootUrl.url() << endl; openRoot( rootUrl, true ); m_defaultBranch->setOpen(true); /* open more configurable image repositories TODO */ } KFileTreeBranch* ScanPackager::openRoot( const KURL& root, bool ) { KIconLoader *loader = KGlobal::iconLoader(); /* working on the global branch. FIXME */ m_defaultBranch = addBranch( root, i18n("Kooka Gallery"), loader->loadIcon( "folder_image", KIcon::Small ), false /* do not showHidden */ ); // Q_CHECK_PTR( m_defaultBranch ); m_defaultBranch->setOpenPixmap( loader->loadIcon( "folder_blue_open", KIcon::Small )); setDirOnlyMode( m_defaultBranch, false ); m_defaultBranch->setShowExtensions( true ); // false ); connect( m_defaultBranch, SIGNAL( newTreeViewItems( KFileTreeBranch*, const KFileTreeViewItemList& )), this, SLOT( slotDecorate(KFileTreeBranch*, const KFileTreeViewItemList& ))); connect( m_defaultBranch, SIGNAL( directoryChildCount( KFileTreeViewItem* , int )), this, SLOT( slotDirCount( KFileTreeViewItem *, int ))); connect( m_defaultBranch, SIGNAL( deleteItem( KFileItem* )), this, SLOT( slotDeleteFromBranch(KFileItem*))); connect( m_defaultBranch, SIGNAL( populateFinished( KFileTreeViewItem * )), this, SLOT( slotStartupFinished( KFileTreeViewItem * ))); return( m_defaultBranch ); } void ScanPackager::slotStartupFinished( KFileTreeViewItem *it ) { if( m_startup && (it == m_defaultBranch->root()) ) { kdDebug(28000) << "Slot population finished hit!" << endl; /* If nothing is selected, select the root. */ if( ! currentKFileTreeViewItem() ) { (m_defaultBranch->root())->setSelected( true ); } m_startup = false; } } void ScanPackager::slotDirCount(KFileTreeViewItem* item,int cnt) { if (item==NULL) return; if (!item->isDir()) return; int imgCount = 0; // total these separately, int dirCount = 0; // we want individual counts // for files and subfolders const QListViewItem *ch = item->firstChild(); while (ch!=NULL) { KFileTreeViewItem *ci = static_cast(ch); if (ci->isDir()) ++dirCount; else ++imgCount; ch = ch->nextSibling(); } QString cc = ""; if (dirCount==0) { if (imgCount==0) cc = i18n("empty"); else cc = i18n("one image","%n images",imgCount); } else { if (imgCount>0) cc = i18n("one image, ","%n images, ",imgCount); cc += i18n("1 folder","%n folders",dirCount); } item->setText(1,cc); } void ScanPackager::slotItemExpanded(QListViewItem *item) { if (item==NULL) return; KFileTreeViewItem *it = static_cast(item); if (!it->isDir()) return; const QListViewItem *ch = item->firstChild(); while (ch!=NULL) { KFileTreeViewItem *ci = static_cast(ch); if (ci->isDir() && !ci->alreadyListed()) ci->branch()->populate(ci->url(),ci); ch = ch->nextSibling(); } } void ScanPackager::slotDecorate( KFileTreeViewItem* item ) { if( !item ) return; if( item->isDir()) { // done in extra slot. //kdDebug(28000) << "Decorating directory!" << endl; } else { KFileItem *kfi = item->fileItem(); KookaImage *img = 0L; if( kfi ) { img = static_cast(kfi->extraData( this )); } if( img ) { /* The image appears to be loaded to memory. */ if( img->depth() == 1 ) { /* a bw-image */ item->setPixmap( 0, m_bwPixmap ); } else { if( img->isGrayscale() ) { item->setPixmap( 0, m_grayPixmap ); } else { item->setPixmap( 0, m_colorPixmap ); } } /* set image size in pixels */ QString t = i18n( "%1 x %2" ).arg( img->width()).arg(img->height()); item->setText( 1, t ); kdDebug( 28000) << "Image loaded and decorated!" << endl; } else { /* Item is not yet loaded. Display file information */ item->setPixmap( 0, m_floppyPixmap ); if ( kfi ) { item->setText(1, KIO::convertSize( kfi->size() )); } } /* Image format */ QString format = getImgFormat( item ); item->setText( 2, format ); } // This code is quite similar to m_nextUrlToSelect in KFileTreeView::slotNewTreeViewItems // When scanning a new image, we wait for the KDirLister to notice the new file, // and then we have the KFileTreeViewItem that we need to display the image. if ( ! m_nextUrlToShow.isEmpty() ) { if( m_nextUrlToShow.equals(item->url(), true )) { m_nextUrlToShow = KURL(); // do this first to prevent recursion slClicked( item ); setCurrentItem(item); // neccessary in case of new file from D&D } } } void ScanPackager::slotDecorate( KFileTreeBranch* branch, const KFileTreeViewItemList& list ) { (void) branch; //kdDebug(28000) << "decorating slot for list !" << endl; KFileTreeViewItemListIterator it( list ); bool end = false; for( ; !end && it.current(); ++it ) { KFileTreeViewItem *kftvi = *it; slotDecorate( kftvi ); emit fileChanged( kftvi->fileItem() ); } } - -void ScanPackager::slFileRename( QListViewItem* it, const QString& newStr, int ) +void ScanPackager::slFileRename(QListViewItem *it,const QString &newName) { + if (it==NULL) return; + KFileTreeViewItem *item = static_cast(it); - bool success = true; - if( !it ) return; - - if( newStr.isEmpty() ) - success = false; - - KFileTreeViewItem *item = static_cast(it); + bool success = (!newName.isEmpty()); - /* Free memory and imform everybody who is interested. */ - KURL urlFrom = item->url(); - KURL urlTo( urlFrom ); + KURL urlFrom = item->url(); + KURL urlTo(urlFrom); + urlTo.setFileName(""); /* clean filename, apply new name */ + urlTo.setFileName(newName); - /* clean filename and apply new name */ - urlTo.setFileName(""); - urlTo.setFileName(newStr); - - if( success ) - { - if( urlFrom == urlTo ) - { - kdDebug(28000) << "Renaming to same url does not make sense!" << endl; - success = false; - } - else - { + if (success) + { + if (urlFrom==urlTo) + { + kdDebug(28000) << "Renaming to same!" << endl; + success = false; + } + else + { /* clear selection, because the renamed image comes in through * kdirlister again */ - slotUnloadItem( item ); - - kdDebug(28000) << "Renaming to " << urlTo.prettyURL() << - " from " << urlFrom.prettyURL() << endl; - - /* to urlTo the really used filename is written */ - setSelected( item, false ); - - if( ImgSaver::renameImage( urlFrom, urlTo, false, this ) ) - { - kdDebug(28000) << "renaming OK" << endl; - emit fileRenamed( item->fileItem(), urlTo ); - success=true; - } - else - { - success = false; - } - } - } - - if( !success ) - { - kdDebug(28000) << "renaming failed" << endl; - /* restore the name */ - item->setText(0, urlFrom.fileName() ); - setSelected( item, true ); + // slotUnloadItem(item); // unnecessary, bug 68532 + // because of "note new URL" below + kdDebug(28000) << k_funcinfo + << "Renaming " << urlFrom.prettyURL() + << " -> " << urlTo.prettyURL() << endl; + + //setSelected(item,false); + + success = ImgSaver::renameImage(urlFrom,urlTo,false,this); + if (success) // rename the file + { + item->fileItem()->setURL(urlTo); // note new URL + emit fileRenamed(item->fileItem(),urlTo); + } + } + } - } + if (!success) + { + kdDebug(28000) << "renaming failed" << endl; + item->setText(0,urlFrom.fileName()); // restore the name + } +// setSelected(item,true); // restore the selection } /* ----------------------------------------------------------------------- */ /* * Method that checks if the new filename a user enters while renaming an image is valid. * It checks for a proper extension. */ QString ScanPackager::buildNewFilename( QString cmplFilename, QString currFormat ) const { /* cmplFilename = new name the user wishes. * currFormat = the current format of the image. * if the new filename has a valid extension, which is the same as the * format of the current, fine. A ''-String has to be returned. */ QFileInfo fiNew( cmplFilename ); QString base = fiNew.baseName(); QString newExt = fiNew.extension( false ).lower(); QString nowExt = currFormat.lower(); QString ext = ""; kdDebug(28000) << "Filename wanted: "<< cmplFilename << " <"< <" << nowExt<<">" < return the currFormat-Extension */ ext = base + "." + currFormat; } else if( newExt == nowExt ) { /* also good, no reason to put another extension */ ext = cmplFilename; } else { /* new Ext. differs from the current extension. Later. */ KMessageBox::sorry( 0L, i18n( "You entered a file extension that differs from the existing one. That is not yet possible. Converting 'on the fly' is planned for a future release.\n" "Kooka corrects the extension."), i18n("On the Fly Conversion")); ext = base + "." + currFormat; } return( ext ); } /* ----------------------------------------------------------------------- */ /* This method returns the directory of an image or directory. */ QString ScanPackager::itemDirectory( const KFileTreeViewItem* item, bool relativ ) const { if( ! item ) { kdDebug(28000) << "ERR: itemDirectory without item" << endl; return QString::null; } QString relativUrl= (item->url()).prettyURL(); if( ! item->isDir() ) { // Cut off the filename in case it is not a dir relativUrl.truncate( relativUrl.findRev( '/' )+1); } else { /* add a "/" to the directory if not there */ if( ! relativUrl.endsWith( "/" ) ) relativUrl.append( "/" ); } if( relativ ) { KFileTreeBranch *branch = item->branch(); if( branch ) { kdDebug(28000) << "Relativ URL of the file " << relativUrl << endl; QString rootUrl = (branch->rootUrl()).prettyURL(); // directory of branch root if( relativUrl.startsWith( rootUrl )) { relativUrl.remove( 0, rootUrl.length() ); if( relativUrl.isEmpty() ) relativUrl = "/"; // The root } else { kdDebug(28000) << "ERR: Item-URL does not start with root url " << rootUrl << endl; } } } return( relativUrl ); } /* ----------------------------------------------------------------------- */ /* This slot receives a string from the gallery-path combobox shown under the * image gallery. The form of the string coming in here is - < * relativ directory under the branch. Now it is to assemble a complete path * from the data, find out which KFileTreeViewItem is associated with it and * call slClicked with it. */ void ScanPackager::slotSelectDirectory( const QString & dirString ) { kdDebug(28000) << "Trying to decode directory string " << dirString << endl; QString searchFor = QString::fromLatin1(" - "); int pos = dirString.find( searchFor ); if( pos > -1 ) { /* Splitting up the string coming in */ QString branchName = dirString.left( pos ); QString relPath( dirString ); relPath = relPath.remove( 0, pos + searchFor.length()); kdDebug(28000) << "Splitted up to branch <" << branchName << "> and <" << relPath << endl; KFileTreeViewItem *kfi = findItem( branchName, relPath ); if( kfi ) { kdDebug(28000) << "got a new item to select !" << endl; ensureItemVisible(kfi); setCurrentItem(kfi); slClicked(kfi); // load thumbnails for this dir etc. } } } /* ----------------------------------------------------------------------- */ /* This slot is called when clicking on an item. */ void ScanPackager::slClicked( QListViewItem *newItem ) { KFileTreeViewItem *item = static_cast(newItem); if( item ) // can be 0, when clicking where no item is present { kdDebug(28000) << "Clicked - newItem !" << endl; /* Check if directory, hide image for now, later show a thumb view */ if( item->isDir()) { kdDebug(28000) << "clicked: Is a directory !" << endl; emit showImage(NULL,true); kdDebug(28000) << "emitting showThumbnails" << endl; } else { /* if not a dir, load the image if necessary. This is done by loadImageForItem, * which is async( TODO ). The image finally arrives in slotImageArrived */ QApplication::setOverrideCursor(waitCursor); emit( aboutToShowImage( item->url())); loadImageForItem( item ); QApplication::restoreOverrideCursor(); } /* emit a signal indicating the new directory if there is a new one */ QString wholeDir = itemDirectory( item, false ); /* not relativ to root */ if( currSelectedDir != wholeDir ) { currSelectedDir = wholeDir; QString relativUrl = itemDirectory( item, true ); kdDebug(28000) << "Emitting " << relativUrl << " as new relative Url" << endl; /* Emit the signal with branch and the relative path */ emit( galleryPathSelected( item->branch(), relativUrl )); if( item->isDir() ) { emit( showThumbnails( item )); } else { emit( showThumbnails( static_cast(item->parent()))); } } else { // kdDebug(28000) << "directory is not new: " << currSelectedDir << endl; } } } void ScanPackager::loadImageForItem( KFileTreeViewItem *item ) { if( ! item ) return; bool result = true; KFileItem *kfi = item->fileItem(); if( ! kfi ) return; KookaImage *img = static_cast( kfi->extraData(this)); if( img ) { kdDebug(28000) << "Image already loaded." << endl; /* result is still true, image must be shown. */ } else { /* The image needs to be loaded. Possibly it is a multi-page image. * If it is, the kookaImage has a subImageCount larger than one. We * create an subimage-item for every subimage, but do not yet load * them. */ KURL url = item->url(); img = new KookaImage( ); if( !img || !img->loadFromUrl( url ) ) { kdDebug(28000) << "Loading KookaImage from File failed!" << endl; result = false; } else { /* store the fileitem */ img->setFileItem( kfi ); /* care for subimages, create items for them */ kdDebug(28000) << "subImage-count: " << img->subImagesCount() << endl; if( img->subImagesCount() > 1 ) { KIconLoader *loader = KGlobal::iconLoader(); kdDebug(28000) << "SubImages existing!" << endl; /* Start at the image with index 1, that makes one less than are actually in the * image. But image 0 was already created above. */ KFileTreeViewItem *prevItem=0; for( int i = 1; i < img->subImagesCount(); i++ ) { kdDebug(28000) << "Creating subimage no " << i << endl; KFileItem *newKfi = new KFileItem( *kfi ); KFileTreeViewItem *subImgItem = new KFileTreeViewItem( item, newKfi, item->branch()); if( prevItem ) { subImgItem->moveItem( prevItem ); } prevItem = subImgItem; subImgItem->setPixmap( 0, loader->loadIcon( "editcopy", KIcon::Small )); subImgItem->setText( 0, i18n("Sub-image %1").arg( i ) ); KookaImage *subImgImg = new KookaImage( i, img ); subImgImg->setFileItem( newKfi ); newKfi->setExtraData( (void*) this, (void*) subImgImg ); } } } } if( result && img ) { if( img->isSubImage() ) { kdDebug(28000) << "it _is_ a subimage" << endl; /* load if not loaded */ if( img->isNull()) { kdDebug(28000) << "extracting subimage" << endl; img->extractNow(); } else { kdDebug(28000) << "Is not a null image" << endl; } } slImageArrived( item, img ); } } /* Hit this slot with a file for a kfiletreeviewitem. */ void ScanPackager::slImageArrived( KFileTreeViewItem *item, KookaImage* image ) { if( item && image ) { /* Associate the image for the Scanpackager-Object. */ KFileItem *kfi = item->fileItem(); if( kfi ) { kfi->setExtraData( (void*) this, (void*) image ); } slotDecorate( item ); emit showImage(image,false); } } KookaImage* ScanPackager::getCurrImage(bool loadOnDemand) { KFileTreeViewItem *curr = currentKFileTreeViewItem(); if (curr==NULL) return (NULL); // no current item if (curr->isDir()) return (NULL); // is a directory KFileItem *kfi = curr->fileItem(); if (kfi==NULL) return (NULL); if (kfi->extraData(this)==NULL) // see if already loaded { if (!loadOnDemand) return (NULL); // not loaded, and don't want to slClicked(curr); // select/load this image } return (static_cast(kfi->extraData(this))); } QString ScanPackager::getCurrImageFileName( bool withPath = true ) const { QString result = ""; KFileTreeViewItem *curr = currentKFileTreeViewItem(); if( ! curr ) { kdDebug( 28000) << "getCurrImageFileName: nothing selected !"<< endl; } else { if( withPath ) { result = localFileName(curr); } else { KURL url( localFileName(curr)); url = curr->url(); result = url.fileName(); } } return( result ); } /* ----------------------------------------------------------------------- */ QCString ScanPackager::getImgFormat( KFileTreeViewItem* item ) const { QCString cstr; if( !item ) return( cstr ); #if 0 KFileItem *kfi = item->fileItem(); QString mime = kfi->mimetype(); #endif // TODO find the real extension for use with the filename ! // temporarely: QString f = localFileName( item ); return( QImage::imageFormat( f )); } QString ScanPackager::localFileName( KFileTreeViewItem *it ) const { if( ! it ) return( QString::null ); KURL url = it->url(); QString res; if( url.isLocalFile()) { res = url.directory( false, true ) + url.fileName(); } return( res ); } /* Called if the image exists but was changed by image manipulation func */ void ScanPackager::slotCurrentImageChanged( QImage *img ) { KFileTreeViewItem *curr = currentKFileTreeViewItem(); if( ! curr ) { kdDebug(28000) << "ImageChanged: nothing selected !" << endl; return; } /* Do not save directories */ if( curr->isDir() ) return; /* unload image and free memory */ slotUnloadItem( curr ); const QString filename = localFileName( curr ); const QCString format = getImgFormat( curr ); ImgSaver saver( this ); ImgSaveStat is_stat = ISS_OK; is_stat = saver.saveImage( img, filename, format ); if( is_stat == ISS_ERR_FORMAT_NO_WRITE ) { KMessageBox::error( this, i18n( "Cannot write this image format.\nImage will not be saved!"), i18n("Save Error") ); } else if( is_stat == ISS_ERR_PERM ) { KMessageBox::error( this, i18n( "Image file is write protected.\nImage will not be saved!"), i18n("Save Error") ); } else if( is_stat == ISS_ERR_PROTOCOL ) { KMessageBox::sorry( this, i18n( "Cannot save the image, because the file is local.\n" "Kooka will support other protocols later."), i18n("Save Error") ); } else if( is_stat != ISS_OK ) { kdDebug(28000) << "Error while saving existing image !" << endl; } if( img && !img->isNull()) { emit( imageChanged( curr->fileItem())); KookaImage *newImage = new KookaImage(*img); slImageArrived( curr, newImage ); } } /* ----------------------------------------------------------------------- */ /* This slot takes a new scanned Picture and saves it. * It urgently needs to make a deep copy of the image ! */ void ScanPackager::slAddImage( QImage *img, KookaImageMeta* ) { ImgSaveStat is_stat = ISS_OK; /* Save the image with the help of the ImgSaver */ if( ! img ) return; /* currently selected item is the directory or a file item */ KFileTreeViewItem *curr = currentKFileTreeViewItem(); /* Use root if nothing is selected */ if( ! curr ) { KFileTreeBranch *b = branches().at(0); /* There should be at least one */ if( b ) { curr = findItem( b, i18n( "Incoming/" ) ); if( ! curr ) curr = b->root(); } /* If curr is still undefined, something very tough has happend. Go away here */ if( !curr ) return; setSelected( curr, true ); } /* find the directory above the current one */ KURL dir(itemDirectory( curr )); /* Path of curr sel item */ ImgSaver img_saver( this, dir ); is_stat = img_saver.saveImage( img ); if( is_stat == ISS_ERR_FORMAT_NO_WRITE ) { KMessageBox::error( this, i18n( "Cannot write this image format.\nImage will not be saved!"), i18n("Save Error") ); } else if( is_stat == ISS_ERR_PERM ) { KMessageBox::error( this, i18n( "Image file is write protected.\nImage will not be saved!"), i18n("Save Error") ); } else if( is_stat != ISS_OK ) { if( is_stat == ISS_SAVE_CANCELED ) { return; } kdDebug(28000) << "ERROR: Saving failed: " << img_saver.errorString( is_stat ) << endl; /* And now ?? */ } /* Add the new image to the list of new images */ KURL lurl = img_saver.lastFileUrl(); KFileTreeBranchList branchlist = branches(); KFileTreeBranch *kookaBranch = branchlist.at(0); QString strdir = itemDirectory(curr); if(strdir.endsWith(QString("/"))) strdir.truncate( strdir.length() - 1 ); kdDebug(28000) << "Updating directory with " << strdir << endl; if( kookaBranch ) kookaBranch->updateDirectory( KURL(strdir) ); slotSetNextUrlToSelect( lurl ); m_nextUrlToShow = lurl; QString s; /* Count amount of children of the father */ QListViewItem *paps = curr->parent(); if( curr->isDir() ) /* take only father if the is no directory */ paps = curr; if( paps ) { int childcount = paps->childCount(); s = i18n("%1 images").arg(childcount); paps->setText( 1, s); setOpen( paps, true ); } } /* ----------------------------------------------------------------------- */ /* selects and opens the file with the given name. This is used to restore the * last displayed image by its name. */ void ScanPackager::slSelectImage( const KURL& name ) { KFileTreeViewItem *found = spFindItem( UrlSearch, name.url() ); if( found ) { kdDebug(28000) << "slSelectImage: Found an item !" << endl; ensureItemVisible( found ); setCurrentItem( found ); slClicked( found ); } } KFileTreeViewItem *ScanPackager::spFindItem( SearchType type, const QString name, const KFileTreeBranch *branch ) { /* Prepare a list of branches to go through. If the parameter branch is set, search * only in the parameter branch. If it is zero, search all branches returned by * kfiletreeview.branches() */ KFileTreeBranchList branchList; if( branch ) { branchList.append( branch ); } else { branchList = branches(); } KFileTreeBranchIterator it( branchList ); KFileItem *kfi = 0L; KFileTreeViewItem *foundItem = 0L; /* Leave the loop in case kfi is defined */ KFileTreeBranch *branchloop = 0L; for( ; !kfi && it.current(); ++it ) { branchloop = *it; KURL url(name); switch( type ) { case Dummy: kdDebug(28000) << "Dummy search skipped !" << endl; break; case NameSearch: kdDebug(28000) << "ScanPackager: searching for " << name << endl; kfi = branchloop->findByName( name ); break; case UrlSearch: kdDebug(28000) << "ScanPackager: URL search for " << name << endl; kfi = branchloop->find( url ); break; default: kdDebug(28000) << "Scanpackager: Wrong search type !" << endl; break; } } if( kfi ) { foundItem = static_cast(kfi->extraData(branchloop)); kdDebug(28000) << "spFindItem: Success !" << foundItem << endl; } return( foundItem ); } /* ----------------------------------------------------------------------- */ void ScanPackager::slShowContextMenu(QListViewItem *lvi,const QPoint &p) { KFileTreeViewItem *curr = NULL; if (lvi!=NULL) { curr = currentKFileTreeViewItem(); if (curr->isDir()) setSelected(curr,true); } if (m_contextMenu!=NULL) m_contextMenu->exec(p); } /* ----------------------------------------------------------------------- */ void ScanPackager::slotExportFile( ) { KFileTreeViewItem *curr = currentKFileTreeViewItem(); if( ! curr ) return; if( curr->isDir() ) { kdDebug(28000) << "Not yet implemented!" << endl; } else { KURL fromUrl( curr->url()); QString filter = "*." + getImgFormat(curr).lower(); filter += "\n*|" + i18n( "All Files" ); // initial += fromUrl.filename(false); QString initial = m_currCopyDir + "/"; initial += fromUrl.filename(false); KURL fileName = KFileDialog::getSaveURL ( initial, filter, this ); if ( fileName.isValid() ) // got a file name { if( fromUrl == fileName ) return; /* Since it is asynchron, we will never get if it succeeded. */ ImgSaver::copyImage( fromUrl, fileName ); /* remember the filename for the next export */ fileName.setFileName( QString()); m_currCopyDir = fileName.url( ); } } } void ScanPackager::slotImportFile() { KFileTreeViewItem *curr = currentKFileTreeViewItem(); if( ! curr ) return; KURL impTarget = curr->url(); if( ! curr->isDir() ) { KFileTreeViewItem *pa = static_cast(curr->parent()); impTarget = pa->url(); } kdDebug(28000) << "Importing to " << impTarget.url() << endl; KURL impUrl = KFileDialog::getImageOpenURL ( m_currImportDir, this, i18n("Import Image File to Gallery")); if( ! impUrl.isEmpty() ) { m_currImportDir = impUrl.url(); impTarget.addPath( impUrl.fileName()); // append the name of the sourcefile to the path m_nextUrlToShow = impTarget; ImgSaver::copyImage( impUrl, impTarget ); } } void ScanPackager::slotUrlsDropped( QWidget*, QDropEvent* ev, KURL::List& urls, KURL& copyTo ) { if( !urls.isEmpty() ) { kdDebug(28000) << "Kooka drop event!" << endl; // kdDebug(28000) << "Kooka drop event. First src url=" << urls.first() << " copyTo=" << copyTo // << " move=" << ( ev->action() == QDropEvent::Move ) << endl; /* first make the last url to copy to the one to select next */ if( ! urls.empty() ) { KURL nextSel = copyTo; nextSel.addPath( urls.back().fileName(false)); kdDebug(28000) << "Selecting next url: " << nextSel.url() << endl; m_nextUrlToShow = nextSel; // slotSetNextUrlToSelect( nextSel ); } if ( ev->action() == QDropEvent::Move ) copyjob = KIO::move( urls, copyTo, true ); else copyjob = KIO::copy( urls, copyTo, true ); } } void ScanPackager::slotCanceled( KIO::Job* ) { kdDebug(28000) << i18n("Canceled by user") << endl; } /* ----------------------------------------------------------------------- */ void ScanPackager::slotUnloadItems( ) { KFileTreeViewItem *curr = currentKFileTreeViewItem(); emit showImage(NULL,false); slotUnloadItem( curr ); } void ScanPackager::slotUnloadItem( KFileTreeViewItem *curr ) { if( ! curr ) return; if( curr->isDir()) { KFileTreeViewItem *child = static_cast(curr->firstChild()); while( child ) { kdDebug(28000) << "Unloading item " << child << endl; slotUnloadItem( child ); child = static_cast (child->nextSibling()); } } else { KFileItem *kfi = curr->fileItem(); KookaImage *image = static_cast(kfi->extraData( this )); /* If image is zero, ok, than there is nothing to unload :) */ if( image ) { if( image->subImagesCount() > 0 ) { KFileTreeViewItem *child = static_cast(curr->firstChild()); while( child ) { KFileTreeViewItem *nextChild = 0; kdDebug(28000) << "Unloading subimage item " << child << endl; slotUnloadItem( child ); nextChild = static_cast (child->nextSibling()); delete child; child = nextChild; } } emit( unloadImage( image )); delete image; kfi->removeExtraData( this ); slotDecorate( curr ); } } } /* ----------------------------------------------------------------------- */ void ScanPackager::slotDeleteItems() { KFileTreeViewItem *curr = currentKFileTreeViewItem(); if (curr==NULL) return; KURL urlToDel = curr->url(); QListViewItem *nextToSelect = curr->nextSibling(); // select this afterwards bool ask = true; // for future expansion bool isDir = (curr->fileItem()->isDir()); // deleting a folder if (ask) { QString s; QString dontAskKey; if (isDir) { s = i18n("Do you really want to permanently delete the folder
" "%1
" "and all of its contents? It cannot be restored!").arg(urlToDel.pathOrURL()); dontAskKey = "AskForDeleteDirs"; } else { s = i18n("Do you really want to permanently delete the image
" "%1?
" "It cannot be restored!").arg(urlToDel.pathOrURL()); dontAskKey = "AskForDeleteFiles"; } if (KMessageBox::warningContinueCancel(this,s,i18n("Delete Gallery Item"), KStdGuiItem::del(), dontAskKey)!=KMessageBox::Continue) return; } kdDebug(28000) << k_funcinfo << "Deleting " << urlToDel.prettyURL() << endl; /* Since we are currently talking about local files here, NetAccess is OK */ if (!KIO::NetAccess::del(urlToDel,NULL)) { KMessageBox::error(this,i18n("Unable to delete the image or folder
" "%2

" "%1").arg(KIO::NetAccess::lastErrorString(),urlToDel.pathOrURL())); return; } if (nextToSelect!=NULL) setSelected(nextToSelect,true); /* TODO: remove the directory from the imageNameCombobox */ if (isDir) { /* The directory needs to be removed from the name combo */ emit(directoryToRemove(curr->branch(),itemDirectory(curr,true))); } } void ScanPackager::slotRenameItems( ) { KFileTreeViewItem *curr = currentKFileTreeViewItem(); if (curr!=NULL) rename(curr,0); } /* ----------------------------------------------------------------------- */ void ScanPackager::slotCreateFolder( ) { bool ok; QString folder = KInputDialog::getText( i18n( "New Folder" ), i18n( "Name for the new folder:" ), QString::null, &ok, this ); if( ok ) { /* KIO create folder goes here */ KFileTreeViewItem *it = currentKFileTreeViewItem(); if( it ) { KURL url = it->url(); /* If a directory is selected, the filename needs not to be deleted */ if( ! it->isDir()) url.setFileName( "" ); /* add the folder name from user input */ url.addPath( folder ); kdDebug(28000) << "Creating folder " << url.prettyURL() << endl; /* Since the new directory arrives in the packager in the newItems-slot, we set a * variable urlToSelectOnArrive here. The newItems-slot will honor it and select * the treeviewitem with that url. */ slotSetNextUrlToSelect( url ); if( ! KIO::NetAccess::mkdir( url, 0, -1 )) { kdDebug(28000) << "ERR: creation of " << url.prettyURL() << " failed !" << endl; } else { /* created successfully */ /* open the branch if necessary and select the new folder */ } } } } /* ----------------------------------------------------------------------- */ QString ScanPackager::getImgName( QString name_on_disk ) { QString s; (void) name_on_disk; s = i18n("image %1").arg(img_counter++); return( s ); } /* ----------------------------------------------------------------------- */ ScanPackager::~ScanPackager() { kdDebug(29000) << k_funcinfo << endl; } /* called whenever one branch detects a deleted file */ void ScanPackager::slotDeleteFromBranch( KFileItem* kfi ) { emit fileDeleted( kfi ); } void ScanPackager::contentsDragMoveEvent( QDragMoveEvent *e ) { if( ! acceptDrag( e ) ) { e->ignore(); return; } QListViewItem *afterme = 0; QListViewItem *parent = 0; findDrop( e->pos(), parent, afterme ); // "afterme" is 0 when aiming at a directory itself QListViewItem *item = afterme ? afterme : parent; if( item ) { bool isDir = static_cast (item)->isDir(); if( isDir ) { KFileTreeView::contentsDragMoveEvent( e ); // for the autoopen code return; } } e->acceptAction(); } void ScanPackager::setAllowRename(bool on) { kdDebug(29000) << k_funcinfo << "allow=" << on << endl; setItemsRenameable(on); } void ScanPackager::showOpenWithMenu(KActionMenu *menu) { kdDebug(29000) << k_funcinfo << endl; KFileTreeViewItem *curr = currentKFileTreeViewItem(); QString mimeType = KMimeType::findByURL(curr->url())->name(); kdDebug(28000) << "Trying to open <" << curr->url().prettyURL()<< "> which is " << mimeType << endl; openWithOffers = KTrader::self()->query(mimeType,"Type=='Application'"); if (openWithMapper==NULL) { openWithMapper = new QSignalMapper(this); connect(openWithMapper,SIGNAL(mapped(int)),SLOT(slotOpenWith(int))); } menu->popupMenu()->clear(); int i = 0; for (KTrader::OfferList::ConstIterator it = openWithOffers.begin(); it!=openWithOffers.end(); ++it, ++i) { KService::Ptr service = (*it); kdDebug(29000) << " offer: " << (*it)->name() << endl; QString actionName((*it)->name().replace("&","&&")); KAction *act = new KAction(actionName,(*it)->pixmap(KIcon::Small),0, openWithMapper,SLOT(map()), menu->parentCollection()); openWithMapper->setMapping(act,i); menu->insert(act); } menu->popupMenu()->insertSeparator(); KAction *act = new KAction(i18n("Other..."),0,openWithMapper,SLOT(map()), menu->parentCollection()); openWithMapper->setMapping(act,i); menu->insert(act); } void ScanPackager::slotOpenWith(int idx) { kdDebug(29000) << k_funcinfo << "idx=" << idx << endl; KFileTreeViewItem *ftvi = currentKFileTreeViewItem(); if (ftvi==NULL) return; KURL::List urllist(ftvi->url()); if (idx #include /** *@author Klaas Freitag */ class QPopupMenu; class QImage; class QListViewItem; class QSignalMapper; class KookaImage; class KookaImageMeta; class KURL; class KActionMenu; typedef enum{ Dummy, NameSearch, UrlSearch } SearchType; class ScanPackager : public KFileTreeView { Q_OBJECT public: ScanPackager( QWidget *parent); ~ScanPackager(); virtual QString getImgName( QString name_on_disk ); QString getCurrImageFileName( bool ) const; KookaImage* getCurrImage(bool loadOnDemand = false); KFileTreeBranch* openRoot( const KURL&, bool open=false ); QPopupMenu *contextMenu() const { return m_contextMenu; } void openRoots(); void setAllowRename(bool on); void showOpenWithMenu(KActionMenu *menu); public slots: void slSelectImage( const KURL& ); void slAddImage( QImage *img, KookaImageMeta* meta = 0 ); void slShowContextMenu(QListViewItem *lvi,const QPoint &p); void slotExportFile( ); void slotImportFile(); void slotCanceled(KIO::Job*); void slotCurrentImageChanged( QImage* ); void slotDecorate( KFileTreeViewItem* ); void slotDecorate( KFileTreeBranch*, const KFileTreeViewItemList& ); void slotSelectDirectory( const QString& ); protected: virtual void contentsDragMoveEvent( QDragMoveEvent *e ); protected slots: - void slClicked( QListViewItem * ); - void slFileRename( QListViewItem*, const QString&, int ); + void slClicked(QListViewItem *); + void slFileRename(QListViewItem *it,const QString &newName); // void slFilenameChanged( KFileTreeViewItem*, const KURL & ); void slImageArrived( KFileTreeViewItem *item, KookaImage* image ); void slotCreateFolder( ); void slotDeleteItems( ); void slotRenameItems( ); void slotUnloadItems( ); void slotUnloadItem( KFileTreeViewItem *curr ); void slotDirCount( KFileTreeViewItem *item, int cnt ); void slotUrlsDropped( QWidget*, QDropEvent*, KURL::List& urls, KURL& copyTo ); void slotDeleteFromBranch( KFileItem* ); void slotStartupFinished( KFileTreeViewItem * ); void slotItemExpanded(QListViewItem *item); void slotOpenWith(int idx); signals: - void showImage(KookaImage *img,bool isDir); + void showImage(const KookaImage *img,bool isDir); void deleteImage( KookaImage* ); void unloadImage( KookaImage* ); void galleryPathSelected( KFileTreeBranch* branch, const QString& relativPath ); void directoryToRemove( KFileTreeBranch *branch, const QString& relativPath ); void showThumbnails( KFileTreeViewItem* ); void aboutToShowImage( const KURL& ); /* starting to load image */ void imageChanged( KFileItem* ); /* the image has changed */ void fileDeleted( KFileItem* ); void fileChanged( KFileItem* ); void fileRenamed( KFileItem*, const KURL& ); private: QString localFileName( KFileTreeViewItem* it ) const; void loadImageForItem( KFileTreeViewItem* item ); QCString getImgFormat( KFileTreeViewItem* item ) const; QString buildNewFilename( QString cmplFilename, QString currFormat ) const; KFileTreeViewItem *spFindItem( SearchType type, const QString name, const KFileTreeBranch* branch = 0 ); QString itemDirectory( const KFileTreeViewItem*, bool relativ = false ) const; // int readDir( QListViewItem *parent, QString dir_to_read ); void showContextMenu( QPoint p, bool show_folder = true ); QString m_currImportDir; QString m_currCopyDir; QString currSelectedDir; KIO::Job *copyjob; int img_counter; QPopupMenu *m_contextMenu; KTrader::OfferList openWithOffers; QSignalMapper *openWithMapper; // like m_nextUrlToSelect in KFileTreeView but for our own purposes (showing the image) KURL m_nextUrlToShow; QPixmap m_floppyPixmap; QPixmap m_grayPixmap; QPixmap m_bwPixmap; QPixmap m_colorPixmap; KFileTreeBranch *m_defaultBranch; bool m_startup; }; #endif diff --git a/kooka/scanparamsdialog.cpp b/kooka/scanparamsdialog.cpp new file mode 100644 index 0000000..d640e93 --- /dev/null +++ b/kooka/scanparamsdialog.cpp @@ -0,0 +1,252 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Jonathan Marten + + 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "kscandevice.h" +#include "kscanoptset.h" +#include "newscanparams.h" + +#include "scanparamsdialog.h" +#include "scanparamsdialog.moc" + + +ScanParamsDialog::ScanParamsDialog(QWidget *parent,KScanDevice *scandev) + : KDialogBase(parent,NULL,true,i18n("Scan Parameters"),KDialogBase::Close) +{ + enableButtonSeparator(true); + + QFrame *mf = makeMainWidget(); + QGridLayout *gl = new QGridLayout(mf,8,3,KDialog::marginHint(),KDialog::spacingHint()); + + paramsList = new QListBox(mf); + paramsList->setSelectionMode(QListBox::Single); + paramsList->setVScrollBarMode(QScrollView::AlwaysOn); + paramsList->setMinimumWidth(200); + connect(paramsList,SIGNAL(selectionChanged(QListBoxItem *)), + SLOT(slotSelectionChanged(QListBoxItem *))); + connect(paramsList,SIGNAL(doubleClicked(QListBoxItem *)), + SLOT(slotLoadAndClose(QListBoxItem *))); + gl->addMultiCellWidget(paramsList,1,4,0,0); + + QLabel *l = new QLabel(i18n("Saved scan parameter sets:"),mf); + gl->addWidget(l,0,0,Qt::AlignLeft); + l->setBuddy(paramsList); + + buttonLoad = new QPushButton(i18n("Load"),mf); + connect(buttonLoad,SIGNAL(clicked()),SLOT(slotLoad())); + QToolTip::add(buttonLoad,i18n("Load the selected scan parameter set to use as scanner settings")); + gl->addWidget(buttonLoad,1,2); + + buttonSave = new QPushButton(i18n("Save..."),mf); + connect(buttonSave,SIGNAL(clicked()),SLOT(slotSave())); + QToolTip::add(buttonSave,i18n("Save the current scanner settings as a new scan parameter set")); + gl->addWidget(buttonSave,2,2); + + buttonDelete = new QPushButton(i18n("Delete"),mf); + connect(buttonDelete,SIGNAL(clicked()),SLOT(slotDelete())); + QToolTip::add(buttonDelete,i18n("Delete the selected scan parameter set")); + gl->addWidget(buttonDelete,3,2); + + buttonEdit = new QPushButton(i18n("Edit..."),mf); + connect(buttonEdit,SIGNAL(clicked()),SLOT(slotEdit())); + QToolTip::add(buttonEdit,i18n("Change the name or description of the selected scan parameter set")); + gl->addWidget(buttonEdit,4,2); + + gl->setRowStretch(5,9); + gl->setRowSpacing(6,KDialog::marginHint()); + + gl->setColStretch(0,9); + gl->setColSpacing(1,KDialog::marginHint()); + + descLabel = new QLabel(i18n("-"),mf); + gl->addMultiCellWidget(descLabel,7,7,0,2); + + sane = scandev; + + populateList(); + slotSelectionChanged(NULL); +} + + +void ScanParamsDialog::populateList() +{ + paramsList->clear(); + sets = KScanOptSet::readList(); + + for (KScanOptSet::StringMap::const_iterator it = sets.constBegin(); it!=sets.constEnd(); ++it) + { + kdDebug(29000) << k_funcinfo << "saveset [" << it.key() << "]" << endl; + paramsList->insertItem(it.key()); + } +} + + +void ScanParamsDialog::slotSelectionChanged(QListBoxItem *item) +{ + QString desc; + bool enable = false; + + if (item==NULL) desc = i18n("No save set selected."); + else // something getting selected + { + desc = sets[item->text()]; + enable = true; + } + + descLabel->setText(desc); + buttonLoad->setEnabled(enable); + buttonDelete->setEnabled(enable); + buttonEdit->setEnabled(enable); + + if (enable) buttonLoad->setDefault(true); + else actionButton(KDialogBase::Close)->setDefault(true); +} + + + +void ScanParamsDialog::slotLoad() +{ + + QListBoxItem *item = paramsList->selectedItem(); + if (item==NULL) return; + QString name = item->text(); + kdDebug(29000) << k_funcinfo << "set [" << name << "]" << endl; + + KScanOptSet optSet(name.local8Bit()); + if (!optSet.load()) + { + kdDebug(29000) << k_funcinfo << "Failed to load set [" << name << "]!" << endl; + return; + } + + sane->loadOptionSet(&optSet); + sane->slReloadAll(); +} + + +void ScanParamsDialog::slotLoadAndClose(QListBoxItem *item) +{ + if (item==NULL) return; + + kdDebug(29000) << k_funcinfo << "set [" << item->text() << "]" << endl; + + paramsList->setSelected(item,true); + slotLoad(); + accept(); +} + + +void ScanParamsDialog::slotSave() +{ + QString name = QString::null; + QListBoxItem *item = paramsList->selectedItem(); + if (item!=NULL) name = item->text(); + kdDebug(29000) << k_funcinfo << "selected set [" << name << "]" << endl; + + NewScanParams d(this,name,(sets.contains(name) ? sets[name] : QString::null),false); + if (d.exec()) + { + QString newName = d.getName(); + QString newDesc = d.getDescription(); + + kdDebug(29000) << k_funcinfo << "name=[" << newName << "] desc=[" << newDesc << "]" << endl; + + KScanOptSet optSet(newName.local8Bit()); + sane->getCurrentOptions(&optSet); + + optSet.saveConfig(sane->shortScannerName(),newName,newDesc); + sets[newName] = newDesc; + + paramsList->setCurrentItem(0); + QListBoxItem *item = paramsList->findItem(newName,Qt::ExactMatch|Qt::CaseSensitive); + if (item==NULL) + { + paramsList->insertItem(newName); + item = paramsList->item(paramsList->numRows()-1); + } + paramsList->setSelected(item,true); + slotSelectionChanged(item); + } +} + + +void ScanParamsDialog::slotEdit() +{ + QListBoxItem *item = paramsList->selectedItem(); + if (item==NULL) return; + QString oldName = item->text(); + kdDebug(29000) << k_funcinfo << "selected set [" << oldName << "]" << endl; + + NewScanParams d(this,oldName,sets[oldName],true); + if (d.exec()) + { + QString newName = d.getName(); + QString newDesc = d.getDescription(); + if (newName==oldName && newDesc==sets[oldName]) return; + + kdDebug(29000) << k_funcinfo << "new name=[" << newName << "] desc=[" << newDesc << "]" << endl; + + KScanOptSet optSet(oldName.local8Bit()); + if (!optSet.load()) + { + kdDebug(29000) << k_funcinfo << "Failed to load set [" << oldName << "]!" << endl; + return; + } + + KScanOptSet::deleteSet(oldName); // do first, in case name not changed + optSet.saveConfig(sane->shortScannerName(),newName,newDesc); + + sets.erase(oldName); // do first, ditto + sets[newName] = newDesc; + + int ix = paramsList->index(item); + paramsList->changeItem(newName,ix); + slotSelectionChanged(paramsList->item(ix)); // recalculate 'item', it may change + } +} + + +void ScanParamsDialog::slotDelete() +{ + QListBoxItem *item = paramsList->selectedItem(); + if (item==NULL) return; + QString name = item->text(); + kdDebug(29000) << k_funcinfo << "set [" << name << "]" << endl; + + if (KMessageBox::warningContinueCancel(this,i18n("Do you really want to delete the set '%1'?").arg(name), + i18n("Delete Scan Parameter Set"), + KStdGuiItem::del(), + "deleteSaveSet")!=KMessageBox::Continue) return; + + KScanOptSet::deleteSet(name); + paramsList->removeItem(paramsList->index(item)); + slotSelectionChanged(NULL); +} diff --git a/kooka/scanparamsdialog.h b/kooka/scanparamsdialog.h new file mode 100644 index 0000000..4cf800a --- /dev/null +++ b/kooka/scanparamsdialog.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE Project -*- mode:c++ -*- + Copyright (C) 2008 Jonathan Marten + + 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. +*/ + +#ifndef SCANPARAMSDIALOG_H +#define SCANPARAMSDIALOG_H + +#include + +#include "kscanoptset.h" + +//class KLineEdit; + +class QLabel; +class QListBox; +class QListBoxItem; + +class KScanDevice; + + +/** + * A dialogue to allow the user to enter a name and description for + * a set of saved scan parameters. + */ + +class ScanParamsDialog : public KDialogBase +{ + Q_OBJECT + +public: + ScanParamsDialog(QWidget *parent,KScanDevice *scandev); + +protected slots: + void slotSelectionChanged(QListBoxItem *item); + void slotLoad(); + void slotSave(); + void slotDelete(); + void slotEdit(); + void slotLoadAndClose(QListBoxItem *item); + +private: + void populateList(); + + QLabel *descLabel; + QListBox *paramsList; + QPushButton *buttonLoad; + QPushButton *buttonSave; + QPushButton *buttonDelete; + QPushButton *buttonEdit; + + KScanOptSet::StringMap sets; + KScanDevice *sane; +}; + + +#endif // SCANPARAMSDIALOG_H diff --git a/libkscan/img_canvas.cpp b/libkscan/img_canvas.cpp index 478d955..d0d2fb7 100644 --- a/libkscan/img_canvas.cpp +++ b/libkscan/img_canvas.cpp @@ -1,1120 +1,1120 @@ /* This file is part of the KDE Project Copyright (C) 1999 Klaas Freitag 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define __IMG_CANVAS_CPP__ #include "imgscaledialog.h" #include "img_canvas.h" #define MIN(x,y) (xx() << ", " << r->y() << ", " << r->width() << ", " << r->height() << endl; } class ImageCanvas::ImageCanvasPrivate { public: ImageCanvasPrivate() : keepZoom(false), readOnly(false), scaleKind( UNSPEC ), defaultScaleKind( FIT_ORIG ) {} bool keepZoom; /* keep the zoom settings if images change */ bool readOnly; ScaleKinds scaleKind; ScaleKinds defaultScaleKind; QValueList highlightRects; }; ImageCanvas::ImageCanvas(QWidget *parent, const QImage *start_image, const char *name ): QScrollView( parent, name ), m_contextMenu(0) { kdDebug(29000) << k_funcinfo << endl; d = new ImageCanvasPrivate(); scale_factor = 100; // means original size maintain_aspect = true; selected = new QRect; selected->setWidth(0); selected->setHeight(0); timer_id = 0; pmScaled = 0; image = start_image; moving = MOVE_NONE; QSize img_size; if( image && ! image->isNull() ) { img_size = image->size(); pmScaled = new QPixmap( img_size ); kdDebug(29000) << " image size from image is " << img_size << endl; #ifdef USE_KPIXMAPIO *pmScaled = pixIO.convertToPixmap(*image); #else pmScaled->convertFromImage( *image ); #endif acquired = true; } else { img_size = size(); kdDebug(29000) << " unspecified image size is " << img_size << endl; } update_scaled_pixmap(); // timer-Start and stop connect( this, SIGNAL( newRect()), SLOT( newRectSlot())); connect( this, SIGNAL( noRect()), SLOT( noRectSlot())); //zoomOut();scrollview/scrollview viewport()->setCursor( crossCursor ); cr1 = 0; cr2 = 0; viewport()->setMouseTracking(TRUE); viewport()->setBackgroundMode(PaletteBackground); show(); } ImageCanvas::~ImageCanvas() { kdDebug(29000) << k_funcinfo << endl; noRectSlot(); if( selected ) delete selected; selected = 0; if( pmScaled ) delete pmScaled; pmScaled = 0; delete d; } void ImageCanvas::deleteView( QImage *delimage ) { const QImage *img = rootImage(); if( delimage == img ) { kdDebug(29000) << "ImageCanvas -> emiting newImage(0L)" << endl; newImage( 0L ); noRectSlot(); } } void ImageCanvas::newImageHoldZoom( QImage *new_image ) { bool holdZ = d->keepZoom; d->keepZoom = true; newImage( new_image ); d->keepZoom = holdZ; } -void ImageCanvas::newImage( QImage *new_image ) +void ImageCanvas::newImage( const QImage *new_image ) { /** do cleanups **/ // dont free old image -> not yours image = new_image; if( ! image || image->isNull()) { kdDebug(29000) << "newImage: Got Empty image !" << endl; } if( pmScaled ) { delete pmScaled; pmScaled = 0L; } if( selected ) { noRectSlot(); } /* throw away all highlights */ d->highlightRects.clear(); /** handle the new image **/ if( image ) { kdDebug(29000) << k_funcinfo << "new image size is " << image->size() << endl; if( image->depth() == 1 ) { pmScaled = new QPixmap( image->size(), 1 ); } else { int i = QPixmap::defaultDepth(); pmScaled = new QPixmap( image->size(), i); } // image->convertDepth(32); #ifdef USE_KPIXMAPIO *pmScaled = pixIO.convertToPixmap(*image); #else pmScaled->convertFromImage( *image ); // *pmScaled = image->convertToPixmap( ); #endif acquired = true; if( d->keepZoom ) { kdDebug(29000) << "Preserving Zoom settings!" << endl; } else { kdDebug(29000) << "Resetting Zoom to original size!" << endl; setScaleKind( defaultScaleKind() ); } update_scaled_pixmap(); setContentsPos(0,0); } else { kdDebug(29000) << "New image called without image => deleting!" << endl; acquired = false; resizeContents( 0,0 ); } //kdDebug(29000) << "going to repaint!" << endl; repaint( true ); //kdDebug(29000) << "repaint ok" << endl; } QSize ImageCanvas::sizeHint() const { return( QSize( 2, 2 )); } void ImageCanvas::enableContextMenu( bool wantContextMenu ) { if( wantContextMenu ) { if( ! m_contextMenu ) { m_contextMenu = new KPopupMenu(this, "IMG_CANVAS"); KContextMenuManager::insert( viewport(), m_contextMenu ); } } else { /* remove all items */ if( m_contextMenu ) m_contextMenu->clear(); /* contextMenu is not deleted because there is no way to remove * it from the contextMenuManager */ } } void ImageCanvas::handle_popup( int item ) { if( item < ID_POP_ZOOM || item > ID_ORIG_SIZE ) return; if( ! image ) return; ImgScaleDialog *zoomDia = 0; switch( item ) { case ID_POP_ZOOM: zoomDia = new ImgScaleDialog( this, getScaleFactor() ); if( zoomDia->exec() ) { int sf = zoomDia->getSelected(); setScaleKind(ZOOM); setScaleFactor( sf ); } delete zoomDia; zoomDia = 0; break; case ID_ORIG_SIZE: setScaleKind( FIT_ORIG ); break; case ID_FIT_WIDTH: setScaleKind( FIT_WIDTH ); break; case ID_FIT_HEIGHT: setScaleKind( FIT_HEIGHT ); break; case ID_POP_CLOSE: emit( closingRequested()); break; default: break; } update_scaled_pixmap(); repaint(); } /** * Returns the selected rect in tenth of percent, not in absolute * sizes, eg. 500 means 50.0 % of original width/height. * That makes it easier to work with different scales and units */ QRect ImageCanvas::sel( void ) { QRect retval; retval.setCoords(0, 0, 0, 0); if( selected && image && selected->width()>MIN_AREA_WIDTH && selected->height()>MIN_AREA_HEIGHT ) { /* Get the size in real image pixels */ // debug_rect( "PRE map", selected ); QRect mapped = inv_scale_matrix.map( (const QRect) *selected ); // debug_rect( "Postmap", &mapped ); if( mapped.x() > 0 ) retval.setLeft((int) (1000.0/( (double)image->width() / (double)mapped.x()))); if( mapped.y() > 0 ) retval.setTop((int) (1000.0/( (double)image->height() / (double)mapped.y()))); if( mapped.width() > 0 ) retval.setWidth((int) (1000.0/( (double)image->width() / (double)mapped.width()))); if( mapped.height() > 0 ) retval.setHeight((int)(1000.0/( (double)image->height() / (double)mapped.height()))); } // debug_rect( "sel() return", &retval ); return( retval ); } bool ImageCanvas::selectedImage( QImage *retImg ) { QRect r = sel(); bool result = false; const QImage* entireImg = this->rootImage(); if( entireImg ) { QSize s = entireImg->size(); int x = (s.width() * r.x())/1000; int y = (s.height() * r.y())/1000; int w = (s.width() * r.width())/1000; int h = (s.height() * r.height())/1000; if( w > 0 && h > 0 ) { if (retImg!=NULL) *retImg = entireImg->copy( x, y, w, h ); result = true; } } return(result); } void ImageCanvas::drawContents( QPainter * p, int clipx, int clipy, int clipw, int cliph ) { if( !pmScaled ) return; int x1 = 0; int y1 = 0; int x2 = pmScaled->width(); int y2 = pmScaled->height(); if (x1 < clipx) x1=clipx; if (y1 < clipy) y1=clipy; if (x2 > clipx+clipw-1) x2=clipx+clipw-1; if (y2 > clipy+cliph-1) y2=clipy+cliph-1; // Paint using the small coordinates... // p->scale( used_xscaler, used_yscaler ); // p->scale( used_xscaler, used_yscaler ); if ( x2 >= x1 && y2 >= y1 ) { p->drawPixmap( x1, y1, *pmScaled, x1, y1 ); //, clipw, cliph); // p->setBrush( red ); // p->drawRect( x1, y1, clipw, cliph ); } // p->fillRect(x1, y1, x2-x1+1, y2-y1+1, red); } void ImageCanvas::timerEvent(QTimerEvent *) { if(moving!=MOVE_NONE || !acquired ) return; cr1++; QPainter p(viewport()); // p.setWindow( contentsX(), contentsY(), contentsWidth(), contentsHeight()); drawAreaBorder(&p); } void ImageCanvas::newRectSlot( QRect newSel ) { kdDebug(29000) << k_funcinfo << "rect=" << newSel << endl; QRect to_map; QPainter p(viewport()); drawAreaBorder(&p,TRUE); selected->setWidth(0); selected->setHeight(0); emit( noRect() ); if ( image ) { int rx, ry, rw, rh; int w = image->width(); int h = image->height(); kdDebug(29000) << " Image size is " << w << "x" << h << endl; kdDebug(29000) << " got selection Rect " << newSel << endl; // to_map.setWidth(static_cast(w * newSel.width() / 1000.0)); rw = static_cast(w * newSel.width() / 1000.0); rx = static_cast(w * newSel.x() / 1000.0); ry = static_cast(h * newSel.y() / 1000.0); rh = static_cast(h * newSel.height() / 1000.0); //kdDebug(29000) << " scaled Height is " << rh << endl; to_map.setRect( rx, ry, rw, rh ); *selected = scale_matrix.map( to_map ); kdDebug(29000) << " to_map=" << to_map << " selected=" << *selected << endl; emit( newRect( sel() )); newRectSlot(); } } void ImageCanvas::newRectSlot( ) { // printf( "Timer switched on !\n" ); if( timer_id == 0 ) timer_id = startTimer( 100 ); } void ImageCanvas::noRectSlot( void ) { // printf( "Timer switched off !\n" ); if( timer_id ) { killTimer( timer_id ); timer_id = 0; } if( selected ) selected->setCoords( 0,0,0,0 ); } void ImageCanvas::viewportMousePressEvent(QMouseEvent *ev) { if( ! acquired || ! image ) return; if(ev->button()==LeftButton ) { int cx = contentsX(), cy = contentsY(); int x = lx = ev->x(),y = ly = ev->y(); int ix, iy; scale_matrix.map( image->width(), image->height(), &ix, &iy ); if( x > ix-cx || y > iy-cy ) return; if( moving == MOVE_NONE ) { QPainter p( viewport()); drawAreaBorder(&p,TRUE); moving = classifyPoint( x+cx ,y+cy); if(moving == MOVE_NONE) { //Create new area selected->setCoords( x+cx, y+cy, x+cx, y+cy ); moving = MOVE_BOTTOM_RIGHT; } drawAreaBorder(&p); } } } void ImageCanvas::viewportMouseReleaseEvent(QMouseEvent *ev) { if(ev->button()!=LeftButton || !acquired ) return; //// debug( "Mouse Release at %d/%d", ev->x(), ev->y()); if(moving!=MOVE_NONE) { QPainter p(viewport()); drawAreaBorder(&p,TRUE); moving = MOVE_NONE; *selected = selected->normalize(); if(selected->width () < MIN_AREA_WIDTH || selected->height() < MIN_AREA_HEIGHT) { selected->setWidth(0); selected->setHeight(0); emit noRect(); return; } drawAreaBorder(&p); emit newRect( sel() ); emit newRect( ); } } void ImageCanvas::viewportMouseMoveEvent(QMouseEvent *ev) { if( ! acquired || !image) return; static cursor_type ps = HREN; int x = ev->x(), y = ev->y(); int cx = contentsX(), cy = contentsY(); // debug("Mouse Coord x: %d, y: %d | cx: %d, cy: %d", x,y, cx, cy ); // dont draw out of the scaled Pixmap if( x < 0 ) x = 0; int ix, iy; scale_matrix.map( image->width(), image->height(), &ix, &iy ); if( x >= ix ) return; if( y < 0 ) y = 0; if( y >= iy ) return; // debug( "cx, cy: %dx%d, x,y: %d, %d", // cx, cy, x, y ); #if 0 if( moving == MOVE_NONE ) moving = classifyPoint(x, y); #endif switch( moving!=MOVE_NONE ? moving:classifyPoint(x+cx,y+cy) ) { case MOVE_NONE: if(ps!=CROSS) { viewport()->setCursor(crossCursor); ps = CROSS; } break; case MOVE_LEFT: case MOVE_RIGHT: if(ps!=HSIZE) { viewport()->setCursor(sizeHorCursor); ps = HSIZE; } break; case MOVE_TOP: case MOVE_BOTTOM: if(ps!=VSIZE) { viewport()->setCursor(sizeVerCursor); ps = VSIZE; } break; case MOVE_TOP_LEFT: case MOVE_BOTTOM_RIGHT: if(ps!=FDIAG) { viewport()->setCursor(sizeFDiagCursor); ps = FDIAG; } break; case MOVE_TOP_RIGHT: case MOVE_BOTTOM_LEFT: if(ps!=BDIAG) { viewport()->setCursor(sizeBDiagCursor); ps = BDIAG; } break; case MOVE_WHOLE: if(ps!=ALL) { viewport()->setCursor(sizeAllCursor); ps = ALL; } } //At ButtonRelease : normalize + clip if( moving!=MOVE_NONE ) { int mx, my; QPainter p(viewport()); drawAreaBorder(&p,TRUE); switch(moving) { case MOVE_NONE: //Just to make compiler happy break; case MOVE_TOP_LEFT: selected->setLeft( x + cx ); case MOVE_TOP: // fall through selected->setTop( y + cy ); break; case MOVE_TOP_RIGHT: selected->setTop( y + cy ); case MOVE_RIGHT: // fall through selected->setRight( x + cx ); break; case MOVE_BOTTOM_LEFT: selected->setBottom( y + cy ); case MOVE_LEFT: // fall through selected->setLeft( x + cx ); break; case MOVE_BOTTOM_RIGHT: selected->setRight( x + cx ); case MOVE_BOTTOM: // fall through selected->setBottom( y + cy ); break; case MOVE_WHOLE: if( selected ) { // lx is the Last x-Koord from the run before (global) mx = x-lx; my = y-ly; /* Check if rectangle would run out of the image on right and bottom */ if( selected->x()+ selected->width()+mx >= ix-cx ) { mx = ix -cx - selected->width() - selected->x(); //kdDebug(29000) << "runs out !" << endl; } if( selected->y()+ selected->height()+my >= iy-cy ) { my = iy -cy - selected->height() - selected->y(); //kdDebug(29000) << "runs out !" << endl; } /* Check if rectangle would run out of the image on left and top */ if( selected->x() +mx < 0 ) mx = -selected->x(); if( selected->y()+ +my < 0 ) my = -selected->y(); x = mx+lx; y = my+ly; selected->moveBy( mx, my ); } } drawAreaBorder(&p); lx = x; ly = y; } } void ImageCanvas::setScaleFactor( int i ) { kdDebug(29000) << "Setting Scalefactor to " << i << endl; scale_factor = i; if( i == 0 ){ kdDebug(29000) << "Setting Dynamic Scaling!" << endl; setScaleKind(DYNAMIC); } update_scaled_pixmap(); } void ImageCanvas::resizeEvent( QResizeEvent * event ) { QScrollView::resizeEvent( event ); update_scaled_pixmap(); } void ImageCanvas::update_scaled_pixmap( void ) { resizeContents( 0,0 ); updateScrollBars(); if( !pmScaled || !image) { // debug( "Pixmap px is null in Update_scaled" ); return; } QApplication::setOverrideCursor(waitCursor); kdDebug(28000) << "Updating scaled_pixmap" << endl; if( scaleKind() == DYNAMIC ) kdDebug(28000) << "Scaling DYNAMIC" << endl; QSize noSBSize( visibleWidth(), visibleHeight()); const int sbWidth = kapp->style().pixelMetric( QStyle::PM_ScrollBarExtent ); // if( verticalScrollBar()->visible() ) noSBSize.width()+=sbWidth; // if( horizontalScrollBar()->visible() ) noSBSize.height()+=sbWidth; switch( scaleKind() ) { case DYNAMIC: // do scaling to window-size used_yscaler = ((double)viewport()-> height()) / ((double)image->height()); used_xscaler = ((double)viewport()-> width()) / ((double)image->width()); scale_factor = 0; break; case FIT_ORIG: used_yscaler = used_xscaler = 1.0; scale_factor = 100; break; case FIT_WIDTH: used_xscaler = used_yscaler = double(noSBSize.width()) / double(image->width()); if( used_xscaler * image->height() >= noSBSize.height() ) { /* substract for scrollbar */ used_xscaler = used_yscaler = double(noSBSize.width() - sbWidth) / double(image->width()); kdDebug(29000) << "FIT WIDTH scrollbar to substract: " << sbWidth << endl; } scale_factor = static_cast(100*used_xscaler); break; case FIT_HEIGHT: used_yscaler = used_xscaler = double(noSBSize.height())/double(image->height()); // scale = int(100.0 * noSBSize.height() / image->height()); if( used_xscaler * image->width() >= noSBSize.width() ) { /* substract for scrollbar */ used_xscaler = used_yscaler = double(noSBSize.height() - sbWidth) / double(image->height()); kdDebug(29000) << "FIT HEIGHT scrollbar to substract: " << sbWidth << endl; // scale = int(100.0*(noSBSize.height() -sbWidth) / image->height()); } scale_factor = static_cast(100*used_xscaler); break; case ZOOM: used_xscaler = used_yscaler = double(getScaleFactor())/100.0; scale_factor = static_cast(100*used_xscaler); break; default: break; } // reconvert the selection to orig size if( selected ) { *selected = inv_scale_matrix.map( (const QRect) *selected ); } scale_matrix.reset(); // transformation matrix inv_scale_matrix.reset(); if( scaleKind() == DYNAMIC && maintain_aspect ) { // printf( "Skaler: x: %f, y: %f\n", x_scaler, y_scaler ); used_xscaler = used_yscaler < used_xscaler ? used_yscaler : used_xscaler; used_yscaler = used_xscaler; } scale_matrix.scale( used_xscaler, used_yscaler ); // define scale factors inv_scale_matrix = scale_matrix.invert(); // for redraw of selection if( selected ) { *selected = scale_matrix.map( (const QRect )*selected ); } #ifdef USE_KPIXMAPIO *pmScaled = pixIO.convertToPixmap(*image); #else pmScaled->convertFromImage( *image ); #endif *pmScaled = pmScaled->xForm( scale_matrix ); // create scaled pixmap /* Resizing to 0,0 never may be dropped, otherwise there are problems * with redrawing of new images. */ resizeContents( static_cast(image->width() * used_xscaler), static_cast(image->height() * used_yscaler ) ); QApplication::restoreOverrideCursor(); } void ImageCanvas::drawHAreaBorder(QPainter &p,int x1,int x2,int y,int r) { if( ! acquired || !image ) return; if(moving!=MOVE_NONE) cr2 = 0; int inc = 1; int cx = contentsX(), cy = contentsY(); if(x2 < x1) inc = -1; if(!r) { if(cr2 & 4) p.setPen(black); else p.setPen(white); } else if(!acquired) p.setPen(QPen(QColor(150,150,150))); for(;;) { if(rect().contains(QPoint(x1,y))) { if( r && acquired ) { int re_x1, re_y; inv_scale_matrix.map( x1+cx, y+cy, &re_x1, &re_y ); re_x1 = MIN( image->width()-1, re_x1 ); re_y = MIN( image->height()-1, re_y ); p.setPen( QPen( QColor( image->pixel(re_x1, re_y)))); } p.drawPoint(x1,y); } if(!r) { cr2++; cr2 &= 7; if(!(cr2&3)) { if(cr2&4) p.setPen(black); else p.setPen(white); } } if(x1==x2) break; x1 += inc; } } void ImageCanvas::drawVAreaBorder(QPainter &p, int x, int y1, int y2, int r ) { if( ! acquired || !image ) return; if( moving!=MOVE_NONE ) cr2 = 0; int inc = 1; if( y2 < y1 ) inc = -1; int cx = contentsX(), cy = contentsY(); if( !r ) { if( cr2 & 4 ) p.setPen(black); else p.setPen(white); } else if( !acquired ) p.setPen( QPen( QColor(150,150,150) ) ); for(;;) { if(rect().contains( QPoint(x,y1) )) { if( r && acquired ) { int re_y1, re_x; inv_scale_matrix.map( x+cx, y1+cy, &re_x, &re_y1 ); re_x = MIN( image->width()-1, re_x ); re_y1 = MIN( image->height()-1, re_y1 ); p.setPen( QPen( QColor( image->pixel( re_x, re_y1) ))); } p.drawPoint(x,y1); } if(!r) { cr2++; cr2 &= 7; if(!(cr2&3)) { if(cr2&4) p.setPen(black); else p.setPen(white); } } if(y1==y2) break; y1 += inc; } } void ImageCanvas::drawAreaBorder(QPainter *p,int r ) { if(selected->isNull()) return; cr2 = cr1; int xinc = 1; if( selected->right() < selected->left()) xinc = -1; int yinc = 1; if( selected->bottom() < selected->top()) yinc = -1; if( selected->width() ) drawHAreaBorder(*p, selected->left() - contentsX(), selected->right()- contentsX(), selected->top() - contentsY(), r ); if( selected->height() ) { drawVAreaBorder(*p, selected->right() - contentsX(), selected->top()- contentsY()+yinc, selected->bottom()- contentsY(),r); if(selected->width()) { drawHAreaBorder(*p, selected->right()-xinc- contentsX(), selected->left()- contentsX(), selected->bottom()- contentsY(),r); drawVAreaBorder(*p, selected->left()- contentsX(), selected->bottom()-yinc- contentsY(), selected->top()- contentsY()+yinc,r); } } } // x and y come as real pixmap-coords, not contents-coords preview_state ImageCanvas::classifyPoint(int x,int y) { if(selected->isEmpty()) return MOVE_NONE; QRect a = selected->normalize(); int top = 0,left = 0,right = 0,bottom = 0; int lx = a.left()-x, rx = x-a.right(); int ty = a.top()-y, by = y-a.bottom(); if( a.width() > delta*2+2 ) lx = abs(lx), rx = abs(rx); if(a.height()>delta*2+2) ty = abs(ty), by = abs(by); if( lx>=0 && lx<=delta ) left++; if( rx>=0 && rx<=delta ) right++; if( ty>=0 && ty<=delta ) top++; if( by>=0 && by<=delta ) bottom++; if( y>=a.top() &&y<=a.bottom() ) { if(left) { if(top) return MOVE_TOP_LEFT; if(bottom) return MOVE_BOTTOM_LEFT; return MOVE_LEFT; } if(right) { if(top) return MOVE_TOP_RIGHT; if(bottom) return MOVE_BOTTOM_RIGHT; return MOVE_RIGHT; } } if(x>=a.left()&&x<=a.right()) { if(top) return MOVE_TOP; if(bottom) return MOVE_BOTTOM; if(selected->contains(QPoint(x,y))) return MOVE_WHOLE; } return MOVE_NONE; } int ImageCanvas::getBrightness() const { return brightness; } int ImageCanvas::getContrast() const { return contrast; } int ImageCanvas::getGamma() const { return gamma; } int ImageCanvas::getScaleFactor() const { return( scale_factor ); } const QImage *ImageCanvas::rootImage( ) { return( image ); } void ImageCanvas::setReadOnly( bool ro ) { d->readOnly = ro; emit( imageReadOnly(ro) ); } bool ImageCanvas::readOnly() { return d->readOnly; } void ImageCanvas::setBrightness(int b) { brightness = b; } void ImageCanvas::setContrast(int c) { contrast = c; } void ImageCanvas::setGamma(int c) { gamma = c; } void ImageCanvas::setKeepZoom( bool k ) { d->keepZoom = k; } ImageCanvas::ScaleKinds ImageCanvas::scaleKind() { if( d->scaleKind == UNSPEC ) return defaultScaleKind(); else return d->scaleKind; } ImageCanvas::ScaleKinds ImageCanvas::defaultScaleKind() { return d->defaultScaleKind; } void ImageCanvas::setScaleKind( ScaleKinds k ) { if( k == d->scaleKind ) return; // no change, return d->scaleKind = k; emit scalingChanged(scaleKindString()); } void ImageCanvas::setDefaultScaleKind( ScaleKinds k ) { d->defaultScaleKind = k; } const QString ImageCanvas::imageInfoString( int w, int h, int d ) { if( w == 0 && h == 0 && d == 0 ) { if( image ) { w = image->width(); h = image->height(); d = image->depth(); } else return QString("-"); } return i18n("%1x%2 pixel, %3 bit").arg(w).arg(h).arg(d); } const QString ImageCanvas::scaleKindString() { switch( scaleKind() ) { case DYNAMIC: return i18n("Fit window best"); break; case FIT_ORIG: return i18n("Original size"); break; case FIT_WIDTH: return i18n("Fit Width"); break; case FIT_HEIGHT: return i18n("Fit Height"); break; case ZOOM: return i18n("Zoom to %1 %%").arg( QString::number(getScaleFactor())); break; default: return i18n("Unknown scaling!"); break; } } int ImageCanvas::highlight( const QRect& rect, const QPen& pen, const QBrush&, bool ensureVis ) { QRect saveRect; saveRect.setRect( rect.x()-2, rect.y()-2, rect.width()+4, rect.height()+4 ); d->highlightRects.append( saveRect ); int idx = d->highlightRects.findIndex(saveRect); QRect targetRect = scale_matrix.map( rect ); QPainter p( pmScaled ); p.setPen(pen); p.drawLine( targetRect.x(), targetRect.y()+targetRect.height(), targetRect.x()+targetRect.width(), targetRect.y()+targetRect.height() ); p.flush(); updateContents(targetRect.x()-1, targetRect.y()-1, targetRect.width()+2, targetRect.height()+2 ); if( ensureVis ) { QPoint p = targetRect.center(); ensureVisible( p.x(), p.y(), 10+targetRect.width()/2, 10+targetRect.height()/2 ); } return idx; } void ImageCanvas::removeHighlight( int idx ) { if( (unsigned) idx >= d->highlightRects.count() ) { kdDebug(28000) << "removeHighlight: Not a valid index" << endl; return; } /* take the rectangle from the stored highlight rects and map it to the viewer scaling */ QRect r = d->highlightRects[idx]; d->highlightRects.remove(r); QRect targetRect = scale_matrix.map( r ); /* create a small pixmap with a copy of the region in question of the original image */ QPixmap origPix; origPix.convertFromImage( image->copy(r) ); /* and scale it */ QPixmap scaledPix = origPix.xForm( scale_matrix ); /* and finally draw it */ QPainter p( pmScaled ); p.drawPixmap( targetRect, scaledPix ); p.flush(); /* update the viewers contents */ updateContents(targetRect.x()-1, targetRect.y()-1, targetRect.width()+2, targetRect.height()+2 ); } #include "img_canvas.moc" diff --git a/libkscan/img_canvas.h b/libkscan/img_canvas.h index 5abf267..f3e4f3f 100644 --- a/libkscan/img_canvas.h +++ b/libkscan/img_canvas.h @@ -1,221 +1,221 @@ /* This file is part of the KDE Project Copyright (C) 1999 Klaas Freitag 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. */ #ifndef __IMG_CANVAS_H__ #define __IMG_CANVAS_H__ #include #include #include #include #include #include #include #ifdef USE_KPIXMAPIO #include #endif class QPopupMenu; class QPixmap; class QImage; class QPainter; enum preview_state { MOVE_NONE, MOVE_TOP_LEFT, MOVE_TOP_RIGHT, MOVE_BOTTOM_LEFT, MOVE_BOTTOM_RIGHT, MOVE_LEFT, MOVE_RIGHT, MOVE_TOP, MOVE_BOTTOM, MOVE_WHOLE }; enum cursor_type { CROSS, VSIZE, HSIZE, BDIAG, FDIAG, ALL, HREN }; const int MIN_AREA_WIDTH = 3; const int MIN_AREA_HEIGHT = 3; const int delta = 3; #ifdef __PREVIEW_CPP__ int max_dpi = 600; #else extern int max_dpi; #endif class ImageCanvas: public QScrollView { Q_OBJECT Q_ENUMS( PopupIDs ) Q_PROPERTY( int brightness READ getBrightness WRITE setBrightness ) Q_PROPERTY( int contrast READ getContrast WRITE setContrast ) Q_PROPERTY( int gamma READ getGamma WRITE setGamma ) Q_PROPERTY( int scale_factor READ getScaleFactor WRITE setScaleFactor ) public: ImageCanvas( QWidget *parent = 0, const QImage *start_image = 0, const char *name = 0); ~ImageCanvas( ); int getBrightness() const; int getContrast() const; int getGamma() const; int getScaleFactor() const; const QImage *rootImage(); bool hasImage( void ) { return acquired; } QPopupMenu* contextMenu() { return m_contextMenu; } QRect sel( void ); enum ScaleKinds { UNSPEC, DYNAMIC, FIT_ORIG, FIT_WIDTH, FIT_HEIGHT, ZOOM }; enum PopupIDs { ID_POP_ZOOM, ID_POP_CLOSE, ID_FIT_WIDTH, ID_FIT_HEIGHT, ID_ORIG_SIZE }; bool selectedImage( QImage* ); ScaleKinds scaleKind(); const QString scaleKindString(); ScaleKinds defaultScaleKind(); const QString imageInfoString( int w=0, int h=0, int d=0 ); public slots: void setBrightness(int); void setContrast(int ); void setGamma(int ); void toggleAspect( int aspect_in_mind ) { maintain_aspect = aspect_in_mind; repaint(); } virtual QSize sizeHint() const; - void newImage( QImage* ); + void newImage(const QImage* ); void newImageHoldZoom( QImage* ); void deleteView( QImage *); void newRectSlot(); void newRectSlot( QRect newSel ); void noRectSlot( void ); void setScaleFactor( int i ); void handle_popup(int item ); void enableContextMenu( bool wantContextMenu ); void setKeepZoom( bool k ); void setScaleKind( ScaleKinds k ); void setDefaultScaleKind( ScaleKinds k ); /** * Highlight a rectangular area on the current image using the given brush * and pen. * The function returns a id that needs to be given to the remove method. */ int highlight( const QRect&, const QPen&, const QBrush&, bool ensureVis=false ); /** * reverts the highlighted region back to normal view. */ void removeHighlight( int idx = -1 ); /** * permit to do changes to the image that are saved back to the file */ void setReadOnly( bool ); bool readOnly(); signals: void noRect( void ); void newRect( void ); void newRect( QRect ); void scalingRequested(); void closingRequested(); void scalingChanged( const QString& ); /** * signal emitted if the permission of the currently displayed image changed, * ie. if it goes from writeable to readable. * @param shows if the image is now read only (true) or not. */ void imageReadOnly( bool isRO ); protected: void drawContents( QPainter * p, int clipx, int clipy, int clipw, int cliph ); void timerEvent(QTimerEvent *); void viewportMousePressEvent(QMouseEvent *); void viewportMouseReleaseEvent(QMouseEvent *); void viewportMouseMoveEvent(QMouseEvent *); void resizeEvent( QResizeEvent * event ); private: QStrList urls; int scale_factor; const QImage *image; int brightness, contrast, gamma; #ifdef USE_KPIXMAPIO KPixmapIO pixIO; #endif QWMatrix scale_matrix; QWMatrix inv_scale_matrix; QPixmap *pmScaled; float used_yscaler; float used_xscaler; QPopupMenu *m_contextMenu; bool maintain_aspect; int timer_id; QRect *selected; preview_state moving; int cr1,cr2; int lx,ly; bool acquired; /* private functions for the running ant */ void drawHAreaBorder(QPainter &p,int x1,int x2,int y,int r = FALSE); void drawVAreaBorder(QPainter &p,int x,int y1,int y2,int r = FALSE); void drawAreaBorder(QPainter *p,int r = FALSE); void update_scaled_pixmap( void ); preview_state classifyPoint(int x,int y); class ImageCanvasPrivate; ImageCanvasPrivate *d; }; #endif diff --git a/libkscan/kscandevice.cpp b/libkscan/kscandevice.cpp index a0465bb..57927b1 100644 --- a/libkscan/kscandevice.cpp +++ b/libkscan/kscandevice.cpp @@ -1,1617 +1,1614 @@ /* This file is part of the KDE Project Copyright (C) 1999 Klaas Freitag 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kgammatable.h" #include "kscandevice.h" #include "kscancontrols.h" #include "kscanoption.h" #include "kscanoptset.h" #include "devselector.h" #include "imgscaninfo.h" #include #define MIN_PREVIEW_DPI 75 #define UNDEF_SCANNERNAME I18N_NOOP( "undefined" ) #define MAX_PROGRESS 100 #define USERDEV_GROUP "User Specified Scanners" #define USERDEV_DEVS "Devices" #define USERDEV_DESC "Description" /* --------------------------------------------------------------------------- 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 */ 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 (NULL); if (!optionExists(name)) return (NULL); QCString alias = aliasName( name ); kdDebug(29000) << k_funcinfo << "for name=[" << name << "] desc=[" << desc << "] alias=[" << alias << "]" << endl; /* Check if already exists */ KScanOption *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 ); QWidget *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 ) { kdDebug(29000) << k_funcinfo << endl; /* Get SANE translations - bug 98150 */ KGlobal::dirs()->addResourceDir( "locale", QString::fromLatin1("/usr/share/locale/") ); KGlobal::locale()->insertCatalogue( QString::fromLatin1("sane-backends") ); sane_stat = sane_init(NULL, NULL ); d = new KScanDevicePrivate(); option_dic = new QAsciiDict; 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; } } KSimpleConfig scanConfig(SCANNER_DB_FILE,true); scanConfig.setGroup(USERDEV_GROUP); QStringList devs = scanConfig.readListEntry(USERDEV_DEVS); QStringList dscs = scanConfig.readListEntry(USERDEV_DESC); if (devs.count()>0) { QStringList dscs = scanConfig.readListEntry(USERDEV_DESC); QStringList::const_iterator it2 = dscs.begin(); for (QStringList::const_iterator it1 = devs.begin(); it1!=devs.end(); ++it1,++it2) { addUserSpecifiedDevice((*it1),(*it2)); } } #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; } void KScanDevice::addUserSpecifiedDevice(const QString& backend,const QString& description,bool dontSave) { if (backend.isEmpty()) return; kdDebug(29000) << k_funcinfo << "adding [" << backend << "]='" << description << "' dontSave=" << dontSave << endl; if (!dontSave) { KSimpleConfig scanConfig(SCANNER_DB_FILE); scanConfig.setGroup(USERDEV_GROUP); QStringList devs = scanConfig.readListEntry(USERDEV_DEVS); QStringList dscs = scanConfig.readListEntry(USERDEV_DESC); // get existing device lists int i = devs.findIndex(backend); if (i>=0) // see if already in list { dscs[i] = description; // if so just update } else { devs.append(backend); // add new entry to lists dscs.append(description); } scanConfig.writeEntry(USERDEV_DEVS,devs); scanConfig.writeEntry(USERDEV_DESC,dscs); scanConfig.sync(); } SANE_Device *userdev = new SANE_Device; // Need a permanent copy of the strings, because SANE_Device only holds // pointers to them. Unfortunately there is a memory leak here, the // two QCString's are never deleted. There is only one of these objects // in most applications, so hopefully it won't matter too much. userdev->name = *(new QCString(backend.local8Bit())); userdev->model = *(new QCString(description.local8Bit())); userdev->vendor = "User specified"; userdev->type = "scanner"; scanner_avail.append(userdev->name); scannerDevices.insert(userdev->name,userdev); } KScanStat KScanDevice::openDevice( const QCString& backend ) { KScanStat stat = KSCAN_OK; kdDebug(29000) << k_funcinfo << "backend=[" << backend << "]" << endl; sane_stat = SANE_STATUS_UNSUPPORTED; if( backend.isEmpty() ) return (KSCAN_ERR_PARAM); // search for scanner int indx = scanner_avail.find( backend ); if( indx < 0 ) return (KSCAN_ERR_NO_DEVICE); // if available, build lists of properties 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 ); } QString KScanDevice::lastErrorMessage() const { kdDebug(29000) << k_funcinfo << "stat=" << sane_stat << endl; return (sane_strstatus(sane_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 { 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; iname ) { // 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_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 ) { 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 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 ) { 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 )); } } else { return KSCAN_ERR_PARAM; } } 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_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; 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; } } 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; } /* 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 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) << k_funcinfo << "status=" << status << endl; if( data ) { delete[] data; data = NULL; } 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(d->currScanResolutionX / 0.0254 + 0.5)); img->setDotsPerMeterY(static_cast(d->currScanResolutionY / 0.0254 + 0.5)); 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_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 ) +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 ); + if (setName.isEmpty()) return; + kdDebug(29000) << k_funcinfo << "Saving 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 *KScanDevice::option_dic = 0L; KScanOptSet *KScanDevice::gammaTables = 0L; #include "kscandevice.moc" diff --git a/libkscan/kscanoption.cpp b/libkscan/kscanoption.cpp index b62b9a3..ffd7837 100644 --- a/libkscan/kscanoption.cpp +++ b/libkscan/kscanoption.cpp @@ -1,1111 +1,1112 @@ /* This file is part of the KDE Project Copyright (C) 2000 Klaas Freitag 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 #include #include #include #include #include #include #include extern "C" { #include } #include "kgammatable.h" #include "kscandevice.h" #include "kscancontrols.h" #include "kscanoption.h" #include "kscanoption.moc" // #define MIN_PREVIEW_DPI 20 // this is nowhere used!? Additionally, it's defined to 75 in kscandevice.cpp /* switch to show some from time to time useful alloc-messages */ #undef MEM_DEBUG #undef GETSET_DEBUG #undef APPLY_IN_SITU // This defines the possible resolutions that will be shown by the combo. // Only resolutions from this list falling within the scanner's allowed range // will be included. static const int resList[] = { 50, 75, 100, 150, 200, 300, 600, 900, 1200, 1800, 2400, 4800, 9600, 0 }; /** inline-access to the option descriptor, object to changes with global vars. **/ inline const SANE_Option_Descriptor *getOptionDesc( const QCString& name ) { int *idx = KScanDevice::option_dic->find(name); const SANE_Option_Descriptor *d = 0; // debug( "<< for option %s >>", name ); if ( idx && *idx > 0 ) { d = sane_get_option_descriptor( KScanDevice::scanner_handle, *idx ); // debug( "retrieving Option %s", d->name ); } else { kdDebug(29000) << "no option descriptor for <" << name << ">" << endl; // debug( "Name survived !" ); } // debug( "<< leaving option %s >>", name ); return( d ); } /* ************************************************************************ */ /* KScan Option */ /* ************************************************************************ */ KScanOption::KScanOption(const QCString& new_name) { if (!initOption(new_name)) { kdDebug(29000) << k_funcinfo << "initOption for <" << new_name << "> failed!" << endl; return; } int *num = KScanDevice::option_dic->find(name); if (num==NULL || buffer.isNull()) return; SANE_Status sane_stat = sane_control_option(KScanDevice::scanner_handle, *num, SANE_ACTION_GET_VALUE, buffer.data(),NULL); if (sane_stat==SANE_STATUS_GOOD) buffer_untouched = false; } bool KScanOption::initOption(const QCString& new_name) { desc = NULL; if (new_name.isEmpty()) return (false); name = new_name; desc = getOptionDesc(name); buffer.resize(0); buffer_untouched = true; internal_widget = NULL; if (desc==NULL) return (false); /* Gamma-Table - initial values */ gamma = 0; /* marks as unvalid */ brightness = 0; contrast = 0; gamma = 100; allocForDesc(); KScanOption *gtOption = (*KScanDevice::gammaTables)[ new_name ]; if( gtOption ) { kdDebug(29000) << "Is older GammaTable!" << endl; KGammaTable gt; gtOption->get( > ); gamma = gt.getGamma(); contrast = gt.getContrast(); brightness = gt.getBrightness(); } return (true); } KScanOption::KScanOption(const KScanOption &so) : QObject() { desc = so.desc; // stored by sane-lib -> may be copied name = so.name; // QCString explicit sharing -> shallow copy internal_widget = NULL; // the widget is not copied! gamma = so.gamma; brightness = so.brightness; contrast = so.contrast; if (desc==NULL && name.isNull()) { kdWarning(29000) << "Trying to copy a not healthy option (no name nor desc)" << endl; return; } buffer = so.buffer.copy(); // QByteArray -> deep copy buffer_untouched = so.buffer_untouched; } const QString KScanOption::configLine() { QCString strval = this->get(); - kdDebug(29000) << "configLine returns <" << strval << ">" << endl; + //kdDebug(29000) << "configLine returns <" << strval << ">" << endl; return (strval); } const KScanOption &KScanOption::operator=(const KScanOption &so) { if (this==&so) return (*this); desc = so.desc; // stored by sane-lib -> may be copied name = so.name; // QCString explicit sharing -> shallow copy gamma = so.gamma; brightness = so.brightness; contrast = so.contrast; delete internal_widget; // widget *is* copied here internal_widget = so.internal_widget; buffer.resize(0); // throw away old buffer buffer = so.buffer.copy(); // QByteArray -> deep copy buffer_untouched = so.buffer_untouched; return (*this); } void KScanOption::slWidgetChange( const QCString& t ) { //kdDebug(29000) << k_funcinfo << "received WidgetChange for " << getName() << endl; set( t ); emit( guiChange( this ) ); // emit( optionChanged( this )); } void KScanOption::slWidgetChange( void ) { //kdDebug(29000) << k_funcinfo << "received WidgetChange for " << getName() << endl; /* If Type is bool, the widget is a checkbox. */ if( type() == KScanOption::Bool ) { bool b = ((QCheckBox*) internal_widget)->isChecked(); set( b ); } emit( guiChange( this ) ); // emit( optionChanged( this )); } void KScanOption::slWidgetChange( int i ) { //kdDebug(29000) << k_funcinfo << "received WidgetChange for " << getName() << endl; set( i ); emit( guiChange( this ) ); // emit( optionChanged( this )); } // TODO: eliminate redundant 'so' parameter, see comment below! /* this slot is called on a widget change, if a widget was created. * In normal case, it is internally connected, so the param so and * this are equal ! */ void KScanOption::slRedrawWidget( KScanOption *so ) { int i = 0; QWidget *w = so->widget(); QString string; if (!so->valid() || w==NULL || so->getBuffer()==NULL) return; switch (so->type()) { case KScanOption::Bool: /* Widget Type is Toggle Button */ if (so->get(&i)) static_cast(w)->setChecked((bool) i); break; case KScanOption::SingleValue: /* Widget Type is Entry Field */ break; /* not implemented yet */ case KScanOption::Range: /* Widget Type is Slider */ if (so->get(&i)) static_cast(w)->slSetSlider(i); break; case KScanOption::Resolution: /* Widget Type is Resolution Combo */ static_cast(w)->slSetEntry(so->get()); break; case KScanOption::GammaTable: /* Widget Type is Gamma Table */ break; case KScanOption::StringList: /* Widget Type is Selection Box */ static_cast(w)->slSetEntry(so->get()); break; case KScanOption::String: /* Widget Type is String */ static_cast(w)->slSetEntry(so->get()); break; case KScanOption::File: /* Widget Type is File */ static_cast(w)->slSetEntry(so->get()); break; default: break; } } /* In this slot, the option queries the scanner for current values. */ void KScanOption::slReload() { int *num = KScanDevice::option_dic->find(name); if (!valid() || num==NULL) return; desc = getOptionDesc(name); if (widget()!=NULL) { if (!active()) { kdDebug(29000) << desc->name << " is not active" << endl; widget()->setEnabled(false); } else if (!softwareSetable()) { kdDebug(29000) << desc->name << " is not software setable" << endl; widget()->setEnabled(false); } else widget()->setEnabled(true); } if (buffer.isNull()) /* first get mem if needed */ { kdDebug(29000) << k_funcinfo << "need to allocate now" << endl; - allocForDesc(); + allocForDesc(); // allocate the buffer now } if (!active()) return; if (((size_t) desc->size)>buffer.size()) { - kdDebug(29000) << "ERROR: Buffer too small for <" << name << ">" + kdDebug(29000) << k_funcinfo << "buffer too small for <" << name << ">" << " size " << buffer.size() << " need " << desc->size << endl; - return; + allocForDesc(); // grow the buffer + //return; } SANE_Status sane_stat = sane_control_option( KScanDevice::scanner_handle, *num, SANE_ACTION_GET_VALUE, buffer.data(),NULL); if( sane_stat != SANE_STATUS_GOOD ) { kdDebug(29000) << "ERROR: Can't get value for <" << name << "> " << sane_strstatus( sane_stat ) << endl; return; } //kdDebug(29000) << "Setting buffer untouched to FALSE" << endl; buffer_untouched = false; } KScanOption::~KScanOption() { #ifdef MEM_DEBUG if (!buffer.isNull()) kdDebug(29000) << k_funcinfo << "Freeing " << buffer.size() << " bytes for <" << name << ">" << endl; #endif } bool KScanOption::autoSetable( void ) { /* Refresh description */ desc = getOptionDesc( name ); return( desc && ((desc->cap & SANE_CAP_AUTOMATIC) > 0 ) ); } bool KScanOption::commonOption( void ) { /* Refresh description */ desc = getOptionDesc( name ); return( desc && ((desc->cap & SANE_CAP_ADVANCED) == 0) ); } bool KScanOption::active( void ) { bool ret = false; /* Refresh description */ desc = getOptionDesc( name ); if( desc ) ret = SANE_OPTION_IS_ACTIVE( desc->cap ); return( ret ); } bool KScanOption::softwareSetable( void ) { /* Refresh description */ desc = getOptionDesc( name ); if( desc ) { if( SANE_OPTION_IS_SETTABLE(desc->cap) == SANE_TRUE ) return( true ); } return( false ); } KScanOption::WidgetType KScanOption::type() const { KScanOption::WidgetType ret = KScanOption::Invalid; if (valid()) { switch (desc->type) { case SANE_TYPE_BOOL: ret = KScanOption::Bool; break; case SANE_TYPE_INT: case SANE_TYPE_FIXED: if (desc->constraint_type == SANE_CONSTRAINT_RANGE) { if (desc->unit==SANE_UNIT_DPI) ret = KScanOption::Resolution; else { /* FIXME ! Dies scheint nicht wirklich so zu sein */ /* in other words: This does not really seem to be the case */ if (desc->size==sizeof(SANE_Word)) ret = KScanOption::Range; else ret = KScanOption::GammaTable; } } else if(desc->constraint_type==SANE_CONSTRAINT_NONE) { ret = KScanOption::SingleValue; } else if (desc->constraint_type==SANE_CONSTRAINT_WORD_LIST) { ret = KScanOption::StringList; } else { ret = KScanOption::Invalid; } break; case SANE_TYPE_STRING: if (QCString(desc->name)=="filename") ret = KScanOption::File; else if (desc->constraint_type==SANE_CONSTRAINT_STRING_LIST) ret = KScanOption::StringList; else ret = KScanOption::String; break; default: ret = KScanOption::Invalid; break; } } //kdDebug(29000) << k_funcinfo << "for SANE type " << (desc ? desc->type : -1) << " returning " << ret << endl; return (ret); } bool KScanOption::set(int val) { if (!valid() || buffer.isNull()) return (false); #ifdef GETSET_DEBUG kdDebug(29000) << k_funcinfo << "Setting <" << name << "> to " << val << endl; #endif int word_size; QMemArray qa; SANE_Word sw; switch (desc->type) { case SANE_TYPE_BOOL: // Assign a Boolean value sw = (val ? SANE_TRUE : SANE_FALSE); buffer.duplicate(((const char *) &sw),sizeof(SANE_Word)); break; case SANE_TYPE_INT: // Fill the whole buffer with that value word_size = desc->size/sizeof(SANE_Word); qa.resize(word_size); sw = static_cast(val); qa.fill(sw); buffer.duplicate(((const char *) qa.data()),desc->size); break; case SANE_TYPE_FIXED: // Fill the whole buffer with that value word_size = desc->size/sizeof(SANE_Word); qa.resize(word_size); sw = SANE_FIX(static_cast(val)); qa.fill(sw); buffer.duplicate(((const char *) qa.data()),desc->size); break; default: kdDebug(29000) << k_funcinfo << "Can't set <" << name << "> with this type" << endl; return (false); break; } buffer_untouched = false; #ifdef APPLY_IN_SITU applyVal(); #endif //emit( optionChanged( this )); return (true); } bool KScanOption::set(double val) { if (!valid() || buffer.isNull()) return (false); #ifdef GETSET_DEBUG kdDebug(29000) << k_funcinfo << "Setting <" << name << "> to " << val << endl; #endif int word_size; QMemArray qa; SANE_Word sw; switch (desc->type) { case SANE_TYPE_BOOL: // Assign a Boolean value sw = (val>0 ? SANE_TRUE : SANE_FALSE); buffer.duplicate(((const char *) &sw),sizeof(SANE_Word)); break; case SANE_TYPE_INT: // Fill the whole buffer with that value word_size = desc->size/sizeof(SANE_Word); qa.resize(word_size); sw = static_cast(val); qa.fill(sw); buffer.duplicate(((const char *) qa.data()),desc->size); break; case SANE_TYPE_FIXED: // Fill the whole buffer with that value word_size = desc->size/sizeof(SANE_Word); qa.resize(word_size); sw = SANE_FIX(val); qa.fill(sw); buffer.duplicate(((const char *) qa.data()),desc->size); break; default: kdDebug(29000) << k_funcinfo << "Can't set <" << name << "> with this type" << endl; return (false); break; } buffer_untouched = false; #ifdef APPLY_IN_SITU applyVal(); #endif //emit optionChanged( this ); return (true); } bool KScanOption::set(int *val,int size) { if (!valid() || buffer.isNull()) return (false); if (val==NULL) return (false); #ifdef GETSET_DEBUG kdDebug(29000) << k_funcinfo << "Setting <" << name << "> to [" << size << "]" << endl; #endif int offset = 0; int word_size = desc->size/sizeof(SANE_Word); QMemArray qa(1+word_size); /* add 1 in case offset is needed */ #if 0 if( desc->constraint_type == SANE_CONSTRAINT_WORD_LIST ) { /* That means that the first entry must contain the size */ kdDebug(29000) << "Size: " << size << ", word_size: " << word_size << ", descr-size: "<< desc->size << endl; qa[0] = (SANE_Word) 1+size; kdDebug(29000) << "set length field to " << qa[0] <type) { case SANE_TYPE_INT: for (int i = 0; i with this type" << endl; return (false); break; } int copybyte = desc->size; if (offset) copybyte += sizeof(SANE_Word); //kdDebug(29000) << "Copying " << copybyte << " byte to options buffer" << endl; buffer.duplicate(((const char *) qa.data()),copybyte); buffer_untouched = false; #ifdef APPLY_IN_SITU applyVal(); #endif //emit( optionChanged( this )); return (true); } bool KScanOption::set(const QCString &c_string) { if (!valid() || buffer.isNull()) return (false); #ifdef GETSET_DEBUG kdDebug(29000) << k_funcinfo << "Setting <" << name << "> to '" << c_string << "'" << endl; #endif int val; /* Check if it is a gammatable. If it is, convert to KGammaTable and call * the approbiate set method. */ QRegExp re( "\\d+, \\d+, \\d+" ); re.setMinimal(true); if (QString(c_string).contains(re)) { QStringList relist = QStringList::split( ", ", QString(c_string) ); int brig = (relist[0]).toInt(); int contr = (relist[1]).toInt(); int gamm = (relist[2]).toInt(); KGammaTable gt( brig, contr, gamm ); kdDebug(29000) << "Setting GammaTable with int vals " << brig << "," << contr << "," << gamm << endl; return (set(>)); } /* On String-type the buffer gets malloced in Constructor */ switch (desc->type) { case SANE_TYPE_STRING: buffer.duplicate(c_string.data(),(c_string.length()+1)); break; case SANE_TYPE_INT: case SANE_TYPE_FIXED: bool ok; val = c_string.toInt(&ok); if (ok) set(&val,1); else { kdDebug(29000) << k_funcinfo << "Conversion of string value [" << c_string << "] failed!" << endl; return (false); } break; case SANE_TYPE_BOOL: val = (c_string=="true") ? 1 : 0; set(val); break; default: kdDebug(29000) << k_funcinfo << "Can't set <" << name << "> with this type" << endl; return (false); break; } buffer_untouched = false; #ifdef APPLY_IN_SITU applyVal(); #endif //emit( optionChanged( this )); return (true); } bool KScanOption::set(KGammaTable *gt) { if (!valid() || buffer.isNull()) return (false); #ifdef GETSET_DEBUG kdDebug(29000) << k_funcinfo << "Setting <" << name << ">" << endl; #endif int size = gt->tableSize(); SANE_Word *run = gt->getTable(); int word_size = desc->size/sizeof(SANE_Word); QMemArray qa(word_size); switch (desc->type) { case SANE_TYPE_INT: for (int i = 0; i with this type" << endl; return (false); break; } /* Remember raw values */ gamma = gt->getGamma(); brightness = gt->getBrightness(); contrast = gt->getContrast(); buffer.duplicate(((const char *) qa.data()),desc->size); buffer_untouched = false; #ifdef APPLY_IN_SITU applyVal(); #endif //emit( optionChanged( this )); return (true); } bool KScanOption::get(int *val) const { if (!valid() || buffer.isNull()) return (false); SANE_Word sane_word; double d; switch (desc->type) { case SANE_TYPE_BOOL: /* Buffer has a SANE_Word */ sane_word = *((SANE_Word *) buffer.data()); *val = (sane_word==SANE_TRUE ? 1 : 0); break; case SANE_TYPE_INT: /* reading just the first is OK */ sane_word = *((SANE_Word *) buffer.data()); *val = sane_word; break; case SANE_TYPE_FIXED: /* reading just the first is OK */ d = SANE_UNFIX(*((SANE_Word *) buffer.data())); *val = static_cast(d); break; default: kdDebug(29000) << k_funcinfo << "Can't get <" << name << "> as this type" << endl; return (false); } #ifdef GETSET_DEBUG kdDebug(29000) << k_funcinfo << "Returning <" << name << "> as " << *val << endl; #endif return (true); } QCString KScanOption::get() const { - if (!valid() || buffer.isNull()) return ("parametererror"); + if (!valid() || buffer.isNull()) return (PARAM_ERROR); QCString retstr; SANE_Word sane_word; /* Handle gamma-table correctly */ if (type()==KScanOption::GammaTable) { retstr.sprintf( "%d, %d, %d", gamma, brightness, contrast ); } else { switch (desc->type) { case SANE_TYPE_BOOL: sane_word = *((SANE_Word *) buffer.data()); retstr = (sane_word==SANE_TRUE ? "true" : "false"); break; case SANE_TYPE_STRING: retstr = (const char*) buffer.data(); break; case SANE_TYPE_INT: sane_word = *((SANE_Word *) buffer.data()); retstr.setNum(sane_word); break; case SANE_TYPE_FIXED: sane_word = (SANE_Word) SANE_UNFIX(*((SANE_Word *) buffer.data())); retstr.setNum(sane_word); break; default: kdDebug(29000) << k_funcinfo << "Can't get <" << name << "> as this type" << endl; retstr = "unknown"; break; } } #ifdef GETSET_DEBUG kdDebug(29000) << k_funcinfo << "Returning <" << name << "> as '" << retstr << "'" << endl; #endif return (retstr); } /* Caller needs to have the space ;) */ bool KScanOption::get( KGammaTable *gt ) const { if (gt==NULL) return (false); gt->setAll( gamma, brightness, contrast ); // gt->calcTable(); return (true); } QStrList KScanOption::getList() const { if (desc==NULL) return (false); const char **sstring = NULL; QStrList strList; if (desc->constraint_type==SANE_CONSTRAINT_STRING_LIST) { sstring = (const char **)desc->constraint.string_list; while (*sstring!=NULL) { strList.append(*sstring); sstring++; } } else if (desc->constraint_type==SANE_CONSTRAINT_WORD_LIST) { const SANE_Int *sint = desc->constraint.word_list; int amount_vals = *sint; sint++; QString s; for (int i = 0; itype==SANE_TYPE_FIXED) s.sprintf("%f",SANE_UNFIX(*sint)); else s.sprintf("%d",*sint); sint++; strList.append(s.local8Bit()); } } else if (desc->constraint_type==SANE_CONSTRAINT_RANGE && type()==KScanOption::Resolution) { double min,max,q; int imin,imax; getRange( &min, &max, &q ); imin = static_cast(min); imax = static_cast(max); for (const int *ip = resList; *ip!=0; ++ip) { if (*ipimax) continue; strList.append(QString::number(*ip).ascii()); } } return (strList); } bool KScanOption::getRangeFromList( double *min, double *max, double *q ) const { if( !desc ) return( false ); bool ret = true; if ( desc->constraint_type == SANE_CONSTRAINT_WORD_LIST ) { // Try to read resolutions from word list kdDebug(29000) << "Resolutions are in a word list" << endl; const SANE_Int *sint = desc->constraint.word_list; int amount_vals = *sint; sint ++; double value; *min = 0; *max = 0; *q = -1; // What is q? for( int i=0; i < amount_vals; i++ ) { if( desc->type == SANE_TYPE_FIXED ) { value = (double) SANE_UNFIX( *sint ); } else { value = *sint; } if ((*min > value) || (*min == 0)) *min = value; if ((*max < value) || (*max == 0)) *max = value; if( min != 0 && max != 0 && max > min ) { double newq = max - min; *q = newq; } sint++; } } else { kdDebug(29000) << "getRangeFromList: No list type " << desc->name << endl; ret = false; } return( ret ); } bool KScanOption::getRange( double *min, double *max, double *q ) const { if( !desc ) return( false ); bool ret = true; if( desc->constraint_type == SANE_CONSTRAINT_RANGE || desc->constraint_type == SANE_CONSTRAINT_WORD_LIST ) { const SANE_Range *r = desc->constraint.range; if( desc->type == SANE_TYPE_FIXED ) { *min = (double) SANE_UNFIX( r->min ); *max = (double) SANE_UNFIX( r->max ); *q = (double) SANE_UNFIX( r->quant ); } else { *min = r->min ; *max = r->max ; *q = r->quant ; } } else { kdDebug(29000) << "getRange: No range type " << desc->name << endl; ret = false; } return( ret ); } QWidget *KScanOption::createWidget( QWidget *parent, const QString& w_desc, const QString& tooltip ) { QStrList list; if (!valid()) { kdDebug(29000) << "The option is not valid!" << endl; return (NULL); } delete internal_widget; internal_widget = NULL; /* free the old widget */ text = w_desc; /* check for text */ if (text.isEmpty() && desc!=NULL) text = QString::fromLocal8Bit( desc->title ); if (type()!=KScanOption::Bool) text += ":"; kdDebug(29000) << k_funcinfo << "type=" << type() << " text=[" << text << "]" << endl; QWidget *w = NULL; switch (type()) { case KScanOption::Bool: /* Widget Type is ToggleButton */ w = createToggleButton( parent, text ); break; case KScanOption::SingleValue: /* Widget Type is Entry-Field */ kdDebug(29000) << k_funcinfo << "SingleValue not implemented yet" << endl; break; case KScanOption::Range: /* Widget Type is Slider */ w = createSlider( parent, text ); break; case KScanOption::Resolution: /* Widget Type is Resolution */ w = createComboBox( parent, text ); break; case KScanOption::GammaTable: /* Widget Type is Gamma Table */ kdDebug(29000) << k_funcinfo << "GammaTable not implemented here" << endl; break; // No widget, needs to be a set ! case KScanOption::StringList: w = createComboBox( parent, text ); /* Widget Type is Selection Box */ break; case KScanOption::String: w = createEntryField( parent, text ); /* Widget Type is String Entry */ break; case KScanOption::File: w = createFileField( parent, text ); /* Widget Type is Filename */ break; default: kdDebug(29000) << k_funcinfo << "cannot create widget for unknown type " << type() << endl; break; } if (w!=NULL) { internal_widget = w; connect(this, SIGNAL(optionChanged(KScanOption *)),SLOT(slRedrawWidget(KScanOption *))); QString tt = tooltip; if (tt.isEmpty() && desc!=NULL) tt = QString::fromLocal8Bit( desc->desc ); if (!tt.isEmpty()) QToolTip::add(internal_widget, i18n(tt.utf8())); } slReload(); /* Check if option is active, setEnabled etc. */ if (w!=NULL) slRedrawWidget(this); return (w); } inline QWidget *KScanOption::createToggleButton( QWidget *parent, const QString& text ) { QWidget *cb = new QCheckBox( i18n(text.utf8()), parent); connect( cb, SIGNAL(clicked()), SLOT(slWidgetChange())); return (cb); } inline QWidget *KScanOption::createComboBox( QWidget *parent, const QString& text ) { QStrList list = getList(); KScanCombo *cb = new KScanCombo( parent, text, list); connect(cb, SIGNAL(valueChanged(const QCString&)), SLOT(slWidgetChange(const QCString&))); return (cb); } inline QWidget *KScanOption::createEntryField( QWidget *parent, const QString& text ) { KScanEntry *ent = new KScanEntry( parent, text ); connect(ent, SIGNAL(valueChanged(const QCString &)), SLOT(slWidgetChange(const QCString &))); return (ent); } inline QWidget *KScanOption::createSlider( QWidget *parent, const QString& text ) { double min, max, quant; getRange( &min, &max, &quant ); KScanSlider *slider = new KScanSlider( parent, text, min, max,true ); connect(slider, SIGNAL(valueChanged(int)), SLOT(slWidgetChange(int))); return (slider); } inline QWidget *KScanOption::createFileField( QWidget *parent, const QString& text ) { KScanFileRequester *req = new KScanFileRequester(parent,text); connect( req, SIGNAL(valueChanged(const QCString &)), SLOT(slWidgetChange(const QCString &))); return (req); } void KScanOption::allocForDesc() { if (desc==NULL) return; switch (desc->type) { case SANE_TYPE_INT: case SANE_TYPE_FIXED: case SANE_TYPE_STRING: allocBuffer(desc->size); break; case SANE_TYPE_BOOL: allocBuffer(sizeof(SANE_Word)); break; default: if (desc->size>0) allocBuffer(desc->size); break; } } void KScanOption::allocBuffer(long size) { if (size<1) return; #ifdef MEM_DEBUG kdDebug(29000) << k_funcinfo << "Allocating " << size << " bytes for <" << name << ">" << endl; #endif if (!buffer.resize(size)) // set buffer size { kdDebug(29000) << k_funcinfo << "FATAL: Allocating " << size << " bytes for <" << name << "> failed!" << endl; return; } buffer.fill(0); // clear allocated buffer } #ifdef APPLY_IN_SITU bool KScanOption::applyVal( void ) { bool res = true; int *idx = KScanDevice::option_dic->find(name); if( *idx == 0 ) return( false ); if(buffer.isNull() ) return( false ); SANE_Status stat = sane_control_option ( KScanDevice::scanner_handle, *idx, SANE_ACTION_SET_VALUE, buffer.data(), NULL); if( stat != SANE_STATUS_GOOD ) { kdDebug(29000) << "Error in in situ appliance " << getName() << ": " << sane_strstatus( stat ) << endl; res = false; } else { kdDebug(29000) << "IN SITU appliance " << getName() << ": OK" << endl; } return( res ); } #endif QLabel *KScanOption::getLabel(QWidget *parent) const { QString t = text; if (internal_widget->isA("QCheckBox")) t = QString::null; return (new QLabel(t,parent)); } diff --git a/libkscan/kscanoption.h b/libkscan/kscanoption.h index fa10c8b..80b0248 100644 --- a/libkscan/kscanoption.h +++ b/libkscan/kscanoption.h @@ -1,276 +1,276 @@ /* This file is part of the KDE Project -*- mode:c++; -*- Copyright (C) 2000 Klaas Freitag 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. */ #ifndef _KSCANOPTION_H_ #define _KSCANOPTION_H_ #include extern "C" { #include } typedef enum { KSCAN_OK, KSCAN_ERROR, KSCAN_ERR_NO_DEVICE, KSCAN_ERR_BLOCKED, KSCAN_ERR_NO_DOC, KSCAN_ERR_PARAM, KSCAN_ERR_OPEN_DEV, KSCAN_ERR_CONTROL, KSCAN_ERR_EMPTY_PIC, KSCAN_ERR_MEMORY, KSCAN_ERR_SCAN, KSCAN_UNSUPPORTED, KSCAN_RELOAD, KSCAN_CANCELLED, KSCAN_OPT_NOT_ACTIVE } KScanStat; class QLabel; class KGammaTable; +#define PARAM_ERROR "parametererror" + /** * This is KScanOption, a class which holds a single scanner option. * * @short KScanOption * @author Klaas Freitag * @version 0.1alpha * * is a help class for accessing the scanner options. **/ class KScanOption : public QObject { Q_OBJECT public: enum WidgetType { Invalid, Bool, SingleValue, Range, GammaTable, StringList, String, Resolution, File }; /** * creates a new option object for the named option. After that, the * option is valid and contains the correct value retrieved from the * scanner. **/ KScanOption( const QCString& new_name ); /** * creates a KScanOption from another **/ KScanOption( const KScanOption& so ); ~KScanOption(); /** * tests if the option is initialised. It is initialised, if the value * for the option was once read from the scanner **/ bool initialised( void ) const { return( ! buffer_untouched );} /** * checks if the option is valid, means that the option is known by the scanner **/ bool valid( void ) const { return (desc!=NULL); } /** * checks if the option is auto setable, what means, the scanner can cope * with the option automatically. **/ bool autoSetable( void ); /** * checks if the option is a so called common option. All frontends should * provide gui elements to change them. **/ bool commonOption( void ); /** * checks if the option is active at the moment. Note that this changes * on runtime due to the settings of mode, resolutions etc. **/ bool active( void ); /** * checks if the option is setable by software. Some scanner options can not * be set by software. **/ bool softwareSetable(); /** * returns the type the option is. **/ KScanOption::WidgetType type( void ) const; /** * set the option depending on the type **/ bool set( int val ); bool set( double val ); bool set( int *p_val, int size ); bool set( const QCString& ); bool set( bool b ){ return (set(b ? 1 : 0)); } bool set( KGammaTable *gt ); /** * retrieves the option value, depending on the type. **/ bool get( int* ) const; bool get( KGammaTable* ) const; QCString get( void ) const; /** * This function creates a widget for the scanner option depending * on the type of the option. * * For boolean options, a checkbox is generated. For ranges, a KSaneSlider * is generated. * * For a String list such as mode etc., a KScanCombo is generated. * * For option type string and gamma table, no widget is generated yet. * * The widget is maintained completely by the kscanoption object. * **/ QWidget *createWidget( QWidget *parent, const QString& w_desc = QString::null, const QString& tooltip = QString::null ); QLabel *getLabel(QWidget *parent) const; /* Operators */ const KScanOption& operator= (const KScanOption& so ); const QString configLine( void ); // Possible Values QStrList getList() const; bool getRangeFromList( double*, double*, double* ) const; bool getRange( double*, double*, double* ) const; QCString getName() const { return( name ); } void * getBuffer() const { return( buffer.data() ); } QWidget *widget() const { return( internal_widget ); } /** * returns the type of the selected option. * * You may use the information returned to decide, in which way * the option is to set. * * A SINGLE_VAL is returned in case the value is represented by a * single number, e.g. the resoltion. * * A VAL_LIST is returned in case the value needs to be set by * a list of numbers. You need to check the size to find out, * how many numbers you have to * @param name: the name of a option from a returned option-List * return a option type. ### not implemented at all? **/ //KScanOption::WidgetType typeToSet( const QCString& name ); /** * returns a string describing the unit of given the option. * @return the unit description, e.g. mm * @param name: the name of a option from a returned option-List ### not implemented at all? **/ //QString unitOf( const QCString& name ); public slots: void slRedrawWidget( KScanOption *so ); /** * that slot makes the option to reload the buffer from the scanner. */ void slReload( void ); protected slots: /** * this slot is called if an option has a gui element (not all have) * and if the value of the widget is changed. * This is an internal slot. **/ void slWidgetChange( void ); void slWidgetChange( const QCString& ); void slWidgetChange( int ); signals: // TODO: eliminate, this signal is not used and is never emit'ted!!!!!!!! /** * Signal emitted if a option changed for different reasons. * The signal should be connected by outside objects. **/ void optionChanged( KScanOption*); /** * Signal emitted if the option is set by a call to the set() * member of the option. In the slot needs to be checked, if * a widget exists, it needs to be set to the new value. * This is a more internal signal **/ //void optionSet( void ); /** * Signal called when user changes a gui - element */ void guiChange( KScanOption* ); private: #ifdef APPLY_IN_SITU bool applyVal( void ); #endif - bool initOption( const QCString& new_name ); - void allocForDesc(); + bool initOption( const QCString& new_name ); + void allocForDesc(); void allocBuffer( long ); QWidget *createToggleButton( QWidget *parent, const QString& text ); QWidget *createEntryField ( QWidget *parent, const QString& text ); QWidget *createSlider( QWidget *parent, const QString& text ); QWidget *createComboBox ( QWidget *parent, const QString& text ); QWidget *createFileField( QWidget *parent, const QString& text ); const SANE_Option_Descriptor *desc; - QCString name; - QString text; + QCString name; + QString text; -// void *buffer; - QByteArray buffer; + QByteArray buffer; bool buffer_untouched; -// size_t buffer_size; QWidget *internal_widget; /* For gamma-Tables remember gamma, brightness, contrast */ int gamma, brightness, contrast; }; #endif diff --git a/libkscan/kscanoptset.cpp b/libkscan/kscanoptset.cpp index ac30ae4..ec1c411 100644 --- a/libkscan/kscanoptset.cpp +++ b/libkscan/kscanoptset.cpp @@ -1,221 +1,254 @@ /* This file is part of the KDE Project Copyright (C) 2000 Klaas Freitag 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 #include -#include -#include +//#include +//#include + #include #include +#include #include "kscandevice.h" #include "kscanoption.h" + #include "kscanoptset.h" + +#define SAVESET_GROUP "Save Set" +#define SAVESET_KEY_SETDESC "SetDesc" +#define SAVESET_KEY_SCANNER "ScannerName" + + KScanOptSet::KScanOptSet( const QCString& setName ) { name = setName; setAutoDelete( false ); description = ""; strayCatsList.setAutoDelete( true ); } KScanOptSet::~KScanOptSet() { - /* removes all deep copies from backupOption */ - strayCatsList.clear(); + kdDebug(29000) << k_funcinfo << "have " << strayCatsList.count() << " strays" << endl; + strayCatsList.clear(); /* deep copies from backupOption */ } KScanOption *KScanOptSet::get( const QCString name ) const { KScanOption *ret = 0; ret = (*this) [name]; return( ret ); } QCString KScanOptSet::getValue( const QCString name ) const { KScanOption *re = get( name ); QCString retStr = ""; if( re ) { retStr = re->get(); } else { kdDebug(29000) << "option " << name << " from OptionSet is not available" << endl; } return( retStr ); } bool KScanOptSet::backupOption( const KScanOption& opt ) { bool retval = true; /** Allocate a new option and store it **/ const QCString& optName = opt.getName(); if( !optName ) retval = false; if( retval ) { KScanOption *newopt = find( optName ); if( newopt ) { /** The option already exists **/ /* Copy the new one into the old one. TODO: checken Zuweisungoperatoren OK ? */ *newopt = opt; } else { const QCString& qq = opt.get(); kdDebug(29000) << "Value is now: <" << qq << ">" << endl; const KScanOption *newopt = new KScanOption( opt ); strayCatsList.append( newopt ); if( newopt ) { insert( optName, newopt ); } else { retval = false; } } } return( retval ); } QString KScanOptSet::getDescription() const { return description; } void KScanOptSet::slSetDescription( const QString& str ) { description = str; } void KScanOptSet::backupOptionDict( const QAsciiDict& optDict ) { QAsciiDictIterator it( optDict ); while ( it.current() ) { kdDebug(29000) << "Dict-Backup of Option <" << it.currentKey() << ">" << endl; backupOption( *(it.current())); ++it; } } -/* */ + void KScanOptSet::saveConfig( const QString& scannerName, const QString& configName, const QString& descr ) { - QString confFile = SCANNER_DB_FILE; - kdDebug( 29000) << "Creating scan configuration file <" << confFile << ">" << endl; + QString confFile = SCANNER_DB_FILE; + kdDebug(29000) << k_funcinfo << "scanner [" << scannerName << "]" + << " config=[" << configName << "]" + << " file <" << confFile << ">" << endl; - KConfig *scanConfig = new KConfig( confFile ); - QString cfgName = configName; + KConfig scanConfig(confFile); + QString cfgName = configName; + if (cfgName.isEmpty()) cfgName = "default"; - if( configName.isNull() || configName.isEmpty() ) - cfgName = "default"; + scanConfig.setGroup(QString("%1 %2").arg(SAVESET_GROUP,cfgName)); + scanConfig.writeEntry(SAVESET_KEY_SETDESC,descr); + scanConfig.writeEntry(SAVESET_KEY_SCANNER,scannerName); - scanConfig->setGroup( cfgName ); + QAsciiDictIterator it(*this); + while (it.current()!=NULL) + { + const QString line = it.current()->configLine(); + if (line!=PARAM_ERROR) + { + const QString name = it.current()->getName(); + kdDebug(29000) << " writing <" << name << "> = <" << line << ">" << endl; + scanConfig.writeEntry(name,line); + } + ++it; + } - scanConfig->writeEntry( "description", descr ); - scanConfig->writeEntry( "scannerName", scannerName ); - QAsciiDictIterator it( *this); + scanConfig.sync(); + kdDebug(29000) << k_funcinfo << "done" << endl; +} - while ( it.current() ) - { - const QString line = it.current() -> configLine(); - const QString name = it.current()->getName(); - kdDebug(29000) << "writing " << name << " = <" << line << ">" << endl; +bool KScanOptSet::load(const QString &scannerName) +{ + kdDebug(29000) << k_funcinfo << "Reading <" << name << "> from <" << SCANNER_DB_FILE << ">" << endl; - scanConfig->writeEntry( name, line ); + KConfig conf(SCANNER_DB_FILE,true,false); - ++it; + QString grpName = QString("%1 %2").arg(SAVESET_GROUP,name); /* of the KScanOptSet, given in constructor */ + if (!conf.hasGroup(grpName)) + { + kdDebug(29000) << "Group " << grpName << " does not exist in configuration!" << endl; + return (false); } - scanConfig->sync(); - delete( scanConfig ); -} - -bool KScanOptSet::load( const QString& /*scannerName*/ ) -{ - QString confFile = SCANNER_DB_FILE; - kdDebug( 29000) << "** Reading from scan configuration file <" << confFile << ">" << endl; - bool ret = true; + conf.setGroup(grpName); + StringMap strMap = conf.entryMap(grpName); - KConfig *scanConfig = new KConfig( confFile, true ); - QString cfgName = name; /* of the KScanOptSet, given in constructor */ + for (StringMap::Iterator it = strMap.begin(); it!=strMap.end(); ++it) + { + QString optName = it.key().latin1(); + if (optName==SAVESET_KEY_SETDESC) continue; + if (optName==SAVESET_KEY_SCANNER) continue; - if( cfgName.isNull() || cfgName.isEmpty() ) - cfgName = "default"; + KScanOption optset(optName.latin1()); - if( ! scanConfig->hasGroup( name ) ) - { - kdDebug(29000) << "Group " << name << " does not exist in configuration !" << endl; - ret = false; - } - else - { - scanConfig->setGroup( name ); + QCString val = it.data().latin1(); + kdDebug(29000) << "For <" << optName << "> read value <" << val << ">" << endl; - typedef QMap StringMap; + optset.set(val); + backupOption(optset); + } - StringMap strMap = scanConfig->entryMap( name ); + return (true); +} - StringMap::Iterator it; - for( it = strMap.begin(); it != strMap.end(); ++it ) - { - QCString optName = it.key().latin1(); - KScanOption optset( optName ); - QCString val = it.data().latin1(); - kdDebug(29000) << "Reading for " << optName << " value " << val << endl; - optset.set( val ); +KScanOptSet::StringMap KScanOptSet::readList() +{ + KConfig conf(SCANNER_DB_FILE,true,false ); + const QString groupName = SAVESET_GROUP; + StringMap ret; - backupOption( optset ); - } - } - delete( scanConfig ); + const QStringList groups = conf.groupList(); + for (QStringList::const_iterator it = groups.constBegin(); it!=groups.constEnd(); ++it) + { + QString grp = (*it); + kdDebug(29000) << k_funcinfo << "group [" << grp << "]" << endl; + if (grp.startsWith(groupName)) + { + QString set = grp.mid(groupName.length()+1); + if (set==DEFAULT_OPTIONSET) continue; // don't show this one + + conf.setGroup(grp); + ret[set] = conf.readEntry(SAVESET_KEY_SETDESC,i18n("No description")); + } + } - return( ret ); + return (ret); } -/* END */ + +void KScanOptSet::deleteSet(const QString &name) +{ + KConfig conf(SCANNER_DB_FILE,false,false ); + + QString grpName = QString("%1 %2").arg(SAVESET_GROUP,name); + conf.deleteGroup(grpName); + conf.sync(); +} diff --git a/libkscan/kscanoptset.h b/libkscan/kscanoptset.h index 042dbe8..a3addb0 100644 --- a/libkscan/kscanoptset.h +++ b/libkscan/kscanoptset.h @@ -1,113 +1,114 @@ /* This file is part of the KDE Project Copyright (C) 2000 Klaas Freitag 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. */ #ifndef KSCANOPTSET_H #define KSCANOPTSET_H -#include -#include -#include #include +#include +class KScanOption; -#include "kscanoption.h" /** * This is a container class for KScanOption-objects, which contain information * about single scanner dependant options. It allows you to store a bunch * of options and accessing them via a iterator. * * The class which is inherited from QAsciiDict does no deep copy of the options * to store by with the standard method insert. * @see backupOption to get a deep copy. * * Note that the destructor of the KScanOptSet only clears the options created * by backupOption. * * @author Klaas Freitag@SuSE.de * @version 0.1 */ class KScanOptSet: public QAsciiDict { public: + typedef QMap StringMap; + /** * Constructor to create a new Container. Takes a string as a name, which * has no special meaning yet ;) */ KScanOptSet( const QCString& ); ~KScanOptSet(); /** * function to store a deep copy of an option. Note that this class is inherited * from QAsciiDict and thus does no deep copies. This method does. * @see insert */ bool backupOption( const KScanOption& ); /** * returns a pointer to a stored option given by name. */ KScanOption *get( const QCString name ) const; QCString getValue( const QCString name ) const; void backupOptionDict( const QAsciiDict& ); /** * saves a configuration set to the configuration file 'ScanSettings' * in the default dir config (@see KDir). It uses the group given * in configName and stores the entire option set in that group. * additionally, a description is also saved. * * @param scannerName : the name of the scanner * @param configName: The name of the config, e.g. Black and White * @param descr : A description for the config. */ void saveConfig( const QString&, const QString&, const QString&); /** * allows to load a configuration. Simple create a optionSet with the * approbiate name the config was called ( @see saveConfig ) and call * load for the scanner you want. * @param scannerName: A scanner's name */ - bool load( const QString& scannerName ); + bool load(const QString &scannerName = QString::null); QString getDescription() const; + static StringMap readList(); + static void deleteSet(const QString &name); + public slots: void slSetDescription( const QString& ); private: QCString name; /* List to collect objects for which memory was allocated and must be freed */ QPtrList strayCatsList; - class KScanOptSetPrivate; - KScanOptSetPrivate *d; - QString description; }; + #endif // KScanOptSet diff --git a/libkscan/scanparams.cpp b/libkscan/scanparams.cpp index 55af9cb..055b50e 100644 --- a/libkscan/scanparams.cpp +++ b/libkscan/scanparams.cpp @@ -1,1211 +1,1215 @@ /* This file is part of the KDE Project Copyright (C) 1999 Klaas Freitag 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scansourcedialog.h" #include "massscandialog.h" #include "gammadialog.h" #include "kscancontrols.h" #include "scanparams.h" #include "scanparams.moc" // SANE testing options #define TESTING_OPTIONS #ifndef SANE_NAME_TEST_PICTURE #define SANE_NAME_TEST_PICTURE "test-picture" #define SANE_TITLE_TEST_PICTURE SANE_I18N("Test picture") #define SANE_DESC_TEST_PICTURE SANE_I18N("Select the test picture") #endif #ifndef SANE_NAME_THREE_PASS #define SANE_NAME_THREE_PASS "three-pass" #define SANE_TITLE_THREE_PASS SANE_I18N("3-pass mode") #define SANE_DESC_THREE_PASS SANE_I18N("Select whether to use 3-pass mode") #endif #ifndef SANE_NAME_HAND_SCANNER #define SANE_NAME_HAND_SCANNER "hand-scanner" #define SANE_TITLE_HAND_SCANNER SANE_I18N("Hand scanner") #define SANE_DESC_HAND_SCANNER SANE_I18N("Select whether this is a hand scanner") #endif #ifndef SANE_NAME_GRAYIFY #define SANE_NAME_GRAYIFY "grayify" #define SANE_TITLE_GRAYIFY SANE_I18N("Grayify") #define SANE_DESC_GRAYIFY SANE_I18N("Load the image as grayscale") #endif ScanParams::ScanParams( QWidget *parent, const char *name ) : QFrame( parent, name ) { sane_device = NULL; virt_filename = NULL; pb_edit_gtable = NULL; cb_gray_preview = NULL; xy_resolution_bind = NULL; progressDialog = NULL; source_sel = NULL; m_firstGTEdit = true; /* Preload icons */ pixColor = SmallIcon( "palette_color" ); pixGray = SmallIcon( "palette_gray" ); pixLineArt = SmallIcon( "palette_lineart" ); pixHalftone = SmallIcon( "palette_halftone" ); /* intialise the default last save warnings */ startupOptset = NULL; } bool ScanParams::connectDevice( KScanDevice *newScanDevice,bool galleryMode ) { /* Frame stuff for toplevel of scanparams - beautification */ setFrameStyle(QFrame::Panel|QFrame::Raised); setLineWidth(1); QGridLayout *lay = new QGridLayout(this,6,2,KDialog::marginHint(), KDialog::spacingHint() ); lay->setColStretch(0,9); if (newScanDevice==NULL) // no scanner device { kdDebug(29000) << k_funcinfo << "No scan device, gallery=" << galleryMode << endl; sane_device = NULL; createNoScannerMsg(galleryMode); return (true); } sane_device = newScanDevice; ///* Debug: dump common Options */ //QStrList strl = sane_device->getCommonOptions(); //QString emp; //for ( emp=strl.first(); strl.current(); emp=strl.next() ) // kdDebug(29000) << "Common: " << strl.current() << endl; scan_mode = ID_SCAN; adf = ADF_OFF; QLabel *lab = new QLabel(i18n("Scanner Settings"),this); lay->addWidget(lab,0,0,Qt::AlignLeft); m_led = new KLed(this); m_led->setState( KLed::Off ); m_led->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed )); lay->addWidget(m_led,0,1,Qt::AlignRight); lab = new QLabel(sane_device->getScannerName(),this); lay->addMultiCellWidget(lab,1,1,0,1,Qt::AlignLeft); KSeparator *sep = new KSeparator(KSeparator::HLine,this); lay->addMultiCellWidget(sep,2,2,0,1); /* load the startup scanoptions */ startupOptset = new KScanOptSet( DEFAULT_OPTIONSET ); Q_CHECK_PTR( startupOptset ); if( !startupOptset->load( "Startup" ) ) { kdDebug(29000) << "Could not load Startup-Options" << endl; delete startupOptset; startupOptset = NULL; } /* Now create Widgets for the important scan settings */ QScrollView *sv = scannerParams(); lay->addMultiCellWidget(sv,3,3,0,1); lay->setRowStretch(3,9); /* Reload all options to care for inactive options */ sane_device->slReloadAll(); sep = new KSeparator(KSeparator::HLine,this); lay->addMultiCellWidget(sep,4,4,0,1); /* Create the Scan Buttons */ QPushButton* pb = new QPushButton(i18n("Preview"),this); pb->setMinimumWidth(100); connect( pb, SIGNAL(clicked()), this, SLOT(slAcquirePreview()) ); lay->addWidget(pb,5,0,Qt::AlignLeft); pb = new QPushButton(i18n("Start Scan"),this); pb->setMinimumWidth(100); connect( pb, SIGNAL(clicked()), this, SLOT(slStartScan()) ); lay->addWidget(pb,5,1,Qt::AlignRight); /* Initialise the progress dialog */ progressDialog = new QProgressDialog( i18n("Scanning in progress"), i18n("Stop"), 100, 0L, "SCAN_PROGRESS", true, 0 ); progressDialog->setAutoClose( true ); progressDialog->setAutoReset( true ); progressDialog->setCaption(i18n("Scanning")); connect( sane_device, SIGNAL(sigScanProgress(int)), progressDialog, SLOT(setProgress(int))); /* Connect the Progress Dialogs cancel-Button */ connect( progressDialog, SIGNAL( cancelled() ), sane_device, SLOT( slStopScanning() ) ); return (true); } ScanParams::~ScanParams() { kdDebug(29000) << k_funcinfo << endl; delete startupOptset; startupOptset = NULL; delete progressDialog; progressDialog = NULL; } void ScanParams::initialise(KScanOption *so) { if (so==NULL) return; if (startupOptset==NULL) return; QCString name = so->getName(); if (!name.isEmpty()) { QCString val = startupOptset->getValue(name); kdDebug(29000) << "Initialising <" << name << "> with value <" << val << ">" << endl; so->set(val); sane_device->apply(so); } } QScrollView *ScanParams::scannerParams() { KScanOption *so; QScrollView *sv = new QScrollView(this); sv->setHScrollBarMode( QScrollView::AlwaysOff ); sv->setResizePolicy( QScrollView::AutoOneFit ); QWidget *frame = new QWidget(sv->viewport()); frame->setBackgroundColor(sv->backgroundColor()); QGridLayout *lay = new QGridLayout(frame,1,4,KDialog::marginHint(),2*KDialog::spacingHint()); lay->setColStretch(2,1); lay->setColSpacing(1,KDialog::marginHint()); int row = 0; virt_filename = sane_device->getGuiElement( SANE_NAME_FILE, frame, SANE_TITLE_FILE, SANE_DESC_FILE ); if (virt_filename!=NULL) { initialise( virt_filename ); connect( virt_filename, SIGNAL(guiChange(KScanOption*)), SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(virt_filename->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(virt_filename->widget(),row,row,2,3); ++row; /* Selection for either virtual scanner or SANE debug */ QButtonGroup *vbg = new QButtonGroup( 2, Qt::Vertical, i18n("Testing Mode"),frame); connect(vbg,SIGNAL(clicked(int)),SLOT(slVirtScanModeSelect(int))); QRadioButton *rb1 = new QRadioButton(i18n("SANE Debug (from PNM image)"),vbg); QRadioButton *rb2 = new QRadioButton(i18n("Virtual Scanner (any image format)"),vbg); if (scan_mode==ID_SCAN) scan_mode = ID_SANE_DEBUG; rb1->setChecked(scan_mode==ID_SANE_DEBUG); rb2->setChecked(scan_mode==ID_QT_IMGIO); lay->addMultiCellWidget(vbg,row,row,2,3); ++row; lay->addMultiCellWidget(new KSeparator(KSeparator::HLine,frame),row,row,0,3); ++row; } /* Mode setting */ so = sane_device->getGuiElement( SANE_NAME_SCAN_MODE, frame, SANE_TITLE_SCAN_MODE, SANE_DESC_SCAN_MODE ); if (so!=NULL) { KScanCombo *cb = (KScanCombo*) so->widget(); // Having loaded the 'sane-backends' message catalogue, these strings // are now translatable. cb->slSetIcon( pixLineArt, i18n("Line art") ); cb->slSetIcon( pixLineArt, i18n("Lineart") ); cb->slSetIcon( pixLineArt, i18n("Binary") ); cb->slSetIcon( pixGray, i18n("Gray") ); cb->slSetIcon( pixGray, i18n("Grey") ); cb->slSetIcon( pixColor, i18n("Color") ); cb->slSetIcon( pixColor, i18n("Colour") ); cb->slSetIcon( pixHalftone, i18n("Halftone") ); initialise( so ); connect( so, SIGNAL(guiChange(KScanOption*)), SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(cb,row,row,2,3); ++row; } /* Resolution Setting -> try X-Resolution Setting */ so = sane_device->getGuiElement( SANE_NAME_SCAN_X_RESOLUTION, frame, i18n("Resolution"), SANE_DESC_SCAN_X_RESOLUTION ); if (so!=NULL) { initialise( so ); int x_y_res; so->get( &x_y_res ); so->slRedrawWidget( so ); /* connect to slot that passes the resolution to the previewer */ connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT( slNewXResolution(KScanOption*))); connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addWidget(so->widget(),row,2); lay->addWidget(new QLabel(i18n("dpi"),frame),row,3,Qt::AlignLeft); // SANE resolution always in DPI ++row; xy_resolution_bind = sane_device->getGuiElement(SANE_NAME_RESOLUTION_BIND, frame, SANE_TITLE_RESOLUTION_BIND, SANE_DESC_RESOLUTION_BIND ); if (xy_resolution_bind!=NULL) { initialise( xy_resolution_bind ); xy_resolution_bind->slRedrawWidget( xy_resolution_bind ); /* Connect to Gui-change-Slot */ connect( xy_resolution_bind, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } /* Resolution Setting -> Y-Resolution Setting */ so = sane_device->getGuiElement( SANE_NAME_SCAN_Y_RESOLUTION, frame, SANE_TITLE_SCAN_Y_RESOLUTION, SANE_DESC_SCAN_Y_RESOLUTION ); int y_res = x_y_res; if (so!=NULL) { initialise( so ); if( so->active() ) so->get( &y_res ); so->slRedrawWidget( so ); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addWidget(so->widget(),row,2); lay->addWidget(new QLabel(i18n("dpi"),frame),row,3,Qt::AlignLeft); ++row; } emit scanResolutionChanged( x_y_res, y_res ); } else { /* If the SCAN_X_RES does not exists, perhaps just SCAN_RES does */ so = sane_device->getGuiElement( SANE_NAME_SCAN_RESOLUTION, frame, SANE_TITLE_SCAN_Y_RESOLUTION, SANE_DESC_SCAN_X_RESOLUTION ); if (so!=NULL) { initialise( so ); so->slRedrawWidget( so ); /* connect to slot that passes the resolution to the previewer */ connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT( slNewXResolution(KScanOption*))); connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addWidget(so->widget(),row,2); lay->addWidget(new QLabel(i18n("dpi"),frame),row,3,Qt::AlignLeft); ++row; } else { kdDebug(29000) << "SERIOUS: No Resolution setting possible!" << endl; } } /* Insert another beautification line */ lay->addMultiCellWidget(new KSeparator(KSeparator::HLine,frame),row,row,0,3); ++row; /* Add a button for Source-Selection */ source_sel = sane_device->getGuiElement( SANE_NAME_SCAN_SOURCE, frame, SANE_TITLE_SCAN_SOURCE, SANE_DESC_SCAN_SOURCE ); if (source_sel!=NULL) { initialise( source_sel ); connect( source_sel, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(source_sel->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(source_sel->widget(),row,row,2,3); ++row; // Will need to enable the "Advanced" dialogue later, because that // contains other ADF options. They are not implemented at the moment // but they may be some day... //QPushButton *pb = new QPushButton( i18n("Source && ADF Options..."), frame); //connect(pb, SIGNAL(clicked()), SLOT(slSourceSelect())); //lay->addMultiCellWidget(pb,row,row,2,3,Qt::AlignRight); //++row; } /* Halftoning */ so = sane_device->getGuiElement( SANE_NAME_HALFTONE, frame, SANE_TITLE_HALFTONE, SANE_DESC_HALFTONE ); if (so!=NULL) { initialise(so); connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } so = sane_device->getGuiElement( SANE_NAME_HALFTONE_DIMENSION, frame, SANE_TITLE_HALFTONE_DIMENSION, SANE_DESC_HALFTONE_DIMENSION ); if (so!=NULL) { initialise(so); connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } so = sane_device->getGuiElement( SANE_NAME_HALFTONE_PATTERN, frame, SANE_TITLE_HALFTONE_PATTERN, SANE_DESC_HALFTONE_PATTERN ); if (so!=NULL) { initialise(so); connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } // SANE "test" options so = sane_device->getGuiElement( SANE_NAME_TEST_PICTURE, frame, SANE_TITLE_TEST_PICTURE, SANE_DESC_TEST_PICTURE ); if (so!=NULL) { initialise(so); connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } kdDebug(29000) << "Bit-Depth exists" << endl; so = sane_device->getGuiElement( SANE_NAME_BIT_DEPTH, frame, SANE_TITLE_BIT_DEPTH, SANE_DESC_BIT_DEPTH ); if (so!=NULL) { initialise(so); connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } #ifdef TESTING_OPTIONS so = sane_device->getGuiElement( SANE_NAME_THREE_PASS, frame, SANE_TITLE_THREE_PASS, SANE_DESC_THREE_PASS ); if (so!=NULL) { initialise(so); connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } so = sane_device->getGuiElement( SANE_NAME_HAND_SCANNER, frame, SANE_TITLE_HAND_SCANNER, SANE_DESC_HAND_SCANNER ); if (so!=NULL) { initialise(so); connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } so = sane_device->getGuiElement( SANE_NAME_GRAYIFY, frame, SANE_TITLE_GRAYIFY, SANE_DESC_GRAYIFY ); if (so!=NULL) { initialise(so); connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } #endif /* Speed-Setting - show only if active */ KScanOption kso_speed( SANE_NAME_SCAN_SPEED ); if( kso_speed.valid() && kso_speed.softwareSetable() && kso_speed.active()) { so = sane_device->getGuiElement( SANE_NAME_SCAN_SPEED, frame, SANE_TITLE_SCAN_SPEED, SANE_DESC_SCAN_SPEED ); initialise( so ); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } /* Threshold-Setting */ so = sane_device->getGuiElement( SANE_NAME_THRESHOLD, frame, SANE_TITLE_THRESHOLD, SANE_DESC_THRESHOLD); if (so!=NULL) { initialise( so ); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } /* Brightness-Setting */ so = sane_device->getGuiElement( SANE_NAME_BRIGHTNESS, frame, SANE_TITLE_BRIGHTNESS, SANE_DESC_BRIGHTNESS); if (so!=NULL) { initialise( so ); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } /* Contrast-Setting */ so = sane_device->getGuiElement( SANE_NAME_CONTRAST, frame, SANE_TITLE_CONTRAST, SANE_DESC_CONTRAST ); if (so!=NULL) { initialise( so ); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } /* Sharpness */ so = sane_device->getGuiElement( "sharpness", frame ); if (so!=NULL) { initialise( so ); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } /* The gamma table can be used - add a button for editing */ if (sane_device->optionExists( SANE_NAME_CUSTOM_GAMMA ) ) { QHBox *hb1 = new QHBox(frame); so = sane_device->getGuiElement( SANE_NAME_CUSTOM_GAMMA, hb1, SANE_TITLE_CUSTOM_GAMMA, SANE_DESC_CUSTOM_GAMMA ); if (so!=NULL) { initialise( so ); connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slReloadAllGui( KScanOption* ))); /* This connection cares for enabling/disabling the edit-Button */ connect( so, SIGNAL(guiChange(KScanOption*)), this, SLOT(slOptionNotify(KScanOption*))); } /* Connect a signal to refresh activity of the gamma tables */ (void) new QWidget( hb1 ); /* dummy widget to eat space */ pb_edit_gtable = new QPushButton( i18n("Edit..."), hb1 ); connect( pb_edit_gtable, SIGNAL( clicked () ), this, SLOT( slEditCustGamma () ) ); setEditCustomGammaTableState(); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(hb1,row,row,2,3); ++row; } /* my Epson Perfection backends offer a list of user defined gamma values */ so = sane_device->getGuiElement( SANE_NAME_NEGATIVE, frame, SANE_TITLE_NEGATIVE, SANE_DESC_NEGATIVE ); if (so!=NULL) { initialise( so ); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; } /* Preview-Switch */ so = sane_device->getGuiElement( SANE_NAME_GRAY_PREVIEW, frame, SANE_TITLE_GRAY_PREVIEW, SANE_DESC_GRAY_PREVIEW ); if (so!=NULL) { initialise( so ); lay->addWidget(so->getLabel(frame),row,0,Qt::AlignLeft); lay->addMultiCellWidget(so->widget(),row,row,2,3); ++row; cb_gray_preview = (QCheckBox*) so->widget(); QToolTip::add( cb_gray_preview, i18n("Acquire a gray preview even in color mode (faster)") ); } lay->addMultiCellWidget(new QLabel("",frame),row,row,0,3); lay->setRowStretch(row,1); // dummy row for stretch frame->setMinimumWidth(frame->sizeHint().width()); sv->setMinimumWidth(frame->minimumWidth()); sv->addChild(frame); return (sv); } void ScanParams::createNoScannerMsg(bool galleryMode) { QString msg; if (galleryMode) { msg = i18n("\ Gallery Mode: No scanner selected\

\ In this mode you can browse, manipulate and OCR images already in the gallery.\

\ Select a scanner device \ (use the menu option Settings - Select Scan Device) \ to perform scanning."); } else { msg = i18n("\ Problem: No scanner found, or unable to access it\

\ There was a problem using the SANE (Scanner Access Now Easy) library to access \ the scanner device. There may be a problem with your SANE installation, or it \ may not be configured to support your scanner.\

\ Check that SANE is correctly installed and configured on your system, and \ also that the scanner device name and settings are correct.\

\ See the SANE project home page \ (www.sane-project.org) \ for more information on SANE installation and setup."); } KActiveLabel *lab = new KActiveLabel(msg,this); QGridLayout *lay = dynamic_cast(layout()); if (lay!=NULL) lay->addWidget(lab,0,0); } /* This slot will be called if something changes with the option given as a param. * This is useful if the parameter - Gui has widgets in his own space, which depend * on widget controlled by the KScanOption. */ void ScanParams::slOptionNotify( KScanOption *kso ) { if( !kso || !kso->valid()) return; setEditCustomGammaTableState (); } void ScanParams::slSourceSelect( void ) { kdDebug(29000) << "Open Window for source selection !" << endl; KScanOption so( SANE_NAME_SCAN_SOURCE ); AdfBehaviour adf = ADF_OFF; const QCString& currSource = so.get(); kdDebug(29000) << "Current Source is <" << currSource << ">" << endl; QStrList sources; if( so.valid() ) { sources = so.getList(); #undef CHEAT_FOR_DEBUGGING #ifdef CHEAT_FOR_DEBUGGING if( sources.find( "Automatic Document Feeder" ) == -1) sources.append( "Automatic Document Feeder" ); #endif ScanSourceDialog d( this, sources, adf ); d.slSetSource( currSource ); if( d.exec() == QDialog::Accepted ) { QString sel_source = d.getText(); adf = d.getAdfBehave(); /* set the selected Document source, the behavior is stored in a membervar */ so.set( QCString(sel_source.latin1()) ); // FIX in ScanSourceDialog, then here sane_device->apply( &so ); if (source_sel!=NULL) { source_sel->slReload(); source_sel->slRedrawWidget(source_sel); } kdDebug(29000) << "Dialog finished OK: " << sel_source << ", " << adf << endl; } } } /** Slot which is called if the user switches in the gui between * the SANE-Debug-Mode and the qt image reading */ void ScanParams::slVirtScanModeSelect(int id) { if (id==0) // SANE Debug { scan_mode = ID_SANE_DEBUG; sane_device->guiSetEnabled( SANE_NAME_HAND_SCANNER, true ); sane_device->guiSetEnabled( SANE_NAME_THREE_PASS, true ); sane_device->guiSetEnabled( SANE_NAME_GRAYIFY, true ); sane_device->guiSetEnabled( SANE_NAME_CONTRAST, true ); sane_device->guiSetEnabled( SANE_NAME_BRIGHTNESS, true ); ///* Check if the entered filename is valid for this mode, otherwise clear it */ //if (virt_filename!=NULL) //{ // QString vf = virt_filename->get(); // kdDebug(29000) << "Found File in Filename-Option: " << vf << endl; // QFileInfo fi(vf); // if (fi.extension()!="pnm") virt_filename->set(QCString("")); //} } else // Virtual Scanner { scan_mode = ID_QT_IMGIO; sane_device->guiSetEnabled( SANE_NAME_HAND_SCANNER, false ); sane_device->guiSetEnabled( SANE_NAME_THREE_PASS, false ); sane_device->guiSetEnabled( SANE_NAME_GRAYIFY, false ); sane_device->guiSetEnabled( SANE_NAME_CONTRAST, false ); sane_device->guiSetEnabled( SANE_NAME_BRIGHTNESS, false ); } } KScanStat ScanParams::prepareScan(QString *vfp) { kdDebug(29000) << k_funcinfo << "scan mode=" << scan_mode << endl; KScanStat stat = KSCAN_OK; QString virtfile; if (scan_mode==ID_SANE_DEBUG || scan_mode==ID_QT_IMGIO) { if (virt_filename!=NULL) virtfile = virt_filename->get(); if (virtfile.isEmpty()) { KMessageBox::sorry(this,i18n("A file must be entered for testing or virtual scanning")); stat = KSCAN_ERR_PARAM; } - QFileInfo fi(virtfile); - if (!fi.exists()) + if (stat==KSCAN_OK) { - KMessageBox::sorry(this,i18n("The testing or virtual file
%1
was not found or is not readable").arg(virtfile)); - stat = KSCAN_ERR_PARAM; + QFileInfo fi(virtfile); + if (!fi.exists()) + { + KMessageBox::sorry(this,i18n("The testing or virtual file
%1
was not found or is not readable").arg(virtfile)); + stat = KSCAN_ERR_PARAM; + } } - else + + if (stat==KSCAN_OK) { if (scan_mode==ID_SANE_DEBUG) { QFileInfo fi(virtfile); if (fi.extension().lower()!="pnm") { KMessageBox::sorry(this,i18n("SANE Debug can only read PNM files")); stat = KSCAN_ERR_PARAM; } } } } if (vfp!=NULL) *vfp = virtfile; return (stat); } /* Slot called to start acquiring a preview */ void ScanParams::slAcquirePreview() { if (scan_mode==ID_QT_IMGIO) { KMessageBox::sorry(this,i18n("Cannot preview in Virtual Scanner mode")); return; } QString virtfile; KScanStat stat = prepareScan(&virtfile); if (stat!=KSCAN_OK) return; kdDebug(29000) << k_funcinfo << "scan mode=" << scan_mode << " virtfile=[" << virtfile << "]" << endl; bool gray_preview = false; if (cb_gray_preview!=NULL) gray_preview = cb_gray_preview->isChecked(); slMaximalScanSize(); /* Always preview at maximal size */ stat = sane_device->acquirePreview(gray_preview); if (stat!=KSCAN_OK) kdDebug(29000) << k_funcinfo << "Error, preview status " << stat << endl; } /* Slot called to start scanning */ void ScanParams::slStartScan() { QString virtfile; KScanStat stat = prepareScan(&virtfile); if (stat!=KSCAN_OK) return; kdDebug(29000) << k_funcinfo << "scan mode=" << scan_mode << " virtfile=[" << virtfile << "]" << endl; if (scan_mode!=ID_QT_IMGIO) // acquire via SANE { if (adf==ADF_OFF) { /* Progress-Dialog */ progressDialog->setProgress(0); if (progressDialog->isHidden()) progressDialog->show(); kdDebug(29000) << "Start to acquire image" << endl; stat = sane_device->acquire(); } else { kdDebug(29000) << "ADF Scan not yet implemented :-/" << endl; // stat = performADFScan(); } } else // acquire via Qt-IMGIO { kdDebug(29000) << "Acquiring from virtual file" << endl; stat = sane_device->acquire(virtfile); } if (stat!=KSCAN_OK) kdDebug(29000) << k_funcinfo << "Error, scan status " << stat << endl; } /* Slot called to start the Custom Gamma Table Edit dialog */ void ScanParams::slEditCustGamma( void ) { kdDebug(29000) << "Called EditCustGamma ;)" << endl; KGammaTable old_gt; /* Since gammatable options are not set in the default gui, it must be * checked if it is the first edit. If it is, take from loaded default * set if available there */ if( m_firstGTEdit && startupOptset ) { m_firstGTEdit = false; KScanOption *gt = startupOptset->get(SANE_NAME_GAMMA_VECTOR); if( !gt ) { /* If it not gray, it should be one color. */ gt = startupOptset->get( SANE_NAME_GAMMA_VECTOR_R ); } if( gt ) gt->get( &old_gt ); } else { /* it is not the first edit, use older values */ if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR ) ) { KScanOption grayGt( SANE_NAME_GAMMA_VECTOR ); /* This will be fine for all color gt's. */ grayGt.get( &old_gt ); kdDebug(29000) << "Gray Gamma Table is active " << endl; } else { /* Gray is not active, but at the current implementation without * red/green/blue gammatables, but one for all, all gammatables * are equally. So taking the red one should be fine. TODO */ if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_R )) { KScanOption redGt( SANE_NAME_GAMMA_VECTOR_R ); redGt.get( &old_gt ); kdDebug(29000) << "Getting old gamma table from Red channel" << endl; } else { /* uh ! No current gammatable could be retrieved. Use the 100/0/0 gt * created by KGammaTable's constructor. Nothing to do for that. */ kdDebug(29000) << "WRN: Could not retrieve a gamma table" << endl; } } } kdDebug(29000) << "Old gamma table: " << old_gt.getGamma() << ", " << old_gt.getBrightness() << ", " << old_gt.getContrast() << endl; GammaDialog gdiag( this ); connect( &gdiag, SIGNAL( gammaToApply(KGammaTable*) ), this, SLOT( slApplyGamma(KGammaTable*) ) ); gdiag.setGt( old_gt ); if( gdiag.exec() == QDialog::Accepted ) { slApplyGamma( gdiag.getGt() ); kdDebug(29000) << "Fine, applied new Gamma Table !" << endl; } else { /* reset to old values */ slApplyGamma( &old_gt ); kdDebug(29000) << "Cancel, reverted to old Gamma Table !" << endl; } } void ScanParams::slApplyGamma( KGammaTable* gt ) { if( ! gt ) return; kdDebug(29000) << "Applying gamma table: " << gt->getGamma() << ", " << gt->getBrightness() << ", " << gt->getContrast() << endl; if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR ) ) { KScanOption grayGt( SANE_NAME_GAMMA_VECTOR ); /* Now find out, which gamma-Tables are active. */ if( grayGt.active() ) { grayGt.set( gt ); sane_device->apply( &grayGt, true ); } } if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_R )) { KScanOption rGt( SANE_NAME_GAMMA_VECTOR_R ); if( rGt.active() ) { rGt.set( gt ); sane_device->apply( &rGt, true ); } } if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_G )) { KScanOption gGt( SANE_NAME_GAMMA_VECTOR_G ); if( gGt.active() ) { gGt.set( gt ); sane_device->apply( &gGt, true ); } } if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_B )) { KScanOption bGt( SANE_NAME_GAMMA_VECTOR_B ); if( bGt.active() ) { bGt.set( gt ); sane_device->apply( &bGt, true ); } } } /* Slot calls if a widget changes. Things to do: * - Apply the option and reload all if the option affects all */ void ScanParams::slReloadAllGui( KScanOption* t) { if( !t || ! sane_device ) return; kdDebug(29000) << "This is slReloadAllGui for widget <" << t->getName() << ">" << endl; /* Need to reload all _except_ the one which was actually changed */ sane_device->slReloadAllBut( t ); /* Custom Gamma <- What happens if that does not exist for some scanner ? TODO */ setEditCustomGammaTableState(); } /* * enable editing of the gamma tables if one of the gamma tables * exists and is active at the moment */ void ScanParams::setEditCustomGammaTableState() { if( !(sane_device && pb_edit_gtable) ) return; bool butState = false; kdDebug(29000) << "Checking state of edit custom gamma button !" << endl; if( sane_device->optionExists( SANE_NAME_CUSTOM_GAMMA ) ) { KScanOption kso( SANE_NAME_CUSTOM_GAMMA ); butState = kso.active(); // kdDebug(29000) << "CustomGamma is active: " << butState << endl; } if( !butState && sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_R ) ) { KScanOption kso( SANE_NAME_GAMMA_VECTOR_R ); butState = kso.active(); // kdDebug(29000) << "CustomGamma Red is active: " << butState << endl; } if( !butState && sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_G ) ) { KScanOption kso( SANE_NAME_GAMMA_VECTOR_G ); butState = kso.active(); // kdDebug(29000) << "CustomGamma Green is active: " << butState << endl; } if( !butState && sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_B ) ) { KScanOption kso( SANE_NAME_GAMMA_VECTOR_B ); butState = kso.active(); // kdDebug(29000) << "CustomGamma blue is active: " << butState << endl; } pb_edit_gtable->setEnabled( butState ); } /* Custom Scan size setting. * The custom scan size is given in the QRect parameter. It must contain values * from 0..1000 which are interpreted as tenth of percent of the overall dimension. */ void ScanParams::slCustomScanSize( QRect sel) { kdDebug(29000) << "Custom-Size: " << sel.x() << ", " << sel.y() << " - " << sel.width() << "x" << sel.height() << endl; KScanOption tl_x( SANE_NAME_SCAN_TL_X ); KScanOption tl_y( SANE_NAME_SCAN_TL_Y ); KScanOption br_x( SANE_NAME_SCAN_BR_X ); KScanOption br_y( SANE_NAME_SCAN_BR_Y ); double min1=0.0, max1=0.0, min2=0.0, max2=0.0, dummy1=0.0, dummy2=0.0; tl_x.getRange( &min1, &max1, &dummy1 ); br_x.getRange( &min2, &max2, &dummy2 ); /* overall width */ double range = max2-min1; double w = min1 + double(range * (double(sel.x()) / 1000.0) ); tl_x.set( w ); w = min1 + double(range * double(sel.x() + sel.width())/1000.0); br_x.set( w ); kdDebug(29000) << "set tl_x: " << min1 + double(range * (double(sel.x()) / 1000.0) ) << endl; kdDebug(29000) << "set br_x: " << min1 + double(range * (double(sel.x() + sel.width())/1000.0)) << endl; /** Y-Value setting */ tl_y.getRange( &min1, &max1, &dummy1 ); br_y.getRange(&min2, &max2, &dummy2 ); /* overall width */ range = max2-min1; w = min1 + range * double(sel.y()) / 1000.0; tl_y.set( w ); w = min1 + range * double(sel.y() + sel.height())/1000.0; br_y.set( w ); kdDebug(29000) << "set tl_y: " << min1 + double(range * (double(sel.y()) / 1000.0) ) << endl; kdDebug(29000) << "set br_y: " << min1 + double(range * (double(sel.y() + sel.height())/1000.0)) << endl; sane_device->apply( &tl_x ); sane_device->apply( &tl_y ); sane_device->apply( &br_x ); sane_device->apply( &br_y ); } /** * sets the scan area to the default, which is the whole area. */ void ScanParams::slMaximalScanSize( void ) { kdDebug(29000) << "Setting to default" << endl; slCustomScanSize(QRect( 0,0,1000,1000)); } void ScanParams::slNewXResolution(KScanOption *opt) { if(! opt ) return; kdDebug(29000) << "Got new X-Resolution !" << endl; int x_res = 0; opt->get( &x_res ); int y_res = x_res; if( xy_resolution_bind && xy_resolution_bind->active() ) { /* That means, that x and y may be different */ KScanOption opt_y( SANE_NAME_SCAN_Y_RESOLUTION ); if( opt_y.valid () ) { opt_y.get( &y_res ); } } emit( scanResolutionChanged( x_res, y_res ) ); } void ScanParams::slNewYResolution(KScanOption *opt) { if( ! opt ) return; int y_res = 0; opt->get( &y_res ); int x_res = y_res; if( xy_resolution_bind && xy_resolution_bind->active()) { /* That means, that x and y may be different */ KScanOption opt_x( SANE_NAME_SCAN_X_RESOLUTION ); if( opt_x.valid () ) { opt_x.get( &x_res ); } } emit( scanResolutionChanged( x_res, y_res ) ); } KScanStat ScanParams::performADFScan( void ) { KScanStat stat = KSCAN_OK; bool scan_on = true; MassScanDialog *msd = new MassScanDialog( this ); msd->show(); /* The scan source should be set to ADF by the SourceSelect-Dialog */ while( scan_on ) { scan_on = false; } return( stat ); }