1 /************************************************************************
2  *									*
3  *  This file is part of Kooka, a scanning/OCR application using	*
4  *  Qt <http://www.qt.io> and KDE Frameworks <http://www.kde.org>.	*
5  *									*
6  *  Copyright (C) 2000-2016 Klaas Freitag <freitag@suse.de>		*
7  *                          Jonathan Marten <jjm@keelhaul.me.uk>	*
8  *									*
9  *  Kooka is free software; you can redistribute it and/or modify it	*
10  *  under the terms of the GNU Library General Public License as	*
11  *  published by the Free Software Foundation and appearing in the	*
12  *  file COPYING included in the packaging of this file;  either	*
13  *  version 2 of the License, or (at your option) any later version.	*
14  *									*
15  *  As a special exception, permission is given to link this program	*
16  *  with any version of the KADMOS OCR/ICR engine (a product of		*
17  *  reRecognition GmbH, Kreuzlingen), and distribute the resulting	*
18  *  executable without including the source code for KADMOS in the	*
19  *  source distribution.						*
20  *									*
21  *  This program is distributed in the hope that it will be useful,	*
22  *  but WITHOUT ANY WARRANTY; without even the implied warranty of	*
23  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the	*
24  *  GNU General Public License for more details.			*
25  *									*
26  *  You should have received a copy of the GNU General Public		*
27  *  License along with this program;  see the file COPYING.  If		*
28  *  not, see <http://www.gnu.org/licenses/>.				*
29  *									*
30  ************************************************************************/
31 
32 #include "kooka.h"
33 
34 #include <qevent.h>
35 #include <qaction.h>
36 #include <qicon.h>
37 #include <qmenu.h>
38 #include <qdebug.h>
39 #include <qlabel.h>
40 #include <qmimedata.h>
41 
42 #include <klocalizedstring.h>
43 #include <ktoggleaction.h>
44 #include <kactionmenu.h>
45 #include <kiconloader.h>
46 #include <kmessagebox.h>
47 #include <kstandardaction.h>
48 #include <kactioncollection.h>
49 #include <kxmlguiwindow.h>
50 #ifndef KDE4
51 #include <kprinter.h>
52 #endif
53 
54 #include "scanglobal.h"
55 #include "imagecanvas.h"
56 #include "previewer.h"
57 
58 #include "scangallery.h"
59 #include "kookapref.h"
60 #include "kookasettings.h"
61 #include "kookaview.h"
62 #include "imagetransform.h"
63 
64 
Kooka(const QByteArray & deviceToUse)65 Kooka::Kooka(const QByteArray &deviceToUse)
66     : KXmlGuiWindow(nullptr),
67 #ifndef KDE4
68       m_printer(0),
69 #endif
70       m_prefDialogIndex(0)
71 {
72     setObjectName("Kooka");
73 
74     // Set up the status bar
75     StatusBarManager *sbm = new StatusBarManager(this);
76 
77     /* Start to create the main view framework */
78     m_view = new KookaView(this, deviceToUse);
79     setCentralWidget(m_view);
80 
81     setAcceptDrops(false); // Waba: Not (yet?) supported
82 
83     readProperties(KSharedConfig::openConfig()->group(autoSaveGroup()));
84 
85     // then, setup our actions
86     setupActions();
87 
88     setupGUI(KXmlGuiWindow::Default, "kookaui.rc");
89     setAutoSaveSettings();              // default group, do save
90 
91     // Allow the view to change the status bar and caption
92     connect(m_view, SIGNAL(changeStatus(QString,StatusBarManager::Item)),
93             sbm, SLOT(setStatus(QString,StatusBarManager::Item)));
94     connect(m_view, SIGNAL(clearStatus(StatusBarManager::Item)),
95             sbm, SLOT(clearStatus(StatusBarManager::Item)));
96     connect(m_view, SIGNAL(signalChangeCaption(QString)),
97             SLOT(setCaption(QString)));
98     connect(m_view, SIGNAL(signalScannerChanged(bool)),
99             SLOT(slotUpdateScannerActions(bool)));
100     connect(m_view, SIGNAL(signalRectangleChanged(bool)),
101             SLOT(slotUpdateRectangleActions(bool)));
102     connect(m_view, SIGNAL(signalViewSelectionState(KookaView::StateFlags)),
103             SLOT(slotUpdateViewActions(KookaView::StateFlags)));
104     connect(m_view, SIGNAL(signalOcrResultAvailable(bool)),
105             SLOT(slotUpdateOcrResultActions(bool)));
106     connect(m_view, SIGNAL(signalOcrPrefs()),
107             SLOT(optionsOcrPreferences()));
108 
109     connect(m_view->imageViewer(), SIGNAL(imageReadOnly(bool)),
110             SLOT(slotUpdateReadOnlyActions(bool)));
111 
112     connect(m_view->previewer(), SIGNAL(autoSelectStateChanged(bool,bool)),
113             SLOT(slotUpdateAutoSelectActions(bool,bool)));
114     connect(m_view->previewer(), SIGNAL(previewFileSizeChanged(long)),
115             sbm, SLOT(setFileSize(long)));
116 
117     setCaption(i18n("KDE Scanning"));
118 
119     slotUpdateScannerActions(m_view->isScannerConnected());
120     slotUpdateRectangleActions(false);
121     slotUpdateViewActions(KookaView::GalleryShown | KookaView::IsDirectory | KookaView::RootSelected);
122     slotUpdateOcrResultActions(false);
123     slotUpdateReadOnlyActions(true);
124 }
125 
~Kooka()126 Kooka::~Kooka()
127 {
128     m_view->closeScanDevice();
129     delete m_view;                  // ensure its config saved
130 #ifndef KDE4
131     delete m_printer;
132 #endif
133 }
134 
startup()135 void Kooka::startup()
136 {
137     if (m_view == nullptr) {
138         return;
139     }
140 
141     //qDebug();
142     m_view->gallery()->openRoots();
143     m_view->loadStartupImage();
144 }
145 
146 // TODO: use KStandardShortcut wherever available
setupActions()147 void Kooka::setupActions()
148 {
149     printImageAction = KStandardAction::print(this, SLOT(filePrint()), actionCollection());
150 
151     KStandardAction::quit(this, SLOT(close()), actionCollection());
152     KStandardAction::preferences(this, SLOT(optionsPreferences()), actionCollection());
153 
154     // Image Viewer
155 
156     scaleToWidthAction =  new QAction(QIcon::fromTheme("zoom-fit-width"), i18n("Scale to Width"), this);
157     actionCollection()->addAction("scaleToWidth", scaleToWidthAction);
158     actionCollection()->setDefaultShortcut(scaleToWidthAction, Qt::CTRL + Qt::Key_I);
159     connect(scaleToWidthAction, &QAction::triggered, [=]() { m_view->imageViewerAction(ImageCanvas::UserActionFitWidth); });
160     m_view->connectViewerAction(scaleToWidthAction);
161     m_view->connectPreviewAction(scaleToWidthAction);
162 
163     scaleToHeightAction = new QAction(QIcon::fromTheme("zoom-fit-height"), i18n("Scale to Height"), this);
164     actionCollection()->addAction("scaleToHeight", scaleToHeightAction);
165     actionCollection()->setDefaultShortcut(scaleToHeightAction, Qt::CTRL + Qt::Key_H);
166     connect(scaleToHeightAction, &QAction::triggered, [=]() { m_view->imageViewerAction(ImageCanvas::UserActionFitHeight); });
167     m_view->connectViewerAction(scaleToHeightAction);
168     m_view->connectPreviewAction(scaleToHeightAction);
169 
170     scaleToOriginalAction = new QAction(QIcon::fromTheme("zoom-original"), i18n("Original Size"), this);
171     actionCollection()->addAction("scaleOriginal", scaleToOriginalAction);
172     actionCollection()->setDefaultShortcut(scaleToOriginalAction, Qt::CTRL + Qt::Key_1);
173     connect(scaleToOriginalAction, &QAction::triggered, [=]() { m_view->imageViewerAction(ImageCanvas::UserActionOrigSize); });
174     m_view->connectViewerAction(scaleToOriginalAction);
175 
176     scaleToZoomAction = new QAction(QIcon::fromTheme("page-zoom"), i18n("Set Zoom..."), this);
177     actionCollection()->addAction("showZoomDialog", scaleToZoomAction);
178     // No shortcut.  There wasn't a standard shortcut for "Zoom" in KDE4,
179     // and there is no KStandardShortcut::Zoom in KF5.
180     connect(scaleToZoomAction, &QAction::triggered, [=]() { m_view->imageViewerAction(ImageCanvas::UserActionZoom); });
181     m_view->connectViewerAction(scaleToZoomAction);
182 
183     keepZoomAction = new KToggleAction(QIcon::fromTheme("lockzoom"), i18n("Keep Zoom Setting"), this);
184     actionCollection()->addAction("keepZoom", keepZoomAction);
185     actionCollection()->setDefaultShortcut(keepZoomAction, Qt::CTRL + Qt::Key_Z);
186     connect(keepZoomAction, &KToggleAction::toggled, m_view->imageViewer(), &ImageCanvas::setKeepZoom);
187     m_view->connectViewerAction(keepZoomAction);
188 
189     // Thumb view and gallery actions
190 
191     newFromSelectionAction = new QAction(QIcon::fromTheme("transform-crop"), i18n("New Image From Selection"), this);
192     actionCollection()->addAction("createFromSelection", newFromSelectionAction);
193     actionCollection()->setDefaultShortcut(newFromSelectionAction, Qt::CTRL + Qt::Key_N);
194     connect(newFromSelectionAction, SIGNAL(triggered()), m_view, SLOT(slotCreateNewImgFromSelection()));
195 
196     mirrorVerticallyAction = new QAction(QIcon::fromTheme("object-flip-vertical"), i18n("Mirror Vertically"), this);
197     mirrorVerticallyAction->setData(ImageTransform::MirrorVertical);
198     actionCollection()->addAction("mirrorVertical", mirrorVerticallyAction);
199     actionCollection()->setDefaultShortcut(mirrorVerticallyAction, Qt::CTRL + Qt::Key_V);
200     connect(mirrorVerticallyAction, SIGNAL(triggered()), m_view, SLOT(slotTransformImage()));
201     m_view->connectViewerAction(mirrorVerticallyAction, true);
202 
203     mirrorHorizontallyAction = new QAction(QIcon::fromTheme("object-flip-horizontal"), i18n("Mirror Horizontally"), this);
204     mirrorHorizontallyAction->setData(ImageTransform::MirrorHorizontal);
205     actionCollection()->addAction("mirrorHorizontal", mirrorHorizontallyAction);
206     actionCollection()->setDefaultShortcut(mirrorHorizontallyAction, Qt::CTRL + Qt::Key_M);
207     connect(mirrorHorizontallyAction, SIGNAL(triggered()), m_view, SLOT(slotTransformImage()));
208     m_view->connectViewerAction(mirrorHorizontallyAction);
209 
210     // Standard KDE has icons for 'object-rotate-right' and 'object-rotate-left',
211     // but not for rotate by 180 degrees.  The 3 used here are copies of the 22x22
212     // icons from the old kdeclassic theme.
213     rotateAcwAction = new QAction(QIcon::fromTheme("rotate-acw"), i18n("Rotate Counter-Clockwise"), this);
214     rotateAcwAction->setData(ImageTransform::Rotate270);
215     actionCollection()->addAction("rotateCounterClockwise", rotateAcwAction);
216     actionCollection()->setDefaultShortcut(rotateAcwAction, Qt::CTRL + Qt::Key_7);
217     connect(rotateAcwAction, SIGNAL(triggered()), m_view, SLOT(slotTransformImage()));
218     m_view->connectViewerAction(rotateAcwAction, true);
219 
220     rotateCwAction = new QAction(QIcon::fromTheme("rotate-cw"), i18n("Rotate Clockwise"), this);
221     rotateCwAction->setData(ImageTransform::Rotate90);
222     actionCollection()->addAction("rotateClockwise", rotateCwAction);
223     actionCollection()->setDefaultShortcut(rotateCwAction, Qt::CTRL + Qt::Key_9);
224     connect(rotateCwAction, SIGNAL(triggered()), m_view, SLOT(slotTransformImage()));
225     m_view->connectViewerAction(rotateCwAction);
226 
227     rotate180Action = new QAction(QIcon::fromTheme("rotate-180"), i18n("Rotate 180 Degrees"), this);
228     rotate180Action->setData(ImageTransform::Rotate180);
229     actionCollection()->addAction("upsitedown", rotate180Action);
230     actionCollection()->setDefaultShortcut(rotate180Action, Qt::CTRL + Qt::Key_8);
231     connect(rotate180Action, SIGNAL(triggered()), m_view, SLOT(slotTransformImage()));
232     m_view->connectViewerAction(rotate180Action);
233 
234     // Gallery actions
235 
236     createFolderAction = new QAction(QIcon::fromTheme("folder-new"), i18n("New Folder..."), this);
237     actionCollection()->addAction("foldernew", createFolderAction);
238     connect(createFolderAction, SIGNAL(triggered()), m_view->gallery(), SLOT(slotCreateFolder()));
239     m_view->connectGalleryAction(createFolderAction);
240 
241     openWithMenu = new KActionMenu(QIcon::fromTheme("document-open"), i18n("Open With"), this);
242     actionCollection()->addAction("openWidth", openWithMenu);
243     connect(openWithMenu->menu(), SIGNAL(aboutToShow()), SLOT(slotOpenWithMenu()));
244     m_view->connectGalleryAction(openWithMenu, true);
245     m_view->connectThumbnailAction(openWithMenu);
246 
247     saveImageAction = new QAction(QIcon::fromTheme("document-save"), i18n("Save Image..."), this);
248     actionCollection()->addAction("saveImage", saveImageAction);
249     actionCollection()->setDefaultShortcuts(saveImageAction, KStandardShortcut::save());
250     connect(saveImageAction, SIGNAL(triggered()), m_view->gallery(), SLOT(slotExportFile()));
251     m_view->connectGalleryAction(saveImageAction);
252     m_view->connectThumbnailAction(saveImageAction);
253 
254     importImageAction = new QAction(QIcon::fromTheme("document-import"), i18n("Import Image..."), this);
255     actionCollection()->addAction("importImage", importImageAction);
256     connect(importImageAction, SIGNAL(triggered()), m_view->gallery(), SLOT(slotImportFile()));
257     m_view->connectGalleryAction(importImageAction);
258 
259     deleteImageAction = new QAction(QIcon::fromTheme("edit-delete"), i18n("Delete Image"), this);
260     actionCollection()->addAction("deleteImage", deleteImageAction);
261     actionCollection()->setDefaultShortcut(deleteImageAction, Qt::SHIFT + Qt::Key_Delete);
262     connect(deleteImageAction, SIGNAL(triggered()), m_view->gallery(), SLOT(slotDeleteItems()));
263     m_view->connectGalleryAction(deleteImageAction);
264     m_view->connectThumbnailAction(deleteImageAction);
265 
266     renameImageAction = new QAction(QIcon::fromTheme("edit-rename"), i18n("Rename Image"), this);
267     actionCollection()->addAction("renameImage", renameImageAction);
268     actionCollection()->setDefaultShortcut(renameImageAction, Qt::Key_F2);
269     connect(renameImageAction, SIGNAL(triggered()), m_view->gallery(), SLOT(slotRenameItems()));
270     m_view->connectGalleryAction(renameImageAction);
271 
272     unloadImageAction = new QAction(QIcon::fromTheme("document-close"), i18n("Unload Image"), this);
273     actionCollection()->addAction("unloadImage", unloadImageAction);
274     actionCollection()->setDefaultShortcut(unloadImageAction, Qt::CTRL + Qt::SHIFT + Qt::Key_U);
275     connect(unloadImageAction, SIGNAL(triggered()), m_view->gallery(), SLOT(slotUnloadItems()));
276     m_view->connectGalleryAction(unloadImageAction);
277     m_view->connectThumbnailAction(unloadImageAction);
278 
279     propsImageAction = new QAction(QIcon::fromTheme("document-properties"), i18n("Properties..."), this);
280     actionCollection()->addAction("propsImage", propsImageAction);
281     actionCollection()->setDefaultShortcut(propsImageAction, Qt::ALT + Qt::Key_Return);
282     connect(propsImageAction, SIGNAL(triggered()), m_view->gallery(), SLOT(slotItemProperties()));
283     m_view->connectGalleryAction(propsImageAction, true);
284     m_view->connectThumbnailAction(propsImageAction);
285 
286     // "Settings" menu
287 
288     selectDeviceAction = new QAction(QIcon::fromTheme("scanselect"), i18n("Select Scan Device..."), this);
289     actionCollection()->addAction("selectsource", selectDeviceAction);
290     connect(selectDeviceAction, SIGNAL(triggered()), m_view, SLOT(slotSelectDevice()));
291 
292     addDeviceAction = new QAction(QIcon::fromTheme("scanadd"), i18n("Add Scan Device..."), this);
293     actionCollection()->addAction("addsource", addDeviceAction);
294     connect(addDeviceAction, SIGNAL(triggered()), m_view, SLOT(slotAddDevice()));
295 
296     // Scanning functions
297 
298     previewAction = new QAction(QIcon::fromTheme("preview"), i18n("Preview"), this);
299     actionCollection()->addAction("startPreview", previewAction);
300     actionCollection()->setDefaultShortcut(previewAction, Qt::Key_F3);
301     connect(previewAction, SIGNAL(triggered()), m_view, SLOT(slotStartPreview()));
302 
303     scanAction = new QAction(QIcon::fromTheme("scan"), i18n("Start Scan"), this);
304     actionCollection()->addAction("startScan", scanAction);
305     actionCollection()->setDefaultShortcut(scanAction, Qt::Key_F4);
306     connect(scanAction, SIGNAL(triggered()), m_view, SLOT(slotStartFinalScan()));
307 
308     photocopyAction = new QAction(QIcon::fromTheme("photocopy"), i18n("Photocopy..."), this);
309     actionCollection()->addAction("startPhotoCopy", photocopyAction);
310     actionCollection()->setDefaultShortcut(photocopyAction, Qt::CTRL + Qt::Key_F);
311     connect(photocopyAction, SIGNAL(triggered()), m_view, SLOT(slotStartPhotoCopy()));
312 
313     paramsAction = new QAction(QIcon::fromTheme("bookmark-new"), i18n("Scan Parameters..."), this);
314     actionCollection()->addAction("scanparam", paramsAction);
315     actionCollection()->setDefaultShortcut(paramsAction, Qt::CTRL + Qt::SHIFT + Qt::Key_S);
316     connect(paramsAction, SIGNAL(triggered()), m_view, SLOT(slotScanParams()));
317 
318     autoselAction = new KToggleAction(QIcon::fromTheme("autoselect"), i18n("Auto Select"), this);
319     actionCollection()->addAction("autoselect", autoselAction);
320     actionCollection()->setDefaultShortcut(autoselAction, Qt::CTRL + Qt::Key_A);
321     connect(autoselAction, SIGNAL(toggled(bool)), m_view, SLOT(slotAutoSelect(bool)));
322     m_view->connectPreviewAction(autoselAction);
323 
324     // OCR functions
325 
326     ocrAction = new QAction(QIcon::fromTheme("ocr"), i18n("OCR Image..."), this);
327     actionCollection()->addAction("ocrImage", ocrAction);
328     connect(ocrAction, SIGNAL(triggered()), m_view, SLOT(slotStartOcr()));
329 
330     ocrSelectAction = new QAction(QIcon::fromTheme("ocr-select"), i18n("OCR Selection..."), this);
331     actionCollection()->addAction("ocrImageSelect", ocrSelectAction);
332     connect(ocrSelectAction, SIGNAL(triggered()), m_view, SLOT(slotStartOcrSelection()));
333 
334     m_saveOCRTextAction = new QAction(QIcon::fromTheme("document-save-as"), i18n("Save OCR Result Text..."), this);
335     actionCollection()->addAction("saveOCRResult", m_saveOCRTextAction);
336     actionCollection()->setDefaultShortcut(m_saveOCRTextAction, Qt::CTRL + Qt::Key_U);
337     connect(m_saveOCRTextAction, SIGNAL(triggered()), m_view, SLOT(slotSaveOcrResult()));
338 
339     ocrSpellAction = new QAction(QIcon::fromTheme("tools-check-spelling"), i18n("Spell Check OCR Result..."), this);
340     actionCollection()->addAction("ocrSpellCheck", ocrSpellAction);
341     connect(ocrSpellAction, SIGNAL(triggered()), m_view, SLOT(slotOcrSpellCheck()));
342 }
343 
closeEvent(QCloseEvent * ev)344 void Kooka::closeEvent(QCloseEvent *ev)
345 {
346     KConfigGroup grp = KSharedConfig::openConfig()->group(autoSaveGroup());
347     saveProperties(grp);
348     if (autoSaveSettings())
349     {
350         saveAutoSaveSettings();
351         m_view->saveWindowSettings(grp);
352     }
353 }
354 
saveProperties(KConfigGroup & grp)355 void Kooka::saveProperties(KConfigGroup &grp)
356 {
357     //qDebug() << "to group" << grp.name();
358 
359     // The group object points to the session managed
360     // config file.  Anything you write here will be available
361     // later when this app is restored.
362 
363     KookaSettings::setPreferencesTab(m_prefDialogIndex);
364     //FIXME crash KookaSettings::setStartupSelectedImage(m_view->gallery()->currentImageFileName());
365     KookaSettings::self()->save();
366 }
367 
applyMainWindowSettings(const KConfigGroup & grp)368 void Kooka::applyMainWindowSettings(const KConfigGroup &grp)
369 {
370     //qDebug() << "from group" << grp.name();
371     KXmlGuiWindow::applyMainWindowSettings(grp);
372     m_view->applyWindowSettings(grp);
373 }
374 
readProperties(const KConfigGroup & grp)375 void Kooka::readProperties(const KConfigGroup &grp)
376 {
377     //qDebug() << "from group" << grp.name();
378 
379     // The group object points to the session managed
380     // config file.  This function is automatically called whenever
381     // the app is being restored.  Read in here whatever you wrote
382     // in 'saveProperties'.
383 
384     m_prefDialogIndex = KookaSettings::preferencesTab();
385 }
386 
dragEnterEvent(QDragEnterEvent * ev)387 void Kooka::dragEnterEvent(QDragEnterEvent *ev)
388 {
389     if (ev->mimeData()->hasUrls()) ev->accept();	// accept URI drops only
390 }
391 
filePrint()392 void Kooka::filePrint()
393 {
394     // this slot is called whenever the File->Print menu is selected,
395     // the Print shortcut is pressed (usually CTRL+P) or the Print toolbar
396     // button is clicked
397     m_view->print();
398 }
399 
optionsPreferences()400 void Kooka::optionsPreferences()
401 {
402     KookaPref dlg;
403 
404     dlg.showPageIndex(m_prefDialogIndex);
405     connect(&dlg, SIGNAL(dataSaved()), m_view, SLOT(slotApplySettings()));
406     if (dlg.exec()) {
407         m_prefDialogIndex = dlg.currentPageIndex();
408     }
409 }
410 
optionsOcrPreferences()411 void Kooka::optionsOcrPreferences()
412 {
413     m_prefDialogIndex = 4;              // go to OCR page
414     optionsPreferences();
415 }
416 
slotUpdateScannerActions(bool haveConnection)417 void Kooka::slotUpdateScannerActions(bool haveConnection)
418 {
419     //qDebug() << "haveConnection" << haveConnection;
420 
421     scanAction->setEnabled(haveConnection);
422     previewAction->setEnabled(haveConnection);
423     photocopyAction->setEnabled(haveConnection);
424     paramsAction->setEnabled(haveConnection);
425 
426     if (!ScanGlobal::self()->available()) {
427         selectDeviceAction->setEnabled(false);
428         addDeviceAction->setEnabled(false);
429     }
430 
431     setCaption(m_view->scannerName());
432 }
433 
slotUpdateRectangleActions(bool haveSelection)434 void Kooka::slotUpdateRectangleActions(bool haveSelection)
435 {
436     //qDebug() << "haveSelection" << haveSelection;
437 
438     ocrSelectAction->setEnabled(haveSelection);
439     newFromSelectionAction->setEnabled(haveSelection);
440 }
441 
442 //  This is the most complex action update, depending on the operation
443 //  mode (i.e. the tab selected), the gallery selection, and whether the
444 //  (two) image viewers have an image loaded.  The actions controlled
445 //  here and the conditions for enabling them are:
446 //
447 //  +========================+==================+======================+
448 //  | Action/Operation       | Scan mode        | Gallery/OCR mode     |
449 //  +========================+==================+======================+
450 //  | scale width/height     | preview valid    | image loaded         |
451 //  +------------------------+------------------+----------------------+
452 //  | (!GalleryShown && PreviewValid) || (GalleryShown && ImageValid)  |
453 //  +========================+==================+======================+
454 //  | scale original/zoom    | no               | image loaded         |
455 //  +------------------------+------------------+----------------------+
456 //  | GalleryShown && ImageValid                                       |
457 //  +========================+==================+======================+
458 //  | keep zoom              | no               | yes                  |
459 //  +------------------------+------------------+----------------------+
460 //  | GalleryShown                                                     |
461 //  +========================+==================+======================+
462 //  | mirror/rotate          | no               | 1 image              |
463 //  +------------------------+------------------+----------------------+
464 //  | GalleryShown && FileSelected && !IsSubImage && !HasSubImages     |
465 //  +========================+==================+======================+
466 //  | create folder          | directory        | directory            |
467 //  +------------------------+------------------+----------------------+
468 //  | IsDirectory                                                      |
469 //  +========================+==================+======================+
470 //  | import                 | no               | directory            |
471 //  +------------------------+------------------+----------------------+
472 //  | GalleryShown && IsDirectory                                      |
473 //  +========================+==================+======================+
474 //  | OCR/print              | 1                | 1                    |
475 //  +------------------------+------------------+----------------------+
476 //  | FileSelected && !HasSubImages                                    |
477 //  +========================+==================+======================+
478 //  | Save                   | 1                | 1                    |
479 //  +------------------------+------------------+----------------------+
480 //  | FileSelected && !IsSubImage                                      |
481 //  +========================+==================+======================+
482 //  | unload (dir)           | yes              | yes                  |
483 //  | unload (not dir)       | image loaded     | image loaded         |
484 //  +------------------------+------------------+----------------------+
485 //  | IsDirectory | HasSubImages |                                     |
486 //  |                    ((FileSelected | ManySelected) & ImageValid)  |
487 //  +========================+==================+======================+
488 //  | delete (dir)           | 1 not root       | 1 not root           |
489 //  | delete (not dir)       | >1               | >1                   |
490 //  +------------------------+------------------+----------------------+
491 //  | (IsDirectory & !RootSelected) | ((FileSelected | ManySelected)   |
492 //  |                                                   & !IsSubImage  |
493 //  +========================+==================+======================+
494 //  | rename (dir)           | not root         | not root             |
495 //  | rename (not dir)       | 1 image          | 1 image              |
496 //  +------------------------+------------------+----------------------+
497 //  | (IsDirectory & !RootSelected) | (FileSelected & !IsSubImage)     |
498 //  +========================+==================+======================+
499 //  | props (dir)            | yes              | yes                  |
500 //  | props (not dir)        | 1 image          | 1 image              |
501 //  +------------------------+------------------+----------------------+
502 //  | IsDirectory | FileSelected                                       |
503 //  +========================+==================+======================+
504 //  | Open with              | yes              | not subimage         |
505 //  +------------------------+------------------+----------------------+
506 //  | !IsSubImage                                                      |
507 //  +==================================================================+
508 //
509 //  It is assumed that only one directory may be selected at a time in
510 //  the gallery, but multiple files/images may be.  Currently, though,
511 //  the Kooka gallery allows single selection only.
512 //
513 //  The source of the 'state' is KookaView::updateSelectionState().
514 
slotUpdateViewActions(KookaView::StateFlags state)515 void Kooka::slotUpdateViewActions(KookaView::StateFlags state)
516 {
517     //qDebug() << "state" << hex << state;
518 
519     bool e;
520 
521     e = (!(state & KookaView::GalleryShown) && (state & KookaView::PreviewValid)) ||
522         ((state & KookaView::GalleryShown) && (state & KookaView::ImageValid));
523     scaleToWidthAction->setEnabled(e);
524     scaleToHeightAction->setEnabled(e);
525 
526     e = (state & KookaView::GalleryShown) && (state & KookaView::ImageValid);
527     scaleToOriginalAction->setEnabled(e);
528     scaleToZoomAction->setEnabled(e);
529 
530     e = (state & KookaView::GalleryShown);
531     keepZoomAction->setEnabled(e);
532 
533     e = (state & KookaView::GalleryShown) && (state & KookaView::FileSelected) && !(state & (KookaView::IsSubImage|KookaView::HasSubImages));
534     m_imageChangeAllowed = e;
535     mirrorVerticallyAction->setEnabled(e);
536     mirrorHorizontallyAction->setEnabled(e);
537     rotateCwAction->setEnabled(e);
538     rotateAcwAction->setEnabled(e);
539     rotate180Action->setEnabled(e);
540 
541     e = (state & KookaView::IsDirectory);
542     createFolderAction->setEnabled(e);
543 
544     e = (state & KookaView::GalleryShown) && (state & KookaView::IsDirectory);
545     importImageAction->setEnabled(e);
546 
547     e = (state & KookaView::FileSelected) && !(state & KookaView::HasSubImages);
548     ocrAction->setEnabled(e);
549     printImageAction->setEnabled(e);
550 
551     // TODO: allow this for sub-images (page extraction)
552     e = (state & KookaView::FileSelected) && !(state & KookaView::IsSubImage);
553     saveImageAction->setEnabled(e);
554 
555     if (state & KookaView::IsDirectory) {
556         unloadImageAction->setText(i18n("Unload Folder"));
557         deleteImageAction->setText(i18n("Delete Folder"));
558         renameImageAction->setText(i18n("Rename Folder"));
559 
560         unloadImageAction->setEnabled(true);
561 
562         e = !(state & KookaView::RootSelected);
563         deleteImageAction->setEnabled(e);
564         renameImageAction->setEnabled(e);
565 
566         propsImageAction->setEnabled(true);
567     } else {
568         unloadImageAction->setText(i18n("Unload Image"));
569         deleteImageAction->setText(i18n("Delete Image"));
570         renameImageAction->setText(i18n("Rename Image"));
571 
572         e = ((state & (KookaView::FileSelected | KookaView::ManySelected)) &&
573             (state & KookaView::ImageValid)) || (state & KookaView::HasSubImages);
574         unloadImageAction->setEnabled(e);
575 
576         e = (state & (KookaView::FileSelected | KookaView::ManySelected)) && !(state & KookaView::IsSubImage);
577         deleteImageAction->setEnabled(e);
578 
579         e = (state & KookaView::FileSelected) && !(state & KookaView::IsSubImage);
580         renameImageAction->setEnabled(e);
581         propsImageAction->setEnabled(e);
582     }
583 
584     // TODO: allow this for sub-images
585     openWithMenu->setEnabled(!(state & KookaView::IsSubImage));
586 
587     if (!(state & (KookaView::IsDirectory | KookaView::FileSelected | KookaView::ManySelected))) {
588         slotUpdateRectangleActions(false);
589     }
590 }
591 
slotUpdateOcrResultActions(bool haveText)592 void Kooka::slotUpdateOcrResultActions(bool haveText)
593 {
594     //qDebug() << "haveText" << haveText;
595     m_saveOCRTextAction->setEnabled(haveText);
596     ocrSpellAction->setEnabled(haveText);
597 }
598 
slotUpdateReadOnlyActions(bool ro)599 void Kooka::slotUpdateReadOnlyActions(bool ro)
600 {
601     bool enable = (m_imageChangeAllowed && !ro);    // also check gallery state
602 
603     mirrorVerticallyAction->setEnabled(enable);
604     mirrorHorizontallyAction->setEnabled(enable);
605     rotateCwAction->setEnabled(enable);
606     rotateAcwAction->setEnabled(enable);
607     rotate180Action->setEnabled(enable);
608 }
609 
slotUpdateAutoSelectActions(bool isAvailable,bool isOn)610 void Kooka::slotUpdateAutoSelectActions(bool isAvailable, bool isOn)
611 {
612     //qDebug() << "available" << isAvailable << "on" << isOn;
613 
614     autoselAction->setEnabled(isAvailable);
615     autoselAction->setChecked(isOn);
616 }
617 
slotOpenWithMenu()618 void Kooka::slotOpenWithMenu()
619 {
620     m_view->showOpenWithMenu(openWithMenu);
621 }
622