File indexing completed on 2024-04-21 15:12:05
0001 /************************************************************************ 0002 * * 0003 * This file is part of Kooka, a scanning/OCR application using * 0004 * Qt <http://www.qt.io> and KDE Frameworks <http://www.kde.org>. * 0005 * * 0006 * Copyright (C) 1999-2016 Klaas Freitag <freitag@suse.de> * 0007 * Jonathan Marten <jjm@keelhaul.me.uk> * 0008 * * 0009 * Kooka is free software; you can redistribute it and/or modify it * 0010 * under the terms of the GNU Library General Public License as * 0011 * published by the Free Software Foundation and appearing in the * 0012 * file COPYING included in the packaging of this file; either * 0013 * version 2 of the License, or (at your option) any later version. * 0014 * * 0015 * As a special exception, permission is given to link this program * 0016 * with any version of the KADMOS OCR/ICR engine (a product of * 0017 * reRecognition GmbH, Kreuzlingen), and distribute the resulting * 0018 * executable without including the source code for KADMOS in the * 0019 * source distribution. * 0020 * * 0021 * This program is distributed in the hope that it will be useful, * 0022 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0024 * GNU General Public License for more details. * 0025 * * 0026 * You should have received a copy of the GNU General Public * 0027 * License along with this program; see the file COPYING. If * 0028 * not, see <http://www.gnu.org/licenses/>. * 0029 * * 0030 ************************************************************************/ 0031 0032 #include "kookaview.h" 0033 0034 #include <kio_version.h> 0035 #if KIO_VERSION >= QT_VERSION_CHECK(5, 69, 0) 0036 #define HAVE_KIO_APPLICATIONLAUNCHERJOB 0037 #endif 0038 0039 #include <qlabel.h> 0040 #include <qlayout.h> 0041 #include <qsplitter.h> 0042 #include <qimage.h> 0043 #include <qapplication.h> 0044 #include <qicon.h> 0045 #include <qaction.h> 0046 #include <qmenu.h> 0047 #include <qprintdialog.h> 0048 #include <qprinter.h> 0049 #include <qfiledialog.h> 0050 0051 #include <kapplicationtrader.h> 0052 #include <klocalizedstring.h> 0053 #include <kmessagebox.h> 0054 #include <kled.h> 0055 #include <kactioncollection.h> 0056 #include <kactionmenu.h> 0057 #include <kfileitem.h> 0058 #include <kmainwindow.h> 0059 0060 #ifdef HAVE_KIO_APPLICATIONLAUNCHERJOB 0061 #include <kio/applicationlauncherjob.h> 0062 #include <kio/jobuidelegatefactory.h> 0063 #else 0064 #include <krun.h> 0065 #endif 0066 0067 #include "scandevices.h" 0068 #include "kscandevice.h" 0069 #include "deviceselector.h" 0070 #include "adddevicedialog.h" 0071 #include "previewer.h" 0072 #include "imagecanvas.h" 0073 #include "scanimage.h" 0074 0075 #include "kookapref.h" 0076 #include "galleryhistory.h" 0077 #include "thumbview.h" 0078 #include "abstractocrengine.h" 0079 #include "abstractdestination.h" 0080 #include "pluginmanager.h" 0081 #include "ocrresedit.h" 0082 #include "scanpresetsdialog.h" 0083 #include "kookagallery.h" 0084 #include "kookascanparams.h" 0085 #include "scangallery.h" 0086 #include "imagetransform.h" 0087 #include "statusbarmanager.h" 0088 #include "kookasettings.h" 0089 #include "imagefilter.h" 0090 #include "recentsaver.h" 0091 #include "kooka_logging.h" 0092 0093 #include "imgprintdialog.h" 0094 #include "kookaprint.h" 0095 0096 0097 // --------------------------------------------------------------------------- 0098 0099 // Some of the UI panels (the gallery and the image viewer) are common 0100 // to more that one of the main task tabs. This means that they can't simply 0101 // be added to the tabs/splitters in the normal way, as a widget can only be 0102 // a child of one parent at a time. 0103 // 0104 // This WidgetSite acts as a layout placeholder for such reassignable widgets. 0105 // It is assigned a new child widget when tabs are switched. 0106 // 0107 // It also defines the frame style for the panels. So, in order to maintain a 0108 // consistent look, all of those panels should derive from QWidget (or, if 0109 // from QFrame, do not set a frame style) and should set the margin of any 0110 // internal layout to 0. (KHBox/KVBox do this automatically, but any other 0111 // layout needs the margin explicitly set). 0112 0113 class WidgetSite : public QFrame 0114 { 0115 public: 0116 WidgetSite(QWidget *parent, QWidget *widget = nullptr); 0117 void setWidget(QWidget *widget); 0118 0119 private: 0120 static int sCount; 0121 }; 0122 0123 int WidgetSite::sCount = 0; 0124 0125 WidgetSite::WidgetSite(QWidget *parent, QWidget *widget) 0126 : QFrame(parent) 0127 { 0128 QString name = QString("WidgetSite-#%1").arg(++sCount); 0129 setObjectName(name.toLocal8Bit()); 0130 0131 setFrameStyle(QFrame::Panel | QFrame::Raised); // from "scanparams.cpp" 0132 setLineWidth(1); 0133 0134 QGridLayout *lay = new QGridLayout(this); 0135 lay->setRowStretch(0, 1); 0136 lay->setColumnStretch(0, 1); 0137 0138 if (widget == nullptr) { 0139 QLabel *l = new QLabel(name, this); 0140 l->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); 0141 widget = l; 0142 } 0143 0144 //qCDebug(KOOKA_LOG) << name 0145 // << "widget is a" << widget->metaObject()->className() 0146 // << "parent is a" << widget->parent()->metaObject()->className(); 0147 lay->addWidget(widget, 0, 0); 0148 widget->show(); 0149 } 0150 0151 void WidgetSite::setWidget(QWidget *widget) 0152 { 0153 QGridLayout *lay = static_cast<QGridLayout *>(layout()); 0154 0155 QObjectList childs = children(); 0156 for (QObjectList::iterator it = childs.begin(); it != childs.end(); ++it) { 0157 QObject *ch = (*it); 0158 if (ch->isWidgetType()) { 0159 QWidget *w = static_cast<QWidget *>(ch); 0160 w->hide(); 0161 lay->removeWidget(w); 0162 } 0163 } 0164 0165 //qCDebug(KOOKA_LOG) << objectName() 0166 // << "widget is a" << widget->metaObject()->className() 0167 // << "parent is a" << widget->parent()->metaObject()->className(); 0168 lay->addWidget(widget, 0, 0); 0169 widget->show(); 0170 } 0171 0172 // --------------------------------------------------------------------------- 0173 0174 // Convenience class for layout splitter. 0175 0176 class WidgetSplitter : public QSplitter 0177 { 0178 public: 0179 WidgetSplitter(Qt::Orientation orientation, QWidget *parent = nullptr); 0180 }; 0181 0182 WidgetSplitter::WidgetSplitter(Qt::Orientation orientation, QWidget *parent) 0183 : QSplitter(orientation, parent) 0184 { 0185 setChildrenCollapsible(false); 0186 setContentsMargins(0, 0, 0, 0); 0187 setStretchFactor(1, 1); 0188 } 0189 0190 // --------------------------------------------------------------------------- 0191 0192 KookaView::KookaView(KMainWindow *parent, const QByteArray &deviceToUse) 0193 : QTabWidget(parent) 0194 { 0195 setObjectName("KookaView"); 0196 0197 mMainWindow = parent; 0198 mScanParams = nullptr; 0199 mCurrentTab = KookaView::TabNone; 0200 0201 /** Image Viewer **/ 0202 mImageCanvas = new ImageCanvas(this); 0203 mImageCanvas->setMinimumSize(100, 200); 0204 QMenu *ctxtmenu = mImageCanvas->contextMenu(); 0205 if (ctxtmenu != nullptr) { 0206 ctxtmenu->addSection(i18n("Image View")); 0207 } 0208 0209 // Connections ImageCanvas --> myself 0210 connect(mImageCanvas, QOverload<const QRect &>::of(&ImageCanvas::newRect), this, &KookaView::slotSelectionChanged); 0211 0212 /** Thumbnail View **/ 0213 mThumbView = new ThumbView(this); 0214 0215 /** Scan Gallery **/ 0216 mGallery = new KookaGallery(this); 0217 ScanGallery *packager = mGallery->galleryTree(); 0218 0219 // Connections ScanGallery --> myself 0220 connect(packager, &ScanGallery::itemHighlighted, this, &KookaView::slotGallerySelectionChanged); 0221 connect(packager, &ScanGallery::showImage, this, &KookaView::slotShowImage); 0222 connect(packager, &ScanGallery::aboutToShowImage, this, &KookaView::slotStartLoading); 0223 connect(packager, &ScanGallery::unloadImage, this, &KookaView::slotUnloadImage); 0224 0225 // Connections ScanGallery --> ThumbView 0226 connect(packager, &ScanGallery::itemHighlighted, mThumbView, &ThumbView::slotHighlightItem); 0227 connect(packager, &ScanGallery::imageChanged, mThumbView, &ThumbView::slotImageChanged); 0228 connect(packager, &ScanGallery::fileRenamed, mThumbView, &ThumbView::slotImageRenamed); 0229 0230 // Connections ThumbView --> ScanGallery 0231 connect(mThumbView, &ThumbView::itemHighlighted, packager, &ScanGallery::slotHighlightItem); 0232 connect(mThumbView, &ThumbView::itemActivated, packager, &ScanGallery::slotActivateItem); 0233 0234 GalleryHistory *recentFolder = mGallery->galleryRecent(); 0235 0236 // Connections ScanGallery <--> recent folder history 0237 connect(packager, &ScanGallery::galleryPathChanged, recentFolder, &GalleryHistory::slotPathChanged); 0238 connect(packager, &ScanGallery::galleryDirectoryRemoved, recentFolder, &GalleryHistory::slotPathRemoved); 0239 connect(recentFolder, &GalleryHistory::pathSelected, packager, &ScanGallery::slotSelectDirectory); 0240 0241 /** Scanner Settings **/ 0242 0243 if (!deviceToUse.isEmpty() && deviceToUse != "gallery") { 0244 ScanDevices::self()->addUserSpecifiedDevice(deviceToUse, 0245 "on command line", 0246 "", 0247 true); 0248 } 0249 0250 /** Scanner Device **/ 0251 mScanDevice = new KScanDevice(this); 0252 0253 // Connections KScanDevice --> myself 0254 connect(mScanDevice, &KScanDevice::sigNewImage, this, &KookaView::slotNewImageScanned); 0255 connect(mScanDevice, &KScanDevice::sigNewPreview, this, &KookaView::slotNewPreview); 0256 connect(mScanDevice, &KScanDevice::sigScanStart, this, &KookaView::slotScanStart); 0257 connect(mScanDevice, &KScanDevice::sigAcquireStart, this, &KookaView::slotAcquireStart); 0258 connect(mScanDevice, &KScanDevice::sigScanFinished, this, &KookaView::slotScanFinished); 0259 0260 /** Scan Preview **/ 0261 mPreviewCanvas = new Previewer(this); 0262 mPreviewCanvas->setMinimumSize(100, 100); 0263 ctxtmenu = mPreviewCanvas->getImageCanvas()->contextMenu(); 0264 if (ctxtmenu != nullptr) { 0265 ctxtmenu->addSection(i18n("Scan Preview")); 0266 } 0267 0268 // Connections Previewer --> myself 0269 connect(mPreviewCanvas, &Previewer::previewDimsChanged, this, &KookaView::slotPreviewDimsChanged); 0270 0271 /** Ocr Result Text **/ 0272 mOcrResEdit = new OcrResEdit(this); 0273 mOcrResEdit->setAcceptRichText(false); 0274 mOcrResEdit->setWordWrapMode(QTextOption::NoWrap); 0275 mOcrResEdit->setPlaceholderText(i18n("OCR results will appear here")); 0276 connect(mOcrResEdit, &OcrResEdit::spellCheckStatus, this, [this](const QString &msg){ changeStatus(msg); }); 0277 0278 /** Tabs **/ 0279 // TODO: not sure which tab position is best, make this configurable 0280 setTabPosition(QTabWidget::West); 0281 setTabsClosable(false); 0282 0283 mScanPage = new WidgetSplitter(Qt::Horizontal, this); 0284 addTab(mScanPage, QIcon::fromTheme("scan"), i18n("Scan")); 0285 0286 mGalleryPage = new WidgetSplitter(Qt::Horizontal, this); 0287 addTab(mGalleryPage, QIcon::fromTheme("image-x-generic"), i18n("Gallery")); 0288 0289 mOcrPage = new WidgetSplitter(Qt::Vertical, this); 0290 addTab(mOcrPage, QIcon::fromTheme("ocr"), i18n("OCR")); 0291 0292 connect(this, &QTabWidget::currentChanged, this, &KookaView::slotTabChanged); 0293 0294 // TODO: make the splitter layouts and sizes configurable 0295 0296 mParamsSite = new WidgetSite(this); 0297 0298 mScanGallerySite = new WidgetSite(this); 0299 mGalleryGallerySite = new WidgetSite(this); 0300 mOcrGallerySite = new WidgetSite(this); 0301 0302 mGalleryImgviewSite = new WidgetSite(this); 0303 mOcrImgviewSite = new WidgetSite(this); 0304 0305 // Even widgets that are not shared between tabs are placed in a WidgetSite, 0306 // to keep a consistent frame appearance. 0307 0308 // "Scan" page: gallery top left, scan parameters bottom left, preview right 0309 mScanSubSplitter = new WidgetSplitter(Qt::Vertical, mScanPage); 0310 mScanSubSplitter->addWidget(mScanGallerySite); // TL 0311 mScanSubSplitter->addWidget(mParamsSite); // BL 0312 mScanPage->addWidget(new WidgetSite(this, mPreviewCanvas)); // R 0313 0314 // "Gallery" page: gallery left, viewer top right, thumbnails bottom right 0315 mGalleryPage->addWidget(mGalleryGallerySite); // L 0316 mGallerySubSplitter = new WidgetSplitter(Qt::Vertical, mGalleryPage); 0317 mGallerySubSplitter->addWidget(mGalleryImgviewSite); // TR 0318 mGallerySubSplitter->addWidget(new WidgetSite(this, mThumbView)); // BR 0319 0320 // "OCR" page: gallery top left, viewer top right, results bottom 0321 mOcrSubSplitter = new WidgetSplitter(Qt::Horizontal, mOcrPage); 0322 mOcrSubSplitter->addWidget(mOcrGallerySite); // TL 0323 mOcrSubSplitter->addWidget(mOcrImgviewSite); // TR 0324 mOcrPage->addWidget(new WidgetSite(this, mOcrResEdit)); // B 0325 0326 if (slotSelectDevice(deviceToUse, false)) { 0327 slotTabChanged(KookaView::TabScan); 0328 } else { 0329 setCurrentIndex(KookaView::TabGallery); 0330 } 0331 } 0332 0333 KookaView::~KookaView() 0334 { 0335 delete mScanDevice; 0336 } 0337 0338 // this gets called via Kooka::closeEvent() at shutdown 0339 void KookaView::saveWindowSettings(KConfigGroup &grp) 0340 { 0341 qCDebug(KOOKA_LOG) << "to group" << grp.name(); 0342 KookaSettings::setLayoutTabIndex(currentIndex()); 0343 KookaSettings::setLayoutScan1(mScanPage->saveState().toBase64()); 0344 KookaSettings::setLayoutScan2(mScanSubSplitter->saveState().toBase64()); 0345 KookaSettings::setLayoutGallery1(mGalleryPage->saveState().toBase64()); 0346 KookaSettings::setLayoutGallery2(mGallerySubSplitter->saveState().toBase64()); 0347 KookaSettings::setLayoutOcr1(mOcrPage->saveState().toBase64()); 0348 KookaSettings::setLayoutOcr2(mOcrSubSplitter->saveState().toBase64()); 0349 0350 saveGalleryState(); // for the current tab 0351 KookaSettings::self()->save(); 0352 } 0353 0354 // this gets called by Kooka::applyMainWindowSettings() at startup 0355 void KookaView::applyWindowSettings(const KConfigGroup &grp) 0356 { 0357 qCDebug(KOOKA_LOG) << "from group" << grp.name(); 0358 0359 QString set = KookaSettings::layoutScan1(); 0360 if (!set.isEmpty()) mScanPage->restoreState(QByteArray::fromBase64(set.toLocal8Bit())); 0361 set = KookaSettings::layoutScan2(); 0362 if (!set.isEmpty()) mScanSubSplitter->restoreState(QByteArray::fromBase64(set.toLocal8Bit())); 0363 0364 set = KookaSettings::layoutGallery1(); 0365 if (!set.isEmpty()) mGalleryPage->restoreState(QByteArray::fromBase64(set.toLocal8Bit())); 0366 set = KookaSettings::layoutGallery2(); 0367 if (!set.isEmpty()) mGallerySubSplitter->restoreState(QByteArray::fromBase64(set.toLocal8Bit())); 0368 0369 set = KookaSettings::layoutOcr1(); 0370 if (!set.isEmpty()) mOcrPage->restoreState(QByteArray::fromBase64(set.toLocal8Bit())); 0371 set = KookaSettings::layoutOcr2(); 0372 if (!set.isEmpty()) mOcrSubSplitter->restoreState(QByteArray::fromBase64(set.toLocal8Bit())); 0373 } 0374 0375 void KookaView::saveGalleryState(int index) const 0376 { 0377 if (index == -1) index = currentIndex(); 0378 gallery()->saveHeaderState(index); 0379 } 0380 0381 void KookaView::restoreGalleryState(int index) 0382 { 0383 if (index == -1) index = currentIndex(); 0384 gallery()->restoreHeaderState(index); 0385 } 0386 0387 void KookaView::slotTabChanged(int index) 0388 { 0389 //qCDebug(KOOKA_LOG) << index; 0390 if (mCurrentTab != KookaView::TabNone) { 0391 saveGalleryState(mCurrentTab); 0392 } 0393 // save state of previous tab 0394 switch (index) { 0395 case KookaView::TabScan: // Scan 0396 mScanGallerySite->setWidget(mGallery); 0397 mGalleryImgviewSite->setWidget(mImageCanvas); // somewhere to park it 0398 emit clearStatus(StatusBarManager::ImageDims); 0399 break; 0400 0401 case KookaView::TabGallery: // Gallery 0402 mGalleryGallerySite->setWidget(mGallery); 0403 mGalleryImgviewSite->setWidget(mImageCanvas); 0404 emit clearStatus(StatusBarManager::PreviewDims); 0405 break; 0406 0407 case KookaView::TabOcr: // OCR 0408 mOcrGallerySite->setWidget(mGallery); 0409 mOcrImgviewSite->setWidget(mImageCanvas); 0410 emit clearStatus(StatusBarManager::PreviewDims); 0411 break; 0412 } 0413 0414 restoreGalleryState(index); // restore state of new tab 0415 mCurrentTab = static_cast<KookaView::TabPage>(index); 0416 // note for next tab change 0417 updateSelectionState(); // update image action states 0418 } 0419 0420 bool KookaView::slotSelectDevice(const QByteArray &useDevice, bool alwaysAsk) 0421 { 0422 qCDebug(KOOKA_LOG) << "device" << useDevice << "ask" << alwaysAsk; 0423 0424 bool haveConnection = false; 0425 bool gallery_mode = (useDevice == "gallery"); 0426 QByteArray selDevice; 0427 0428 /* in case useDevice is the term 'gallery', the user does not want to 0429 * connect to a scanner, but only work in gallery mode. Otherwise, try 0430 * to read the device to use from config or from a user dialog */ 0431 if (!gallery_mode) { 0432 selDevice = useDevice; 0433 if (selDevice.isEmpty()) { 0434 selDevice = userDeviceSelection(alwaysAsk); 0435 } 0436 0437 if (selDevice.isEmpty()) { // dialogue cancelled 0438 if (mScanParams != nullptr) { 0439 return (false); // have setup, do nothing 0440 } 0441 gallery_mode = true; 0442 } 0443 } 0444 0445 if (mScanParams != nullptr) { 0446 closeScanDevice(); // remove existing GUI object 0447 } 0448 0449 mScanParams = new KookaScanParams(this); // and create a new one 0450 connect(mScanParams, &KookaScanParams::actionSelectScanner, this, [this](){ slotSelectDevice(); }); 0451 connect(mScanParams, &KookaScanParams::actionAddScanner, this, &KookaView::slotAddDevice); 0452 0453 mParamsSite->setWidget(mScanParams); 0454 0455 if (!selDevice.isEmpty()) { // connect to the selected scanner 0456 while (!haveConnection) { 0457 qCDebug(KOOKA_LOG) << "Opening device" << selDevice; 0458 KScanDevice::Status stat = mScanDevice->openDevice(selDevice); 0459 if (stat == KScanDevice::Ok) { 0460 haveConnection = true; 0461 } else { 0462 QString msg = xi18nc("@info", 0463 "There was a problem opening the scanner device." 0464 "<nl/><nl/>" 0465 "Check that the scanner is connected and switched on, " 0466 "and that SANE support for it is correctly configured." 0467 "<nl/><nl/>" 0468 "Trying to use scanner device: <icode>%3</icode><nl/>" 0469 "libkookascan reported error: <message>%2</message><nl/>" 0470 "SANE reported error: <message>%1</message>", 0471 mScanDevice->lastSaneErrorMessage(), 0472 KScanDevice::statusMessage(stat), 0473 selDevice.constData()); 0474 0475 int tryAgain = KMessageBox::warningContinueCancel(mMainWindow, msg, QString(), 0476 KGuiItem("Retry")); 0477 if (tryAgain == KMessageBox::Cancel) { 0478 break; 0479 } 0480 } 0481 } 0482 0483 if (haveConnection) { // scanner connected OK 0484 QSize s = mScanDevice->getMaxScanSize(); // fix for 160148 0485 mPreviewCanvas->setScannerBedSize(s.width(), s.height()); 0486 0487 // Connections ScanParams --> Previewer 0488 connect(mScanParams, &ScanParams::scanResolutionChanged, mPreviewCanvas, &Previewer::slotNewScanResolutions); 0489 connect(mScanParams, &ScanParams::scanModeChanged, mPreviewCanvas, &Previewer::slotNewScanMode); 0490 connect(mScanParams, &ScanParams::newCustomScanSize, mPreviewCanvas, &Previewer::slotNewCustomScanSize); 0491 0492 // Connections Previewer --> ScanParams 0493 connect(mPreviewCanvas, &Previewer::newPreviewRect, mScanParams, &ScanParams::slotNewPreviewRect); 0494 0495 mScanParams->connectDevice(mScanDevice); // create scanning options 0496 0497 // Load the saved preview image, if there is one 0498 ScanImage *previewImage = new ScanImage(mScanDevice->loadPreviewImage()); 0499 mPreviewCanvas->setPreviewImage(ScanImage::Ptr(previewImage)); 0500 // Load auto-selection options for the selected scanner 0501 mPreviewCanvas->connectScanner(mScanDevice); 0502 } 0503 } 0504 0505 if (!haveConnection) { // no scanner device available, 0506 // or starting in gallery mode 0507 if (mScanParams != nullptr) { 0508 mScanParams->connectDevice(nullptr, gallery_mode); 0509 } 0510 } 0511 0512 emit signalScannerChanged(haveConnection); 0513 return (haveConnection); 0514 } 0515 0516 void KookaView::slotAddDevice() 0517 { 0518 AddDeviceDialog d(mMainWindow, i18n("Add Scan Device")); 0519 if (d.exec()) { 0520 QByteArray dev = d.getDevice(); 0521 QString dsc = d.getDescription(); 0522 QByteArray type = d.getType(); 0523 qCDebug(KOOKA_LOG) << "dev" << dev << "type" << type << "desc" << dsc; 0524 0525 ScanDevices::self()->addUserSpecifiedDevice(dev, dsc, type); 0526 } 0527 } 0528 0529 QByteArray KookaView::userDeviceSelection(bool alwaysAsk) 0530 { 0531 /* a list of backends the scan backend knows */ 0532 QList<QByteArray> backends = ScanDevices::self()->allDevices(); 0533 if (backends.count() == 0) { 0534 if (KMessageBox::warningContinueCancel(mMainWindow, 0535 xi18nc("@info", 0536 "No scanner devices are available." 0537 "<nl/><nl/>" 0538 "If your scanner is a type that can be auto-detected by SANE, " 0539 "check that it is connected, switched on and configured correctly." 0540 "<nl/><nl/>" 0541 "If the scanner cannot be auto-detected by SANE (this includes some network scanners), " 0542 "you need to specify the device to use. " 0543 "Use the <interface>Add Scan Device</interface> option to enter the backend name and parameters, " 0544 "or see that dialog for more information."), 0545 QString(), 0546 KGuiItem(i18n("Add Scan Device..."))) != KMessageBox::Continue) 0547 { 0548 return (""); 0549 } 0550 0551 slotAddDevice(); 0552 backends = ScanDevices::self()->allDevices(); // refresh the list 0553 if (backends.count() == 0) { 0554 return (""); // give up this time 0555 } 0556 } 0557 0558 QByteArray selDevice; 0559 DeviceSelector ds(mMainWindow, backends, 0560 (alwaysAsk ? KGuiItem() : KGuiItem(i18n("Gallery"), QIcon::fromTheme("image-x-generic")))); 0561 if (!alwaysAsk) { 0562 selDevice = ds.getDeviceFromConfig(); 0563 } 0564 0565 if (selDevice.isEmpty()) { 0566 qCDebug(KOOKA_LOG) << "no selDevice, starting selector"; 0567 if (ds.exec() == QDialog::Accepted) { 0568 selDevice = ds.getSelectedDevice(); 0569 } 0570 qCDebug(KOOKA_LOG) << "selector returned device" << selDevice; 0571 } 0572 0573 return (selDevice); 0574 } 0575 0576 QString KookaView::scannerName() const 0577 { 0578 if (mScanDevice->scannerBackendName().isEmpty()) { 0579 return (i18n("Gallery")); 0580 } 0581 if (!mScanDevice->isScannerConnected()) { 0582 return (i18n("No scanner connected")); 0583 } 0584 return (mScanDevice->scannerDescription()); 0585 } 0586 0587 bool KookaView::isScannerConnected() const 0588 { 0589 return (mScanDevice->isScannerConnected()); 0590 } 0591 0592 void KookaView::slotSelectionChanged(const QRect &newSelection) 0593 { 0594 emit signalRectangleChanged(newSelection.isValid()); 0595 } 0596 0597 // The 'state' generated here is used by Kooka::slotUpdateViewActions(). 0598 0599 void KookaView::updateSelectionState() 0600 { 0601 KookaView::StateFlags state = KookaView::StateFlags(); 0602 const FileTreeViewItem *ftvi = mGallery->galleryTree()->highlightedFileTreeViewItem(); 0603 const KFileItem *fi = (ftvi != nullptr ? ftvi->fileItem() : nullptr); 0604 const bool scanmode = (mCurrentTab == KookaView::TabScan); 0605 0606 if (scanmode) // Scan mode 0607 { 0608 if (mPreviewCanvas->getImageCanvas()->hasImage()) state |= KookaView::PreviewValid; 0609 } 0610 else // Gallery/OCR mode 0611 { 0612 state |= KookaView::GalleryShown; 0613 } 0614 0615 if (fi != nullptr && !fi->isNull()) // have a gallery selection 0616 { 0617 if (fi->isDir()) state |= KookaView::IsDirectory; 0618 else 0619 { 0620 state |= KookaView::FileSelected; 0621 if (fi->url().hasFragment()) state |= KookaView::IsSubImage; 0622 if (ftvi->childCount()>0) state |= KookaView::HasSubImages; 0623 } 0624 } 0625 0626 if (ftvi != nullptr && ftvi->isRoot()) state |= KookaView::RootSelected; 0627 if (imageViewer()->hasImage()) state |= KookaView::ImageValid; 0628 0629 //qCDebug(KOOKA_LOG) << "state" << state; 0630 emit signalViewSelectionState(state); 0631 } 0632 0633 void KookaView::slotGallerySelectionChanged() 0634 { 0635 const FileTreeViewItem *ftvi = mGallery->galleryTree()->highlightedFileTreeViewItem(); 0636 const KFileItem *fi = (ftvi != nullptr ? ftvi->fileItem() : nullptr); 0637 const bool scanmode = (mCurrentTab == KookaView::TabScan); 0638 0639 if (fi == nullptr || fi->isNull()) { // no gallery selection 0640 if (!scanmode) { 0641 emit changeStatus(i18n("No selection")); 0642 } 0643 } else { // have a gallery selection 0644 if (!scanmode) { 0645 KLocalizedString str = fi->isDir() ? ki18n("Gallery folder %1") : ki18n("Gallery image %1"); 0646 emit changeStatus(str.subs(fi->url().url(QUrl::PreferLocalFile)).toString()); 0647 } 0648 } 0649 0650 updateSelectionState(); 0651 } 0652 0653 void KookaView::loadStartupImage() 0654 { 0655 const bool wantReadOnStart = KookaSettings::startupReadImage(); 0656 if (wantReadOnStart) { 0657 QString startup = KookaSettings::startupSelectedImage(); 0658 qCDebug(KOOKA_LOG) << "load startup image" << startup; 0659 if (!startup.isEmpty()) gallery()->slotSelectImage(QUrl::fromLocalFile(startup)); 0660 } 0661 } 0662 0663 void KookaView::print() 0664 { 0665 ScanImage::Ptr img = gallery()->getCurrImage(true); 0666 if (img.isNull()) return; // load image if necessary 0667 0668 // create a KookaPrint (subclass of a QPrinter) 0669 KookaPrint printer; 0670 printer.setImage(img.data()); 0671 0672 QPrintDialog d(&printer, this); 0673 d.setWindowTitle(i18nc("@title:window", "Print Image")); 0674 d.setOptions(QAbstractPrintDialog::PrintToFile|QAbstractPrintDialog::PrintShowPageSize); 0675 0676 // TODO (investigate): even with the options set as above, the options below still 0677 // appear in the print dialogue. Is this as intended by Qt? 0678 // d.setOption(QAbstractPrintDialog::PrintSelection, false); 0679 // d.setOption(QAbstractPrintDialog::PrintPageRange, false); 0680 0681 ImgPrintDialog imgTab(img, &printer); // create tab for our options 0682 d.setOptionTabs(QList<QWidget *>() << &imgTab); // add tab to print dialogue 0683 0684 if (!d.exec()) return; // open the dialogue 0685 QString msg = imgTab.checkValid(); // check that settings are valid 0686 if (!msg.isEmpty()) // if not, display error message 0687 { 0688 KMessageBox::error(this, 0689 i18nc("@info", "Invalid print options were specified:\n\n%1", msg), 0690 i18nc("@title:window", "Cannot Print")); 0691 return; 0692 } 0693 0694 imgTab.updatePrintParameters(); // set final printer options 0695 printer.printImage(); // print the image 0696 } 0697 0698 0699 void KookaView::slotNewPreview(ScanImage::Ptr newimg) 0700 { 0701 if (newimg.isNull()) return; 0702 0703 mPreviewCanvas->newImage(newimg); // set new image and size 0704 updateSelectionState(); 0705 } 0706 0707 0708 void KookaView::slotStartOcrSelection() 0709 { 0710 emit changeStatus(i18n("Starting OCR on selection")); 0711 startOCR(mImageCanvas->selectedImage()); 0712 } 0713 0714 void KookaView::slotStartOcr() 0715 { 0716 emit changeStatus(i18n("Starting OCR on the image")); 0717 startOCR(gallery()->getCurrImage(true)); 0718 } 0719 0720 void KookaView::slotStartOcrFile() 0721 { 0722 RecentSaver saver("ocrFile"); 0723 QUrl url = QFileDialog::getOpenFileUrl(this, i18n("OCR File"), 0724 saver.recentUrl(), 0725 ImageFilter::qtFilterString(ImageFilter::Reading, ImageFilter::AllImages|ImageFilter::AllFiles)); 0726 if (!url.isValid()) return; 0727 saver.save(url); 0728 0729 ScanImage::Ptr img(new ScanImage(url)); 0730 if (!img->isFileBound()) 0731 { 0732 KMessageBox::error(mMainWindow, 0733 xi18nc("@info", "Cannot load <filename>%1</filename> for OCR:<nl/>%2", 0734 url.toDisplayString(), img->errorString()), 0735 i18n("Cannot Read OCR File")); 0736 return; 0737 } 0738 0739 emit changeStatus(xi18nc("@info", "Starting OCR on file <filename>%1</filename>", url.url(QUrl::PreferLocalFile))); 0740 startOCR(img); 0741 } 0742 0743 void KookaView::slotSetOcrSpellConfig(const QString &configFile) 0744 { 0745 #ifndef KF5 0746 qCDebug(KOOKA_LOG) << configFile; 0747 if (mOcrResEdit!=nullptr) mOcrResEdit->setSpellCheckingConfigFileName(configFile); 0748 #endif 0749 } 0750 0751 void KookaView::slotOcrSpellCheck(bool interactive, bool background) 0752 { 0753 if (!interactive && !background) // not doing anything 0754 { 0755 emit changeStatus(i18n("OCR finished")); 0756 return; 0757 } 0758 0759 if (mOcrResEdit->document()->isEmpty()) 0760 { 0761 KMessageBox::error(mMainWindow, 0762 i18n("There is no OCR result text to spell check."), 0763 i18n("OCR Spell Check not possible")); 0764 return; 0765 } 0766 0767 setCurrentIndex(KookaView::TabOcr); 0768 emit changeStatus(i18n("OCR Spell Check")); 0769 if (background) mOcrResEdit->setCheckSpellingEnabled(true); 0770 if (interactive) mOcrResEdit->checkSpelling(); 0771 } 0772 0773 0774 void KookaView::startOCR(ScanImage::Ptr img) 0775 { 0776 if (img.isNull()) return; // no image to OCR 0777 0778 setCurrentIndex(KookaView::TabOcr); 0779 0780 const QString engineName = KookaSettings::ocrEngineName(); 0781 if (engineName.isEmpty()) 0782 { 0783 int result = KMessageBox::warningContinueCancel(mMainWindow, 0784 i18n("No OCR engine is configured.\n" 0785 "Please select and configure one in order to perform OCR."), 0786 i18n("OCR Not Configured"), 0787 KGuiItem(i18n("Configure OCR..."))); 0788 if (result==KMessageBox::Continue) emit signalOcrPrefs(); 0789 return; 0790 } 0791 0792 AbstractOcrEngine *engine = qobject_cast<AbstractOcrEngine *>(PluginManager::self()->loadPlugin(PluginManager::OcrPlugin, engineName)); 0793 if (engine==nullptr) 0794 { 0795 KMessageBox::error(mMainWindow, i18n("Cannot load OCR plugin '%1'", engineName)); 0796 return; 0797 } 0798 0799 // We don't know whether the plugin object has been used before. 0800 // So disconnect all of its existing signals so that they do not 0801 // get double connected. 0802 engine->disconnect(); 0803 0804 // Connections OCR Engine --> myself 0805 connect(engine, &AbstractOcrEngine::newOCRResultText, this, &KookaView::slotOcrResultAvailable); 0806 connect(engine, &AbstractOcrEngine::setSpellCheckConfig, this, &KookaView::slotSetOcrSpellConfig); 0807 connect(engine, &AbstractOcrEngine::startSpellCheck, this, &KookaView::slotOcrSpellCheck); 0808 connect(engine, &AbstractOcrEngine::openOcrPrefs, this, &KookaView::signalOcrPrefs); 0809 0810 // Connections OCR Results --> OCR Engine 0811 connect(mOcrResEdit, &OcrResEdit::highlightWord, engine, &AbstractOcrEngine::slotHighlightWord); 0812 connect(mOcrResEdit, &OcrResEdit::scrollToWord, engine, &AbstractOcrEngine::slotScrollToWord); 0813 0814 // Connections OCR Engine --> OCR Results 0815 connect(engine, &AbstractOcrEngine::readOnlyEditor, mOcrResEdit, &OcrResEdit::slotSetReadOnly); 0816 connect(engine, &AbstractOcrEngine::selectWord, mOcrResEdit, &OcrResEdit::slotSelectWord); 0817 0818 engine->setImageCanvas(mImageCanvas); 0819 engine->setTextDocument(mOcrResEdit->document()); 0820 engine->setImage(img); 0821 engine->openOcrDialogue(this); 0822 } 0823 0824 0825 void KookaView::slotOcrResultAvailable() 0826 { 0827 emit signalOcrResultAvailable(mOcrResEdit->document()->characterCount()>0); 0828 updateSelectionState(); 0829 } 0830 0831 0832 void KookaView::slotScanStart(ScanImage::ImageType type) 0833 { 0834 // The scan parameters GUI must exist here, in order to locate the 0835 // destination plugin which is loaded and managed by KookaScanParams. 0836 AbstractDestination *dest = (mScanParams!=nullptr) ? mScanParams->destinationPlugin() : nullptr; 0837 if (dest==nullptr) 0838 { 0839 qCWarning(KOOKA_LOG) << "No destination plugin"; 0840 mScanDevice->slotStopScanning(); // no point starting scan 0841 return; 0842 } 0843 0844 // Allow the plugin to find the scan gallery, if it needs to send 0845 // the scan output to there. 0846 dest->setScanGallery(gallery()); 0847 0848 // Tell the plugin that a scan is starting. 0849 if (type!=ScanImage::Preview) 0850 { 0851 if (!dest->scanStarting(type)) // get ready to save 0852 { // user cancelled file prompt 0853 mScanDevice->slotStopScanning(); // abort the scan now 0854 return; 0855 } 0856 } 0857 0858 mScanParams->setEnabled(false); // disable GUI while scanning 0859 KLed *led = mScanParams->operationLED(); // update the LED indicator 0860 if (led!=nullptr) 0861 { 0862 led->setColor(Qt::red); // scanner warming up 0863 led->setState(KLed::On); 0864 qApp->processEvents(); // let the change show 0865 } 0866 0867 // Set the destination string displayed in the "Scan in Progress" dialogue 0868 if (type!=ScanImage::Preview) 0869 { 0870 mScanParams->setScanDestination(dest->scanDestinationString()); 0871 } 0872 } 0873 0874 0875 void KookaView::slotAcquireStart() 0876 { 0877 if (mScanParams!=nullptr) 0878 { 0879 KLed *led = mScanParams->operationLED(); // update the LED indicator 0880 if (led!=nullptr) 0881 { 0882 led->setColor(Qt::green); // scanning active 0883 qApp->processEvents(); // let the change show 0884 } 0885 } 0886 } 0887 0888 0889 void KookaView::slotNewImageScanned(ScanImage::Ptr img) 0890 { 0891 AbstractDestination *dest = (mScanParams!=nullptr) ? mScanParams->destinationPlugin() : nullptr; 0892 if (dest!=nullptr) dest->imageScanned(img); 0893 else qCWarning(KOOKA_LOG) << "No destination plugin"; 0894 } 0895 0896 0897 void KookaView::slotScanFinished(KScanDevice::Status stat) 0898 { 0899 qCDebug(KOOKA_LOG) << "Scan finished with status" << stat; 0900 0901 if (stat != KScanDevice::Ok && stat != KScanDevice::Cancelled) { 0902 QString msg = xi18nc("@info", 0903 "There was a problem during preview or scanning." 0904 "<nl/>" 0905 "Check that the scanner is still connected and switched on, " 0906 "<nl/>" 0907 "and that media is loaded if required." 0908 "<nl/><nl/>" 0909 "Trying to use scanner device: <icode>%3</icode><nl/>" 0910 "libkookascan reported error: <message>%2</message><nl/>" 0911 "SANE reported error: <message>%1</message>", 0912 mScanDevice->lastSaneErrorMessage(), 0913 KScanDevice::statusMessage(stat), 0914 mScanDevice->scannerBackendName().constData()); 0915 KMessageBox::error(mMainWindow, msg); 0916 } 0917 0918 if (mScanParams != nullptr) { 0919 mScanParams->setEnabled(true); 0920 KLed *led = mScanParams->operationLED(); 0921 if (led != nullptr) { 0922 led->setColor(Qt::green); 0923 led->setState(KLed::Off); 0924 } 0925 } 0926 } 0927 0928 0929 void KookaView::closeScanDevice() 0930 { 0931 qCDebug(KOOKA_LOG); 0932 0933 if (mScanParams!=nullptr) 0934 { 0935 // First save the current destination plugin settings 0936 mScanParams->saveDestinationSettings(); 0937 // Then ensure that the current destination plugin is unloaded 0938 PluginManager::self()->loadPlugin(PluginManager::DestinationPlugin, QString()); 0939 // Then the scan parameters GUI can be destroyed 0940 delete mScanParams; 0941 mScanParams = nullptr; 0942 } 0943 0944 mScanDevice->closeDevice(); 0945 } 0946 0947 0948 void KookaView::slotCreateNewImgFromSelection() 0949 { 0950 ScanImage::Ptr img = mImageCanvas->selectedImage(); 0951 if (img.isNull()) return; // no selection 0952 0953 emit changeStatus(i18n("Create new image from selection")); 0954 gallery()->addImage(img); 0955 emit clearStatus(); 0956 } 0957 0958 0959 void KookaView::slotTransformImage() 0960 { 0961 if (imageViewer()->isReadOnly()) { 0962 emit changeStatus(i18n("Cannot transform, image is read-only")); 0963 return; 0964 } 0965 0966 // Get operation code from action that was triggered 0967 QAction *act = static_cast<QAction *>(sender()); 0968 if (act==nullptr) return; 0969 ImageTransform::Operation op = static_cast<ImageTransform::Operation>(act->data().toInt()); 0970 0971 // This may appear to be a waste, since we are immediately unloading the image. 0972 // But we need a copy of the image anyway, so there will still be a load needed. 0973 ScanImage::Ptr loadedImage = gallery()->getCurrImage(true); 0974 if (loadedImage.isNull()) return; 0975 0976 const QImage img = *loadedImage.data(); // get a copy of the image 0977 0978 QString imageFile = gallery()->currentImageFileName(); 0979 if (imageFile.isEmpty()) return; // get file to save back to 0980 gallery()->slotUnloadItems(); // unload original from memory 0981 0982 ImageTransform *transformer = new ImageTransform(img, op, imageFile, this); 0983 connect(transformer, &ImageTransform::done, gallery(), &ScanGallery::slotUpdatedItem); 0984 connect(transformer, &ImageTransform::statusMessage, this, [this](const QString &msg){ changeStatus(msg); }); 0985 connect(transformer, &QThread::finished, transformer, &QObject::deleteLater); 0986 transformer->start(); 0987 } 0988 0989 0990 void KookaView::slotSaveOcrResult() 0991 { 0992 if (mOcrResEdit != nullptr) { 0993 mOcrResEdit->slotSaveText(); 0994 } 0995 } 0996 0997 0998 void KookaView::slotScanParams() 0999 { 1000 if (mScanDevice == nullptr) return; // must have a scanner device 1001 ScanPresetsDialog d(mScanDevice, this); 1002 d.exec(); 1003 } 1004 1005 1006 void KookaView::slotShowImage(ScanImage::Ptr img, bool isDir) 1007 { 1008 if (mImageCanvas!=nullptr) // load into image viewer 1009 { 1010 mImageCanvas->newImage(img); 1011 mImageCanvas->setReadOnly(false); 1012 emit changeStatus(mImageCanvas->imageInfoString(), StatusBarManager::ImageDims); 1013 } 1014 1015 if (!img.isNull()) emit changeStatus(i18n("Loaded image %1", img->url().url(QUrl::PreferLocalFile))); 1016 else if (!isDir) emit changeStatus(i18n("Unloaded image")); 1017 updateSelectionState(); 1018 } 1019 1020 1021 void KookaView::slotUnloadImage() 1022 { 1023 if (mImageCanvas!=nullptr) mImageCanvas->newImage(nullptr); 1024 updateSelectionState(); 1025 } 1026 1027 1028 /* this slot is called when the user clicks on an image in the packager 1029 * and loading of the image starts 1030 */ 1031 void KookaView::slotStartLoading(const QUrl &url) 1032 { 1033 emit changeStatus(i18n("Loading %1...", url.url(QUrl::PreferLocalFile))); 1034 } 1035 1036 void KookaView::connectViewerAction(QAction *action, bool sepBefore) 1037 { 1038 if (action == nullptr) { 1039 return; 1040 } 1041 QMenu *popup = mImageCanvas->contextMenu(); 1042 if (popup == nullptr) { 1043 return; 1044 } 1045 1046 if (sepBefore) { 1047 popup->addSeparator(); 1048 } 1049 popup->addAction(action); 1050 } 1051 1052 void KookaView::connectGalleryAction(QAction *action, bool sepBefore) 1053 { 1054 if (action == nullptr) { 1055 return; 1056 } 1057 QMenu *popup = gallery()->contextMenu(); 1058 if (popup == nullptr) { 1059 return; 1060 } 1061 1062 if (sepBefore) { 1063 popup->addSeparator(); 1064 } 1065 popup->addAction(action); 1066 } 1067 1068 void KookaView::connectThumbnailAction(QAction *action) 1069 { 1070 if (action == nullptr) { 1071 return; 1072 } 1073 QMenu *popup = mThumbView->contextMenu(); 1074 if (popup != nullptr) { 1075 popup->addAction(action); 1076 } 1077 } 1078 1079 void KookaView::connectPreviewAction(QAction *action) 1080 { 1081 if (action == nullptr) { 1082 return; 1083 } 1084 QMenu *popup = mPreviewCanvas->getImageCanvas()->contextMenu(); 1085 if (popup != nullptr) { 1086 popup->addAction(action); 1087 } 1088 } 1089 1090 void KookaView::slotApplySettings() 1091 { 1092 if (mThumbView != nullptr) { 1093 mThumbView->readSettings(); // size and background 1094 } 1095 if (mGallery != nullptr) { 1096 mGallery->readSettings(); // layout and rename 1097 } 1098 } 1099 1100 // Starting a scan or preview, switch tab and tell the scan device 1101 1102 void KookaView::slotStartPreview() 1103 { 1104 if (mScanParams == nullptr) { 1105 return; 1106 } 1107 setCurrentIndex(KookaView::TabScan); 1108 qApp->processEvents(); // let the tab appear 1109 mScanParams->slotAcquirePreview(); 1110 } 1111 1112 void KookaView::slotStartFinalScan() 1113 { 1114 if (mScanParams == nullptr) { 1115 return; 1116 } 1117 setCurrentIndex(KookaView::TabScan); 1118 qApp->processEvents(); // let the tab appear 1119 mScanParams->slotStartScan(); 1120 } 1121 1122 void KookaView::slotAutoSelect(bool on) 1123 { 1124 if (mPreviewCanvas == nullptr) { 1125 return; 1126 } 1127 setCurrentIndex(KookaView::TabScan); 1128 qApp->processEvents(); // let the tab appear 1129 mPreviewCanvas->slotAutoSelToggled(on); 1130 } 1131 1132 1133 ScanGallery *KookaView::gallery() const 1134 { 1135 return (mGallery->galleryTree()); 1136 } 1137 1138 ImageCanvas *KookaView::imageViewer() const 1139 { 1140 return (mImageCanvas); 1141 } 1142 1143 Previewer *KookaView::previewer() const 1144 { 1145 return (mPreviewCanvas); 1146 } 1147 1148 void KookaView::imageViewerAction(ImageCanvas::UserAction act) 1149 { 1150 if (mCurrentTab == KookaView::TabScan) { // Scan 1151 mPreviewCanvas->getImageCanvas()->performUserAction(act); 1152 } else { // Gallery or OCR 1153 mImageCanvas->performUserAction(act); 1154 } 1155 } 1156 1157 void KookaView::showOpenWithMenu(KActionMenu *menu) 1158 { 1159 FileTreeViewItem *curr = gallery()->highlightedFileTreeViewItem(); 1160 QString mimeType = curr->fileItem()->mimetype(); 1161 //qCDebug(KOOKA_LOG) << "Trying to open" << curr->url() << "which is" << mimeType; 1162 1163 menu->menu()->clear(); 1164 1165 int i = 0; 1166 mOpenWithOffers = KApplicationTrader::queryByMimeType(mimeType); 1167 for (const KService::Ptr &service : qAsConst(mOpenWithOffers)) 1168 { 1169 //qCDebug(KOOKA_LOG) << "> offer:" << service->name(); 1170 QString actionName(service->name().replace("&", "&&")); 1171 QAction *act = new QAction(QIcon::fromTheme(service->icon()), actionName, this); 1172 connect(act, &QAction::triggered, this, [=]() { slotOpenWith(i); }); 1173 menu->addAction(act); 1174 } 1175 1176 menu->menu()->addSeparator(); 1177 QAction *act = new QAction(i18n("Other..."), this); 1178 connect(act, &QAction::triggered, this, [=]() { slotOpenWith(-1); }); 1179 menu->addAction(act); 1180 } 1181 1182 void KookaView::slotOpenWith(int idx) 1183 { 1184 FileTreeViewItem *ftvi = gallery()->highlightedFileTreeViewItem(); 1185 if (ftvi == nullptr) return; 1186 1187 QList<QUrl> urllist; 1188 urllist.append(ftvi->url()); 1189 1190 #ifdef HAVE_KIO_APPLICATIONLAUNCHERJOB 1191 KIO::ApplicationLauncherJob *job; 1192 #endif 1193 if (idx!=-1) // application from the menu 1194 { 1195 Q_ASSERT(idx<mOpenWithOffers.count()); 1196 KService::Ptr ptr = mOpenWithOffers[idx]; 1197 #ifdef HAVE_KIO_APPLICATIONLAUNCHERJOB 1198 job = new KIO::ApplicationLauncherJob(ptr, mMainWindow); 1199 #else 1200 KRun::runService(*ptr, urllist, mMainWindow); 1201 #endif 1202 } 1203 else // last item = "Other..." 1204 { 1205 #ifdef HAVE_KIO_APPLICATIONLAUNCHERJOB 1206 job = new KIO::ApplicationLauncherJob(mMainWindow); 1207 #else 1208 KRun::displayOpenWithDialog(urllist, mMainWindow); 1209 #endif 1210 } 1211 #ifdef HAVE_KIO_APPLICATIONLAUNCHERJOB 1212 job->setUrls(urllist); 1213 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this)); 1214 job->start(); 1215 #endif 1216 } 1217 1218 1219 void KookaView::slotPreviewDimsChanged(const QString &dims) 1220 { 1221 emit changeStatus(dims, StatusBarManager::PreviewDims); 1222 }