diff --git a/kooka/formatdialog.cpp b/kooka/formatdialog.cpp index 1ccdb93..59adc0f 100644 --- a/kooka/formatdialog.cpp +++ b/kooka/formatdialog.cpp @@ -1,336 +1,335 @@ /*************************************************************************** * * * 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; - picType recForTypes[5]; + const char *format; + QString helpString; + int recForTypes; }; static struct formatInfo formats[] = { - { "BMP", I18N_NOOP( + { "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."), - { PT_FINISHED } }, + ImgSaver::ImgNone }, - { "PBM", I18N_NOOP( + { "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."), - { PT_BW_IMAGE, PT_FINISHED } }, + ImgSaver::ImgBW }, - { "PGM", I18N_NOOP( + { "PGM", I18N_NOOP( "Portable Greymap, as used by Netpbm, is an uncompressed format for grayscale \ images. Only 8 bit per pixel depth is supported."), - { PT_GRAY_IMAGE, PT_FINISHED } }, + ImgSaver::ImgGray }, - { "PPM", I18N_NOOP( + { "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."), - { PT_COLOR_IMAGE, PT_HICOLOR_IMAGE, PT_FINISHED } }, + ImgSaver::ImgColor|ImgSaver::ImgHicolor }, - { "PCX", - I18N_NOOP( + { "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."), - { PT_FINISHED } }, + ImgSaver::ImgNone }, - { "XBM", I18N_NOOP( + { "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."), - { PT_FINISHED } }, + ImgSaver::ImgNone }, - { "XPM", I18N_NOOP( + { "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."), - { PT_FINISHED } }, + ImgSaver::ImgNone }, - { "PNG", - I18N_NOOP( + { "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."), - { PT_BW_IMAGE, PT_COLOR_IMAGE, PT_GRAY_IMAGE, PT_HICOLOR_IMAGE, PT_FINISHED } }, + ImgSaver::ImgBW|ImgSaver::ImgColor|ImgSaver::ImgGray|ImgSaver::ImgHicolor }, - { "JPEG", I18N_NOOP( + { "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."), - { PT_HICOLOR_IMAGE, PT_GRAY_IMAGE, PT_FINISHED } }, + ImgSaver::ImgHicolor|ImgSaver::ImgGray }, - { "JP2", I18N_NOOP( + { "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."), - { PT_FINISHED } }, + ImgSaver::ImgNone }, - { "EPS", - I18N_NOOP( + { "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."), - { PT_FINISHED } }, + ImgSaver::ImgNone }, - { "TGA", I18N_NOOP( + { "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."), - { PT_FINISHED } }, + ImgSaver::ImgNone }, - { "XV", "", - { PT_FINISHED } }, + { "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 }, - { "RGB", "", - { PT_FINISHED } }, + { "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 }, - { NULL, "", { } } + { "XV", "", ImgSaver::ImgNone }, + + { "RGB", "", ImgSaver::ImgNone }, + + { NULL, "", ImgSaver::ImgNone } }; -FormatDialog::FormatDialog( QWidget *parent, picType type) +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->setText( i18n("-No format selected-" )); 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( QString fo ) +void FormatDialog::setSelectedFormat(const QString &fo) { - QListBoxItem *item = lb_format->findItem( fo ); - - if( item ) - { - // Select it. - lb_format->setSelected( lb_format->index(item), true ); - } + 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) { 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; - for (picType *pt = &ip->recForTypes[0]; *pt!=PT_FINISHED; ++pt) - { // look through list of types - if (*pt==imgType) // recommended for this type - { - formatOk = true; // this format to be shown - break; // no more to do - } + if (ip->recForTypes & imgType) // recommended for this type? + { + formatOk = true; // this format to be shown + break; // no more to do } - - if (formatOk) break; // found this format to be shown } 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/formatdialog.h b/kooka/formatdialog.h index cf930d5..a676024 100644 --- a/kooka/formatdialog.h +++ b/kooka/formatdialog.h @@ -1,78 +1,77 @@ /*************************************************************************** * * * 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 __FORMATDIALOG_H__ #define __FORMATDIALOG_H__ #include #include #include "imgsaver.h" class QComboBox; class QListBox; class QLabel; class QCheckBox; /** * Class FormatDialog: * Asks the user for the image-Format and gives help for * selecting it. **/ class FormatDialog : public KDialogBase { Q_OBJECT public: - FormatDialog( QWidget *parent, picType type); + FormatDialog( QWidget *parent, ImgSaver::ImageType type); QString getFormat( ) const; QCString getSubFormat( ) const; + void setSelectedFormat(const QString &fo); bool askForFormat( ) const { return( ! cbDontAsk->isChecked()); } static void forgetRemembered(); -public slots: - void setSelectedFormat( QString ); protected slots: void showHelp( const QString& item ); void buildFormatList(bool recOnly); void slotOk(); private: void check_subformat( const QString & format ); QStringList formatList; - picType imgType; + ImgSaver::ImageType imgType; QComboBox *cb_subf; QListBox *lb_format; QLabel *l_help; QLabel *l2; QCheckBox *cbRemember; QCheckBox *cbDontAsk; QCheckBox *cbRecOnly; }; #endif diff --git a/kooka/imgsaver.cpp b/kooka/imgsaver.cpp index 25d804a..b9d9f9b 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; - picType imgType; + ImgSaver::ImageType imgType; if( !image ) return( ISS_ERR_PARAM ); /* Find out what kind of image it is */ if( image->depth() > 8 ) { - imgType = PT_HICOLOR_IMAGE; + imgType = ImgSaver::ImgHicolor; } else { if( image->depth() == 1 || image->numColors() == 2 ) { kdDebug(28000) << "This is black And White!" << endl; - imgType = PT_BW_IMAGE; + imgType = ImgSaver::ImgBW; } else { - imgType = PT_COLOR_IMAGE; + imgType = ImgSaver::ImgColor; if( image->allGray() ) { - imgType = PT_GRAY_IMAGE; + 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( picType type ) +QString ImgSaver::findFormat( ImgSaver::ImageType type ) { - if (type==PT_THUMBNAIL) return ("BMP"); // thumbnail always this format - if (type==PT_PREVIEW) return ("BMP"); // preview always this format + 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 PT_COLOR_IMAGE: + case ImgSaver::ImgColor: format = konf->readEntry( OP_FORMAT_COLOR, "nothing" ); kdDebug( 28000 ) << "Format for Color: " << format << endl; break; - case PT_GRAY_IMAGE: + case ImgSaver::ImgGray: format = konf->readEntry( OP_FORMAT_GRAY, "nothing" ); kdDebug( 28000 ) << "Format for Gray: " << format << endl; break; - case PT_BW_IMAGE: + case ImgSaver::ImgBW: format = konf->readEntry( OP_FORMAT_BW, "nothing" ); kdDebug( 28000 ) << "Format for BlackAndWhite: " << format << endl; break; - case PT_HICOLOR_IMAGE: + 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(picType type) +QString ImgSaver::picTypeAsString(ImgSaver::ImageType type) { QString res; switch (type) { -case PT_COLOR_IMAGE: +case ImgSaver::ImgColor: res = i18n("indexed color image (up to 8 bit depth)"); break; -case PT_GRAY_IMAGE: +case ImgSaver::ImgGray: res = i18n("gray scale image (up to 8 bit depth)"); break; -case PT_BW_IMAGE: +case ImgSaver::ImgBW: res = i18n("lineart image (black and white, 1 bit depth)"); break; -case PT_HICOLOR_IMAGE: +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( picType type) +QString ImgSaver::startFormatDialog( ImgSaver::ImageType type) { FormatDialog fd(NULL,type); // set default values - if( type != PT_PREVIEW ) + 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( picType type, QString format ) const +bool ImgSaver::isRememberedFormat( ImgSaver::ImageType type, QString format ) const { if( getFormatForType( type ) == format ) { return( true ); } else { return( false ); } } -QString ImgSaver::getFormatForType( picType type ) const +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 PT_COLOR_IMAGE: + case ImgSaver::ImgColor: f = konf->readEntry( OP_FORMAT_COLOR, "BMP" ); break; - case PT_GRAY_IMAGE: + case ImgSaver::ImgGray: f = konf->readEntry( OP_FORMAT_GRAY, "BMP" ); break; - case PT_BW_IMAGE: + case ImgSaver::ImgBW: f = konf->readEntry( OP_FORMAT_BW, "BMP" ); break; - case PT_HICOLOR_IMAGE: + case ImgSaver::ImgHicolor: f = konf->readEntry( OP_FORMAT_HICOLOR, "BMP" ); break; default: f = "BMP"; break; } return( f ); } -void ImgSaver::storeFormatForType( picType type, QString format, bool ask ) +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 PT_COLOR_IMAGE: + case ImgSaver::ImgColor: konf->writeEntry( OP_FORMAT_COLOR, format ); break; - case PT_GRAY_IMAGE: + case ImgSaver::ImgGray: konf->writeEntry( OP_FORMAT_GRAY, format ); break; - case PT_BW_IMAGE: + case ImgSaver::ImgBW: konf->writeEntry( OP_FORMAT_BW, format ); break; - case PT_HICOLOR_IMAGE: + 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 ) { /* 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 219c309..77a4779 100644 --- a/kooka/imgsaver.h +++ b/kooka/imgsaver.h @@ -1,155 +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; -/** - * enum picType: - * Specifies the type of the image to save. This is important for - * getting the format. - **/ -typedef enum { - PT_PREVIEW, - PT_THUMBNAIL, - PT_HICOLOR_IMAGE, - PT_COLOR_IMAGE, - PT_GRAY_IMAGE, - PT_BW_IMAGE, - PT_FINISHED -} picType; - - 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 + 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( picType ) const; - void storeFormatForType( picType, QString, bool ); - bool isRememberedFormat( picType type, QString format ) const; + 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 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(picType type); + 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( picType type ); + 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( picType ); + 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 aa0bf6e..9055178 100644 --- a/kooka/kooka.cpp +++ b/kooka/kooka.cpp @@ -1,499 +1,508 @@ /************************************************************************** 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( QString(), KookaView::StatusTemp ); + 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::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()); + 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("Create From Selection"), "crop", CTRL+Key_N, + 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" ); - openWithAction = new KAction(i18n("Open With..."), "fileopen", KStdAccel::open(), - m_view, SLOT( slOpenCurrInGraphApp() ), - actionCollection(), "openInGraphApp" ); - 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 ); + //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, m_view, SLOT( slStartPreview()), actionCollection(), "startPreview" ); previewAction = new KAction(i18n("Start Scan"), "scanner", 0, m_view, SLOT( slStartFinalScan()), actionCollection(), "startScan" ); // 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); 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); - openWithAction->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 5ae0605..d25462c 100644 --- a/kooka/kooka.h +++ b/kooka/kooka.h @@ -1,167 +1,169 @@ /************************************************************************** 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 *openWithAction; 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/kookaui.rc b/kooka/kookaui.rc index 01beb2e..9762f9a 100644 --- a/kooka/kookaui.rc +++ b/kooka/kookaui.rc @@ -1,81 +1,87 @@ - + -

File + Gallery + + + + - + + + + S&can &Image - - - - + + Settings + - + - + Image Viewer Toolbar Scanning Toolbar diff --git a/kooka/kookaview.cpp b/kooka/kookaview.cpp index 3ad650c..00e8330 100644 --- a/kooka/kookaview.cpp +++ b/kooka/kookaview.cpp @@ -1,1205 +1,1189 @@ /************************************************************************** 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 #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))); /* * 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( 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)); 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 } // 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 \ 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() { - kdDebug( 28000) << k_funcinfo << endl; - 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 signalGallerySelectionChanged(fti->isDir(),packager->selectedItems().count()); } } void KookaView::slotLoadedImageChanged(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(); - if ( !img ) - return; + 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(); + KookaImage *img = packager->getCurrImage(true); startOCR( img ); emit( signalCleanStatusbar( )); } -void KookaView::startOCR( KookaImage *img ) + +void KookaView::startOCR(KookaImage *img) { - if( img && ! img->isNull() ) + if (img==NULL || img->isNull()) return; + + if (ocrFabric==NULL) { - if( ocrFabric == 0L ) - { - ocrFabric = new KSaneOcr( m_mainDock, KGlobal::config() ); - ocrFabric->setImageCanvas( img_canvas ); + 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_ocrResEdit, SLOT(setText( const QString& ))); - connect( ocrFabric, SIGNAL( newOCRResultText( const QString& )), - m_dockOCRText, SLOT( show() )); + 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( repaintOCRResImage( )), + img_canvas, SLOT(repaint())); - connect( ocrFabric, SIGNAL( updateWord(int, const QString&, const QString& )), - m_ocrResEdit, SLOT( slUpdateOCRResult( int, const QString&, const QString& ))); + connect( ocrFabric, SIGNAL( clearOCRResultText()), + m_ocrResEdit, SLOT(clear())); - connect( ocrFabric, SIGNAL( ignoreWord(int, const ocrWord&)), - m_ocrResEdit, SLOT( slIgnoreWrongWord( int, const ocrWord& ))); + connect( ocrFabric, SIGNAL( updateWord(int, const QString&, const QString& )), + m_ocrResEdit, SLOT( slUpdateOCRResult( int, const QString&, const QString& ))); - connect( ocrFabric, SIGNAL( markWordWrong(int, const ocrWord& )), - m_ocrResEdit, SLOT( slMarkWordWrong( int, const ocrWord& ))); + connect( ocrFabric, SIGNAL( ignoreWord(int, const ocrWord&)), + m_ocrResEdit, SLOT( slIgnoreWrongWord( int, const ocrWord& ))); - connect( ocrFabric, SIGNAL( readOnlyEditor( bool )), - m_ocrResEdit, SLOT( setReadOnly( bool ))); + connect( ocrFabric, SIGNAL( markWordWrong(int, const ocrWord& )), + m_ocrResEdit, SLOT( slMarkWordWrong( int, const ocrWord& ))); - connect( ocrFabric, SIGNAL( selectWord( int, const ocrWord& )), - m_ocrResEdit, SLOT( slSelectWord( int, const ocrWord& ))); + connect( ocrFabric, SIGNAL( readOnlyEditor( bool )), + m_ocrResEdit, SLOT( setReadOnly( bool ))); - } - - Q_CHECK_PTR( ocrFabric ); - ocrFabric->slSetImage( img ); - - if( !ocrFabric->startOCRVisible(m_mainDock) ) - { - KMessageBox::sorry(0, i18n("Could not start OCR-Process.\n" - "Probably there is already one running." )); - - } + 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(); } void KookaView::slLoadScanParams( ) { if( ! sane ) return; #if 0 /* not yet cooked */ LoadSetDialog loadDialog( m_mainDock, sane->shortScannerName(), sane ); if( loadDialog.exec()) { kdDebug(28000)<< "Executed successfully" << endl; } #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::slShowAImage( KookaImage *img ) + +void KookaView::slShowAImage(KookaImage *img) { - kdDebug(28000) << "Show new Image" << endl; - if( img_canvas ) - { - img_canvas->newImage( img ); - img_canvas->setReadOnly(false); - } + if (img_canvas) + { + img_canvas->newImage(img); + img_canvas->setReadOnly(false); + } - /* tell ocr about */ - if( ocrFabric ) - { - ocrFabric->slSetImage( img ); - } + if (ocrFabric) ocrFabric->slSetImage(img); /* tell ocr about it */ - /* Status Bar */ - KStatusBar *statBar = m_mainWindow->statusBar(); - if( img_canvas ) - statBar->changeItem( img_canvas->imageInfoString(), StatusImage ); + 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( void ) +void KookaView::slOpenCurrInGraphApp() { - QString file; - - if( packager ) - { - KFileTreeViewItem *ftvi = packager->currentKFileTreeViewItem(); - - if( ! ftvi ) return; + if (packager==NULL) return; - kdDebug(28000) << "Trying to open <" << ftvi->url().prettyURL()<< ">" << endl; - KURL::List urllist; + KFileTreeViewItem *ftvi = packager->currentKFileTreeViewItem(); + if (ftvi==NULL) return; - urllist.append( ftvi->url()); - - KRun::displayOpenWithDialog( urllist ); - } + 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/ksaneocr.cpp b/kooka/ksaneocr.cpp index 7d34e4e..7e18590 100644 --- a/kooka/ksaneocr.cpp +++ b/kooka/ksaneocr.cpp @@ -1,1504 +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 ) { 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 ) +bool KSaneOcr::startOCRVisible(QWidget *parent) { - if( visibleOCRRunning ) return( false ); - bool res = true; + if (visibleOCRRunning) + { + KMessageBox::sorry(NULL,i18n("An OCR process is already running.")); + return (false); + } - m_parent = parent; + 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 ) - { + 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 -/*** Kadmos Engine OCR ***/ - m_ocrProcessDia = new KadmosDialog( parent, m_spellInitialConfig ); + m_ocrProcessDia = new KadmosDialog(parent, m_spellInitialConfig); #else - KMessageBox::sorry(0, i18n("This version of Kooka was not compiled with KADMOS support.\n" - "Please select another OCR engine in Kooka's options dialog.")); - kdDebug(28000) << "Sorry, this version of Kooka has no KADMOS support" << endl; -#endif /* HAVE_KADMOS */ - } - else if( m_ocrEngine == KSaneOcr::OcrNone ) - { - KMessageBox::sorry(NULL,i18n("No OCR engine is configured.\n" - "Please select one in Kooka's options dialog.")); - return (false); - } - else - { - kdDebug(28000) << "Unknown OCR engine requested!" << endl; - return (false); - } + 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 ) - { - m_ocrProcessDia->setupGui(); + if (m_ocrProcessDia==NULL) return (false); - m_ocrProcessDia->introduceImage( m_img ); - visibleOCRRunning = true; + 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(); + 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( res ); + 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/scanpackager.cpp b/kooka/scanpackager.cpp index c3ca233..1d16f35 100644 --- a/kooka/scanpackager.cpp +++ b/kooka/scanpackager.cpp @@ -1,1282 +1,1392 @@ /*************************************************************************** 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 "scanpackager.h" #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); setDefaultRenameAction( QListView::Reject ); 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))); + 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 ) + +void ScanPackager::slotDirCount(KFileTreeViewItem* item,int cnt) { - if( item && item->isDir() ) - { - QString cc = i18n( "one item", "%n items", cnt); - item->setText( 1, cc ); - } - else - { - kdDebug(28000) << "Item is NOT directory - do not set child count!" << endl; - } + 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 ) { bool success = true; if( !it ) return; if( newStr.isEmpty() ) success = false; KFileTreeViewItem *item = static_cast(it); /* Free memory and imform everybody who is interested. */ KURL urlFrom = item->url(); KURL urlTo( urlFrom ); /* 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 { /* 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 ); } } /* ----------------------------------------------------------------------- */ /* * 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() const + +KookaImage* ScanPackager::getCurrImage(bool loadOnDemand) { KFileTreeViewItem *curr = currentKFileTreeViewItem(); - KookaImage *img = 0L; + if (curr==NULL) return (NULL); // no current item + if (curr->isDir()) return (NULL); // is a directory - if( curr ) + KFileItem *kfi = curr->fileItem(); + if (kfi==NULL) return (NULL); + if (kfi->extraData(this)==NULL) // see if already loaded { - KFileItem *kfi = curr->fileItem(); - if( kfi ) - { - img = static_cast(kfi->extraData( this )); - } + if (!loadOnDemand) return (NULL); // not loaded, and don't want to + slClicked(curr); // select/load this image } - return(img); + + 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); } -#include "scanpackager.moc" + + + + +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() 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 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 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/libkscan/kscandevice.h b/libkscan/kscandevice.h index 1fcf594..9515104 100644 --- a/libkscan/kscandevice.h +++ b/libkscan/kscandevice.h @@ -1,476 +1,476 @@ /* This file is part of the KDE Project -*- mode:c++; -*- 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 _KSCANDEV_H_ #define _KSCANDEV_H_ #include #include #include #include #include #include #include "kscanoption.h" #include "kscanoptset.h" #define DEFAULT_OPTIONSET "saveSet" #define SCANNER_DB_FILE "scannerrc" class ImgScanInfo; extern "C" { #include #include } /** * This is KScanDevice, a class for accessing the SANE scanning functions under KDE * * @short KScanDevice * @author Klaas Freitag * @version 0.1alpha * */ typedef enum { SSTAT_SILENT, SSTAT_IN_PROGRESS, SSTAT_NEXT_FRAME, SSTAT_STOP_NOW, STTAT_STOP_ADF_FINISHED } SCANSTATUS; /** * KScanStat * shows the status of a KScan operation **/ class KScanDevice : public QObject { Q_OBJECT /* Hmmm - No Q_PROPS ? */ public: /** * KScanDevice - the KDE Scanner Device * constructor. Does not much. * * @see openDevice * */ KScanDevice( QObject *parent = 0 ); /** * Destructor * * releases internal allocated memory. */ ~KScanDevice( ); /** * add an explicitly specified device to the list of known ones. * @param backend: the device name+parameters of the backend to add * @param description: a readable description for it * @param dontSave: if true, don't save the new device in the permanent configuration */ void addUserSpecifiedDevice(const QString& backend,const QString& description, bool dontSave = false); /** * opens the device named backend. * @return the state of the operation * @param backend: the name of the backend to open */ KScanStat openDevice( const QCString& backend ); /** * get an error message for the last SANE operation. * @return the error message string */ QString lastErrorMessage() const; /** * returns the names of all existing Scan Devices in the system. * @param enable_net: allow net devices. * @return a QStrList of available Scanners in the system * @see KScanDevice */ QStrList getDevices( ) const { return( scanner_avail ); } /** * returns the short, technical name of the currently attached backend. * It is in the form 'umax:/dev/sg1'. */ QCString shortScannerName() const { return scanner_name; } /** * returns a long, human readable name of the scanner, like * 'Mustek SP1200 Flatbed scanner'. The parameter name takes * the name of a backend, what means something like "/dev/scanner". * If the name of the backend is skipped, the selected backend is * returned. * * @param a QString with a backend string * @return a QString containing a human readable scanner name **/ QString getScannerName( const QCString& name = 0 ) const; /* * ========= Preview Functions ========== */ /** * QImage * acquirePreview( bool forceGray = 0, int dpi = 0 ) * * acquires a preview on the selected device. KScanDevice allocs * memory for the qimage returned. This qimage remains valid * until the next call to acquirePreview or until KScanDevice is * destructed. * * @return a pointer to a qimage or null on error * @param bool forceGray if true, acquire a preview in gray * @param int dpi dpi setting. If 0, the dpi setting is * elected by the object, e.g. is the smallest possible * resolution. */ KScanStat acquirePreview( bool forceGray = 0, int dpi = 0 ); /** acquires a qimage on the given settings. * the Parameter image needs to point to a object qimage, * which is filled by the SANE-Dev. After the function returns, * the qimage is valid, if the returnvalue is OK. * @param QImage *image - Pointer to a reserved image * @return the state of the operation in KScanStat */ KScanStat acquire( const QString& filename = QString::null ); /** * returns the default filename of the preview image of this scanner. * @return default filename **/ const QString previewFile(); /** * loads the last saved previewed image on this device from the default file * @return a bitmap as QImage **/ QImage loadPreviewImage(); /** * saves current previewed image from this device to the default file * @param image current preview image which should be saved * @return true if the saving was sucessful **/ bool savePreviewImage( const QImage& image ); /* * ========= List Functions ========== */ /** * returns all existing options of the selected device. * @see openDevice * @return a QStrList with the names of all options **/ QStrList getAllOptions(); /** * returns the common options of the device. A frontend should * display the returned options in a convinient way and easy to * use. * @see getAllOptions * @see getAdvancedOptions * @return a QStrList with with names of the common options. */ QStrList getCommonOptions(); /** * returns a list of advanced scan options. A frontend should * display these options in a special dialog. * @see getAllOptions * @see getCommonOptions * @return a QStrList with names of advanced scan options. */ QStrList getAdvancedOptions(); /** * retrieves a set of the current scan options and writes them * to the object pointed to by parameter optSet * @see KScanOptSet * @param optSet, a pointer to the option set object */ void getCurrentOptions( KScanOptSet *optSet ); /** * load scanner parameter sets. All options stored in @param optSet * are loaded into the scan device. */ void loadOptionSet( KScanOptSet *optSet ); /** * applys the values in an scan-option object. The value to * set must be set in the KScanOption object prior. However, * it takes effect after being applied with this function. * @see KScanOption * @return the status of the operation * @param a scan-option object with the scanner option to set and * an optional boolean parameter if it is a gamma table. **/ KScanStat apply( KScanOption *opt, bool=false ); /** * returns true if the option exists in that device. * @param name: the name of a option from a returned option-List * @return true, if the option exists */ bool optionExists( const QCString& name ); /** * checks if the backend knows the option with the required name under * a different name, like some backends do. If it does, the known name * is returned, otherwise a null QCString. */ QCString aliasName( const QCString& name ); /** * returns a Widget suitable for the selected Option and creates the * KScanOption. It is internally connected to the scan device, every * change to the widget is automaticly considered by the scan device. * @param name: Name of the SANE Option * @param parent: pointer to the parent widget * @param desc: pointer to the text appearing as widget text * @param tooltip: tooltip text. If zero, the SANE text will be used. **/ KScanOption *getGuiElement( const QCString& name, QWidget *parent, const QString& desc = QString::null, const QString& tooltip = QString::null ); /** * returns the pointer to an already created Scanoption from the * gui element list. Cares for option name aliases. */ KScanOption *getExistingGuiElement( const QCString& name ); /** * sets an widget of the named option enabled/disabled **/ void guiSetEnabled( const QCString& name, bool state ); /** * returns the maximum scan size. This is interesting e.g. for the * preview widget etc. * The unit of the return value is millimeter, if the sane backend * returns millimeter. (TODO) **/ QSize getMaxScanSize( void ) const; /** * returns the information bit stored for the currently attached scanner * identified by the key. */ QString getConfig( const QString& key, const QString& def = QString() ) const; static bool scanner_initialised; static SANE_Handle scanner_handle; - static QAsciiDict* option_dic; + static QAsciiDict *option_dic; static SANE_Device const **dev_list; static KScanOptSet *gammaTables; public slots: void slOptChanged( KScanOption* ); /** * The setting of some options require a complete reload of all * options, because they might have changed. In that case, slot * slReloadAll() is called. **/ void slReloadAll( ); /** * This slot does the same as ReloadAll, but it does not reload * the scanner option given as parameter. This is useful to make * sure that no recursion takes places during reload of options. **/ void slReloadAllBut( KScanOption*); /** * Notifies the scan device to stop the current scanning. * This slot only makes sense, if a scan is in progress. * Note that if the slot is called, it can take a few moments * to stop the scanning */ void slStopScanning( void ); /** * Image ready-slot in asynchronous scanning */ void slScanFinished( KScanStat ); /** * Save the current configuration parameter set. Only changed options are saved. **/ void slSaveScanConfigSet( const QString&, const QString& ); void slSetDirty( const QCString& name ); /** * Closes the scan device and frees all related data, makes * the system ready to open a new, other scanner. */ void slCloseDevice( ); /** * stores the info bit in a config file for the currently connected * scanner. For this, the config file $KDEHOME/.kde/share/config/scannerrc * is opened, a group is created that identifies the scanner and the * device where it is connected. The information is stored into that group. */ void slStoreConfig( const QString&, const QString& ); signals: /** * emitted, if an option change was applied, which made other * options changed. The application may connect a slot to this * signal to see, if other options became active/inactive. **/ void sigOptionsChanged(); /** * emitted, if an option change was applied, which made the * scan parameters change. The parameters need to be reread * by sane_get_parameters(). **/ void sigScanParamsChanged(); /** * emitted if a new image was acquired. The image has to be * copied in the signal handler. */ void sigNewImage( QImage*, ImgScanInfo* ); /** * emitted if a new preview was acquired. The image has to be * copied in the signal handler. */ void sigNewPreview( QImage*, ImgScanInfo* ); /** * emitted to tell the application the start of scanning. That is * before the enquiry of the scanner starts. Between this signal * and @see sigScanProgress(0) can be some time consumed by a scanner * warming up etc. */ void sigScanStart(); /** * signal to indicate the start of acquireing data. It is equal * to @see sigScanProgress emitted with value 0 and thus it is sent * after sigScanStart. * The time between sigScanStart and sigAcquireStart differs very much * from scanner to scanner. */ void sigAcquireStart(); /** * emitted to tell the application the progress of the scanning * of one page. The final value is always 1000. The signal is * emitted for zero to give the change to initialize the progress * dialog. **/ void sigScanProgress( int ); /** * emitted to show that a scan finished and provides a status how * the scan finished. */ void sigScanFinished( KScanStat ); /** * emitted to give all objects using this device to free all dependencies * on the scan device, because this signal means, that the scan device is * going to be closed. The can not be stopped. All receiving objects have * to free their stuff using the device and pray. While handling the signal, * the device is still open and valid. */ void sigCloseDevice( ); private slots: /** * Slot to acquire a whole image */ void doProcessABlock( void ); private: /** * Function which applies all Options which need to be applied. * See SANE-Documentation Table 4.5, description for SANE_CAP_SOFT_DETECT. * The function sets the options which have SANE_CAP_AUTOMATIC set, * to autmatic adjust. **/ void prepareScan( void ); KScanStat createNewImage( SANE_Parameters *p ); // not implemented // QWidget *entryField( QWidget *parent, const QString& text, // const QString& tooltip ); KScanStat find_options(); // help fct. to process options KScanStat acquire_data( bool isPreview = false ); QStrList scanner_avail; // list of names of all scan dev. QStrList option_list; // list of names of all options QStrList dirtyList; // option changes inline QString optionNotifyString(int) const; QPtrList gui_elements; QAsciiDict scannerDevices; QSocketNotifier *sn; SCANSTATUS scanStatus; /* Data for the scan process */ /* This could/should go to a small help object */ QCString scanner_name; SANE_Byte *data; QImage *img; SANE_Parameters sane_scan_param; long overall_bytes; int rest_bytes; int pixel_x, pixel_y; bool scanningPreview; KScanOptSet *storeOptions; class KScanDevicePrivate; KScanDevicePrivate *d; SANE_Status sane_stat; }; #endif diff --git a/libkscan/kscanoption.cpp b/libkscan/kscanoption.cpp index d19db79..b62b9a3 100644 --- a/libkscan/kscanoption.cpp +++ b/libkscan/kscanoption.cpp @@ -1,1254 +1,1111 @@ /* 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 -#include #include -#include #include #include #include +extern "C" { +#include +} + #include "kgammatable.h" #include "kscandevice.h" #include "kscancontrols.h" -#include "kscanoptset.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)[ 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 ) : - QObject() + +KScanOption::KScanOption(const QCString& new_name) { - if( initOption( new_name ) ) - { - int *num = (*KScanDevice::option_dic)[ getName() ]; - if( !num || !buffer ) - return; + if (!initOption(new_name)) + { + kdDebug(29000) << k_funcinfo << "initOption for <" << new_name << "> failed!" << endl; + return; + } - SANE_Status sane_stat = sane_control_option( KScanDevice::scanner_handle, *num, - SANE_ACTION_GET_VALUE, - buffer, 0 ); + int *num = KScanDevice::option_dic->find(name); + if (num==NULL || buffer.isNull()) return; - if( sane_stat == SANE_STATUS_GOOD ) - { - buffer_untouched = false; - } - } - else - { - kdDebug(29000) << "Had problems to create KScanOption - initOption failed !" << endl; - } + 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 ) +bool KScanOption::initOption(const QCString& new_name) { - desc = 0; - if( new_name.isEmpty() ) return( false ); + desc = NULL; + if (new_name.isEmpty()) return (false); - name = new_name; - desc = getOptionDesc( name ); - buffer = 0; - internal_widget = 0; - buffer_untouched = true; - buffer_size = 0; + name = new_name; + desc = getOptionDesc(name); + buffer.resize(0); + buffer_untouched = true; + internal_widget = NULL; - if( desc ) - { - /* Gamma-Table - initial values */ - gamma = 0; /* marks as unvalid */ - brightness = 0; - contrast = 0; - gamma = 100; + if (desc==NULL) return (false); + + /* Gamma-Table - initial values */ + gamma = 0; /* marks as unvalid */ + brightness = 0; + contrast = 0; + gamma = 100; - // allocate memory - switch( desc->type ) - { - case SANE_TYPE_INT: - case SANE_TYPE_FIXED: - case SANE_TYPE_STRING: - buffer = allocBuffer( desc->size ); - break; - case SANE_TYPE_BOOL: - buffer = allocBuffer( sizeof( SANE_Word ) ); - break; - default: - buffer_size = 0; - buffer = 0; - } - - KScanOption *gtOption = (*KScanDevice::gammaTables)[ new_name ]; - if( gtOption ) - { - kdDebug(29000) << "Is older GammaTable!" << endl; - KGammaTable gt; - gtOption->get( > ); + 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(); - } - else - { - // kdDebug(29000) << "Is NOT older GammaTable!" << endl; - } + gamma = gt.getGamma(); + contrast = gt.getContrast(); + brightness = gt.getBrightness(); } - return desc; + return (true); } -KScanOption::KScanOption( const KScanOption &so ) : - QObject() + +KScanOption::KScanOption(const KScanOption &so) + : QObject() { - /* desc is stored by sane-lib and may be copied */ - desc = so.desc; - name = so.name; - buffer_untouched = so.buffer_untouched; - gamma = so.gamma; - brightness = so.brightness; - contrast = so.contrast; - - /* intialise */ - buffer = 0; - buffer_size = 0; - - /* the widget is not copied ! */ - internal_widget = 0; - - if( ! ( desc && name ) ) - { - kdWarning( 29000) << "Trying to copy a not healthy option (no name nor desc)" << endl; - return; - } - - if( so.buffer_untouched ) - kdDebug(29000) << "Buffer of source is untouched!" << endl; + 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! - switch( desc->type ) - { - case SANE_TYPE_INT: - case SANE_TYPE_FIXED: - case SANE_TYPE_STRING: - buffer = allocBuffer( desc->size ); - // desc->size / sizeof( SANE_Word ) - memcpy( buffer, so.buffer, buffer_size ); - break; - case SANE_TYPE_BOOL: - buffer = allocBuffer( sizeof( SANE_Word ) ); - memcpy( buffer, so.buffer, buffer_size ); - break; - default: - kdWarning( 29000 ) << "unknown option type in copy constructor" << endl; - break; + 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( void ) +const QString KScanOption::configLine() { - QCString strval = this->get(); - kdDebug(29000) << "configLine returns <" << strval << ">" << endl; - return( strval ); + QCString strval = this->get(); + kdDebug(29000) << "configLine returns <" << strval << ">" << endl; + return (strval); } -const KScanOption& KScanOption::operator= (const KScanOption& so ) +const KScanOption &KScanOption::operator=(const KScanOption &so) { - /* desc is stored by sane-lib and may be copied */ - if( this == &so ) return( *this ); - - desc = so.desc; - name = so.name; - buffer_untouched = so.buffer_untouched; - gamma = so.gamma; - brightness = so.brightness; - contrast = so.contrast; - - delete internal_widget; - internal_widget = so.internal_widget; - - if( buffer ) { - delete [] buffer; - buffer = 0; - } + if (this==&so) return (*this); - switch( desc->type ) - { - case SANE_TYPE_INT: - case SANE_TYPE_FIXED: - case SANE_TYPE_STRING: - buffer = allocBuffer( desc->size ); - memcpy( buffer, so.buffer, buffer_size ); - break; - case SANE_TYPE_BOOL: - buffer = allocBuffer( sizeof( SANE_Word ) ); - memcpy( buffer, so.buffer, buffer_size ); - break; - default: - buffer = 0; - buffer_size = 0; - } - 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( void ) +void KScanOption::slReload() { - int *num = (*KScanDevice::option_dic)[ getName() ]; - desc = getOptionDesc( getName() ); - - if( !desc || !num ) - return; + int *num = KScanDevice::option_dic->find(name); + if (!valid() || num==NULL) return; + desc = getOptionDesc(name); - if( widget() ) + 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) << "constraint is " << desc->cap << endl; - if( !active() ) - kdDebug(29000) << desc->name << " is not active now" << endl; + kdDebug(29000) << k_funcinfo << "need to allocate now" << endl; + allocForDesc(); + } - if( !softwareSetable() ) - kdDebug(29000) << desc->name << " is not software setable" << endl; + if (!active()) return; - if( !active() || !softwareSetable() ) - { - kdDebug(29000) << "Disabling widget " << getName() << " !" << endl; - widget()->setEnabled( false ); - } - else - widget()->setEnabled( true ); - } - - /* first get mem if nothing is approbiate */ - if( !buffer ) + if (((size_t) desc->size)>buffer.size()) { - kdDebug(29000) << " *********** getting without space **********" << endl; - // allocate memory - switch( desc->type ) - { - case SANE_TYPE_INT: - case SANE_TYPE_FIXED: - case SANE_TYPE_STRING: - buffer = allocBuffer( desc->size ); - break; - case SANE_TYPE_BOOL: - buffer = allocBuffer( sizeof( SANE_Word ) ); - break; - default: - if( desc->size > 0 ) - { - buffer = allocBuffer( desc->size ); - } - } + kdDebug(29000) << "ERROR: Buffer too small for <" << name << ">" + << " size " << buffer.size() << " need " << desc->size << endl; + return; } - if( active()) + SANE_Status sane_stat = sane_control_option( KScanDevice::scanner_handle, *num, + SANE_ACTION_GET_VALUE, buffer.data(),NULL); + if( sane_stat != SANE_STATUS_GOOD ) { - if( (size_t) desc->size > buffer_size ) - { - kdDebug(29000) << "ERROR: Buffer to small" << endl; - } - else - { - SANE_Status sane_stat = sane_control_option( KScanDevice::scanner_handle, *num, - SANE_ACTION_GET_VALUE, buffer, 0 ); - - if( sane_stat != SANE_STATUS_GOOD ) - { - kdDebug(29000) << "ERROR: Cant get value for " << getName() << ": " << sane_strstatus( sane_stat ) << endl; - } else { - buffer_untouched = false; - //kdDebug(29000) << "Setting buffer untouched to FALSE" << endl; - } - } + 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 - qDebug( "M: FREEing %d byte of option <%s>", buffer_size, (const char*) getName()); + if (!buffer.isNull()) kdDebug(29000) << k_funcinfo << "Freeing " << buffer.size() << " bytes for <" << name << ">" << endl; #endif } -bool KScanOption::valid( void ) const -{ - return( desc && 1 ); -} 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 ) +bool KScanOption::set(int val) { - if( ! desc ) return( false ); - bool ret = false; + if (!valid() || buffer.isNull()) return (false); +#ifdef GETSET_DEBUG + kdDebug(29000) << k_funcinfo << "Setting <" << name << "> to " << val << endl; +#endif - int word_size = 0; - QMemArray qa; - SANE_Word sw = SANE_TRUE; - const SANE_Word sw1 = val; - const SANE_Word sw2 = SANE_FIX( (double) val ); + int word_size; + QMemArray qa; + SANE_Word sw; - switch( desc->type ) - { - case SANE_TYPE_BOOL: - - if( ! val ) - sw = SANE_FALSE; - if( buffer ) { - memcpy( buffer, &sw, sizeof( SANE_Word )); - ret = true; - } - break; - // Type int: Fill the whole buffer with that value - case SANE_TYPE_INT: - word_size = desc->size / sizeof( SANE_Word ); - - qa.resize( word_size ); - qa.fill( sw1 ); - - if( buffer ) { - memcpy( buffer, qa.data(), desc->size ); - ret = true; - } - break; - - // Type fixed: Fill the whole buffer with that value - case SANE_TYPE_FIXED: - word_size = desc->size / sizeof( SANE_Word ); - - qa.resize( word_size ); - qa.fill( sw2 ); - - if( buffer ) { - memcpy( buffer, qa.data(), desc->size ); - ret = true; - } - break; - default: - kdDebug(29000) << "Cant set " << name << " with type int" << endl; - } - if( ret ) - { - buffer_untouched = false; -#ifdef APPLY_IN_SITU - applyVal(); -#endif + 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; - //emit( optionChanged( this )); - } +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; - return( ret ); +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 ) +bool KScanOption::set(double val) { - if( ! desc ) return( false ); - bool ret = false; - int word_size = 0; - QMemArray qa; - SANE_Word sw = SANE_FALSE; + if (!valid() || buffer.isNull()) return (false); +#ifdef GETSET_DEBUG + kdDebug(29000) << k_funcinfo << "Setting <" << name << "> to " << val << endl; +#endif - switch( desc->type ) - { - case SANE_TYPE_BOOL: - - if( val > 0 ) - sw = SANE_TRUE; - if( buffer ) { - memcpy( buffer, &sw, sizeof( SANE_Word )); - ret = true; - } - break; - // Type int: Fill the whole buffer with that value - case SANE_TYPE_INT: - sw = (SANE_Word) val; - word_size = desc->size / sizeof( SANE_Word ); - - qa.resize( word_size ); - qa.fill( sw ); - - if( buffer ) { - memcpy( buffer, qa.data(), desc->size ); - ret = true; - } - break; - - // Type fixed: Fill the whole buffer with that value - case SANE_TYPE_FIXED: - sw = SANE_FIX( val ); - word_size = desc->size / sizeof( SANE_Word ); - - qa.resize( word_size ); - qa.fill( sw ); - - if( buffer ) { - memcpy( buffer, qa.data(), desc->size ); - ret = true; - } - break; - default: - kdDebug(29000) << "Cant set " << name << " with type double" << endl; + 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; } - if( ret ) - { - buffer_untouched = false; + buffer_untouched = false; #ifdef APPLY_IN_SITU - applyVal(); + applyVal(); #endif - //emit( optionChanged( this )); - } - - return( ret ); + //emit optionChanged( this ); + return (true); } - -bool KScanOption::set( int *val, int size ) +bool KScanOption::set(int *val,int size) { - if( ! desc || ! val ) return( false ); - bool ret = false; - int offset = 0; + 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 */ - int word_size = desc->size / sizeof( SANE_Word ); /* add 1 in case offset is needed */ - QMemArray qa( 1+word_size ); #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 ) + switch (desc->type) { - case SANE_TYPE_INT: - for( int i = 0; i < word_size; i++ ) - { - if( i < size ) - qa[offset+i] = (SANE_Word) *(val++); - else - qa[offset+i] = (SANE_Word) *val; - } - ret = true; - break; - - // Type fixed: Fill the whole buffer with that value - case SANE_TYPE_FIXED: - for( int i = 0; i < word_size; i++ ) - { - if( i < size ) - qa[offset+i] = SANE_FIX((double)*(val++)); - else - qa[offset+i] = SANE_FIX((double) *val ); - } - ret = true; - break; - default: - kdDebug(29000) << "Cant set " << name << " with type int*" << endl; - - } +case SANE_TYPE_INT: + for (int i = 0; isize; - - if( offset ) - copybyte += sizeof( SANE_Word ); +case SANE_TYPE_FIXED: + for (int i = 0; i with this type" << endl; + return (false); + break; } - if( ret ) - { - buffer_untouched = false; + 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(); + applyVal(); #endif - //emit( optionChanged( this )); - } - - return( ret ); + //emit( optionChanged( this )); + return (true); } -bool KScanOption::set( const QCString& c_string ) + +bool KScanOption::set(const QCString &c_string) { - bool ret = false; - int val = 0; - - if( ! desc ) return( false ); + if (!valid() || buffer.isNull()) return (false); +#ifdef GETSET_DEBUG + kdDebug(29000) << k_funcinfo << "Setting <" << name << "> to '" << c_string << "'" << endl; +#endif - /* 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); + int val; - if( QString(c_string).contains( re )) - { - QStringList relist = QStringList::split( ", ", QString(c_string) ); + /* 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(); + int brig = (relist[0]).toInt(); + int contr = (relist[1]).toInt(); + int gamm = (relist[2]).toInt(); - KGammaTable gt( brig, contr, gamm ); - ret = set( > ); - kdDebug(29000) << "Setting GammaTable with int vals " << brig << "|" << contr << "|" << gamm << endl; - - return( ret ); - } + 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: - kdDebug(29000) << "Setting " << c_string << " as String" << endl; - - if( buffer_size >= c_string.length() ) - { - memset( buffer, 0, buffer_size ); - qstrncpy( (char*) buffer, (const char*) c_string, buffer_size ); - ret = true; - } - else - { - kdDebug(29000) << "ERROR: Buffer for String " << c_string << " too small: " << buffer_size << " < " << c_string.length() << endl; - } - break; - case SANE_TYPE_INT: - case SANE_TYPE_FIXED: - kdDebug(29000) << "Type is INT or FIXED, try to set value <" << c_string << ">" << endl; - val = c_string.toInt( &ret ); - if( ret ) - set( &val, 1 ); - else - kdDebug(29000) << "Conversion of string value failed!" << endl; - - break; - case SANE_TYPE_BOOL: - kdDebug(29000) << "Type is BOOL, setting value <" << c_string << ">" << endl; - val = 0; - if( c_string == "true" ) val = 1; - set( val ); - break; - default: - kdDebug(29000) << "Type of " << name << " is " << desc->type << endl; - kdDebug(29000) << "Cant set " << name << " with type string" << endl; - break; - } + /* 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; - if( ret ) - { - buffer_untouched = false; +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(); + applyVal(); #endif - //emit( optionChanged( this )); - } - kdDebug(29000) << "Returning " << ret << endl; - return( ret ); + //emit( optionChanged( this )); + return (true); } -bool KScanOption::set( KGammaTable *gt ) +bool KScanOption::set(KGammaTable *gt) { - if( ! desc ) return( false ); - bool ret = true; - int size = gt->tableSize(); - SANE_Word *run = gt->getTable(); + if (!valid() || buffer.isNull()) return (false); +#ifdef GETSET_DEBUG + kdDebug(29000) << k_funcinfo << "Setting <" << name << ">" << endl; +#endif - int word_size = desc->size / sizeof( SANE_Word ); - QMemArray qa( word_size ); - kdDebug(29000) << "KScanOption::set for Gammatable !" << endl; - switch( desc->type ) - { - case SANE_TYPE_INT: - for( int i = 0; i < word_size; i++ ){ - if( i < size ) - qa[i] = *run++; - else - qa[i] = *run; - } - break; - - // Type fixed: Fill the whole buffer with that value - case SANE_TYPE_FIXED: - for( int i = 0; i < word_size; i++ ){ - if( i < size ) - qa[i] = SANE_FIX((double)*(run++)); - else - qa[i] = SANE_FIX((double) *run ); - } - break; - default: - kdDebug(29000) << "Cant set " << name << " with type GammaTable" << endl; - ret = false; - - } + int size = gt->tableSize(); + SANE_Word *run = gt->getTable(); - if( ret && buffer ) - { - /* remember raw vals */ - gamma = gt->getGamma(); - brightness = gt->getBrightness(); - contrast = gt->getContrast(); + int word_size = desc->size/sizeof(SANE_Word); + QMemArray qa(word_size); - memcpy( buffer, qa.data(), desc->size ); - buffer_untouched = false; + 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(); + applyVal(); #endif - //emit( optionChanged( this )); - } - - return( ret ); + //emit( optionChanged( this )); + return (true); } -bool KScanOption::get( int *val ) const + +bool KScanOption::get(int *val) const { - SANE_Word sane_word; - double d; - if( !valid() || !getBuffer()) - return( false ); + if (!valid() || buffer.isNull()) return (false); + + SANE_Word sane_word; + double d; - switch( desc->type ) + switch (desc->type) { - case SANE_TYPE_BOOL: - /* Buffer has a SANE_Word */ - sane_word = *((SANE_Word*)buffer); - if( sane_word == SANE_TRUE ) - *val = 1; - else - *val = 0; - break; - /* Type int: Fill the whole buffer with that value */ - /* reading the first is OK */ - case SANE_TYPE_INT: - sane_word = *((SANE_Word*)buffer); - *val = sane_word; - break; - - // Type fixed: whole buffer filled with the same value - case SANE_TYPE_FIXED: - d = SANE_UNFIX(*(SANE_Word*)buffer); - *val = (int)d; - break; - default: - kdDebug(29000) << "Cant get " << name << " to type int" << endl; - return( false ); +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); } - // qDebug( "option::get returns %d", *val ); - return( true ); +#ifdef GETSET_DEBUG + kdDebug(29000) << k_funcinfo << "Returning <" << name << "> as " << *val << endl; +#endif + return (true); } - -QCString KScanOption::get( void ) const +QCString KScanOption::get() const { - QCString retstr; + if (!valid() || buffer.isNull()) return ("parametererror"); - SANE_Word sane_word; + 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; - if( !valid() || !getBuffer()) - return( "parametererror" ); - - switch( desc->type ) - { - case SANE_TYPE_BOOL: - sane_word = *((SANE_Word*)buffer); - if( sane_word == SANE_TRUE ) - retstr = "true"; - else - retstr = "false"; - break; - case SANE_TYPE_STRING: - retstr = (const char*) getBuffer(); - // retstr.sprintf( "%s", cc ); - break; - case SANE_TYPE_INT: - sane_word = *((SANE_Word*)buffer); - retstr.setNum( sane_word ); - break; - case SANE_TYPE_FIXED: - sane_word = (SANE_Word) SANE_UNFIX(*(SANE_Word*)buffer); - retstr.setNum( sane_word ); - break; - - default: - kdDebug(29000) << "Cant get " << getName() << " to type String !" << endl; - retstr = "unknown"; - } +case SANE_TYPE_STRING: + retstr = (const char*) buffer.data(); + break; - /* Handle gamma-table correctly */ - ; - if( type() == KScanOption::GammaTable ) - { - retstr.sprintf( "%d, %d, %d", gamma, brightness, contrast ); - } +case SANE_TYPE_INT: + sane_word = *((SANE_Word *) buffer.data()); + retstr.setNum(sane_word); + break; - kdDebug(29000) << "option::get returns " << retstr << endl; - return( retstr ); +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 ) - { - gt->setAll( gamma, brightness, contrast ); - // gt->calcTable(); - return( true ); - } - return( false ); + 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 */ +case KScanOption::Bool: /* Widget Type is ToggleButton */ w = createToggleButton( parent, text ); break; -case KScanOption::SingleValue: /* Widget Type is Entry-Field */ +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 */ +case KScanOption::Range: /* Widget Type is Slider */ w = createSlider( parent, text ); break; -case KScanOption::Resolution: /* Widget Type is Resolution */ +case KScanOption::Resolution: /* Widget Type is Resolution */ w = createComboBox( parent, text ); break; -case KScanOption::GammaTable: /* Widget Type is Gamma Table */ +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 *))); + 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); } -QWidget *KScanOption::createToggleButton( QWidget *parent, const QString& text ) +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); } -QWidget *KScanOption::createComboBox( QWidget *parent, const QString& text ) +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); } -QWidget *KScanOption::createEntryField( QWidget *parent, const QString& text ) +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); } -QWidget *KScanOption::createSlider( QWidget *parent, const QString& text ) +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); } -QWidget *KScanOption::createFileField( QWidget *parent, const QString& text ) +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 ) +void KScanOption::allocBuffer(long size) { - if( size < 1 ) return( 0 ); + if (size<1) return; #ifdef MEM_DEBUG - qDebug( "M: Reserving %ld bytes of mem for <%s>", size, (const char*) getName() ); + 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; + } - void *r = new char[ size ]; - buffer_size = size; - - if( r ) memset( r, 0, size ); - - return( r ); - + buffer.fill(0); // clear allocated buffer } - +#ifdef APPLY_IN_SITU bool KScanOption::applyVal( void ) { bool res = true; - int *idx = (*KScanDevice::option_dic)[ name ]; + int *idx = KScanDevice::option_dic->find(name); if( *idx == 0 ) return( false ); - if( ! buffer ) return( false ); + if(buffer.isNull() ) return( false ); SANE_Status stat = sane_control_option ( KScanDevice::scanner_handle, *idx, - SANE_ACTION_SET_VALUE, buffer, - 0 ); + 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 c5a8cd0..fa10c8b 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 -#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; /** * 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 ){ return( ! buffer_untouched );} + 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; + 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 ); } - QWidget *widget( ) const { return( internal_widget ); } + 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 ); - bool initOption( const QCString& new_name ); - void *allocBuffer( long ); +#endif + 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; - void *buffer; - QWidget *internal_widget; +// void *buffer; + QByteArray buffer; bool buffer_untouched; - size_t buffer_size; +// size_t buffer_size; + QWidget *internal_widget; /* For gamma-Tables remember gamma, brightness, contrast */ int gamma, brightness, contrast; - - class KScanOptionPrivate; - KScanOptionPrivate *d; }; #endif