1 /*
2  *  Copyright (C) 2013-2015 Ofer Kashayov <oferkv@live.com>
3  *  This file is part of Phototonic Image Viewer.
4  *
5  *  Phototonic is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  Phototonic is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with Phototonic.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "DirCompleter.h"
20 #include "Phototonic.h"
21 #include "Settings.h"
22 #include "CopyMoveDialog.h"
23 #include "ResizeDialog.h"
24 #include "CropDialog.h"
25 #include "ColorsDialog.h"
26 #include "ExternalAppsDialog.h"
27 #include "ProgressDialog.h"
28 #include "ImagePreview.h"
29 #include "FileListWidget.h"
30 #include "RenameDialog.h"
31 #include "Trashcan.h"
32 #include "MessageBox.h"
33 
Phototonic(QStringList argumentsList,int filesStartAt,QWidget * parent)34 Phototonic::Phototonic(QStringList argumentsList, int filesStartAt, QWidget *parent) : QMainWindow(parent) {
35     Settings::appSettings = new QSettings("phototonic", "phototonic");
36     setDockOptions(QMainWindow::AllowNestedDocks);
37     readSettings();
38     createThumbsViewer();
39     createActions();
40     createMenus();
41     createToolBars();
42     createStatusBar();
43     createFileSystemDock();
44     createBookmarksDock();
45     createImagePreviewDock();
46     createImageTagsDock();
47     createImageViewer();
48     updateExternalApps();
49     loadShortcuts();
50     setupDocks();
51 
52     connect(qApp, SIGNAL(focusChanged(QWidget * , QWidget * )), this, SLOT(updateActions()));
53 
54     restoreGeometry(Settings::appSettings->value(Settings::optionGeometry).toByteArray());
55     restoreState(Settings::appSettings->value(Settings::optionWindowState).toByteArray());
56     defaultApplicationIcon = QIcon(":/images/phototonic.png");
57     setWindowIcon(defaultApplicationIcon);
58 
59     stackedLayout = new QStackedLayout;
60     QWidget *stackedLayoutWidget = new QWidget;
61     stackedLayout->addWidget(thumbsViewer);
62     stackedLayout->addWidget(imageViewer);
63     stackedLayoutWidget->setLayout(stackedLayout);
64     setCentralWidget(stackedLayoutWidget);
65     processStartupArguments(argumentsList, filesStartAt);
66 
67     copyMoveToDialog = nullptr;
68     colorsDialog = nullptr;
69     cropDialog = nullptr;
70     initComplete = true;
71     thumbsViewer->isBusy = false;
72     currentHistoryIdx = -1;
73     needHistoryRecord = true;
74     interfaceDisabled = false;
75 
76     refreshThumbs(true);
77     if (Settings::layoutMode == ThumbViewWidget) {
78         thumbsViewer->setFocus(Qt::OtherFocusReason);
79     }
80 }
81 
processStartupArguments(QStringList argumentsList,int filesStartAt)82 void Phototonic::processStartupArguments(QStringList argumentsList, int filesStartAt) {
83     if (argumentsList.size() > filesStartAt) {
84         QFileInfo firstArgument(argumentsList.at(filesStartAt));
85         if (firstArgument.isDir()) {
86             Settings::currentDirectory = argumentsList.at(filesStartAt);
87         } else if (argumentsList.size() > filesStartAt + 1) {
88             loadStartupFileList(argumentsList, filesStartAt);
89             return;
90         } else {
91             Settings::currentDirectory = firstArgument.absolutePath();
92             QString cliFileName = Settings::currentDirectory + QDir::separator() + firstArgument.fileName();
93             loadImageFromCliArguments(cliFileName);
94             QTimer::singleShot(1000, this, SLOT(updateIndexByViewerImage()));
95         }
96     } else {
97         if (Settings::startupDir == Settings::SpecifiedDir) {
98             Settings::currentDirectory = Settings::specifiedStartDir;
99         } else if (Settings::startupDir == Settings::RememberLastDir) {
100             Settings::currentDirectory = Settings::appSettings->value(Settings::optionLastDir).toString();
101         }
102     }
103     selectCurrentViewDir();
104 }
105 
getDefaultWindowIcon()106 QIcon &Phototonic::getDefaultWindowIcon() {
107     return defaultApplicationIcon;
108 }
109 
loadStartupFileList(QStringList argumentsList,int filesStartAt)110 void Phototonic::loadStartupFileList(QStringList argumentsList, int filesStartAt) {
111     Settings::filesList.clear();
112     for (int i = filesStartAt; i < argumentsList.size(); i++) {
113         QFile currentFileFullPath(argumentsList[i]);
114         QFileInfo currentFileInfo(currentFileFullPath);
115 
116         if (!Settings::filesList.contains(currentFileInfo.absoluteFilePath())) {
117             Settings::filesList << currentFileInfo.absoluteFilePath();
118         }
119     }
120     fileSystemTree->clearSelection();
121     fileListWidget->setItemSelected(fileListWidget->itemAt(0, 0), true);
122     Settings::isFileListLoaded = true;
123 }
124 
event(QEvent * event)125 bool Phototonic::event(QEvent *event) {
126     if (event->type() == QEvent::ActivationChange ||
127         (Settings::layoutMode == ThumbViewWidget && event->type() == QEvent::MouseButtonRelease)) {
128         thumbsViewer->loadVisibleThumbs();
129     }
130 
131     return QMainWindow::event(event);
132 }
133 
createThumbsViewer()134 void Phototonic::createThumbsViewer() {
135     metadataCache = new MetadataCache;
136     thumbsViewer = new ThumbsViewer(this, metadataCache);
137     thumbsViewer->thumbsSortFlags = (QDir::SortFlags) Settings::appSettings->value(
138             Settings::optionThumbsSortFlags).toInt();
139     thumbsViewer->thumbsSortFlags |= QDir::IgnoreCase;
140 
141     connect(thumbsViewer->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
142             this, SLOT(updateActions()));
143 
144     imageInfoDock = new QDockWidget(tr("Image Info"), this);
145     imageInfoDock->setObjectName("Image Info");
146     imageInfoDock->setWidget(thumbsViewer->infoView);
147     connect(imageInfoDock->toggleViewAction(), SIGNAL(triggered()), this, SLOT(setImageInfoDockVisibility()));
148     connect(imageInfoDock, SIGNAL(visibilityChanged(bool)), this, SLOT(setImageInfoDockVisibility()));
149 }
150 
addMenuSeparator(QWidget * widget)151 void Phototonic::addMenuSeparator(QWidget *widget) {
152     QAction *separator = new QAction(this);
153     separator->setSeparator(true);
154     widget->addAction(separator);
155 }
156 
createImageViewer()157 void Phototonic::createImageViewer() {
158     imageViewer = new ImageViewer(this, metadataCache);
159     connect(saveAction, SIGNAL(triggered()), imageViewer, SLOT(saveImage()));
160     connect(saveAsAction, SIGNAL(triggered()), imageViewer, SLOT(saveImageAs()));
161     connect(copyImageAction, SIGNAL(triggered()), imageViewer, SLOT(copyImage()));
162     connect(pasteImageAction, SIGNAL(triggered()), imageViewer, SLOT(pasteImage()));
163     connect(cropToSelectionAction, SIGNAL(triggered()), imageViewer, SLOT(cropToSelection()));
164     imageViewer->ImagePopUpMenu = new QMenu();
165 
166     // Widget actions
167     imageViewer->addAction(slideShowAction);
168     imageViewer->addAction(nextImageAction);
169     imageViewer->addAction(prevImageAction);
170     imageViewer->addAction(firstImageAction);
171     imageViewer->addAction(lastImageAction);
172     imageViewer->addAction(randomImageAction);
173     imageViewer->addAction(zoomInAction);
174     imageViewer->addAction(zoomOutAction);
175     imageViewer->addAction(origZoomAction);
176     imageViewer->addAction(resetZoomAction);
177     imageViewer->addAction(rotateRightAction);
178     imageViewer->addAction(rotateLeftAction);
179     imageViewer->addAction(freeRotateRightAction);
180     imageViewer->addAction(freeRotateLeftAction);
181     imageViewer->addAction(flipHorizontalAction);
182     imageViewer->addAction(flipVerticalAction);
183     imageViewer->addAction(cropAction);
184     imageViewer->addAction(cropToSelectionAction);
185     imageViewer->addAction(resizeAction);
186     imageViewer->addAction(saveAction);
187     imageViewer->addAction(saveAsAction);
188     imageViewer->addAction(copyImageAction);
189     imageViewer->addAction(pasteImageAction);
190     imageViewer->addAction(deleteAction);
191     imageViewer->addAction(deletePermanentlyAction);
192     imageViewer->addAction(renameAction);
193     imageViewer->addAction(CloseImageAction);
194     imageViewer->addAction(fullScreenAction);
195     imageViewer->addAction(settingsAction);
196     imageViewer->addAction(mirrorDisabledAction);
197     imageViewer->addAction(mirrorDualAction);
198     imageViewer->addAction(mirrorTripleAction);
199     imageViewer->addAction(mirrorDualVerticalAction);
200     imageViewer->addAction(mirrorQuadAction);
201     imageViewer->addAction(keepTransformAction);
202     imageViewer->addAction(keepZoomAction);
203     imageViewer->addAction(refreshAction);
204     imageViewer->addAction(colorsAction);
205     imageViewer->addAction(moveRightAction);
206     imageViewer->addAction(moveLeftAction);
207     imageViewer->addAction(moveUpAction);
208     imageViewer->addAction(moveDownAction);
209     imageViewer->addAction(showClipboardAction);
210     imageViewer->addAction(copyToAction);
211     imageViewer->addAction(moveToAction);
212     imageViewer->addAction(resizeAction);
213     imageViewer->addAction(viewImageAction);
214     imageViewer->addAction(exitAction);
215     imageViewer->addAction(showViewerToolbarAction);
216     imageViewer->addAction(externalAppsAction);
217 
218     // Actions
219     addMenuSeparator(imageViewer->ImagePopUpMenu);
220     imageViewer->ImagePopUpMenu->addAction(nextImageAction);
221     imageViewer->ImagePopUpMenu->addAction(prevImageAction);
222     imageViewer->ImagePopUpMenu->addAction(firstImageAction);
223     imageViewer->ImagePopUpMenu->addAction(lastImageAction);
224     imageViewer->ImagePopUpMenu->addAction(randomImageAction);
225     imageViewer->ImagePopUpMenu->addAction(slideShowAction);
226 
227     addMenuSeparator(imageViewer->ImagePopUpMenu);
228     zoomSubMenu = new QMenu(tr("Zoom"));
229     zoomSubMenuAction = new QAction(tr("Zoom"), this);
230     zoomSubMenuAction->setIcon(QIcon::fromTheme("edit-find", QIcon(":/images/zoom.png")));
231     zoomSubMenuAction->setMenu(zoomSubMenu);
232     imageViewer->ImagePopUpMenu->addAction(zoomSubMenuAction);
233     zoomSubMenu->addAction(zoomInAction);
234     zoomSubMenu->addAction(zoomOutAction);
235     zoomSubMenu->addAction(origZoomAction);
236     zoomSubMenu->addAction(resetZoomAction);
237     addMenuSeparator(zoomSubMenu);
238     zoomSubMenu->addAction(keepZoomAction);
239 
240     MirroringSubMenu = new QMenu(tr("Mirroring"));
241     mirrorSubMenuAction = new QAction(tr("Mirroring"), this);
242     mirrorSubMenuAction->setMenu(MirroringSubMenu);
243     mirroringActionGroup = new QActionGroup(this);
244     mirroringActionGroup->addAction(mirrorDisabledAction);
245     mirroringActionGroup->addAction(mirrorDualAction);
246     mirroringActionGroup->addAction(mirrorTripleAction);
247     mirroringActionGroup->addAction(mirrorDualVerticalAction);
248     mirroringActionGroup->addAction(mirrorQuadAction);
249     MirroringSubMenu->addActions(mirroringActionGroup->actions());
250 
251     transformSubMenu = new QMenu(tr("Transform"));
252     transformSubMenuAction = new QAction(tr("Transform"), this);
253     transformSubMenuAction->setMenu(transformSubMenu);
254     imageViewer->ImagePopUpMenu->addAction(resizeAction);
255     imageViewer->ImagePopUpMenu->addAction(cropToSelectionAction);
256     imageViewer->ImagePopUpMenu->addAction(transformSubMenuAction);
257     transformSubMenu->addAction(colorsAction);
258     transformSubMenu->addAction(rotateRightAction);
259     transformSubMenu->addAction(rotateLeftAction);
260     transformSubMenu->addAction(freeRotateRightAction);
261     transformSubMenu->addAction(freeRotateLeftAction);
262     transformSubMenu->addAction(flipHorizontalAction);
263     transformSubMenu->addAction(flipVerticalAction);
264     transformSubMenu->addAction(cropAction);
265 
266     addMenuSeparator(transformSubMenu);
267     transformSubMenu->addAction(keepTransformAction);
268     imageViewer->ImagePopUpMenu->addAction(mirrorSubMenuAction);
269 
270     addMenuSeparator(imageViewer->ImagePopUpMenu);
271     imageViewer->ImagePopUpMenu->addAction(copyToAction);
272     imageViewer->ImagePopUpMenu->addAction(moveToAction);
273     imageViewer->ImagePopUpMenu->addAction(saveAction);
274     imageViewer->ImagePopUpMenu->addAction(saveAsAction);
275     imageViewer->ImagePopUpMenu->addAction(renameAction);
276     imageViewer->ImagePopUpMenu->addAction(deleteAction);
277     imageViewer->ImagePopUpMenu->addAction(deletePermanentlyAction);
278     imageViewer->ImagePopUpMenu->addAction(openWithMenuAction);
279 
280     addMenuSeparator(imageViewer->ImagePopUpMenu);
281     viewSubMenu = new QMenu(tr("View"));
282     viewSubMenuAction = new QAction(tr("View"), this);
283     viewSubMenuAction->setMenu(viewSubMenu);
284     imageViewer->ImagePopUpMenu->addAction(viewSubMenuAction);
285     viewSubMenu->addAction(fullScreenAction);
286     viewSubMenu->addAction(showClipboardAction);
287     viewSubMenu->addAction(showViewerToolbarAction);
288     viewSubMenu->addAction(refreshAction);
289     imageViewer->ImagePopUpMenu->addAction(copyImageAction);
290     imageViewer->ImagePopUpMenu->addAction(pasteImageAction);
291     imageViewer->ImagePopUpMenu->addAction(CloseImageAction);
292     imageViewer->ImagePopUpMenu->addAction(exitAction);
293 
294     addMenuSeparator(imageViewer->ImagePopUpMenu);
295     imageViewer->ImagePopUpMenu->addAction(settingsAction);
296 
297     imageViewer->setContextMenuPolicy(Qt::DefaultContextMenu);
298     Settings::isFullScreen = Settings::appSettings->value(Settings::optionFullScreenMode).toBool();
299     fullScreenAction->setChecked(Settings::isFullScreen);
300     thumbsViewer->imagePreview->setImageViewer(imageViewer);
301 }
302 
createActions()303 void Phototonic::createActions() {
304     thumbsGoToTopAction = new QAction(tr("Top"), this);
305     thumbsGoToTopAction->setObjectName("thumbsGoTop");
306     thumbsGoToTopAction->setIcon(QIcon::fromTheme("go-top", QIcon(":/images/top.png")));
307     connect(thumbsGoToTopAction, SIGNAL(triggered()), this, SLOT(goTop()));
308 
309     thumbsGoToBottomAction = new QAction(tr("Bottom"), this);
310     thumbsGoToBottomAction->setObjectName("thumbsGoBottom");
311     thumbsGoToBottomAction->setIcon(QIcon::fromTheme("go-bottom", QIcon(":/images/bottom.png")));
312     connect(thumbsGoToBottomAction, SIGNAL(triggered()), this, SLOT(goBottom()));
313 
314     CloseImageAction = new QAction(tr("Close Viewer"), this);
315     CloseImageAction->setObjectName("closeImage");
316     connect(CloseImageAction, SIGNAL(triggered()), this, SLOT(hideViewer()));
317 
318     fullScreenAction = new QAction(tr("Full Screen"), this);
319     fullScreenAction->setObjectName("fullScreen");
320     fullScreenAction->setCheckable(true);
321     connect(fullScreenAction, SIGNAL(triggered()), this, SLOT(toggleFullScreen()));
322 
323     settingsAction = new QAction(tr("Preferences"), this);
324     settingsAction->setObjectName("settings");
325     settingsAction->setIcon(QIcon::fromTheme("preferences-system", QIcon(":/images/settings.png")));
326     connect(settingsAction, SIGNAL(triggered()), this, SLOT(showSettings()));
327 
328     exitAction = new QAction(tr("Exit"), this);
329     exitAction->setObjectName("exit");
330     connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));
331 
332     thumbsZoomInAction = new QAction(tr("Enlarge Thumbnails"), this);
333     thumbsZoomInAction->setObjectName("thumbsZoomIn");
334     connect(thumbsZoomInAction, SIGNAL(triggered()), this, SLOT(thumbsZoomIn()));
335     thumbsZoomInAction->setIcon(QIcon::fromTheme("zoom-in", QIcon(":/images/zoom_in.png")));
336     if (thumbsViewer->thumbSize == THUMB_SIZE_MAX) {
337         thumbsZoomInAction->setEnabled(false);
338     }
339 
340     thumbsZoomOutAction = new QAction(tr("Shrink Thumbnails"), this);
341     thumbsZoomOutAction->setObjectName("thumbsZoomOut");
342     connect(thumbsZoomOutAction, SIGNAL(triggered()), this, SLOT(thumbsZoomOut()));
343     thumbsZoomOutAction->setIcon(QIcon::fromTheme("zoom-out", QIcon(":/images/zoom_out.png")));
344     if (thumbsViewer->thumbSize == THUMB_SIZE_MIN) {
345         thumbsZoomOutAction->setEnabled(false);
346     }
347 
348     cutAction = new QAction(tr("Cut"), this);
349     cutAction->setObjectName("cut");
350     cutAction->setIcon(QIcon::fromTheme("edit-cut", QIcon(":/images/cut.png")));
351     connect(cutAction, SIGNAL(triggered()), this, SLOT(cutThumbs()));
352     cutAction->setEnabled(false);
353 
354     copyAction = new QAction(tr("Copy"), this);
355     copyAction->setObjectName("copy");
356     copyAction->setIcon(QIcon::fromTheme("edit-copy", QIcon(":/images/copy.png")));
357     connect(copyAction, SIGNAL(triggered()), this, SLOT(copyThumbs()));
358     copyAction->setEnabled(false);
359 
360     copyToAction = new QAction(tr("Copy to..."), this);
361     copyToAction->setObjectName("copyTo");
362     connect(copyToAction, SIGNAL(triggered()), this, SLOT(copyImagesTo()));
363 
364     moveToAction = new QAction(tr("Move to..."), this);
365     moveToAction->setObjectName("moveTo");
366     connect(moveToAction, SIGNAL(triggered()), this, SLOT(moveImagesTo()));
367 
368     deleteAction = new QAction(tr("Move to Trash"), this);
369     deleteAction->setObjectName("moveToTrash");
370     deleteAction->setIcon(style()->standardIcon(QStyle::SP_TrashIcon));
371     connect(deleteAction, SIGNAL(triggered()), this, SLOT(deleteOperation()));
372 
373     deletePermanentlyAction = new QAction(tr("Delete"), this);
374     deletePermanentlyAction->setObjectName("delete");
375     deletePermanentlyAction->setIcon(QIcon::fromTheme("edit-delete", QIcon(":/images/delete.png")));
376     connect(deletePermanentlyAction, SIGNAL(triggered()), this, SLOT(deletePermanentlyOperation()));
377 
378     saveAction = new QAction(tr("Save"), this);
379     saveAction->setObjectName("save");
380     saveAction->setIcon(QIcon::fromTheme("document-save", QIcon(":/images/save.png")));
381 
382     saveAsAction = new QAction(tr("Save As"), this);
383     saveAsAction->setObjectName("saveAs");
384     saveAsAction->setIcon(QIcon::fromTheme("document-save-as", QIcon(":/images/save_as.png")));
385 
386     copyImageAction = new QAction(tr("Copy Image"), this);
387     copyImageAction->setObjectName("copyImage");
388     pasteImageAction = new QAction(tr("Paste Image"), this);
389     pasteImageAction->setObjectName("pasteImage");
390 
391     renameAction = new QAction(tr("Rename"), this);
392     renameAction->setObjectName("rename");
393     connect(renameAction, SIGNAL(triggered()), this, SLOT(rename()));
394 
395     removeMetadataAction = new QAction(tr("Remove Metadata"), this);
396     removeMetadataAction->setObjectName("removeMetadata");
397     connect(removeMetadataAction, SIGNAL(triggered()), this, SLOT(removeMetadata()));
398 
399     selectAllAction = new QAction(tr("Select All"), this);
400     selectAllAction->setObjectName("selectAll");
401     connect(selectAllAction, SIGNAL(triggered()), this, SLOT(selectAllThumbs()));
402 
403     aboutAction = new QAction(tr("About"), this);
404     aboutAction->setObjectName("about");
405     connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
406 
407     // Sort actions
408     sortByNameAction = new QAction(tr("Sort by Name"), this);
409     sortByNameAction->setObjectName("name");
410     sortByTimeAction = new QAction(tr("Sort by Time"), this);
411     sortByTimeAction->setObjectName("time");
412     sortBySizeAction = new QAction(tr("Sort by Size"), this);
413     sortBySizeAction->setObjectName("size");
414     sortByTypeAction = new QAction(tr("Sort by Type"), this);
415     sortByTypeAction->setObjectName("type");
416     sortReverseAction = new QAction(tr("Reverse Order"), this);
417     sortReverseAction->setObjectName("reverse");
418     sortByNameAction->setCheckable(true);
419     sortByTimeAction->setCheckable(true);
420     sortBySizeAction->setCheckable(true);
421     sortByTypeAction->setCheckable(true);
422     sortReverseAction->setCheckable(true);
423     connect(sortByNameAction, SIGNAL(triggered()), this, SLOT(sortThumbnails()));
424     connect(sortByTimeAction, SIGNAL(triggered()), this, SLOT(sortThumbnails()));
425     connect(sortBySizeAction, SIGNAL(triggered()), this, SLOT(sortThumbnails()));
426     connect(sortByTypeAction, SIGNAL(triggered()), this, SLOT(sortThumbnails()));
427     connect(sortReverseAction, SIGNAL(triggered()), this, SLOT(sortThumbnails()));
428 
429     if (thumbsViewer->thumbsSortFlags & QDir::Time) {
430         sortByTimeAction->setChecked(true);
431     } else if (thumbsViewer->thumbsSortFlags & QDir::Size) {
432         sortBySizeAction->setChecked(true);
433     } else if (thumbsViewer->thumbsSortFlags & QDir::Type) {
434         sortByTypeAction->setChecked(true);
435     } else {
436         sortByNameAction->setChecked(true);
437     }
438     sortReverseAction->setChecked(thumbsViewer->thumbsSortFlags & QDir::Reversed);
439 
440     showHiddenFilesAction = new QAction(tr("Show Hidden Files"), this);
441     showHiddenFilesAction->setObjectName("showHidden");
442     showHiddenFilesAction->setCheckable(true);
443     showHiddenFilesAction->setChecked(Settings::showHiddenFiles);
444     connect(showHiddenFilesAction, SIGNAL(triggered()), this, SLOT(showHiddenFiles()));
445 
446     smallToolbarIconsAction = new QAction(tr("Small Toolbar Icons"), this);
447     smallToolbarIconsAction->setObjectName("smallToolbarIcons");
448     smallToolbarIconsAction->setCheckable(true);
449     smallToolbarIconsAction->setChecked(Settings::smallToolbarIcons);
450     connect(smallToolbarIconsAction, SIGNAL(triggered()), this, SLOT(setToolbarIconSize()));
451 
452     lockDocksAction = new QAction(tr("Hide Dock Title Bars"), this);
453     lockDocksAction->setObjectName("lockDocks");
454     lockDocksAction->setCheckable(true);
455     lockDocksAction->setChecked(Settings::hideDockTitlebars);
456     connect(lockDocksAction, SIGNAL(triggered()), this, SLOT(lockDocks()));
457 
458     showViewerToolbarAction = new QAction(tr("Show Toolbar"), this);
459     showViewerToolbarAction->setObjectName("showViewerToolbars");
460     showViewerToolbarAction->setCheckable(true);
461     showViewerToolbarAction->setChecked(Settings::showViewerToolbar);
462     connect(showViewerToolbarAction, SIGNAL(triggered()), this, SLOT(toggleImageViewerToolbar()));
463 
464     refreshAction = new QAction(tr("Reload"), this);
465     refreshAction->setObjectName("refresh");
466     refreshAction->setIcon(QIcon::fromTheme("view-refresh", QIcon(":/images/refresh.png")));
467     connect(refreshAction, SIGNAL(triggered()), this, SLOT(reload()));
468 
469     includeSubDirectoriesAction = new QAction(tr("Include Sub-directories"), this);
470     includeSubDirectoriesAction->setObjectName("subFolders");
471     includeSubDirectoriesAction->setIcon(QIcon(":/images/tree.png"));
472     includeSubDirectoriesAction->setCheckable(true);
473     connect(includeSubDirectoriesAction, SIGNAL(triggered()), this, SLOT(setIncludeSubDirs()));
474 
475     pasteAction = new QAction(tr("Paste Here"), this);
476     pasteAction->setObjectName("paste");
477     pasteAction->setIcon(QIcon::fromTheme("edit-paste", QIcon(":/images/paste.png")));
478     connect(pasteAction, SIGNAL(triggered()), this, SLOT(pasteThumbs()));
479     pasteAction->setEnabled(false);
480 
481     createDirectoryAction = new QAction(tr("New Directory"), this);
482     createDirectoryAction->setObjectName("createDir");
483     connect(createDirectoryAction, SIGNAL(triggered()), this, SLOT(createSubDirectory()));
484     createDirectoryAction->setIcon(QIcon::fromTheme("folder-new", QIcon(":/images/new_folder.png")));
485 
486     goBackAction = new QAction(tr("Back"), this);
487     goBackAction->setObjectName("goBack");
488     goBackAction->setIcon(QIcon::fromTheme("go-previous", QIcon(":/images/back.png")));
489     connect(goBackAction, SIGNAL(triggered()), this, SLOT(goBack()));
490     goBackAction->setEnabled(false);
491 
492     goFrwdAction = new QAction(tr("Forward"), this);
493     goFrwdAction->setObjectName("goFrwd");
494     goFrwdAction->setIcon(QIcon::fromTheme("go-next", QIcon(":/images/next.png")));
495     connect(goFrwdAction, SIGNAL(triggered()), this, SLOT(goForward()));
496     goFrwdAction->setEnabled(false);
497 
498     goUpAction = new QAction(tr("Go Up"), this);
499     goUpAction->setObjectName("up");
500     goUpAction->setIcon(QIcon::fromTheme("go-up", QIcon(":/images/up.png")));
501     connect(goUpAction, SIGNAL(triggered()), this, SLOT(goUp()));
502 
503     goHomeAction = new QAction(tr("Home"), this);
504     goHomeAction->setObjectName("home");
505     connect(goHomeAction, SIGNAL(triggered()), this, SLOT(goHome()));
506     goHomeAction->setIcon(QIcon::fromTheme("go-home", QIcon(":/images/home.png")));
507 
508     slideShowAction = new QAction(tr("Slide Show"), this);
509     slideShowAction->setObjectName("toggleSlideShow");
510     connect(slideShowAction, SIGNAL(triggered()), this, SLOT(toggleSlideShow()));
511     slideShowAction->setIcon(QIcon::fromTheme("media-playback-start", QIcon(":/images/play.png")));
512 
513     nextImageAction = new QAction(tr("Next Image"), this);
514     nextImageAction->setObjectName("nextImage");
515     nextImageAction->setIcon(QIcon::fromTheme("go-next", QIcon(":/images/next.png")));
516     connect(nextImageAction, SIGNAL(triggered()), this, SLOT(loadNextImage()));
517 
518     prevImageAction = new QAction(tr("Previous Image"), this);
519     prevImageAction->setObjectName("prevImage");
520     prevImageAction->setIcon(QIcon::fromTheme("go-previous", QIcon(":/images/back.png")));
521     connect(prevImageAction, SIGNAL(triggered()), this, SLOT(loadPreviousImage()));
522 
523     firstImageAction = new QAction(tr("First Image"), this);
524     firstImageAction->setObjectName("firstImage");
525     firstImageAction->setIcon(QIcon::fromTheme("go-first", QIcon(":/images/first.png")));
526     connect(firstImageAction, SIGNAL(triggered()), this, SLOT(loadFirstImage()));
527 
528     lastImageAction = new QAction(tr("Last Image"), this);
529     lastImageAction->setObjectName("lastImage");
530     lastImageAction->setIcon(QIcon::fromTheme("go-last", QIcon(":/images/last.png")));
531     connect(lastImageAction, SIGNAL(triggered()), this, SLOT(loadLastImage()));
532 
533     randomImageAction = new QAction(tr("Random Image"), this);
534     randomImageAction->setObjectName("randomImage");
535     connect(randomImageAction, SIGNAL(triggered()), this, SLOT(loadRandomImage()));
536 
537     viewImageAction = new QAction(tr("View Image"), this);
538     viewImageAction->setObjectName("open");
539     viewImageAction->setIcon(QIcon::fromTheme("document-open", QIcon(":/images/open.png")));
540     connect(viewImageAction, SIGNAL(triggered()), this, SLOT(viewImage()));
541 
542     showClipboardAction = new QAction(tr("Load Clipboard"), this);
543     showClipboardAction->setObjectName("showClipboard");
544     showClipboardAction->setIcon(QIcon::fromTheme("insert-image", QIcon(":/images/new.png")));
545     connect(showClipboardAction, SIGNAL(triggered()), this, SLOT(newImage()));
546 
547     openWithSubMenu = new QMenu(tr("Open With..."));
548     openWithMenuAction = new QAction(tr("Open With..."), this);
549     openWithMenuAction->setObjectName("openWithMenu");
550     openWithMenuAction->setMenu(openWithSubMenu);
551     externalAppsAction = new QAction(tr("External Applications"), this);
552     externalAppsAction->setIcon(QIcon::fromTheme("preferences-other", QIcon(":/images/settings.png")));
553     externalAppsAction->setObjectName("chooseApp");
554     connect(externalAppsAction, SIGNAL(triggered()), this, SLOT(chooseExternalApp()));
555 
556     addBookmarkAction = new QAction(tr("Add Bookmark"), this);
557     addBookmarkAction->setObjectName("addBookmark");
558     addBookmarkAction->setIcon(QIcon(":/images/new_bookmark.png"));
559     connect(addBookmarkAction, SIGNAL(triggered()), this, SLOT(addNewBookmark()));
560 
561     removeBookmarkAction = new QAction(tr("Delete Bookmark"), this);
562     removeBookmarkAction->setObjectName("deleteBookmark");
563     removeBookmarkAction->setIcon(QIcon::fromTheme("edit-delete", QIcon(":/images/delete.png")));
564 
565     zoomOutAction = new QAction(tr("Zoom Out"), this);
566     zoomOutAction->setObjectName("zoomOut");
567     connect(zoomOutAction, SIGNAL(triggered()), this, SLOT(zoomOut()));
568     zoomOutAction->setIcon(QIcon::fromTheme("zoom-out", QIcon(":/images/zoom_out.png")));
569 
570     zoomInAction = new QAction(tr("Zoom In"), this);
571     zoomInAction->setObjectName("zoomIn");
572     connect(zoomInAction, SIGNAL(triggered()), this, SLOT(zoomIn()));
573     zoomInAction->setIcon(QIcon::fromTheme("zoom-in", QIcon(":/images/zoom_out.png")));
574 
575     resetZoomAction = new QAction(tr("Reset Zoom"), this);
576     resetZoomAction->setObjectName("resetZoom");
577     resetZoomAction->setIcon(QIcon::fromTheme("zoom-fit-best", QIcon(":/images/zoom.png")));
578     connect(resetZoomAction, SIGNAL(triggered()), this, SLOT(resetZoom()));
579 
580     origZoomAction = new QAction(tr("Original Size"), this);
581     origZoomAction->setObjectName("origZoom");
582     origZoomAction->setIcon(QIcon::fromTheme("zoom-original", QIcon(":/images/zoom1.png")));
583     connect(origZoomAction, SIGNAL(triggered()), this, SLOT(origZoom()));
584 
585     keepZoomAction = new QAction(tr("Keep Zoom"), this);
586     keepZoomAction->setObjectName("keepZoom");
587     keepZoomAction->setCheckable(true);
588     connect(keepZoomAction, SIGNAL(triggered()), this, SLOT(keepZoom()));
589 
590     rotateLeftAction = new QAction(tr("Rotate 90 degree CCW"), this);
591     rotateLeftAction->setObjectName("rotateLeft");
592     rotateLeftAction->setIcon(QIcon::fromTheme("object-rotate-left", QIcon(":/images/rotate_left.png")));
593     connect(rotateLeftAction, SIGNAL(triggered()), this, SLOT(rotateLeft()));
594 
595     rotateRightAction = new QAction(tr("Rotate 90 degree CW"), this);
596     rotateRightAction->setObjectName("rotateRight");
597     rotateRightAction->setIcon(QIcon::fromTheme("object-rotate-right", QIcon(":/images/rotate_right.png")));
598     connect(rotateRightAction, SIGNAL(triggered()), this, SLOT(rotateRight()));
599 
600     flipHorizontalAction = new QAction(tr("Flip Horizontally"), this);
601     flipHorizontalAction->setObjectName("flipH");
602     flipHorizontalAction->setIcon(QIcon::fromTheme("object-flip-horizontal", QIcon(":/images/flipH.png")));
603     connect(flipHorizontalAction, SIGNAL(triggered()), this, SLOT(flipHorizontal()));
604 
605     flipVerticalAction = new QAction(tr("Flip Vertically"), this);
606     flipVerticalAction->setObjectName("flipV");
607     flipVerticalAction->setIcon(QIcon::fromTheme("object-flip-vertical", QIcon(":/images/flipV.png")));
608     connect(flipVerticalAction, SIGNAL(triggered()), this, SLOT(flipVertical()));
609 
610     cropAction = new QAction(tr("Cropping"), this);
611     cropAction->setObjectName("crop");
612     cropAction->setIcon(QIcon(":/images/crop.png"));
613     connect(cropAction, SIGNAL(triggered()), this, SLOT(cropImage()));
614 
615     cropToSelectionAction = new QAction(tr("Crop to Selection"), this);
616     cropToSelectionAction->setObjectName("cropToSelection");
617     cropToSelectionAction->setIcon(QIcon(":/images/crop.png"));
618 
619     resizeAction = new QAction(tr("Scale Image"), this);
620     resizeAction->setObjectName("resize");
621     resizeAction->setIcon(QIcon::fromTheme("transform-scale", QIcon(":/images/scale.png")));
622     connect(resizeAction, SIGNAL(triggered()), this, SLOT(scaleImage()));
623 
624     freeRotateLeftAction = new QAction(tr("Rotate 1 degree CCW"), this);
625     freeRotateLeftAction->setObjectName("freeRotateLeft");
626     connect(freeRotateLeftAction, SIGNAL(triggered()), this, SLOT(freeRotateLeft()));
627 
628     freeRotateRightAction = new QAction(tr("Rotate 1 degree CW"), this);
629     freeRotateRightAction->setObjectName("freeRotateRight");
630     connect(freeRotateRightAction, SIGNAL(triggered()), this, SLOT(freeRotateRight()));
631 
632     colorsAction = new QAction(tr("Colors"), this);
633     colorsAction->setObjectName("colors");
634     connect(colorsAction, SIGNAL(triggered()), this, SLOT(showColorsDialog()));
635     colorsAction->setIcon(QIcon(":/images/colors.png"));
636 
637     mirrorDisabledAction = new QAction(tr("Disable Mirror"), this);
638     mirrorDisabledAction->setObjectName("mirrorDisabled");
639     mirrorDualAction = new QAction(tr("Dual Mirror"), this);
640     mirrorDualAction->setObjectName("mirrorDual");
641     mirrorTripleAction = new QAction(tr("Triple Mirror"), this);
642     mirrorTripleAction->setObjectName("mirrorTriple");
643     mirrorDualVerticalAction = new QAction(tr("Dual Vertical Mirror"), this);
644     mirrorDualVerticalAction->setObjectName("mirrorVDual");
645     mirrorQuadAction = new QAction(tr("Quad Mirror"), this);
646     mirrorQuadAction->setObjectName("mirrorQuad");
647 
648     mirrorDisabledAction->setCheckable(true);
649     mirrorDualAction->setCheckable(true);
650     mirrorTripleAction->setCheckable(true);
651     mirrorDualVerticalAction->setCheckable(true);
652     mirrorQuadAction->setCheckable(true);
653     connect(mirrorDisabledAction, SIGNAL(triggered()), this, SLOT(setMirrorDisabled()));
654     connect(mirrorDualAction, SIGNAL(triggered()), this, SLOT(setMirrorDual()));
655     connect(mirrorTripleAction, SIGNAL(triggered()), this, SLOT(setMirrorTriple()));
656     connect(mirrorDualVerticalAction, SIGNAL(triggered()), this, SLOT(setMirrorVDual()));
657     connect(mirrorQuadAction, SIGNAL(triggered()), this, SLOT(setMirrorQuad()));
658     mirrorDisabledAction->setChecked(true);
659 
660     keepTransformAction = new QAction(tr("Keep Transformations"), this);
661     keepTransformAction->setObjectName("keepTransform");
662     keepTransformAction->setCheckable(true);
663     connect(keepTransformAction, SIGNAL(triggered()), this, SLOT(keepTransformClicked()));
664 
665     moveLeftAction = new QAction(tr("Move Image Left"), this);
666     moveLeftAction->setObjectName("moveLeft");
667     connect(moveLeftAction, SIGNAL(triggered()), this, SLOT(moveLeft()));
668     moveRightAction = new QAction(tr("Move Image Right"), this);
669     moveRightAction->setObjectName("moveRight");
670     connect(moveRightAction, SIGNAL(triggered()), this, SLOT(moveRight()));
671     moveUpAction = new QAction(tr("Move Image Up"), this);
672     moveUpAction->setObjectName("moveUp");
673     connect(moveUpAction, SIGNAL(triggered()), this, SLOT(moveUp()));
674     moveDownAction = new QAction(tr("Move Image Down"), this);
675     moveDownAction->setObjectName("moveDown");
676     connect(moveDownAction, SIGNAL(triggered()), this, SLOT(moveDown()));
677 
678     invertSelectionAction = new QAction(tr("Invert Selection"), this);
679     invertSelectionAction->setObjectName("invertSelection");
680     connect(invertSelectionAction, SIGNAL(triggered()), thumbsViewer, SLOT(invertSelection()));
681 
682     filterImagesFocusAction = new QAction(tr("Filter by Name"), this);
683     filterImagesFocusAction->setObjectName("filterImagesFocus");
684     connect(filterImagesFocusAction, SIGNAL(triggered()), this, SLOT(filterImagesFocus()));
685     setPathFocusAction = new QAction(tr("Edit Current Path"), this);
686     setPathFocusAction->setObjectName("setPathFocus");
687     connect(setPathFocusAction, SIGNAL(triggered()), this, SLOT(setPathFocus()));
688 }
689 
createMenus()690 void Phototonic::createMenus() {
691     fileMenu = menuBar()->addMenu(tr("&File"));
692     fileMenu->addAction(includeSubDirectoriesAction);
693     fileMenu->addAction(createDirectoryAction);
694     fileMenu->addAction(showClipboardAction);
695     fileMenu->addAction(addBookmarkAction);
696     fileMenu->addSeparator();
697     fileMenu->addAction(exitAction);
698 
699     editMenu = menuBar()->addMenu(tr("&Edit"));
700     editMenu->addAction(cutAction);
701     editMenu->addAction(copyAction);
702     editMenu->addAction(copyToAction);
703     editMenu->addAction(moveToAction);
704     editMenu->addAction(pasteAction);
705     editMenu->addAction(renameAction);
706     editMenu->addAction(removeMetadataAction);
707     editMenu->addAction(deleteAction);
708     editMenu->addAction(deletePermanentlyAction);
709     editMenu->addSeparator();
710     editMenu->addAction(selectAllAction);
711     editMenu->addAction(invertSelectionAction);
712     addAction(filterImagesFocusAction);
713     addAction(setPathFocusAction);
714     editMenu->addSeparator();
715     editMenu->addAction(externalAppsAction);
716     editMenu->addAction(settingsAction);
717 
718     goMenu = menuBar()->addMenu(tr("&Go"));
719     goMenu->addAction(goBackAction);
720     goMenu->addAction(goFrwdAction);
721     goMenu->addAction(goUpAction);
722     goMenu->addAction(goHomeAction);
723     goMenu->addSeparator();
724     goMenu->addAction(prevImageAction);
725     goMenu->addAction(nextImageAction);
726     goMenu->addSeparator();
727     goMenu->addAction(thumbsGoToTopAction);
728     goMenu->addAction(thumbsGoToBottomAction);
729 
730     viewMenu = menuBar()->addMenu(tr("&View"));
731     viewMenu->addAction(slideShowAction);
732     viewMenu->addSeparator();
733 
734     viewMenu->addAction(thumbsZoomInAction);
735     viewMenu->addAction(thumbsZoomOutAction);
736     sortMenu = viewMenu->addMenu(tr("Thumbnails Sorting"));
737     sortTypesGroup = new QActionGroup(this);
738     sortTypesGroup->addAction(sortByNameAction);
739     sortTypesGroup->addAction(sortByTimeAction);
740     sortTypesGroup->addAction(sortBySizeAction);
741     sortTypesGroup->addAction(sortByTypeAction);
742     sortMenu->addActions(sortTypesGroup->actions());
743     sortMenu->addSeparator();
744     sortMenu->addAction(sortReverseAction);
745     viewMenu->addSeparator();
746 
747     viewMenu->addAction(showHiddenFilesAction);
748     viewMenu->addSeparator();
749     viewMenu->addAction(refreshAction);
750 
751     // thumbs viewer context menu
752     thumbsViewer->addAction(viewImageAction);
753     thumbsViewer->addAction(openWithMenuAction);
754     thumbsViewer->addAction(cutAction);
755     thumbsViewer->addAction(copyAction);
756     thumbsViewer->addAction(pasteAction);
757     addMenuSeparator(thumbsViewer);
758     thumbsViewer->addAction(copyToAction);
759     thumbsViewer->addAction(moveToAction);
760     thumbsViewer->addAction(renameAction);
761     thumbsViewer->addAction(removeMetadataAction);
762     thumbsViewer->addAction(deleteAction);
763     thumbsViewer->addAction(deletePermanentlyAction);
764     addMenuSeparator(thumbsViewer);
765     thumbsViewer->addAction(selectAllAction);
766     thumbsViewer->addAction(invertSelectionAction);
767     thumbsViewer->setContextMenuPolicy(Qt::ActionsContextMenu);
768     menuBar()->setVisible(true);
769 }
770 
createToolBars()771 void Phototonic::createToolBars() {
772     /* Edit */
773     editToolBar = addToolBar(tr("Edit Toolbar"));
774     editToolBar->setObjectName("Edit");
775     editToolBar->addAction(cutAction);
776     editToolBar->addAction(copyAction);
777     editToolBar->addAction(pasteAction);
778     editToolBar->addAction(deleteAction);
779     editToolBar->addAction(deletePermanentlyAction);
780     editToolBar->addAction(showClipboardAction);
781     connect(editToolBar->toggleViewAction(), SIGNAL(triggered()), this, SLOT(setEditToolBarVisibility()));
782 
783     /* Navigation */
784     goToolBar = addToolBar(tr("Navigation Toolbar"));
785     goToolBar->setObjectName("Navigation");
786     goToolBar->addAction(goBackAction);
787     goToolBar->addAction(goFrwdAction);
788     goToolBar->addAction(goUpAction);
789     goToolBar->addAction(goHomeAction);
790     goToolBar->addAction(refreshAction);
791 
792     /* path bar */
793     pathLineEdit = new QLineEdit;
794     pathLineEdit->setCompleter(new DirCompleter(pathLineEdit));
795     pathLineEdit->setMinimumWidth(200);
796     pathLineEdit->setMaximumWidth(600);
797     connect(pathLineEdit, SIGNAL(returnPressed()), this, SLOT(goPathBarDir()));
798     goToolBar->addWidget(pathLineEdit);
799     goToolBar->addAction(includeSubDirectoriesAction);
800     connect(goToolBar->toggleViewAction(), SIGNAL(triggered()), this, SLOT(setGoToolBarVisibility()));
801 
802     /* View */
803     viewToolBar = addToolBar(tr("View Toolbar"));
804     viewToolBar->setObjectName("View");
805     viewToolBar->addAction(thumbsZoomInAction);
806     viewToolBar->addAction(thumbsZoomOutAction);
807     viewToolBar->addAction(slideShowAction);
808 
809     /* filter bar */
810     QAction *filterAct = new QAction(tr("Filter"), this);
811     filterAct->setIcon(QIcon::fromTheme("edit-find", QIcon(":/images/zoom.png")));
812     connect(filterAct, SIGNAL(triggered()), this, SLOT(setThumbsFilter()));
813     filterLineEdit = new QLineEdit;
814     filterLineEdit->setMinimumWidth(100);
815     filterLineEdit->setMaximumWidth(200);
816     connect(filterLineEdit, SIGNAL(returnPressed()), this, SLOT(setThumbsFilter()));
817     connect(filterLineEdit, SIGNAL(textChanged(
818                                            const QString&)), this, SLOT(clearThumbsFilter()));
819     filterLineEdit->setClearButtonEnabled(true);
820     filterLineEdit->addAction(filterAct, QLineEdit::LeadingPosition);
821 
822     viewToolBar->addSeparator();
823     viewToolBar->addWidget(filterLineEdit);
824     viewToolBar->addAction(settingsAction);
825     connect(viewToolBar->toggleViewAction(), SIGNAL(triggered()), this, SLOT(setViewToolBarVisibility()));
826 
827     /* image */
828     imageToolBar = new QToolBar(tr("Image Toolbar"));
829     imageToolBar->setObjectName("Image");
830     imageToolBar->addAction(prevImageAction);
831     imageToolBar->addAction(nextImageAction);
832     imageToolBar->addAction(firstImageAction);
833     imageToolBar->addAction(lastImageAction);
834     imageToolBar->addAction(slideShowAction);
835     imageToolBar->addSeparator();
836     imageToolBar->addAction(saveAction);
837     imageToolBar->addAction(saveAsAction);
838     imageToolBar->addAction(deleteAction);
839     imageToolBar->addAction(deletePermanentlyAction);
840     imageToolBar->addSeparator();
841     imageToolBar->addAction(zoomInAction);
842     imageToolBar->addAction(zoomOutAction);
843     imageToolBar->addAction(resetZoomAction);
844     imageToolBar->addAction(origZoomAction);
845     imageToolBar->addSeparator();
846     imageToolBar->addAction(resizeAction);
847     imageToolBar->addAction(rotateRightAction);
848     imageToolBar->addAction(rotateLeftAction);
849     imageToolBar->addAction(flipHorizontalAction);
850     imageToolBar->addAction(flipVerticalAction);
851     imageToolBar->addAction(cropAction);
852     imageToolBar->addAction(colorsAction);
853     imageToolBar->setVisible(false);
854     connect(imageToolBar->toggleViewAction(), SIGNAL(triggered()), this, SLOT(setImageToolBarVisibility()));
855 
856     setToolbarIconSize();
857 }
858 
setToolbarIconSize()859 void Phototonic::setToolbarIconSize() {
860     if (initComplete) {
861         Settings::smallToolbarIcons = smallToolbarIconsAction->isChecked();
862     }
863     int iconSize = Settings::smallToolbarIcons ? 16 : 24;
864     QSize iconQSize(iconSize, iconSize);
865 
866     editToolBar->setIconSize(iconQSize);
867     goToolBar->setIconSize(iconQSize);
868     viewToolBar->setIconSize(iconQSize);
869     imageToolBar->setIconSize(iconQSize);
870 }
871 
createStatusBar()872 void Phototonic::createStatusBar() {
873     statusLabel = new QLabel(tr("Initializing..."));
874     statusBar()->addWidget(statusLabel);
875 
876     busyMovie = new QMovie(":/images/busy.gif");
877     busyLabel = new QLabel(this);
878     busyLabel->setMovie(busyMovie);
879     statusBar()->addWidget(busyLabel);
880     busyLabel->setVisible(false);
881 
882     statusBar()->setStyleSheet("QStatusBar::item { border: 0px solid black }; ");
883 }
884 
onFileListSelected()885 void Phototonic::onFileListSelected() {
886     if (initComplete && fileListWidget->itemAt(0, 0)->isSelected()) {
887         Settings::isFileListLoaded = true;
888         fileSystemTree->clearSelection();
889         refreshThumbs(true);
890     }
891 }
892 
createFileSystemDock()893 void Phototonic::createFileSystemDock() {
894     fileSystemDock = new QDockWidget(tr("File System"), this);
895     fileSystemDock->setObjectName("File System");
896 
897     fileListWidget = new FileListWidget(fileSystemDock);
898     connect(fileListWidget, SIGNAL(itemSelectionChanged()), this, SLOT(onFileListSelected()));
899 
900     fileSystemTree = new FileSystemTree(fileSystemDock);
901     fileSystemTree->addAction(createDirectoryAction);
902     fileSystemTree->addAction(renameAction);
903     fileSystemTree->addAction(deleteAction);
904     fileSystemTree->addAction(deletePermanentlyAction);
905     addMenuSeparator(fileSystemTree);
906     fileSystemTree->addAction(pasteAction);
907     addMenuSeparator(fileSystemTree);
908     fileSystemTree->addAction(openWithMenuAction);
909     fileSystemTree->addAction(addBookmarkAction);
910     fileSystemTree->setContextMenuPolicy(Qt::ActionsContextMenu);
911 
912     connect(fileSystemTree, SIGNAL(clicked(
913                                            const QModelIndex&)), this, SLOT(goSelectedDir(
914                                                                                     const QModelIndex &)));
915 
916     connect(fileSystemTree->fileSystemModel, SIGNAL(rowsRemoved(
917                                                             const QModelIndex &, int, int)),
918             this, SLOT(checkDirState(
919                                const QModelIndex &, int, int)));
920 
921     connect(fileSystemTree, SIGNAL(dropOp(Qt::KeyboardModifiers, bool, QString)),
922             this, SLOT(dropOp(Qt::KeyboardModifiers, bool, QString)));
923 
924     fileSystemTree->setCurrentIndex(fileSystemTree->fileSystemModel->index(QDir::currentPath()));
925 
926     connect(fileSystemTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
927             this, SLOT(updateActions()));
928 
929     QVBoxLayout *mainLayout = new QVBoxLayout;
930     mainLayout->setContentsMargins(0, 0, 0, 0);
931     mainLayout->setSpacing(0);
932     mainLayout->addWidget(fileListWidget);
933     mainLayout->addWidget(fileSystemTree);
934 
935     QWidget *fileSystemTreeMainWidget = new QWidget(fileSystemDock);
936     fileSystemTreeMainWidget->setLayout(mainLayout);
937 
938     fileSystemDock->setWidget(fileSystemTreeMainWidget);
939     connect(fileSystemDock->toggleViewAction(), SIGNAL(triggered()), this, SLOT(setFileSystemDockVisibility()));
940     connect(fileSystemDock, SIGNAL(visibilityChanged(bool)), this, SLOT(setFileSystemDockVisibility()));
941     addDockWidget(Qt::LeftDockWidgetArea, fileSystemDock);
942 }
943 
createBookmarksDock()944 void Phototonic::createBookmarksDock() {
945     bookmarksDock = new QDockWidget(tr("Bookmarks"), this);
946     bookmarksDock->setObjectName("Bookmarks");
947     bookmarks = new BookMarks(bookmarksDock);
948     bookmarksDock->setWidget(bookmarks);
949 
950     connect(bookmarksDock->toggleViewAction(), SIGNAL(triggered()), this, SLOT(setBookmarksDockVisibility()));
951     connect(bookmarksDock, SIGNAL(visibilityChanged(bool)), this, SLOT(setBookmarksDockVisibility()));
952     connect(bookmarks, SIGNAL(itemClicked(QTreeWidgetItem * , int)),
953             this, SLOT(bookmarkClicked(QTreeWidgetItem * , int)));
954     connect(removeBookmarkAction, SIGNAL(triggered()), bookmarks, SLOT(removeBookmark()));
955     connect(bookmarks, SIGNAL(dropOp(Qt::KeyboardModifiers, bool, QString)),
956             this, SLOT(dropOp(Qt::KeyboardModifiers, bool, QString)));
957 
958     addDockWidget(Qt::LeftDockWidgetArea, bookmarksDock);
959 
960     bookmarks->addAction(pasteAction);
961     bookmarks->addAction(removeBookmarkAction);
962     bookmarks->setContextMenuPolicy(Qt::ActionsContextMenu);
963 }
964 
createImagePreviewDock()965 void Phototonic::createImagePreviewDock() {
966     imagePreviewDock = new QDockWidget(tr("Preview"), this);
967     imagePreviewDock->setObjectName("ImagePreview");
968     imagePreviewDock->setWidget(thumbsViewer->imagePreview);
969     connect(imagePreviewDock->toggleViewAction(), SIGNAL(triggered()), this, SLOT(setImagePreviewDockVisibility()));
970     connect(imagePreviewDock, SIGNAL(visibilityChanged(bool)), this, SLOT(setImagePreviewDockVisibility()));
971     addDockWidget(Qt::RightDockWidgetArea, imagePreviewDock);
972 }
973 
createImageTagsDock()974 void Phototonic::createImageTagsDock() {
975     tagsDock = new QDockWidget(tr("Tags"), this);
976     tagsDock->setObjectName("Tags");
977     thumbsViewer->imageTags = new ImageTags(tagsDock, thumbsViewer, metadataCache);
978     tagsDock->setWidget(thumbsViewer->imageTags);
979 
980     connect(tagsDock->toggleViewAction(), SIGNAL(triggered()), this, SLOT(setTagsDockVisibility()));
981     connect(tagsDock, SIGNAL(visibilityChanged(bool)), this, SLOT(setTagsDockVisibility()));
982     connect(thumbsViewer->imageTags, SIGNAL(reloadThumbs()), this, SLOT(onReloadThumbs()));
983     connect(thumbsViewer->imageTags->removeTagAction, SIGNAL(triggered()), this, SLOT(deleteOperation()));
984 }
985 
sortThumbnails()986 void Phototonic::sortThumbnails() {
987     thumbsViewer->thumbsSortFlags = QDir::IgnoreCase;
988 
989     if (sortByNameAction->isChecked()) {
990         thumbsViewer->thumbsSortFlags |= QDir::Name;
991     } else if (sortByTimeAction->isChecked()) {
992         thumbsViewer->thumbsSortFlags |= QDir::Time;
993     } else if (sortBySizeAction->isChecked()) {
994         thumbsViewer->thumbsSortFlags |= QDir::Size;
995     } else if (sortByTypeAction->isChecked()) {
996         thumbsViewer->thumbsSortFlags |= QDir::Type;
997     }
998 
999     if (sortReverseAction->isChecked()) {
1000         thumbsViewer->thumbsSortFlags |= QDir::Reversed;
1001     }
1002     refreshThumbs(false);
1003 }
1004 
reload()1005 void Phototonic::reload() {
1006     if (Settings::layoutMode == ThumbViewWidget) {
1007         refreshThumbs(false);
1008     } else {
1009         imageViewer->reload();
1010     }
1011 }
1012 
setIncludeSubDirs()1013 void Phototonic::setIncludeSubDirs() {
1014     Settings::includeSubDirectories = includeSubDirectoriesAction->isChecked();
1015     refreshThumbs(false);
1016 }
1017 
refreshThumbs(bool scrollToTop)1018 void Phototonic::refreshThumbs(bool scrollToTop) {
1019     thumbsViewer->setNeedToScroll(scrollToTop);
1020     QTimer::singleShot(0, this, SLOT(onReloadThumbs()));
1021 }
1022 
showHiddenFiles()1023 void Phototonic::showHiddenFiles() {
1024     Settings::showHiddenFiles = showHiddenFilesAction->isChecked();
1025     fileSystemTree->setModelFlags();
1026     refreshThumbs(false);
1027 }
1028 
toggleImageViewerToolbar()1029 void Phototonic::toggleImageViewerToolbar() {
1030     imageToolBar->setVisible(showViewerToolbarAction->isChecked());
1031     addToolBar(imageToolBar);
1032     Settings::showViewerToolbar = showViewerToolbarAction->isChecked();
1033 }
1034 
about()1035 void Phototonic::about() {
1036     MessageBox messageBox(this);
1037     messageBox.about();
1038 }
1039 
filterImagesFocus()1040 void Phototonic::filterImagesFocus() {
1041     if (Settings::layoutMode == ThumbViewWidget) {
1042         if (!viewToolBar->isVisible()) {
1043             viewToolBar->setVisible(true);
1044         }
1045         setViewToolBarVisibility();
1046         filterLineEdit->setFocus(Qt::OtherFocusReason);
1047         filterLineEdit->selectAll();
1048     }
1049 }
1050 
setPathFocus()1051 void Phototonic::setPathFocus() {
1052     if (Settings::layoutMode == ThumbViewWidget) {
1053         if (!goToolBar->isVisible()) {
1054             goToolBar->setVisible(true);
1055         }
1056         setGoToolBarVisibility();
1057         pathLineEdit->setFocus(Qt::OtherFocusReason);
1058         pathLineEdit->selectAll();
1059     }
1060 }
1061 
cleanupSender()1062 void Phototonic::cleanupSender() {
1063     delete QObject::sender();
1064 }
1065 
externalAppError()1066 void Phototonic::externalAppError() {
1067     MessageBox msgBox(this);
1068     msgBox.critical(tr("Error"), tr("Failed to start external application."));
1069 }
1070 
runExternalApp()1071 void Phototonic::runExternalApp() {
1072     QString execCommand;
1073     QString selectedFileNames("");
1074     execCommand = Settings::externalApps[((QAction *) sender())->text()];
1075 
1076     if (Settings::layoutMode == ImageViewWidget) {
1077         if (imageViewer->isNewImage()) {
1078             showNewImageWarning();
1079             return;
1080         }
1081 
1082         execCommand += " \"" + imageViewer->viewerImageFullPath + "\"";
1083     } else {
1084         if (QApplication::focusWidget() == fileSystemTree) {
1085             selectedFileNames += " \"" + getSelectedPath() + "\"";
1086         } else {
1087 
1088             QModelIndexList selectedIdxList = thumbsViewer->selectionModel()->selectedIndexes();
1089             if (selectedIdxList.size() < 1) {
1090                 setStatus(tr("Invalid selection."));
1091                 return;
1092             }
1093 
1094             selectedFileNames += " ";
1095             for (int tn = selectedIdxList.size() - 1; tn >= 0; --tn) {
1096                 selectedFileNames += "\"" +
1097                                      thumbsViewer->thumbsViewerModel->item(selectedIdxList[tn].row())->data(
1098                                              thumbsViewer->FileNameRole).toString();
1099                 if (tn)
1100                     selectedFileNames += "\" ";
1101             }
1102         }
1103 
1104         execCommand += selectedFileNames;
1105     }
1106 
1107     QProcess *externalProcess = new QProcess();
1108     connect(externalProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(cleanupSender()));
1109     connect(externalProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(externalAppError()));
1110     externalProcess->start(execCommand);
1111 }
1112 
updateExternalApps()1113 void Phototonic::updateExternalApps() {
1114     int actionNumber = 0;
1115     QMapIterator<QString, QString> externalAppsIterator(Settings::externalApps);
1116 
1117     QList<QAction *> actionList = openWithSubMenu->actions();
1118     if (!actionList.empty()) {
1119 
1120         for (int i = 0; i < actionList.size(); ++i) {
1121             QAction *action = actionList.at(i);
1122             if (action->isSeparator()) {
1123                 break;
1124             }
1125 
1126             openWithSubMenu->removeAction(action);
1127             imageViewer->removeAction(action);
1128             delete action;
1129         }
1130 
1131         openWithSubMenu->clear();
1132     }
1133 
1134     while (externalAppsIterator.hasNext()) {
1135         ++actionNumber;
1136         externalAppsIterator.next();
1137         QAction *extAppAct = new QAction(externalAppsIterator.key(), this);
1138         if (actionNumber < 10) {
1139             extAppAct->setShortcut(QKeySequence("Alt+" + QString::number(actionNumber)));
1140         }
1141         extAppAct->setIcon(QIcon::fromTheme(externalAppsIterator.key()));
1142         connect(extAppAct, SIGNAL(triggered()), this, SLOT(runExternalApp()));
1143         openWithSubMenu->addAction(extAppAct);
1144         imageViewer->addAction(extAppAct);
1145     }
1146 
1147     openWithSubMenu->addSeparator();
1148     openWithSubMenu->addAction(externalAppsAction);
1149 }
1150 
chooseExternalApp()1151 void Phototonic::chooseExternalApp() {
1152     ExternalAppsDialog *externalAppsDialog = new ExternalAppsDialog(this);
1153 
1154     if (Settings::slideShowActive) {
1155         toggleSlideShow();
1156     }
1157     imageViewer->setCursorHiding(false);
1158 
1159     externalAppsDialog->exec();
1160     updateExternalApps();
1161     delete (externalAppsDialog);
1162 
1163     if (isFullScreen()) {
1164         imageViewer->setCursorHiding(true);
1165     }
1166 }
1167 
showSettings()1168 void Phototonic::showSettings() {
1169     if (Settings::slideShowActive) {
1170         toggleSlideShow();
1171     }
1172 
1173     imageViewer->setCursorHiding(false);
1174 
1175     SettingsDialog *settingsDialog = new SettingsDialog(this);
1176     if (settingsDialog->exec()) {
1177         imageViewer->setBackgroundColor();
1178         thumbsViewer->setThumbColors();
1179         thumbsViewer->imagePreview->setBackgroundColor();
1180         Settings::imageZoomFactor = 1.0;
1181         imageViewer->imageInfoLabel->setVisible(Settings::showImageName);
1182 
1183         if (Settings::layoutMode == ImageViewWidget) {
1184             imageViewer->reload();
1185             needThumbsRefresh = true;
1186         } else {
1187             refreshThumbs(false);
1188         }
1189 
1190         if (!Settings::setWindowIcon) {
1191             setWindowIcon(defaultApplicationIcon);
1192         }
1193         writeSettings();
1194     }
1195 
1196     if (isFullScreen()) {
1197         imageViewer->setCursorHiding(true);
1198     }
1199     delete settingsDialog;
1200 }
1201 
toggleFullScreen()1202 void Phototonic::toggleFullScreen() {
1203     if (fullScreenAction->isChecked()) {
1204         shouldMaximize = isMaximized();
1205         showFullScreen();
1206         Settings::isFullScreen = true;
1207         imageViewer->setCursorHiding(true);
1208     } else {
1209         showNormal();
1210         if (shouldMaximize) {
1211             showMaximized();
1212         }
1213         imageViewer->setCursorHiding(false);
1214         Settings::isFullScreen = false;
1215     }
1216 }
1217 
selectAllThumbs()1218 void Phototonic::selectAllThumbs() {
1219     thumbsViewer->selectAll();
1220 }
1221 
copyOrCutThumbs(bool isCopyOperation)1222 void Phototonic::copyOrCutThumbs(bool isCopyOperation) {
1223     Settings::copyCutIndexList = thumbsViewer->selectionModel()->selectedIndexes();
1224     copyCutThumbsCount = Settings::copyCutIndexList.size();
1225 
1226     Settings::copyCutFileList.clear();
1227     for (int thumb = 0; thumb < copyCutThumbsCount; ++thumb) {
1228         Settings::copyCutFileList.append(thumbsViewer->thumbsViewerModel->item(Settings::copyCutIndexList[thumb].
1229                 row())->data(thumbsViewer->FileNameRole).toString());
1230     }
1231 
1232     Settings::isCopyOperation = isCopyOperation;
1233     pasteAction->setEnabled(true);
1234 
1235     QString state = QString((Settings::isCopyOperation ? tr("Copied") : tr("Cut")) + " " +
1236                             tr("%n image(s) to clipboard", "", copyCutThumbsCount));
1237     setStatus(state);
1238 }
1239 
cutThumbs()1240 void Phototonic::cutThumbs() {
1241     copyOrCutThumbs(false);
1242 }
1243 
copyThumbs()1244 void Phototonic::copyThumbs() {
1245     copyOrCutThumbs(true);
1246 }
1247 
copyImagesTo()1248 void Phototonic::copyImagesTo() {
1249     copyOrMoveImages(false);
1250 }
1251 
moveImagesTo()1252 void Phototonic::moveImagesTo() {
1253     copyOrMoveImages(true);
1254 }
1255 
copyOrMoveImages(bool isMoveOperation)1256 void Phototonic::copyOrMoveImages(bool isMoveOperation) {
1257     if (Settings::slideShowActive) {
1258         toggleSlideShow();
1259     }
1260     imageViewer->setCursorHiding(false);
1261 
1262     copyMoveToDialog = new CopyMoveToDialog(this, getSelectedPath(), isMoveOperation);
1263     if (copyMoveToDialog->exec()) {
1264         if (Settings::layoutMode == ThumbViewWidget) {
1265             if (copyMoveToDialog->copyOp) {
1266                 copyThumbs();
1267             } else {
1268                 cutThumbs();
1269             }
1270 
1271             pasteThumbs();
1272         } else {
1273             if (imageViewer->isNewImage()) {
1274                 showNewImageWarning();
1275                 if (isFullScreen()) {
1276                     imageViewer->setCursorHiding(true);
1277                 }
1278 
1279                 return;
1280             }
1281 
1282             QFileInfo fileInfo = QFileInfo(imageViewer->viewerImageFullPath);
1283             QString fileName = fileInfo.fileName();
1284             QString destFile = copyMoveToDialog->selectedPath + QDir::separator() + fileInfo.fileName();
1285 
1286             int result = CopyMoveDialog::copyOrMoveFile(copyMoveToDialog->copyOp, fileName,
1287                                                         imageViewer->viewerImageFullPath,
1288                                                         destFile, copyMoveToDialog->selectedPath);
1289 
1290             if (!result) {
1291                 MessageBox msgBox(this);
1292                 msgBox.critical(tr("Error"), tr("Failed to copy or move image."));
1293             } else {
1294                 if (!copyMoveToDialog->copyOp) {
1295                     int currentRow = thumbsViewer->getCurrentRow();
1296                     thumbsViewer->thumbsViewerModel->removeRow(currentRow);
1297                     loadCurrentImage(currentRow);
1298                 }
1299             }
1300         }
1301     }
1302 
1303     bookmarks->reloadBookmarks();
1304     delete (copyMoveToDialog);
1305     copyMoveToDialog = 0;
1306 
1307     if (isFullScreen()) {
1308         imageViewer->setCursorHiding(true);
1309     }
1310 }
1311 
thumbsZoomIn()1312 void Phototonic::thumbsZoomIn() {
1313     if (thumbsViewer->thumbSize < THUMB_SIZE_MAX) {
1314         thumbsViewer->thumbSize += THUMB_SIZE_MIN;
1315         thumbsZoomOutAction->setEnabled(true);
1316         if (thumbsViewer->thumbSize == THUMB_SIZE_MAX)
1317             thumbsZoomInAction->setEnabled(false);
1318         refreshThumbs(false);
1319     }
1320 }
1321 
thumbsZoomOut()1322 void Phototonic::thumbsZoomOut() {
1323     if (thumbsViewer->thumbSize > THUMB_SIZE_MIN) {
1324         thumbsViewer->thumbSize -= THUMB_SIZE_MIN;
1325         thumbsZoomInAction->setEnabled(true);
1326         if (thumbsViewer->thumbSize == THUMB_SIZE_MIN)
1327             thumbsZoomOutAction->setEnabled(false);
1328         refreshThumbs(false);
1329     }
1330 }
1331 
zoomOut()1332 void Phototonic::zoomOut() {
1333     if (Settings::imageZoomFactor <= 4.0 && Settings::imageZoomFactor > 0.25) {
1334         Settings::imageZoomFactor -= 0.25;
1335     } else if (Settings::imageZoomFactor <= 8.0 && Settings::imageZoomFactor >= 4.0) {
1336         Settings::imageZoomFactor -= 0.50;
1337     } else if (Settings::imageZoomFactor <= 16.0 && Settings::imageZoomFactor >= 8.0) {
1338         Settings::imageZoomFactor -= 1.0;
1339     } else {
1340         imageViewer->setFeedback(tr("Minimum zoom"));
1341         return;
1342     }
1343 
1344     imageViewer->tempDisableResize = false;
1345     imageViewer->resizeImage();
1346     imageViewer->setFeedback(tr("Zoom %1%").arg(QString::number(Settings::imageZoomFactor * 100)));
1347 }
1348 
zoomIn()1349 void Phototonic::zoomIn() {
1350     if (Settings::imageZoomFactor < 4.0 && Settings::imageZoomFactor >= 0.25) {
1351         Settings::imageZoomFactor += 0.25;
1352     } else if (Settings::imageZoomFactor < 8.0 && Settings::imageZoomFactor >= 4.0) {
1353         Settings::imageZoomFactor += 0.50;
1354     } else if (Settings::imageZoomFactor < 16.0 && Settings::imageZoomFactor >= 8.0) {
1355         Settings::imageZoomFactor += 1.00;
1356     } else {
1357         imageViewer->setFeedback(tr("Maximum zoom"));
1358         return;
1359     }
1360 
1361     imageViewer->tempDisableResize = false;
1362     imageViewer->resizeImage();
1363     imageViewer->setFeedback(tr("Zoom %1%").arg(QString::number(Settings::imageZoomFactor * 100)));
1364 }
1365 
resetZoom()1366 void Phototonic::resetZoom() {
1367     Settings::imageZoomFactor = 1.0;
1368     imageViewer->tempDisableResize = false;
1369     imageViewer->resizeImage();
1370     imageViewer->setFeedback(tr("Zoom Reset"));
1371 }
1372 
origZoom()1373 void Phototonic::origZoom() {
1374     Settings::imageZoomFactor = 1.0;
1375     imageViewer->tempDisableResize = true;
1376     imageViewer->resizeImage();
1377     imageViewer->setFeedback(tr("Original Size"));
1378 }
1379 
keepZoom()1380 void Phototonic::keepZoom() {
1381     Settings::keepZoomFactor = keepZoomAction->isChecked();
1382     if (Settings::keepZoomFactor) {
1383         imageViewer->setFeedback(tr("Zoom Locked"));
1384     } else {
1385         imageViewer->setFeedback(tr("Zoom Unlocked"));
1386     }
1387 }
1388 
keepTransformClicked()1389 void Phototonic::keepTransformClicked() {
1390     Settings::keepTransform = keepTransformAction->isChecked();
1391 
1392     if (Settings::keepTransform) {
1393         imageViewer->setFeedback(tr("Transformations Locked"));
1394         if (cropDialog) {
1395             cropDialog->applyCrop(0);
1396         }
1397     } else {
1398         Settings::cropLeftPercent = Settings::cropTopPercent = Settings::cropWidthPercent = Settings::cropHeightPercent = 0;
1399         imageViewer->setFeedback(tr("Transformations Unlocked"));
1400     }
1401 
1402     imageViewer->refresh();
1403 }
1404 
rotateLeft()1405 void Phototonic::rotateLeft() {
1406     Settings::rotation -= 90;
1407     if (Settings::rotation < 0)
1408         Settings::rotation = 270;
1409     imageViewer->refresh();
1410     imageViewer->setFeedback(tr("Rotation %1°").arg(QString::number(Settings::rotation)));
1411 }
1412 
rotateRight()1413 void Phototonic::rotateRight() {
1414     Settings::rotation += 90;
1415     if (Settings::rotation > 270)
1416         Settings::rotation = 0;
1417     imageViewer->refresh();
1418     imageViewer->setFeedback(tr("Rotation %1°").arg(QString::number(Settings::rotation)));
1419 }
1420 
flipVertical()1421 void Phototonic::flipVertical() {
1422     Settings::flipV = !Settings::flipV;
1423     imageViewer->refresh();
1424     imageViewer->setFeedback(Settings::flipV ? tr("Flipped Vertically") : tr("Unflipped Vertically"));
1425 }
1426 
flipHorizontal()1427 void Phototonic::flipHorizontal() {
1428     Settings::flipH = !Settings::flipH;
1429     imageViewer->refresh();
1430     imageViewer->setFeedback(Settings::flipH ? tr("Flipped Horizontally") : tr("Unflipped Horizontally"));
1431 }
1432 
cropImage()1433 void Phototonic::cropImage() {
1434     if (Settings::slideShowActive) {
1435         toggleSlideShow();
1436     }
1437 
1438     if (!cropDialog) {
1439         cropDialog = new CropDialog(this, imageViewer);
1440         connect(cropDialog, SIGNAL(accepted()), this, SLOT(cleanupCropDialog()));
1441         connect(cropDialog, SIGNAL(rejected()), this, SLOT(cleanupCropDialog()));
1442     }
1443 
1444     cropDialog->show();
1445     setInterfaceEnabled(false);
1446     cropDialog->applyCrop(0);
1447 }
1448 
scaleImage()1449 void Phototonic::scaleImage() {
1450     if (Settings::slideShowActive) {
1451         toggleSlideShow();
1452     }
1453 
1454     if (Settings::layoutMode == ThumbViewWidget && thumbsViewer->selectionModel()->selectedIndexes().size() < 1) {
1455         setStatus(tr("No selection"));
1456         return;
1457     }
1458 
1459     resizeDialog = new ResizeDialog(this, imageViewer);
1460     connect(resizeDialog, SIGNAL(accepted()), this, SLOT(cleanupResizeDialog()));
1461     connect(resizeDialog, SIGNAL(rejected()), this, SLOT(cleanupResizeDialog()));
1462 
1463     resizeDialog->show();
1464     setInterfaceEnabled(false);
1465 }
1466 
freeRotateLeft()1467 void Phototonic::freeRotateLeft() {
1468     --Settings::rotation;
1469     if (Settings::rotation < 0)
1470         Settings::rotation = 359;
1471     imageViewer->refresh();
1472     imageViewer->setFeedback(tr("Rotation %1°").arg(QString::number(Settings::rotation)));
1473 }
1474 
freeRotateRight()1475 void Phototonic::freeRotateRight() {
1476     ++Settings::rotation;
1477     if (Settings::rotation > 360)
1478         Settings::rotation = 1;
1479     imageViewer->refresh();
1480     imageViewer->setFeedback(tr("Rotation %1°").arg(QString::number(Settings::rotation)));
1481 }
1482 
showColorsDialog()1483 void Phototonic::showColorsDialog() {
1484     if (Settings::slideShowActive) {
1485         toggleSlideShow();
1486     }
1487 
1488     if (!colorsDialog) {
1489         colorsDialog = new ColorsDialog(this, imageViewer);
1490         connect(colorsDialog, SIGNAL(accepted()), this, SLOT(cleanupColorsDialog()));
1491         connect(colorsDialog, SIGNAL(rejected()), this, SLOT(cleanupColorsDialog()));
1492     }
1493 
1494     Settings::colorsActive = true;
1495     colorsDialog->show();
1496     colorsDialog->applyColors(0);
1497     setInterfaceEnabled(false);
1498 }
1499 
moveRight()1500 void Phototonic::moveRight() {
1501     imageViewer->keyMoveEvent(ImageViewer::MoveRight);
1502 }
1503 
moveLeft()1504 void Phototonic::moveLeft() {
1505     imageViewer->keyMoveEvent(ImageViewer::MoveLeft);
1506 }
1507 
moveUp()1508 void Phototonic::moveUp() {
1509     imageViewer->keyMoveEvent(ImageViewer::MoveUp);
1510 }
1511 
moveDown()1512 void Phototonic::moveDown() {
1513     imageViewer->keyMoveEvent(ImageViewer::MoveDown);
1514 }
1515 
setMirrorDisabled()1516 void Phototonic::setMirrorDisabled() {
1517     imageViewer->mirrorLayout = ImageViewer::LayNone;
1518     imageViewer->refresh();
1519     imageViewer->setFeedback(tr("Mirroring Disabled"));
1520 }
1521 
setMirrorDual()1522 void Phototonic::setMirrorDual() {
1523     imageViewer->mirrorLayout = ImageViewer::LayDual;
1524     imageViewer->refresh();
1525     imageViewer->setFeedback(tr("Mirroring: Dual"));
1526 }
1527 
setMirrorTriple()1528 void Phototonic::setMirrorTriple() {
1529     imageViewer->mirrorLayout = ImageViewer::LayTriple;
1530     imageViewer->refresh();
1531     imageViewer->setFeedback(tr("Mirroring: Triple"));
1532 }
1533 
setMirrorVDual()1534 void Phototonic::setMirrorVDual() {
1535     imageViewer->mirrorLayout = ImageViewer::LayVDual;
1536     imageViewer->refresh();
1537     imageViewer->setFeedback(tr("Mirroring: Dual Vertical"));
1538 }
1539 
setMirrorQuad()1540 void Phototonic::setMirrorQuad() {
1541     imageViewer->mirrorLayout = ImageViewer::LayQuad;
1542     imageViewer->refresh();
1543     imageViewer->setFeedback(tr("Mirroring: Quad"));
1544 }
1545 
isValidPath(QString & path)1546 bool Phototonic::isValidPath(QString &path) {
1547     QDir checkPath(path);
1548     if (!checkPath.exists() || !checkPath.isReadable()) {
1549         return false;
1550     }
1551     return true;
1552 }
1553 
pasteThumbs()1554 void Phototonic::pasteThumbs() {
1555     if (!copyCutThumbsCount) {
1556         return;
1557     }
1558 
1559     QString destDir;
1560     if (copyMoveToDialog) {
1561         destDir = copyMoveToDialog->selectedPath;
1562     } else {
1563         if (QApplication::focusWidget() == bookmarks) {
1564             if (bookmarks->currentItem()) {
1565                 destDir = bookmarks->currentItem()->toolTip(0);
1566             }
1567         } else {
1568             destDir = getSelectedPath();
1569         }
1570     }
1571 
1572     if (!isValidPath(destDir)) {
1573         MessageBox msgBox(this);
1574         msgBox.critical(tr("Error"), tr("Can not copy or move to ") + destDir);
1575         selectCurrentViewDir();
1576         return;
1577     }
1578 
1579     bool pasteInCurrDir = (Settings::currentDirectory == destDir);
1580     QFileInfo fileInfo;
1581     if (!Settings::isCopyOperation && pasteInCurrDir) {
1582         for (int thumb = 0; thumb < Settings::copyCutFileList.size(); ++thumb) {
1583             fileInfo = QFileInfo(Settings::copyCutFileList[thumb]);
1584             if (fileInfo.absolutePath() == destDir) {
1585                 MessageBox msgBox(this);
1586                 msgBox.critical(tr("Error"), tr("Can not move to the same directory"));
1587                 return;
1588             }
1589         }
1590     }
1591 
1592     CopyMoveDialog *copyMoveDialog = new CopyMoveDialog(this);
1593     copyMoveDialog->exec(thumbsViewer, destDir, pasteInCurrDir);
1594     if (pasteInCurrDir) {
1595         for (int thumb = 0; thumb < Settings::copyCutFileList.size(); ++thumb) {
1596             thumbsViewer->addThumb(Settings::copyCutFileList[thumb]);
1597         }
1598     } else {
1599         int row = copyMoveDialog->latestRow;
1600         if (thumbsViewer->thumbsViewerModel->rowCount()) {
1601             if (row >= thumbsViewer->thumbsViewerModel->rowCount()) {
1602                 row = thumbsViewer->thumbsViewerModel->rowCount() - 1;
1603             }
1604 
1605             thumbsViewer->setCurrentRow(row);
1606             thumbsViewer->selectThumbByRow(row);
1607         }
1608     }
1609 
1610     QString state = QString((Settings::isCopyOperation ? tr("Copied") : tr("Moved")) + " " +
1611                             tr("%n image(s)", "", copyMoveDialog->nFiles));
1612     setStatus(state);
1613     delete (copyMoveDialog);
1614     selectCurrentViewDir();
1615 
1616     copyCutThumbsCount = 0;
1617     Settings::copyCutIndexList.clear();
1618     Settings::copyCutFileList.clear();
1619     pasteAction->setEnabled(false);
1620 
1621     thumbsViewer->loadVisibleThumbs();
1622 }
1623 
loadCurrentImage(int currentRow)1624 void Phototonic::loadCurrentImage(int currentRow) {
1625     bool wrapImageListTmp = Settings::wrapImageList;
1626     Settings::wrapImageList = false;
1627 
1628     if (currentRow == thumbsViewer->thumbsViewerModel->rowCount()) {
1629         thumbsViewer->setCurrentRow(currentRow - 1);
1630     }
1631 
1632     if (thumbsViewer->getNextRow() < 0 && currentRow > 0) {
1633         imageViewer->loadImage(thumbsViewer->thumbsViewerModel->item(currentRow - 1)->
1634                 data(thumbsViewer->FileNameRole).toString());
1635     } else {
1636         if (thumbsViewer->thumbsViewerModel->rowCount() == 0) {
1637             hideViewer();
1638             refreshThumbs(true);
1639             return;
1640         }
1641 
1642         if (currentRow > (thumbsViewer->thumbsViewerModel->rowCount() - 1))
1643             currentRow = thumbsViewer->thumbsViewerModel->rowCount() - 1;
1644 
1645         imageViewer->loadImage(thumbsViewer->thumbsViewerModel->item(currentRow)->
1646                 data(thumbsViewer->FileNameRole).toString());
1647     }
1648 
1649     Settings::wrapImageList = wrapImageListTmp;
1650     thumbsViewer->setImageViewerWindowTitle();
1651 }
1652 
deleteImages(bool trash)1653 void Phototonic::deleteImages(bool trash) {
1654     // Deleting selected thumbnails
1655     if (thumbsViewer->selectionModel()->selectedIndexes().size() < 1) {
1656         setStatus(tr("No selection"));
1657         return;
1658     }
1659 
1660     if (Settings::deleteConfirm) {
1661         MessageBox msgBox(this);
1662         msgBox.setText(trash ? tr("Move selected images to the trash?") : tr("Permanently delete selected images?"));
1663         msgBox.setWindowTitle(trash ? tr("Move to Trash") : tr("Delete images"));
1664         msgBox.setIcon(MessageBox::Warning);
1665         msgBox.setStandardButtons(MessageBox::Yes | MessageBox::Cancel);
1666         msgBox.setDefaultButton(MessageBox::Yes);
1667         msgBox.setButtonText(MessageBox::Yes, tr("Yes"));
1668         msgBox.setButtonText(MessageBox::Cancel, tr("Cancel"));
1669 
1670         if (msgBox.exec() != MessageBox::Yes) {
1671             return;
1672         }
1673     }
1674 
1675     ProgressDialog *progressDialog = new ProgressDialog(this);
1676     progressDialog->show();
1677 
1678     int deleteFilesCount = 0;
1679     bool deleteOk;
1680     QList<int> rows;
1681     int row;
1682     QModelIndexList indexesList;
1683     while ((indexesList = thumbsViewer->selectionModel()->selectedIndexes()).size()) {
1684         QString fileNameFullPath = thumbsViewer->thumbsViewerModel->item(
1685                 indexesList.first().row())->data(thumbsViewer->FileNameRole).toString();
1686         progressDialog->opLabel->setText("Deleting " + fileNameFullPath);
1687         QString deleteError;
1688         if (trash) {
1689             deleteOk = Trash::moveToTrash(fileNameFullPath, deleteError) == Trash::Success;
1690         } else {
1691             QFile fileToRemove(fileNameFullPath);
1692             deleteOk = fileToRemove.remove();
1693             if (!deleteOk) {
1694                 deleteError = fileToRemove.errorString();
1695             }
1696         }
1697 
1698         ++deleteFilesCount;
1699         if (deleteOk) {
1700             row = indexesList.first().row();
1701             rows << row;
1702             thumbsViewer->thumbsViewerModel->removeRow(row);
1703         } else {
1704             MessageBox msgBox(this);
1705             msgBox.critical(tr("Error"),
1706                             (trash ? tr("Failed to move image to the trash.") : tr("Failed to delete image.")) + "\n" +
1707                             deleteError);
1708             break;
1709         }
1710 
1711         Settings::filesList.removeOne(fileNameFullPath);
1712 
1713         if (progressDialog->abortOp) {
1714             break;
1715         }
1716     }
1717 
1718     if (thumbsViewer->thumbsViewerModel->rowCount() && rows.count()) {
1719         qSort(rows.begin(), rows.end());
1720         row = rows.at(0);
1721 
1722         if (row >= thumbsViewer->thumbsViewerModel->rowCount()) {
1723             row = thumbsViewer->thumbsViewerModel->rowCount() - 1;
1724         }
1725 
1726         thumbsViewer->setCurrentRow(row);
1727         thumbsViewer->selectThumbByRow(row);
1728     }
1729 
1730     progressDialog->close();
1731     delete (progressDialog);
1732 
1733     QString state = QString(tr("Deleted") + " " + tr("%n image(s)", "", deleteFilesCount));
1734     setStatus(state);
1735 }
1736 
deleteFromViewer(bool trash)1737 void Phototonic::deleteFromViewer(bool trash) {
1738     if (imageViewer->isNewImage()) {
1739         showNewImageWarning();
1740         return;
1741     }
1742 
1743     if (Settings::slideShowActive) {
1744         toggleSlideShow();
1745     }
1746     imageViewer->setCursorHiding(false);
1747 
1748     bool ok;
1749     QFileInfo fileInfo = QFileInfo(imageViewer->viewerImageFullPath);
1750     QString fileName = fileInfo.fileName();
1751 
1752     bool deleteConfirmed = true;
1753     if (Settings::deleteConfirm) {
1754         MessageBox msgBox(this);
1755         msgBox.setText(trash ? tr("Move %1 to the trash").arg(fileName) : tr("Permanently delete %1").arg(fileName));
1756         msgBox.setWindowTitle(trash ? tr("Move to Trash") : tr("Delete images"));
1757         msgBox.setIcon(MessageBox::Warning);
1758         msgBox.setStandardButtons(MessageBox::Yes | MessageBox::Cancel);
1759         msgBox.setDefaultButton(MessageBox::Yes);
1760         msgBox.setButtonText(MessageBox::Yes, tr("Yes"));
1761         msgBox.setButtonText(MessageBox::Cancel, tr("Cancel"));
1762 
1763         if (msgBox.exec() != MessageBox::Yes) {
1764             deleteConfirmed = false;
1765         }
1766     }
1767 
1768     if (deleteConfirmed) {
1769         int currentRow = thumbsViewer->getCurrentRow();
1770 
1771         QString trashError;
1772         ok = trash ? (Trash::moveToTrash(imageViewer->viewerImageFullPath, trashError) == Trash::Success)
1773                    : QFile::remove(imageViewer->viewerImageFullPath);
1774         if (ok) {
1775             thumbsViewer->thumbsViewerModel->removeRow(currentRow);
1776             imageViewer->setFeedback(tr("Deleted ") + fileName);
1777         } else {
1778             MessageBox msgBox(this);
1779             msgBox.critical(tr("Error"), trash ? trashError : tr("Failed to delete image"));
1780             if (isFullScreen()) {
1781                 imageViewer->setCursorHiding(true);
1782             }
1783             return;
1784         }
1785 
1786         loadCurrentImage(currentRow);
1787     }
1788     if (isFullScreen()) {
1789         imageViewer->setCursorHiding(true);
1790     }
1791 }
1792 
1793 // Main delete operation
deleteOperation()1794 void Phototonic::deleteOperation() {
1795     if (QApplication::focusWidget() == thumbsViewer->imageTags->tagsTree) {
1796         thumbsViewer->imageTags->removeTag();
1797         return;
1798     }
1799 
1800     if (QApplication::focusWidget() == bookmarks) {
1801         bookmarks->removeBookmark();
1802         return;
1803     }
1804 
1805     if (QApplication::focusWidget() == fileSystemTree) {
1806         deleteDirectory(true);
1807         return;
1808     }
1809 
1810     if (Settings::layoutMode == ImageViewWidget) {
1811         deleteFromViewer(true);
1812         return;
1813     }
1814 
1815     deleteImages(true);
1816 }
1817 
deletePermanentlyOperation()1818 void Phototonic::deletePermanentlyOperation() {
1819     if (QApplication::focusWidget() == fileSystemTree) {
1820         deleteDirectory(false);
1821         return;
1822     }
1823 
1824     if (Settings::layoutMode == ImageViewWidget) {
1825         deleteFromViewer(false);
1826         return;
1827     }
1828 
1829     deleteImages(false);
1830 }
1831 
goTo(QString path)1832 void Phototonic::goTo(QString path) {
1833     Settings::isFileListLoaded = false;
1834     fileListWidget->clearSelection();
1835     thumbsViewer->setNeedToScroll(true);
1836     fileSystemTree->setCurrentIndex(fileSystemTree->fileSystemModel->index(path));
1837     Settings::currentDirectory = path;
1838     refreshThumbs(true);
1839 }
1840 
goSelectedDir(const QModelIndex & idx)1841 void Phototonic::goSelectedDir(const QModelIndex &idx) {
1842     Settings::isFileListLoaded = false;
1843     fileListWidget->clearSelection();
1844     thumbsViewer->setNeedToScroll(true);
1845     Settings::currentDirectory = getSelectedPath();
1846     refreshThumbs(true);
1847     fileSystemTree->expand(idx);
1848 }
1849 
goPathBarDir()1850 void Phototonic::goPathBarDir() {
1851     thumbsViewer->setNeedToScroll(true);
1852 
1853     QDir checkPath(pathLineEdit->text());
1854     if (!checkPath.exists() || !checkPath.isReadable()) {
1855         MessageBox msgBox(this);
1856         msgBox.critical(tr("Error"), tr("Invalid Path:") + " " + pathLineEdit->text());
1857         pathLineEdit->setText(Settings::currentDirectory);
1858         return;
1859     }
1860 
1861     Settings::currentDirectory = pathLineEdit->text();
1862     refreshThumbs(true);
1863     selectCurrentViewDir();
1864 }
1865 
bookmarkClicked(QTreeWidgetItem * item,int col)1866 void Phototonic::bookmarkClicked(QTreeWidgetItem *item, int col) {
1867     goTo(item->toolTip(col));
1868 }
1869 
setThumbsFilter()1870 void Phototonic::setThumbsFilter() {
1871     thumbsViewer->filterString = filterLineEdit->text();
1872     refreshThumbs(true);
1873 }
1874 
clearThumbsFilter()1875 void Phototonic::clearThumbsFilter() {
1876     if (filterLineEdit->text() == "") {
1877         thumbsViewer->filterString = filterLineEdit->text();
1878         refreshThumbs(true);
1879     }
1880 }
1881 
goBack()1882 void Phototonic::goBack() {
1883     if (currentHistoryIdx > 0) {
1884         needHistoryRecord = false;
1885         goTo(pathHistoryList.at(--currentHistoryIdx));
1886         goFrwdAction->setEnabled(true);
1887         if (currentHistoryIdx == 0)
1888             goBackAction->setEnabled(false);
1889     }
1890 }
1891 
goForward()1892 void Phototonic::goForward() {
1893 
1894     if (currentHistoryIdx < pathHistoryList.size() - 1) {
1895         needHistoryRecord = false;
1896         goTo(pathHistoryList.at(++currentHistoryIdx));
1897         if (currentHistoryIdx == (pathHistoryList.size() - 1))
1898             goFrwdAction->setEnabled(false);
1899     }
1900 }
1901 
goUp()1902 void Phototonic::goUp() {
1903     QFileInfo fileInfo = QFileInfo(Settings::currentDirectory);
1904     goTo(fileInfo.dir().absolutePath());
1905 }
1906 
goHome()1907 void Phototonic::goHome() {
1908     goTo(QDir::homePath());
1909 }
1910 
setCopyCutActions(bool setEnabled)1911 void Phototonic::setCopyCutActions(bool setEnabled) {
1912     cutAction->setEnabled(setEnabled);
1913     copyAction->setEnabled(setEnabled);
1914 }
1915 
updateActions()1916 void Phototonic::updateActions() {
1917     if (QApplication::focusWidget() == thumbsViewer) {
1918         bool hasSelectedItems = thumbsViewer->selectionModel()->selectedIndexes().size() > 0;
1919         setCopyCutActions(hasSelectedItems);
1920     } else if (QApplication::focusWidget() == bookmarks) {
1921         setCopyCutActions(false);
1922     } else if (QApplication::focusWidget() == fileSystemTree) {
1923         setCopyCutActions(false);
1924     } else if (Settings::layoutMode == ImageViewWidget || QApplication::focusWidget() == imageViewer->scrollArea) {
1925         setCopyCutActions(false);
1926     } else {
1927         setCopyCutActions(false);
1928     }
1929 
1930     if (Settings::layoutMode == ImageViewWidget && !interfaceDisabled) {
1931         setViewerKeyEventsEnabled(true);
1932         fullScreenAction->setEnabled(true);
1933         CloseImageAction->setEnabled(true);
1934     } else {
1935         if (QApplication::focusWidget() == imageViewer->scrollArea) {
1936             setViewerKeyEventsEnabled(true);
1937             fullScreenAction->setEnabled(false);
1938             CloseImageAction->setEnabled(false);
1939         } else {
1940             setViewerKeyEventsEnabled(false);
1941             fullScreenAction->setEnabled(false);
1942             CloseImageAction->setEnabled(false);
1943         }
1944     }
1945 }
1946 
writeSettings()1947 void Phototonic::writeSettings() {
1948     if (Settings::layoutMode == ThumbViewWidget) {
1949         Settings::appSettings->setValue(Settings::optionGeometry, saveGeometry());
1950         Settings::appSettings->setValue(Settings::optionWindowState, saveState());
1951     }
1952 
1953     Settings::appSettings->setValue(Settings::optionThumbsSortFlags, (int) thumbsViewer->thumbsSortFlags);
1954     Settings::appSettings->setValue(Settings::optionThumbsZoomLevel, thumbsViewer->thumbSize);
1955     Settings::appSettings->setValue(Settings::optionFullScreenMode, (bool) Settings::isFullScreen);
1956     Settings::appSettings->setValue(Settings::optionViewerBackgroundColor, Settings::viewerBackgroundColor);
1957     Settings::appSettings->setValue(Settings::optionThumbsBackgroundColor, Settings::thumbsBackgroundColor);
1958     Settings::appSettings->setValue(Settings::optionThumbsTextColor, Settings::thumbsTextColor);
1959     Settings::appSettings->setValue(Settings::optionThumbsPagesReadCount, (int) Settings::thumbsPagesReadCount);
1960     Settings::appSettings->setValue(Settings::optionEnableAnimations, (bool) Settings::enableAnimations);
1961     Settings::appSettings->setValue(Settings::optionExifRotationEnabled, (bool) Settings::exifRotationEnabled);
1962     Settings::appSettings->setValue(Settings::optionExifThumbRotationEnabled,
1963                                     (bool) Settings::exifThumbRotationEnabled);
1964     Settings::appSettings->setValue(Settings::optionReverseMouseBehavior, (bool) Settings::reverseMouseBehavior);
1965     Settings::appSettings->setValue(Settings::optionDeleteConfirm, (bool) Settings::deleteConfirm);
1966     Settings::appSettings->setValue(Settings::optionShowHiddenFiles, (bool) Settings::showHiddenFiles);
1967     Settings::appSettings->setValue(Settings::optionWrapImageList, (bool) Settings::wrapImageList);
1968     Settings::appSettings->setValue(Settings::optionImageZoomFactor, Settings::imageZoomFactor);
1969     Settings::appSettings->setValue(Settings::optionShouldMaximize, (bool) isMaximized());
1970     Settings::appSettings->setValue(Settings::optionDefaultSaveQuality, Settings::defaultSaveQuality);
1971     Settings::appSettings->setValue(Settings::optionSlideShowDelay, Settings::slideShowDelay);
1972     Settings::appSettings->setValue(Settings::optionSlideShowRandom, (bool) Settings::slideShowRandom);
1973     Settings::appSettings->setValue(Settings::optionEditToolBarVisible, (bool) editToolBarVisible);
1974     Settings::appSettings->setValue(Settings::optionGoToolBarVisible, (bool) goToolBarVisible);
1975     Settings::appSettings->setValue(Settings::optionViewToolBarVisible, (bool) viewToolBarVisible);
1976     Settings::appSettings->setValue(Settings::optionImageToolBarVisible, (bool) imageToolBarVisible);
1977     Settings::appSettings->setValue(Settings::optionFileSystemDockVisible, (bool) Settings::fileSystemDockVisible);
1978     Settings::appSettings->setValue(Settings::optionImageInfoDockVisible, (bool) Settings::imageInfoDockVisible);
1979     Settings::appSettings->setValue(Settings::optionBookmarksDockVisible, (bool) Settings::bookmarksDockVisible);
1980     Settings::appSettings->setValue(Settings::optionTagsDockVisible, (bool) Settings::tagsDockVisible);
1981     Settings::appSettings->setValue(Settings::optionImagePreviewDockVisible, (bool) Settings::imagePreviewDockVisible);
1982     Settings::appSettings->setValue(Settings::optionStartupDir, (int) Settings::startupDir);
1983     Settings::appSettings->setValue(Settings::optionSpecifiedStartDir, Settings::specifiedStartDir);
1984     Settings::appSettings->setValue(Settings::optionThumbsBackgroundImage, Settings::thumbsBackgroundImage);
1985     Settings::appSettings->setValue(Settings::optionLastDir,
1986                                     Settings::startupDir == Settings::RememberLastDir ? Settings::currentDirectory
1987                                                                                       : "");
1988     Settings::appSettings->setValue(Settings::optionShowImageName, (bool) Settings::showImageName);
1989     Settings::appSettings->setValue(Settings::optionSmallToolbarIcons, (bool) Settings::smallToolbarIcons);
1990     Settings::appSettings->setValue(Settings::optionHideDockTitlebars, (bool) Settings::hideDockTitlebars);
1991     Settings::appSettings->setValue(Settings::optionShowViewerToolbar, (bool) Settings::showViewerToolbar);
1992     Settings::appSettings->setValue(Settings::optionSetWindowIcon, (bool) Settings::setWindowIcon);
1993 
1994     /* Action shortcuts */
1995     Settings::appSettings->beginGroup(Settings::optionShortcuts);
1996     QMapIterator<QString, QAction *> shortcutsIterator(Settings::actionKeys);
1997     while (shortcutsIterator.hasNext()) {
1998         shortcutsIterator.next();
1999         Settings::appSettings->setValue(shortcutsIterator.key(), shortcutsIterator.value()->shortcut().toString());
2000     }
2001     Settings::appSettings->endGroup();
2002 
2003     /* External apps */
2004     Settings::appSettings->beginGroup(Settings::optionExternalApps);
2005     Settings::appSettings->remove("");
2006     QMapIterator<QString, QString> eaIter(Settings::externalApps);
2007     while (eaIter.hasNext()) {
2008         eaIter.next();
2009         Settings::appSettings->setValue(eaIter.key(), eaIter.value());
2010     }
2011     Settings::appSettings->endGroup();
2012 
2013     /* save bookmarks */
2014     int idx = 0;
2015     Settings::appSettings->beginGroup(Settings::optionCopyMoveToPaths);
2016     Settings::appSettings->remove("");
2017     QSetIterator<QString> pathsIter(Settings::bookmarkPaths);
2018     while (pathsIter.hasNext()) {
2019         Settings::appSettings->setValue("path" + QString::number(++idx), pathsIter.next());
2020     }
2021     Settings::appSettings->endGroup();
2022 
2023     /* save known Tags */
2024     idx = 0;
2025     Settings::appSettings->beginGroup(Settings::optionKnownTags);
2026     Settings::appSettings->remove("");
2027     QSetIterator<QString> tagsIter(Settings::knownTags);
2028     while (tagsIter.hasNext()) {
2029         Settings::appSettings->setValue("tag" + QString::number(++idx), tagsIter.next());
2030     }
2031     Settings::appSettings->endGroup();
2032 }
2033 
readSettings()2034 void Phototonic::readSettings() {
2035     initComplete = false;
2036     needThumbsRefresh = false;
2037 
2038     if (!Settings::appSettings->contains(Settings::optionThumbsZoomLevel)) {
2039         resize(800, 600);
2040         Settings::appSettings->setValue(Settings::optionThumbsSortFlags, (int) 0);
2041         Settings::appSettings->setValue(Settings::optionThumbsZoomLevel, (int) 200);
2042         Settings::appSettings->setValue(Settings::optionFullScreenMode, (bool) false);
2043         Settings::appSettings->setValue(Settings::optionViewerBackgroundColor, QColor(25, 25, 25));
2044         Settings::appSettings->setValue(Settings::optionThumbsBackgroundColor, QColor(200, 200, 200));
2045         Settings::appSettings->setValue(Settings::optionThumbsTextColor, QColor(25, 25, 25));
2046         Settings::appSettings->setValue(Settings::optionThumbsPagesReadCount, (int) 2);
2047         Settings::appSettings->setValue(Settings::optionViewerZoomOutFlags, (int) 1);
2048         Settings::appSettings->setValue(Settings::optionViewerZoomInFlags, (int) 0);
2049         Settings::appSettings->setValue(Settings::optionWrapImageList, (bool) false);
2050         Settings::appSettings->setValue(Settings::optionImageZoomFactor, (float) 1.0);
2051         Settings::appSettings->setValue(Settings::optionDefaultSaveQuality, (int) 90);
2052         Settings::appSettings->setValue(Settings::optionEnableAnimations, (bool) true);
2053         Settings::appSettings->setValue(Settings::optionExifRotationEnabled, (bool) true);
2054         Settings::appSettings->setValue(Settings::optionExifThumbRotationEnabled, (bool) false);
2055         Settings::appSettings->setValue(Settings::optionReverseMouseBehavior, (bool) false);
2056         Settings::appSettings->setValue(Settings::optionDeleteConfirm, (bool) true);
2057         Settings::appSettings->setValue(Settings::optionShowHiddenFiles, (bool) false);
2058         Settings::appSettings->setValue(Settings::optionSlideShowDelay, (int) 5);
2059         Settings::appSettings->setValue(Settings::optionSlideShowRandom, (bool) false);
2060         Settings::appSettings->setValue(Settings::optionEditToolBarVisible, (bool) true);
2061         Settings::appSettings->setValue(Settings::optionGoToolBarVisible, (bool) true);
2062         Settings::appSettings->setValue(Settings::optionViewToolBarVisible, (bool) true);
2063         Settings::appSettings->setValue(Settings::optionImageToolBarVisible, (bool) false);
2064         Settings::appSettings->setValue(Settings::optionFileSystemDockVisible, (bool) true);
2065         Settings::appSettings->setValue(Settings::optionBookmarksDockVisible, (bool) true);
2066         Settings::appSettings->setValue(Settings::optionTagsDockVisible, (bool) true);
2067         Settings::appSettings->setValue(Settings::optionImagePreviewDockVisible, (bool) true);
2068         Settings::appSettings->setValue(Settings::optionImageInfoDockVisible, (bool) true);
2069         Settings::appSettings->setValue(Settings::optionShowImageName, (bool) false);
2070         Settings::appSettings->setValue(Settings::optionSmallToolbarIcons, (bool) false);
2071         Settings::appSettings->setValue(Settings::optionHideDockTitlebars, (bool) false);
2072         Settings::appSettings->setValue(Settings::optionShowViewerToolbar, (bool) false);
2073         Settings::appSettings->setValue(Settings::optionSmallToolbarIcons, (bool) true);
2074         Settings::bookmarkPaths.insert(QDir::homePath());
2075         const QString picturesLocation = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
2076         if (!picturesLocation.isEmpty()) {
2077             Settings::bookmarkPaths.insert(picturesLocation);
2078         }
2079     }
2080 
2081     Settings::viewerBackgroundColor = Settings::appSettings->value(
2082             Settings::optionViewerBackgroundColor).value<QColor>();
2083     Settings::enableAnimations = Settings::appSettings->value(Settings::optionEnableAnimations).toBool();
2084     Settings::exifRotationEnabled = Settings::appSettings->value(Settings::optionExifRotationEnabled).toBool();
2085     Settings::exifThumbRotationEnabled = Settings::appSettings->value(
2086             Settings::optionExifThumbRotationEnabled).toBool();
2087     Settings::reverseMouseBehavior = Settings::appSettings->value(Settings::optionReverseMouseBehavior).toBool();
2088     Settings::deleteConfirm = Settings::appSettings->value(Settings::optionDeleteConfirm).toBool();
2089     Settings::showHiddenFiles = Settings::appSettings->value(Settings::optionShowHiddenFiles).toBool();
2090     Settings::wrapImageList = Settings::appSettings->value(Settings::optionWrapImageList).toBool();
2091     Settings::imageZoomFactor = Settings::appSettings->value(Settings::optionImageZoomFactor).toFloat();
2092     Settings::zoomOutFlags = Settings::appSettings->value(Settings::optionViewerZoomOutFlags).toUInt();
2093     Settings::zoomInFlags = Settings::appSettings->value(Settings::optionViewerZoomInFlags).toUInt();
2094     Settings::rotation = 0;
2095     Settings::keepTransform = false;
2096     shouldMaximize = Settings::appSettings->value(Settings::optionShouldMaximize).toBool();
2097     Settings::flipH = false;
2098     Settings::flipV = false;
2099     Settings::defaultSaveQuality = Settings::appSettings->value(Settings::optionDefaultSaveQuality).toInt();
2100     Settings::slideShowDelay = Settings::appSettings->value(Settings::optionSlideShowDelay).toInt();
2101     Settings::slideShowRandom = Settings::appSettings->value(Settings::optionSlideShowRandom).toBool();
2102     Settings::slideShowActive = false;
2103     editToolBarVisible = Settings::appSettings->value(Settings::optionEditToolBarVisible).toBool();
2104     goToolBarVisible = Settings::appSettings->value(Settings::optionGoToolBarVisible).toBool();
2105     viewToolBarVisible = Settings::appSettings->value(Settings::optionViewToolBarVisible).toBool();
2106     imageToolBarVisible = Settings::appSettings->value(Settings::optionImageToolBarVisible).toBool();
2107     Settings::fileSystemDockVisible = Settings::appSettings->value(Settings::optionFileSystemDockVisible).toBool();
2108     Settings::bookmarksDockVisible = Settings::appSettings->value(Settings::optionBookmarksDockVisible).toBool();
2109     Settings::tagsDockVisible = Settings::appSettings->value(Settings::optionTagsDockVisible).toBool();
2110     Settings::imagePreviewDockVisible = Settings::appSettings->value(Settings::optionImagePreviewDockVisible).toBool();
2111     Settings::imageInfoDockVisible = Settings::appSettings->value(Settings::optionImageInfoDockVisible).toBool();
2112     Settings::startupDir = (Settings::StartupDir) Settings::appSettings->value(Settings::optionStartupDir).toInt();
2113     Settings::specifiedStartDir = Settings::appSettings->value(Settings::optionSpecifiedStartDir).toString();
2114     Settings::thumbsBackgroundImage = Settings::appSettings->value(Settings::optionThumbsBackgroundImage).toString();
2115     Settings::showImageName = Settings::appSettings->value(Settings::optionShowImageName).toBool();
2116     Settings::smallToolbarIcons = Settings::appSettings->value(Settings::optionSmallToolbarIcons).toBool();
2117     Settings::hideDockTitlebars = Settings::appSettings->value(Settings::optionHideDockTitlebars).toBool();
2118     Settings::showViewerToolbar = Settings::appSettings->value(Settings::optionShowViewerToolbar).toBool();
2119     Settings::setWindowIcon = Settings::appSettings->value(Settings::optionSetWindowIcon).toBool();
2120 
2121     /* read external apps */
2122     Settings::appSettings->beginGroup(Settings::optionExternalApps);
2123     QStringList extApps = Settings::appSettings->childKeys();
2124     for (int i = 0; i < extApps.size(); ++i) {
2125         Settings::externalApps[extApps.at(i)] = Settings::appSettings->value(extApps.at(i)).toString();
2126     }
2127     Settings::appSettings->endGroup();
2128 
2129     /* read bookmarks */
2130     Settings::appSettings->beginGroup(Settings::optionCopyMoveToPaths);
2131     QStringList paths = Settings::appSettings->childKeys();
2132     for (int i = 0; i < paths.size(); ++i) {
2133         Settings::bookmarkPaths.insert(Settings::appSettings->value(paths.at(i)).toString());
2134     }
2135     Settings::appSettings->endGroup();
2136 
2137     /* read known tags */
2138     Settings::appSettings->beginGroup(Settings::optionKnownTags);
2139     QStringList tags = Settings::appSettings->childKeys();
2140     for (int i = 0; i < tags.size(); ++i) {
2141         Settings::knownTags.insert(Settings::appSettings->value(tags.at(i)).toString());
2142     }
2143     Settings::appSettings->endGroup();
2144 
2145     Settings::isFileListLoaded = false;
2146 }
2147 
setupDocks()2148 void Phototonic::setupDocks() {
2149 
2150     addDockWidget(Qt::RightDockWidgetArea, imageInfoDock);
2151     addDockWidget(Qt::RightDockWidgetArea, tagsDock);
2152 
2153     menuBar()->addMenu(createPopupMenu())->setText(tr("Window"));
2154     menuBar()->addSeparator();
2155     helpMenu = menuBar()->addMenu(tr("&Help"));
2156     helpMenu->addAction(aboutAction);
2157 
2158     fileSystemDockOrigWidget = fileSystemDock->titleBarWidget();
2159     bookmarksDockOrigWidget = bookmarksDock->titleBarWidget();
2160     imagePreviewDockOrigWidget = imagePreviewDock->titleBarWidget();
2161     tagsDockOrigWidget = tagsDock->titleBarWidget();
2162     imageInfoDockOrigWidget = imageInfoDock->titleBarWidget();
2163     fileSystemDockEmptyWidget = new QWidget;
2164     bookmarksDockEmptyWidget = new QWidget;
2165     imagePreviewDockEmptyWidget = new QWidget;
2166     tagsDockEmptyWidget = new QWidget;
2167     imageInfoDockEmptyWidget = new QWidget;
2168     lockDocks();
2169 }
2170 
lockDocks()2171 void Phototonic::lockDocks() {
2172     if (initComplete)
2173         Settings::hideDockTitlebars = lockDocksAction->isChecked();
2174 
2175     if (Settings::hideDockTitlebars) {
2176         fileSystemDock->setTitleBarWidget(fileSystemDockEmptyWidget);
2177         bookmarksDock->setTitleBarWidget(bookmarksDockEmptyWidget);
2178         imagePreviewDock->setTitleBarWidget(imagePreviewDockEmptyWidget);
2179         tagsDock->setTitleBarWidget(tagsDockEmptyWidget);
2180         imageInfoDock->setTitleBarWidget(imageInfoDockEmptyWidget);
2181     } else {
2182         fileSystemDock->setTitleBarWidget(fileSystemDockOrigWidget);
2183         bookmarksDock->setTitleBarWidget(bookmarksDockOrigWidget);
2184         imagePreviewDock->setTitleBarWidget(imagePreviewDockOrigWidget);
2185         tagsDock->setTitleBarWidget(tagsDockOrigWidget);
2186         imageInfoDock->setTitleBarWidget(imageInfoDockOrigWidget);
2187     }
2188 }
2189 
createPopupMenu()2190 QMenu *Phototonic::createPopupMenu() {
2191     QMenu *extraActsMenu = QMainWindow::createPopupMenu();
2192     extraActsMenu->addSeparator();
2193     extraActsMenu->addAction(smallToolbarIconsAction);
2194     extraActsMenu->addAction(lockDocksAction);
2195     return extraActsMenu;
2196 }
2197 
loadShortcuts()2198 void Phototonic::loadShortcuts() {
2199     // Add customizable key shortcut actions
2200     Settings::actionKeys[thumbsGoToTopAction->objectName()] = thumbsGoToTopAction;
2201     Settings::actionKeys[thumbsGoToBottomAction->objectName()] = thumbsGoToBottomAction;
2202     Settings::actionKeys[CloseImageAction->objectName()] = CloseImageAction;
2203     Settings::actionKeys[fullScreenAction->objectName()] = fullScreenAction;
2204     Settings::actionKeys[settingsAction->objectName()] = settingsAction;
2205     Settings::actionKeys[exitAction->objectName()] = exitAction;
2206     Settings::actionKeys[thumbsZoomInAction->objectName()] = thumbsZoomInAction;
2207     Settings::actionKeys[thumbsZoomOutAction->objectName()] = thumbsZoomOutAction;
2208     Settings::actionKeys[cutAction->objectName()] = cutAction;
2209     Settings::actionKeys[copyAction->objectName()] = copyAction;
2210     Settings::actionKeys[nextImageAction->objectName()] = nextImageAction;
2211     Settings::actionKeys[prevImageAction->objectName()] = prevImageAction;
2212     Settings::actionKeys[deletePermanentlyAction->objectName()] = deletePermanentlyAction;
2213     Settings::actionKeys[deleteAction->objectName()] = deleteAction;
2214     Settings::actionKeys[saveAction->objectName()] = saveAction;
2215     Settings::actionKeys[saveAsAction->objectName()] = saveAsAction;
2216     Settings::actionKeys[keepTransformAction->objectName()] = keepTransformAction;
2217     Settings::actionKeys[keepZoomAction->objectName()] = keepZoomAction;
2218     Settings::actionKeys[showClipboardAction->objectName()] = showClipboardAction;
2219     Settings::actionKeys[copyImageAction->objectName()] = copyImageAction;
2220     Settings::actionKeys[pasteImageAction->objectName()] = pasteImageAction;
2221     Settings::actionKeys[renameAction->objectName()] = renameAction;
2222     Settings::actionKeys[refreshAction->objectName()] = refreshAction;
2223     Settings::actionKeys[pasteAction->objectName()] = pasteAction;
2224     Settings::actionKeys[goBackAction->objectName()] = goBackAction;
2225     Settings::actionKeys[goFrwdAction->objectName()] = goFrwdAction;
2226     Settings::actionKeys[slideShowAction->objectName()] = slideShowAction;
2227     Settings::actionKeys[firstImageAction->objectName()] = firstImageAction;
2228     Settings::actionKeys[lastImageAction->objectName()] = lastImageAction;
2229     Settings::actionKeys[randomImageAction->objectName()] = randomImageAction;
2230     Settings::actionKeys[viewImageAction->objectName()] = viewImageAction;
2231     Settings::actionKeys[zoomOutAction->objectName()] = zoomOutAction;
2232     Settings::actionKeys[zoomInAction->objectName()] = zoomInAction;
2233     Settings::actionKeys[resetZoomAction->objectName()] = resetZoomAction;
2234     Settings::actionKeys[origZoomAction->objectName()] = origZoomAction;
2235     Settings::actionKeys[rotateLeftAction->objectName()] = rotateLeftAction;
2236     Settings::actionKeys[rotateRightAction->objectName()] = rotateRightAction;
2237     Settings::actionKeys[freeRotateLeftAction->objectName()] = freeRotateLeftAction;
2238     Settings::actionKeys[freeRotateRightAction->objectName()] = freeRotateRightAction;
2239     Settings::actionKeys[flipHorizontalAction->objectName()] = flipHorizontalAction;
2240     Settings::actionKeys[flipVerticalAction->objectName()] = flipVerticalAction;
2241     Settings::actionKeys[cropAction->objectName()] = cropAction;
2242     Settings::actionKeys[cropToSelectionAction->objectName()] = cropToSelectionAction;
2243     Settings::actionKeys[colorsAction->objectName()] = colorsAction;
2244     Settings::actionKeys[mirrorDisabledAction->objectName()] = mirrorDisabledAction;
2245     Settings::actionKeys[mirrorDualAction->objectName()] = mirrorDualAction;
2246     Settings::actionKeys[mirrorTripleAction->objectName()] = mirrorTripleAction;
2247     Settings::actionKeys[mirrorDualVerticalAction->objectName()] = mirrorDualVerticalAction;
2248     Settings::actionKeys[mirrorQuadAction->objectName()] = mirrorQuadAction;
2249     Settings::actionKeys[moveDownAction->objectName()] = moveDownAction;
2250     Settings::actionKeys[moveUpAction->objectName()] = moveUpAction;
2251     Settings::actionKeys[moveRightAction->objectName()] = moveRightAction;
2252     Settings::actionKeys[moveLeftAction->objectName()] = moveLeftAction;
2253     Settings::actionKeys[copyToAction->objectName()] = copyToAction;
2254     Settings::actionKeys[moveToAction->objectName()] = moveToAction;
2255     Settings::actionKeys[goUpAction->objectName()] = goUpAction;
2256     Settings::actionKeys[resizeAction->objectName()] = resizeAction;
2257     Settings::actionKeys[filterImagesFocusAction->objectName()] = filterImagesFocusAction;
2258     Settings::actionKeys[setPathFocusAction->objectName()] = setPathFocusAction;
2259     Settings::actionKeys[invertSelectionAction->objectName()] = invertSelectionAction;
2260     Settings::actionKeys[includeSubDirectoriesAction->objectName()] = includeSubDirectoriesAction;
2261     Settings::actionKeys[createDirectoryAction->objectName()] = createDirectoryAction;
2262     Settings::actionKeys[addBookmarkAction->objectName()] = addBookmarkAction;
2263     Settings::actionKeys[removeMetadataAction->objectName()] = removeMetadataAction;
2264     Settings::actionKeys[externalAppsAction->objectName()] = externalAppsAction;
2265     Settings::actionKeys[goHomeAction->objectName()] = goHomeAction;
2266     Settings::actionKeys[sortByNameAction->objectName()] = sortByNameAction;
2267     Settings::actionKeys[sortBySizeAction->objectName()] = sortBySizeAction;
2268     Settings::actionKeys[sortByTimeAction->objectName()] = sortByTimeAction;
2269     Settings::actionKeys[sortByTypeAction->objectName()] = sortByTypeAction;
2270     Settings::actionKeys[sortReverseAction->objectName()] = sortReverseAction;
2271     Settings::actionKeys[showHiddenFilesAction->objectName()] = showHiddenFilesAction;
2272     Settings::actionKeys[showViewerToolbarAction->objectName()] = showViewerToolbarAction;
2273 
2274     Settings::appSettings->beginGroup(Settings::optionShortcuts);
2275     QStringList groupKeys = Settings::appSettings->childKeys();
2276 
2277     if (groupKeys.size()) {
2278         if (groupKeys.contains(thumbsGoToTopAction->text())) {
2279             QMapIterator<QString, QAction *> key(Settings::actionKeys);
2280             while (key.hasNext()) {
2281                 key.next();
2282                 if (groupKeys.contains(key.value()->text())) {
2283                     key.value()->setShortcut(Settings::appSettings->value(key.value()->text()).toString());
2284                     Settings::appSettings->remove(key.value()->text());
2285                     Settings::appSettings->setValue(key.key(), key.value()->shortcut().toString());
2286                 }
2287             }
2288         } else {
2289             for (int i = 0; i < groupKeys.size(); ++i) {
2290                 if (Settings::actionKeys.value(groupKeys.at(i)))
2291                     Settings::actionKeys.value(groupKeys.at(i))->setShortcut
2292                             (Settings::appSettings->value(groupKeys.at(i)).toString());
2293             }
2294         }
2295     } else {
2296         thumbsGoToTopAction->setShortcut(QKeySequence("Ctrl+Home"));
2297         thumbsGoToBottomAction->setShortcut(QKeySequence("Ctrl+End"));
2298         CloseImageAction->setShortcut(Qt::Key_Escape);
2299         fullScreenAction->setShortcut(QKeySequence("Alt+Return"));
2300         settingsAction->setShortcut(QKeySequence("Ctrl+P"));
2301         exitAction->setShortcut(QKeySequence("Ctrl+Q"));
2302         cutAction->setShortcut(QKeySequence("Ctrl+X"));
2303         copyAction->setShortcut(QKeySequence("Ctrl+C"));
2304         deleteAction->setShortcut(QKeySequence("Del"));
2305         deletePermanentlyAction->setShortcut(QKeySequence("Shift+Del"));
2306         saveAction->setShortcut(QKeySequence("Ctrl+S"));
2307         copyImageAction->setShortcut(QKeySequence("Ctrl+Shift+C"));
2308         pasteImageAction->setShortcut(QKeySequence("Ctrl+Shift+V"));
2309         renameAction->setShortcut(QKeySequence("F2"));
2310         refreshAction->setShortcut(QKeySequence("F5"));
2311         pasteAction->setShortcut(QKeySequence("Ctrl+V"));
2312         goBackAction->setShortcut(QKeySequence("Alt+Left"));
2313         goFrwdAction->setShortcut(QKeySequence("Alt+Right"));
2314         goUpAction->setShortcut(QKeySequence("Alt+Up"));
2315         slideShowAction->setShortcut(QKeySequence("Ctrl+W"));
2316         nextImageAction->setShortcut(QKeySequence("PgDown"));
2317         prevImageAction->setShortcut(QKeySequence("PgUp"));
2318         firstImageAction->setShortcut(QKeySequence("Home"));
2319         lastImageAction->setShortcut(QKeySequence("End"));
2320         randomImageAction->setShortcut(QKeySequence("Ctrl+D"));
2321         viewImageAction->setShortcut(QKeySequence("Return"));
2322         zoomOutAction->setShortcut(QKeySequence("-"));
2323         zoomInAction->setShortcut(QKeySequence("+"));
2324         resetZoomAction->setShortcut(QKeySequence("*"));
2325         origZoomAction->setShortcut(QKeySequence("/"));
2326         rotateLeftAction->setShortcut(QKeySequence("Ctrl+Left"));
2327         rotateRightAction->setShortcut(QKeySequence("Ctrl+Right"));
2328         freeRotateLeftAction->setShortcut(QKeySequence("Ctrl+Shift+Left"));
2329         freeRotateRightAction->setShortcut(QKeySequence("Ctrl+Shift+Right"));
2330         flipHorizontalAction->setShortcut(QKeySequence("Ctrl+Down"));
2331         flipVerticalAction->setShortcut(QKeySequence("Ctrl+Up"));
2332         cropAction->setShortcut(QKeySequence("Ctrl+G"));
2333         cropToSelectionAction->setShortcut(QKeySequence("Ctrl+R"));
2334         colorsAction->setShortcut(QKeySequence("Ctrl+O"));
2335         mirrorDisabledAction->setShortcut(QKeySequence("Ctrl+1"));
2336         mirrorDualAction->setShortcut(QKeySequence("Ctrl+2"));
2337         mirrorTripleAction->setShortcut(QKeySequence("Ctrl+3"));
2338         mirrorDualVerticalAction->setShortcut(QKeySequence("Ctrl+4"));
2339         mirrorQuadAction->setShortcut(QKeySequence("Ctrl+5"));
2340         moveDownAction->setShortcut(QKeySequence("Down"));
2341         moveUpAction->setShortcut(QKeySequence("Up"));
2342         moveLeftAction->setShortcut(QKeySequence("Left"));
2343         moveRightAction->setShortcut(QKeySequence("Right"));
2344         copyToAction->setShortcut(QKeySequence("Ctrl+Y"));
2345         moveToAction->setShortcut(QKeySequence("Ctrl+M"));
2346         resizeAction->setShortcut(QKeySequence("Ctrl+I"));
2347         filterImagesFocusAction->setShortcut(QKeySequence("Ctrl+F"));
2348         setPathFocusAction->setShortcut(QKeySequence("Ctrl+L"));
2349         keepTransformAction->setShortcut(QKeySequence("Ctrl+K"));
2350         showHiddenFilesAction->setShortcut(QKeySequence("Ctrl+H"));
2351     }
2352 
2353     Settings::appSettings->endGroup();
2354 }
2355 
closeEvent(QCloseEvent * event)2356 void Phototonic::closeEvent(QCloseEvent *event) {
2357     thumbsViewer->abort();
2358     writeSettings();
2359     hide();
2360     if (!QApplication::clipboard()->image().isNull()) {
2361         QApplication::clipboard()->clear();
2362     }
2363     event->accept();
2364 }
2365 
setStatus(QString state)2366 void Phototonic::setStatus(QString state) {
2367     statusLabel->setText("    " + state + "    ");
2368 }
2369 
mouseDoubleClickEvent(QMouseEvent * event)2370 void Phototonic::mouseDoubleClickEvent(QMouseEvent *event) {
2371     if (interfaceDisabled) {
2372         return;
2373     }
2374 
2375     if (event->button() == Qt::LeftButton) {
2376         if (Settings::layoutMode == ImageViewWidget) {
2377             if (Settings::reverseMouseBehavior) {
2378                 fullScreenAction->setChecked(!(fullScreenAction->isChecked()));
2379                 toggleFullScreen();
2380                 event->accept();
2381             } else if (CloseImageAction->isEnabled()) {
2382                 hideViewer();
2383                 event->accept();
2384             }
2385         } else {
2386             if (QApplication::focusWidget() == thumbsViewer->imagePreview->scrollArea) {
2387                 viewImage();
2388             }
2389         }
2390     }
2391 }
2392 
mousePressEvent(QMouseEvent * event)2393 void Phototonic::mousePressEvent(QMouseEvent *event) {
2394     if (interfaceDisabled) {
2395         return;
2396     }
2397 
2398     if (Settings::layoutMode == ImageViewWidget) {
2399         if (event->button() == Qt::MiddleButton) {
2400 
2401             if (event->modifiers() == Qt::ShiftModifier) {
2402                 origZoom();
2403                 event->accept();
2404                 return;
2405             }
2406             if (event->modifiers() == Qt::ControlModifier) {
2407                 resetZoom();
2408                 event->accept();
2409                 return;
2410             }
2411 
2412             if (Settings::reverseMouseBehavior && CloseImageAction->isEnabled()) {
2413                 hideViewer();
2414                 event->accept();
2415             } else {
2416                 fullScreenAction->setChecked(!(fullScreenAction->isChecked()));
2417                 toggleFullScreen();
2418                 event->accept();
2419             }
2420         }
2421     } else if (QApplication::focusWidget() == thumbsViewer->imagePreview->scrollArea) {
2422         if (event->button() == Qt::MiddleButton) {
2423             viewImage();
2424         }
2425     }
2426 }
2427 
newImage()2428 void Phototonic::newImage() {
2429     if (Settings::layoutMode == ThumbViewWidget) {
2430         showViewer();
2431     }
2432 
2433     imageViewer->loadImage("");
2434 }
2435 
setDocksVisibility(bool visible)2436 void Phototonic::setDocksVisibility(bool visible) {
2437     fileSystemDock->setVisible(visible ? Settings::fileSystemDockVisible : false);
2438     bookmarksDock->setVisible(visible ? Settings::bookmarksDockVisible : false);
2439     imagePreviewDock->setVisible(visible ? Settings::imagePreviewDockVisible : false);
2440     tagsDock->setVisible(visible ? Settings::tagsDockVisible : false);
2441     imageInfoDock->setVisible(visible ? Settings::imageInfoDockVisible : false);
2442 
2443     menuBar()->setVisible(visible);
2444     menuBar()->setDisabled(!visible);
2445     statusBar()->setVisible(visible);
2446 
2447     editToolBar->setVisible(visible ? editToolBarVisible : false);
2448     goToolBar->setVisible(visible ? goToolBarVisible : false);
2449     viewToolBar->setVisible(visible ? viewToolBarVisible : false);
2450     imageToolBar->setVisible(visible ? imageToolBarVisible : Settings::showViewerToolbar);
2451     addToolBar(imageToolBar);
2452 
2453     setContextMenuPolicy(Qt::PreventContextMenu);
2454 }
2455 
viewImage()2456 void Phototonic::viewImage() {
2457     if (Settings::layoutMode == ImageViewWidget) {
2458         hideViewer();
2459         return;
2460     }
2461 
2462     if (QApplication::focusWidget() == fileSystemTree) {
2463         goSelectedDir(fileSystemTree->getCurrentIndex());
2464         return;
2465     } else if (QApplication::focusWidget() == thumbsViewer
2466                || QApplication::focusWidget() == thumbsViewer->imagePreview->scrollArea
2467                || QApplication::focusWidget() == imageViewer->scrollArea) {
2468         QModelIndex selectedImageIndex;
2469         QModelIndexList selectedIndexes = thumbsViewer->selectionModel()->selectedIndexes();
2470         if (selectedIndexes.size() > 0) {
2471             selectedImageIndex = selectedIndexes.first();
2472         } else {
2473             if (thumbsViewer->thumbsViewerModel->rowCount() == 0) {
2474                 setStatus(tr("No images"));
2475                 return;
2476             }
2477 
2478             selectedImageIndex = thumbsViewer->thumbsViewerModel->indexFromItem(
2479                     thumbsViewer->thumbsViewerModel->item(0));
2480             thumbsViewer->selectionModel()->select(selectedImageIndex, QItemSelectionModel::Toggle);
2481             thumbsViewer->setCurrentRow(0);
2482         }
2483 
2484         loadSelectedThumbImage(selectedImageIndex);
2485         return;
2486     } else if (QApplication::focusWidget() == filterLineEdit) {
2487         setThumbsFilter();
2488         return;
2489     } else if (QApplication::focusWidget() == pathLineEdit) {
2490         goPathBarDir();
2491         return;
2492     }
2493 }
2494 
setEditToolBarVisibility()2495 void Phototonic::setEditToolBarVisibility() {
2496     editToolBarVisible = editToolBar->isVisible();
2497 }
2498 
setGoToolBarVisibility()2499 void Phototonic::setGoToolBarVisibility() {
2500     goToolBarVisible = goToolBar->isVisible();
2501 }
2502 
setViewToolBarVisibility()2503 void Phototonic::setViewToolBarVisibility() {
2504     viewToolBarVisible = viewToolBar->isVisible();
2505 }
2506 
setImageToolBarVisibility()2507 void Phototonic::setImageToolBarVisibility() {
2508     imageToolBarVisible = imageToolBar->isVisible();
2509 }
2510 
setFileSystemDockVisibility()2511 void Phototonic::setFileSystemDockVisibility() {
2512     if (Settings::layoutMode != ImageViewWidget) {
2513         Settings::fileSystemDockVisible = fileSystemDock->isVisible();
2514     }
2515 }
2516 
setBookmarksDockVisibility()2517 void Phototonic::setBookmarksDockVisibility() {
2518     if (Settings::layoutMode != ImageViewWidget) {
2519         Settings::bookmarksDockVisible = bookmarksDock->isVisible();
2520     }
2521 }
2522 
setImagePreviewDockVisibility()2523 void Phototonic::setImagePreviewDockVisibility() {
2524     if (Settings::layoutMode != ImageViewWidget) {
2525         Settings::imagePreviewDockVisible = imagePreviewDock->isVisible();
2526     }
2527 }
2528 
setTagsDockVisibility()2529 void Phototonic::setTagsDockVisibility() {
2530     if (Settings::layoutMode != ImageViewWidget) {
2531         Settings::tagsDockVisible = tagsDock->isVisible();
2532     }
2533 }
2534 
setImageInfoDockVisibility()2535 void Phototonic::setImageInfoDockVisibility() {
2536     if (Settings::layoutMode != ImageViewWidget) {
2537         Settings::imageInfoDockVisible = imageInfoDock->isVisible();
2538     }
2539 }
2540 
showViewer()2541 void Phototonic::showViewer() {
2542     if (Settings::layoutMode == ThumbViewWidget) {
2543         Settings::layoutMode = ImageViewWidget;
2544         Settings::appSettings->setValue("Geometry", saveGeometry());
2545         Settings::appSettings->setValue("WindowState", saveState());
2546 
2547         stackedLayout->setCurrentWidget(imageViewer);
2548         setDocksVisibility(false);
2549 
2550         if (Settings::isFullScreen) {
2551             shouldMaximize = isMaximized();
2552             showFullScreen();
2553             imageViewer->setCursorHiding(true);
2554             QApplication::processEvents();
2555         }
2556         imageViewer->setFocus(Qt::OtherFocusReason);
2557     }
2558 }
2559 
showBusyAnimation(bool busy)2560 void Phototonic::showBusyAnimation(bool busy) {
2561     static int busyStatus = 0;
2562 
2563     if (busy) {
2564         ++busyStatus;
2565     } else {
2566         --busyStatus;
2567     }
2568 
2569     if (busyStatus > 0) {
2570         busyMovie->start();
2571         busyLabel->setVisible(true);
2572     } else {
2573         busyLabel->setVisible(false);
2574         busyMovie->stop();
2575         busyStatus = 0;
2576     }
2577 }
2578 
loadSelectedThumbImage(const QModelIndex & idx)2579 void Phototonic::loadSelectedThumbImage(const QModelIndex &idx) {
2580     thumbsViewer->setCurrentRow(idx.row());
2581     showViewer();
2582     imageViewer->loadImage(
2583             thumbsViewer->thumbsViewerModel->item(idx.row())->data(thumbsViewer->FileNameRole).toString());
2584     thumbsViewer->setImageViewerWindowTitle();
2585 }
2586 
loadImageFromCliArguments(QString cliFileName)2587 void Phototonic::loadImageFromCliArguments(QString cliFileName) {
2588     QFile imageFile(cliFileName);
2589     if (!imageFile.exists()) {
2590         MessageBox msgBox(this);
2591         msgBox.critical(tr("Error"), tr("Failed to open file %1, file not found.").arg(cliFileName));
2592         return;
2593     }
2594 
2595     showViewer();
2596     imageViewer->loadImage(cliFileName);
2597     setWindowTitle(cliFileName + " - Phototonic");
2598 }
2599 
toggleSlideShow()2600 void Phototonic::toggleSlideShow() {
2601     if (Settings::slideShowActive) {
2602         Settings::slideShowActive = false;
2603         slideShowAction->setText(tr("Slide Show"));
2604         imageViewer->setFeedback(tr("Slide show stopped"));
2605 
2606         SlideShowTimer->stop();
2607         delete SlideShowTimer;
2608         slideShowAction->setIcon(QIcon::fromTheme("media-playback-start", QIcon(":/images/play.png")));
2609     } else {
2610         if (thumbsViewer->thumbsViewerModel->rowCount() <= 0) {
2611             return;
2612         }
2613 
2614         if (Settings::layoutMode == ThumbViewWidget) {
2615             QModelIndexList indexesList = thumbsViewer->selectionModel()->selectedIndexes();
2616             if (indexesList.size() != 1) {
2617                 thumbsViewer->setCurrentRow(0);
2618             } else {
2619                 thumbsViewer->setCurrentRow(indexesList.first().row());
2620             }
2621 
2622             showViewer();
2623         }
2624 
2625         Settings::slideShowActive = true;
2626 
2627         SlideShowTimer = new QTimer(this);
2628         connect(SlideShowTimer, SIGNAL(timeout()), this, SLOT(slideShowHandler()));
2629         SlideShowTimer->start(Settings::slideShowDelay * 1000);
2630 
2631         slideShowAction->setText(tr("Stop Slide Show"));
2632         imageViewer->setFeedback(tr("Slide show started"));
2633         slideShowAction->setIcon(QIcon::fromTheme("media-playback-stop", QIcon(":/images/stop.png")));
2634 
2635         slideShowHandler();
2636     }
2637 }
2638 
slideShowHandler()2639 void Phototonic::slideShowHandler() {
2640     if (Settings::slideShowActive) {
2641         if (Settings::slideShowRandom) {
2642             loadRandomImage();
2643         } else {
2644             int currentRow = thumbsViewer->getCurrentRow();
2645             imageViewer->loadImage(
2646                     thumbsViewer->thumbsViewerModel->item(currentRow)->data(thumbsViewer->FileNameRole).toString());
2647             thumbsViewer->setImageViewerWindowTitle();
2648 
2649             if (thumbsViewer->getNextRow() > 0) {
2650                 thumbsViewer->setCurrentRow(thumbsViewer->getNextRow());
2651             } else {
2652                 if (Settings::wrapImageList) {
2653                     thumbsViewer->setCurrentRow(0);
2654                 } else {
2655                     toggleSlideShow();
2656                 }
2657             }
2658         }
2659     }
2660 }
2661 
loadNextImage()2662 void Phototonic::loadNextImage() {
2663     if (thumbsViewer->thumbsViewerModel->rowCount() <= 0) {
2664         return;
2665     }
2666 
2667     int nextThumb = thumbsViewer->getNextRow();
2668     if (nextThumb < 0) {
2669         if (Settings::wrapImageList) {
2670             nextThumb = 0;
2671         } else {
2672             return;
2673         }
2674     }
2675 
2676     if (Settings::layoutMode == ImageViewWidget) {
2677         imageViewer->loadImage(
2678                 thumbsViewer->thumbsViewerModel->item(nextThumb)->data(thumbsViewer->FileNameRole).toString());
2679     }
2680 
2681     thumbsViewer->setCurrentRow(nextThumb);
2682     thumbsViewer->setImageViewerWindowTitle();
2683 
2684     if (Settings::layoutMode == ThumbViewWidget) {
2685         thumbsViewer->selectThumbByRow(nextThumb);
2686     }
2687 }
2688 
loadPreviousImage()2689 void Phototonic::loadPreviousImage() {
2690     if (thumbsViewer->thumbsViewerModel->rowCount() <= 0) {
2691         return;
2692     }
2693 
2694     int previousThumb = thumbsViewer->getPrevRow();
2695     if (previousThumb < 0) {
2696         if (Settings::wrapImageList) {
2697             previousThumb = thumbsViewer->getLastRow();
2698         } else {
2699             return;
2700         }
2701     }
2702 
2703     if (Settings::layoutMode == ImageViewWidget) {
2704         imageViewer->loadImage(
2705                 thumbsViewer->thumbsViewerModel->item(previousThumb)->data(thumbsViewer->FileNameRole).toString());
2706     }
2707 
2708     thumbsViewer->setCurrentRow(previousThumb);
2709     thumbsViewer->setImageViewerWindowTitle();
2710 
2711     if (Settings::layoutMode == ThumbViewWidget) {
2712         thumbsViewer->selectThumbByRow(previousThumb);
2713     }
2714 }
2715 
loadFirstImage()2716 void Phototonic::loadFirstImage() {
2717     if (thumbsViewer->thumbsViewerModel->rowCount() <= 0) {
2718         return;
2719     }
2720 
2721     imageViewer->loadImage(thumbsViewer->thumbsViewerModel->item(0)->data(thumbsViewer->FileNameRole).toString());
2722     thumbsViewer->setCurrentRow(0);
2723     thumbsViewer->setImageViewerWindowTitle();
2724 
2725     if (Settings::layoutMode == ThumbViewWidget) {
2726         thumbsViewer->selectThumbByRow(0);
2727     }
2728 }
2729 
loadLastImage()2730 void Phototonic::loadLastImage() {
2731     if (thumbsViewer->thumbsViewerModel->rowCount() <= 0) {
2732         return;
2733     }
2734 
2735     int lastRow = thumbsViewer->getLastRow();
2736     imageViewer->loadImage(thumbsViewer->thumbsViewerModel->item(lastRow)->data(thumbsViewer->FileNameRole).toString());
2737     thumbsViewer->setCurrentRow(lastRow);
2738     thumbsViewer->setImageViewerWindowTitle();
2739 
2740     if (Settings::layoutMode == ThumbViewWidget) {
2741         thumbsViewer->selectThumbByRow(lastRow);
2742     }
2743 }
2744 
loadRandomImage()2745 void Phototonic::loadRandomImage() {
2746     if (thumbsViewer->thumbsViewerModel->rowCount() <= 0) {
2747         return;
2748     }
2749 
2750     int randomRow = thumbsViewer->getRandomRow();
2751     imageViewer->loadImage(
2752             thumbsViewer->thumbsViewerModel->item(randomRow)->data(thumbsViewer->FileNameRole).toString());
2753     thumbsViewer->setCurrentRow(randomRow);
2754     thumbsViewer->setImageViewerWindowTitle();
2755 
2756     if (Settings::layoutMode == ThumbViewWidget) {
2757         thumbsViewer->selectThumbByRow(randomRow);
2758     }
2759 }
2760 
setViewerKeyEventsEnabled(bool enabled)2761 void Phototonic::setViewerKeyEventsEnabled(bool enabled) {
2762     moveLeftAction->setEnabled(enabled);
2763     moveRightAction->setEnabled(enabled);
2764     moveUpAction->setEnabled(enabled);
2765     moveDownAction->setEnabled(enabled);
2766 }
2767 
updateIndexByViewerImage()2768 void Phototonic::updateIndexByViewerImage() {
2769     if (thumbsViewer->thumbsViewerModel->rowCount() > 0 &&
2770         thumbsViewer->setCurrentIndexByName(imageViewer->viewerImageFullPath)) {
2771         thumbsViewer->selectCurrentIndex();
2772     }
2773 }
2774 
hideViewer()2775 void Phototonic::hideViewer() {
2776     if (isFullScreen()) {
2777         showNormal();
2778         if (shouldMaximize) {
2779             showMaximized();
2780         }
2781         imageViewer->setCursorHiding(false);
2782     }
2783 
2784     restoreGeometry(Settings::appSettings->value(Settings::optionGeometry).toByteArray());
2785     restoreState(Settings::appSettings->value(Settings::optionWindowState).toByteArray());
2786 
2787     Settings::layoutMode = ThumbViewWidget;
2788     stackedLayout->setCurrentWidget(thumbsViewer);
2789 
2790     setDocksVisibility(true);
2791     while (QApplication::overrideCursor()) {
2792         QApplication::restoreOverrideCursor();
2793     }
2794 
2795     if (Settings::slideShowActive) {
2796         toggleSlideShow();
2797     }
2798 
2799     setThumbsViewerWindowTitle();
2800 
2801     for (int i = 0; i <= 10 && qApp->hasPendingEvents(); ++i) {
2802         QApplication::processEvents();
2803     }
2804 
2805     if (needThumbsRefresh) {
2806         needThumbsRefresh = false;
2807         refreshThumbs(true);
2808     } else {
2809         if (thumbsViewer->thumbsViewerModel->rowCount() > 0) {
2810             if (thumbsViewer->setCurrentIndexByName(imageViewer->viewerImageFullPath)) {
2811                 thumbsViewer->selectCurrentIndex();
2812             }
2813         }
2814 
2815         thumbsViewer->loadVisibleThumbs();
2816     }
2817 
2818     imageViewer->clearImage();
2819     thumbsViewer->setFocus(Qt::OtherFocusReason);
2820     setContextMenuPolicy(Qt::DefaultContextMenu);
2821 }
2822 
goBottom()2823 void Phototonic::goBottom() {
2824     thumbsViewer->scrollToBottom();
2825 }
2826 
goTop()2827 void Phototonic::goTop() {
2828     thumbsViewer->scrollToTop();
2829 }
2830 
dropOp(Qt::KeyboardModifiers keyMods,bool dirOp,QString copyMoveDirPath)2831 void Phototonic::dropOp(Qt::KeyboardModifiers keyMods, bool dirOp, QString copyMoveDirPath) {
2832     QApplication::restoreOverrideCursor();
2833     Settings::isCopyOperation = (keyMods == Qt::ControlModifier);
2834     QString destDir;
2835 
2836     if (QObject::sender() == fileSystemTree) {
2837         destDir = getSelectedPath();
2838     } else if (QObject::sender() == bookmarks) {
2839         if (bookmarks->currentItem()) {
2840             destDir = bookmarks->currentItem()->toolTip(0);
2841         } else {
2842             addBookmark(copyMoveDirPath);
2843             return;
2844         }
2845     } else {
2846         // Unknown sender
2847         return;
2848     }
2849 
2850     MessageBox msgBox(this);
2851     if (!isValidPath(destDir)) {
2852         msgBox.critical(tr("Error"), tr("Can not move or copy images to this directory."));
2853         selectCurrentViewDir();
2854         return;
2855     }
2856 
2857     if (destDir == Settings::currentDirectory) {
2858         msgBox.critical(tr("Error"), tr("Destination directory is the same as the source directory."));
2859         return;
2860     }
2861 
2862     if (dirOp) {
2863         QString dirOnly = copyMoveDirPath.right(
2864                 copyMoveDirPath.size() - copyMoveDirPath.lastIndexOf(QDir::separator()) - 1);
2865 
2866         QString question = tr("Move directory %1 to %2?").arg(dirOnly).arg(destDir);
2867 
2868         MessageBox moveDirMessageBox(this);
2869         moveDirMessageBox.setText(question);
2870         moveDirMessageBox.setWindowTitle(tr("Move directory"));
2871         moveDirMessageBox.setIcon(MessageBox::Warning);
2872         moveDirMessageBox.setStandardButtons(MessageBox::Yes | MessageBox::Cancel);
2873         moveDirMessageBox.setDefaultButton(MessageBox::Cancel);
2874         moveDirMessageBox.setButtonText(MessageBox::Yes, tr("Move Directory"));
2875         moveDirMessageBox.setButtonText(MessageBox::Cancel, tr("Cancel"));
2876         int ret = moveDirMessageBox.exec();
2877 
2878         if (ret == MessageBox::Yes) {
2879             QFile dir(copyMoveDirPath);
2880             bool moveOk = dir.rename(destDir + QDir::separator() + dirOnly);
2881             if (!moveOk) {
2882                 moveDirMessageBox.critical(tr("Error"), tr("Failed to move directory."));
2883             }
2884             setStatus(tr("Directory moved"));
2885         }
2886     } else {
2887         CopyMoveDialog *copyMoveDialog = new CopyMoveDialog(this);
2888         Settings::copyCutIndexList = thumbsViewer->selectionModel()->selectedIndexes();
2889         copyMoveDialog->exec(thumbsViewer, destDir, false);
2890 
2891         if (!Settings::isCopyOperation) {
2892             int row = copyMoveDialog->latestRow;
2893             if (thumbsViewer->thumbsViewerModel->rowCount()) {
2894                 if (row >= thumbsViewer->thumbsViewerModel->rowCount()) {
2895                     row = thumbsViewer->thumbsViewerModel->rowCount() - 1;
2896                 }
2897 
2898                 thumbsViewer->setCurrentRow(row);
2899                 thumbsViewer->selectThumbByRow(row);
2900             }
2901         }
2902 
2903         QString stateString = QString((Settings::isCopyOperation ? tr("Copied") : tr("Moved")) + " " +
2904                                       tr("%n image(s)", "", copyMoveDialog->nFiles));
2905         setStatus(stateString);
2906         delete (copyMoveDialog);
2907     }
2908 
2909     thumbsViewer->loadVisibleThumbs();
2910 }
2911 
selectCurrentViewDir()2912 void Phototonic::selectCurrentViewDir() {
2913     QModelIndex idx = fileSystemTree->fileSystemModel->index(Settings::currentDirectory);
2914     if (idx.isValid()) {
2915         fileSystemTree->setCurrentIndex(idx);
2916     }
2917 }
2918 
checkDirState(const QModelIndex &,int,int)2919 void Phototonic::checkDirState(const QModelIndex &, int, int) {
2920     if (!initComplete) {
2921         return;
2922     }
2923 
2924     if (thumbsViewer->isBusy) {
2925         thumbsViewer->abort();
2926     }
2927 
2928     if (!QDir().exists(Settings::currentDirectory)) {
2929         Settings::currentDirectory.clear();
2930         QTimer::singleShot(0, this, SLOT(onReloadThumbs()));
2931     }
2932 }
2933 
addPathHistoryRecord(QString dir)2934 void Phototonic::addPathHistoryRecord(QString dir) {
2935     if (!needHistoryRecord) {
2936         needHistoryRecord = true;
2937         return;
2938     }
2939 
2940     if (pathHistoryList.size() && dir == pathHistoryList.at(currentHistoryIdx)) {
2941         return;
2942     }
2943 
2944     pathHistoryList.insert(++currentHistoryIdx, dir);
2945 
2946     // Need to clear irrelevant items from list
2947     if (currentHistoryIdx != pathHistoryList.size() - 1) {
2948         goFrwdAction->setEnabled(false);
2949         for (int i = pathHistoryList.size() - 1; i > currentHistoryIdx; --i) {
2950             pathHistoryList.removeAt(i);
2951         }
2952     }
2953 }
2954 
onReloadThumbs()2955 void Phototonic::onReloadThumbs() {
2956     if (thumbsViewer->isBusy || !initComplete) {
2957         thumbsViewer->abort();
2958         QTimer::singleShot(0, this, SLOT(onReloadThumbs()));
2959         return;
2960     }
2961 
2962     if (!Settings::isFileListLoaded) {
2963         if (Settings::currentDirectory.isEmpty()) {
2964             Settings::currentDirectory = getSelectedPath();
2965             if (Settings::currentDirectory.isEmpty()) {
2966                 return;
2967             }
2968         }
2969 
2970         QDir checkPath(Settings::currentDirectory);
2971         if (!checkPath.exists() || !checkPath.isReadable()) {
2972             MessageBox msgBox(this);
2973             msgBox.critical(tr("Error"), tr("Failed to open directory ") + Settings::currentDirectory);
2974             setStatus(tr("No directory selected"));
2975             return;
2976         }
2977 
2978         thumbsViewer->infoView->clear();
2979         thumbsViewer->imagePreview->clear();
2980         if (Settings::setWindowIcon && Settings::layoutMode == Phototonic::ThumbViewWidget) {
2981             setWindowIcon(defaultApplicationIcon);
2982         }
2983         pathLineEdit->setText(Settings::currentDirectory);
2984         addPathHistoryRecord(Settings::currentDirectory);
2985         if (currentHistoryIdx > 0) {
2986             goBackAction->setEnabled(true);
2987         }
2988     }
2989 
2990     if (Settings::layoutMode == ThumbViewWidget) {
2991         setThumbsViewerWindowTitle();
2992     }
2993 
2994     thumbsViewer->reLoad();
2995 }
2996 
setThumbsViewerWindowTitle()2997 void Phototonic::setThumbsViewerWindowTitle() {
2998 
2999     if (Settings::isFileListLoaded) {
3000         setWindowTitle(tr("Files List") + " - Phototonic");
3001     } else {
3002         setWindowTitle(Settings::currentDirectory + " - Phototonic");
3003     }
3004 }
3005 
renameDir()3006 void Phototonic::renameDir() {
3007     QModelIndexList selectedDirs = fileSystemTree->selectionModel()->selectedRows();
3008     QFileInfo dirInfo = QFileInfo(fileSystemTree->fileSystemModel->filePath(selectedDirs[0]));
3009 
3010     bool renameOk;
3011     QString title = tr("Rename") + " " + dirInfo.completeBaseName();
3012     QString newDirName = QInputDialog::getText(this, title,
3013                                                tr("New name:"), QLineEdit::Normal, dirInfo.completeBaseName(),
3014                                                &renameOk);
3015 
3016     if (!renameOk) {
3017         selectCurrentViewDir();
3018         return;
3019     }
3020 
3021     if (newDirName.isEmpty()) {
3022         MessageBox msgBox(this);
3023         msgBox.critical(tr("Error"), tr("Invalid name entered."));
3024         selectCurrentViewDir();
3025         return;
3026     }
3027 
3028     QFile dir(dirInfo.absoluteFilePath());
3029     QString newFullPathName = dirInfo.absolutePath() + QDir::separator() + newDirName;
3030     renameOk = dir.rename(newFullPathName);
3031     if (!renameOk) {
3032         MessageBox msgBox(this);
3033         msgBox.critical(tr("Error"), tr("Failed to rename directory."));
3034         selectCurrentViewDir();
3035         return;
3036     }
3037 
3038     if (Settings::currentDirectory == dirInfo.absoluteFilePath()) {
3039         fileSystemTree->setCurrentIndex(fileSystemTree->fileSystemModel->index(newFullPathName));
3040     } else {
3041         selectCurrentViewDir();
3042     }
3043 }
3044 
rename()3045 void Phototonic::rename() {
3046     if (QApplication::focusWidget() == fileSystemTree) {
3047         renameDir();
3048         return;
3049     }
3050 
3051     if (Settings::layoutMode == ImageViewWidget) {
3052         if (imageViewer->isNewImage()) {
3053             showNewImageWarning();
3054             return;
3055         }
3056 
3057         if (thumbsViewer->thumbsViewerModel->rowCount() > 0) {
3058             if (thumbsViewer->setCurrentIndexByName(imageViewer->viewerImageFullPath))
3059                 thumbsViewer->selectCurrentIndex();
3060         }
3061     }
3062 
3063     QString selectedImageFileName = thumbsViewer->getSingleSelectionFilename();
3064     if (selectedImageFileName.isEmpty()) {
3065         setStatus(tr("Invalid selection"));
3066         return;
3067     }
3068 
3069     if (Settings::slideShowActive) {
3070         toggleSlideShow();
3071     }
3072     imageViewer->setCursorHiding(false);
3073 
3074     QFile currentFileFullPath(selectedImageFileName);
3075     QFileInfo currentFileInfo(currentFileFullPath);
3076     int renameConfirmed;
3077 
3078     RenameDialog *renameDialog = new RenameDialog(this);
3079     renameDialog->setModal(true);
3080     renameDialog->setFileName(currentFileInfo.fileName());
3081     renameConfirmed = renameDialog->exec();
3082 
3083     QString newFileName = renameDialog->getFileName();
3084     delete (renameDialog);
3085 
3086     if (renameConfirmed && newFileName.isEmpty()) {
3087         MessageBox msgBox(this);
3088         msgBox.critical(tr("Error"), tr("No name entered."));
3089         renameConfirmed = 0;
3090     }
3091 
3092     if (renameConfirmed) {
3093         QString newFileNameFullPath = currentFileInfo.absolutePath() + QDir::separator() + newFileName;
3094         if (currentFileFullPath.rename(newFileNameFullPath)) {
3095             QModelIndexList indexesList = thumbsViewer->selectionModel()->selectedIndexes();
3096             thumbsViewer->thumbsViewerModel->item(indexesList.first().row())->setData(newFileNameFullPath,
3097                                                                                       thumbsViewer->FileNameRole);
3098             thumbsViewer->thumbsViewerModel->item(indexesList.first().row())->setData(newFileName, Qt::DisplayRole);
3099 
3100             imageViewer->setInfo(newFileName);
3101             imageViewer->viewerImageFullPath = newFileNameFullPath;
3102 
3103             if (Settings::filesList.contains(currentFileInfo.absoluteFilePath())) {
3104                 Settings::filesList.replace(Settings::filesList.indexOf(currentFileInfo.absoluteFilePath()),
3105                                             newFileNameFullPath);
3106             }
3107 
3108             if (Settings::layoutMode == ImageViewWidget) {
3109                 thumbsViewer->setImageViewerWindowTitle();
3110             }
3111         } else {
3112             MessageBox msgBox(this);
3113             msgBox.critical(tr("Error"), tr("Failed to rename image."));
3114         }
3115     }
3116 
3117     if (isFullScreen()) {
3118         imageViewer->setCursorHiding(true);
3119     }
3120 }
3121 
removeMetadata()3122 void Phototonic::removeMetadata() {
3123 
3124     QModelIndexList indexList = thumbsViewer->selectionModel()->selectedIndexes();
3125     QStringList fileList;
3126     copyCutThumbsCount = indexList.size();
3127 
3128     for (int thumb = 0; thumb < copyCutThumbsCount; ++thumb) {
3129         fileList.append(thumbsViewer->thumbsViewerModel->item(indexList[thumb].
3130                 row())->data(thumbsViewer->FileNameRole).toString());
3131     }
3132 
3133     if (fileList.isEmpty()) {
3134         setStatus(tr("Invalid selection"));
3135         return;
3136     }
3137 
3138     if (Settings::slideShowActive) {
3139         toggleSlideShow();
3140     }
3141 
3142     MessageBox msgBox(this);
3143     msgBox.setText(tr("Permanently remove all Exif metadata from selected images?"));
3144     msgBox.setWindowTitle(tr("Remove Metadata"));
3145     msgBox.setIcon(MessageBox::Warning);
3146     msgBox.setStandardButtons(MessageBox::Yes | MessageBox::Cancel);
3147     msgBox.setDefaultButton(MessageBox::Cancel);
3148     msgBox.setButtonText(MessageBox::Yes, tr("Remove Metadata"));
3149     msgBox.setButtonText(MessageBox::Cancel, tr("Cancel"));
3150     int ret = msgBox.exec();
3151 
3152     if (ret == MessageBox::Yes) {
3153         for (int file = 0; file < fileList.size(); ++file) {
3154             Exiv2::Image::AutoPtr image;
3155             try {
3156                 image = Exiv2::ImageFactory::open(fileList[file].toStdString());
3157                 image->clearMetadata();
3158                 image->writeMetadata();
3159                 metadataCache->removeImage(fileList[file]);
3160             }
3161             catch (Exiv2::Error &error) {
3162                 msgBox.critical(tr("Error"), tr("Failed to remove Exif metadata."));
3163                 return;
3164             }
3165         }
3166 
3167         QItemSelection dummy;
3168         thumbsViewer->onSelectionChanged(dummy);
3169         QString state = QString(tr("Metadata removed from selected images"));
3170         setStatus(state);
3171     }
3172 }
3173 
deleteDirectory(bool trash)3174 void Phototonic::deleteDirectory(bool trash) {
3175     bool removeDirectoryOk;
3176     QModelIndexList selectedDirs = fileSystemTree->selectionModel()->selectedRows();
3177     QString deletePath = fileSystemTree->fileSystemModel->filePath(selectedDirs[0]);
3178     QModelIndex idxAbove = fileSystemTree->indexAbove(selectedDirs[0]);
3179     QFileInfo dirInfo = QFileInfo(deletePath);
3180     QString question = (trash ? tr("Move directory %1 to the trash?") : tr(
3181             "Permanently delete the directory %1 and all of its contents?")).arg(
3182             dirInfo.completeBaseName());
3183 
3184     MessageBox msgBox(this);
3185     msgBox.setText(question);
3186     msgBox.setWindowTitle(tr("Delete directory"));
3187     msgBox.setIcon(MessageBox::Warning);
3188     msgBox.setStandardButtons(MessageBox::Yes | MessageBox::Cancel);
3189     msgBox.setDefaultButton(MessageBox::Cancel);
3190     msgBox.setButtonText(MessageBox::Yes, trash ? tr("OK") : tr("Delete Directory"));
3191     msgBox.setButtonText(MessageBox::Cancel, tr("Cancel"));
3192     int ret = msgBox.exec();
3193 
3194     QString trashError;
3195     if (ret == MessageBox::Yes) {
3196         if (trash) {
3197             removeDirectoryOk = Trash::moveToTrash(deletePath, trashError) == Trash::Success;
3198         } else {
3199             removeDirectoryOk = removeDirectoryOperation(deletePath);
3200         }
3201     } else {
3202         selectCurrentViewDir();
3203         return;
3204     }
3205 
3206     if (!removeDirectoryOk) {
3207         msgBox.critical(tr("Error"), trash ? tr("Failed to move directory to the trash: %1").arg(trashError)
3208                                            : tr("Failed to delete directory."));
3209         selectCurrentViewDir();
3210         return;
3211     }
3212 
3213     QString state = QString(tr("Removed \"%1\"").arg(deletePath));
3214     setStatus(state);
3215 
3216     if (Settings::currentDirectory == deletePath) {
3217         if (idxAbove.isValid()) {
3218             fileSystemTree->setCurrentIndex(idxAbove);
3219         }
3220     } else {
3221         selectCurrentViewDir();
3222     }
3223 }
3224 
createSubDirectory()3225 void Phototonic::createSubDirectory() {
3226     QModelIndexList selectedDirs = fileSystemTree->selectionModel()->selectedRows();
3227     QFileInfo dirInfo = QFileInfo(fileSystemTree->fileSystemModel->filePath(selectedDirs[0]));
3228 
3229     bool ok;
3230     QString newDirName = QInputDialog::getText(this, tr("New Sub directory"),
3231                                                tr("New directory name:"), QLineEdit::Normal, "", &ok);
3232 
3233     if (!ok) {
3234         selectCurrentViewDir();
3235         return;
3236     }
3237 
3238     if (newDirName.isEmpty()) {
3239         MessageBox msgBox(this);
3240         msgBox.critical(tr("Error"), tr("Invalid name entered."));
3241         selectCurrentViewDir();
3242         return;
3243     }
3244 
3245     QDir dir(dirInfo.absoluteFilePath());
3246     ok = dir.mkdir(dirInfo.absoluteFilePath() + QDir::separator() + newDirName);
3247 
3248     if (!ok) {
3249         MessageBox msgBox(this);
3250         msgBox.critical(tr("Error"), tr("Failed to create new directory."));
3251         selectCurrentViewDir();
3252         return;
3253     }
3254 
3255     setStatus(tr("Created %1").arg(newDirName));
3256     fileSystemTree->expand(selectedDirs[0]);
3257 }
3258 
getSelectedPath()3259 QString Phototonic::getSelectedPath() {
3260     QModelIndexList selectedDirs = fileSystemTree->selectionModel()->selectedRows();
3261     if (selectedDirs.size() && selectedDirs[0].isValid()) {
3262         QFileInfo dirInfo = QFileInfo(fileSystemTree->fileSystemModel->filePath(selectedDirs[0]));
3263         return dirInfo.absoluteFilePath();
3264     } else
3265         return "";
3266 }
3267 
wheelEvent(QWheelEvent * event)3268 void Phototonic::wheelEvent(QWheelEvent *event) {
3269     if (Settings::layoutMode == ImageViewWidget) {
3270         if (event->modifiers() == Qt::ControlModifier) {
3271             if (event->delta() < 0) {
3272                 zoomOut();
3273             } else {
3274                 zoomIn();
3275             }
3276         } else if (nextImageAction->isEnabled()) {
3277             if (event->delta() < 0) {
3278                 loadNextImage();
3279             } else {
3280                 loadPreviousImage();
3281             }
3282         }
3283         event->accept();
3284     } else if (event->modifiers() == Qt::ControlModifier && QApplication::focusWidget() == thumbsViewer) {
3285         if (event->delta() < 0) {
3286             thumbsZoomOut();
3287         } else {
3288             thumbsZoomIn();
3289         }
3290     }
3291 }
3292 
showNewImageWarning()3293 void Phototonic::showNewImageWarning() {
3294     MessageBox msgBox(this);
3295     msgBox.warning(tr("Warning"), tr("Cannot perform action with temporary image."));
3296 }
3297 
removeDirectoryOperation(QString dirToDelete)3298 bool Phototonic::removeDirectoryOperation(QString dirToDelete) {
3299     bool removeDirOk;
3300     QDir dir(dirToDelete);
3301 
3302     Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden |
3303                                                 QDir::AllDirs | QDir::Files, QDir::DirsFirst)) {
3304             if (info.isDir()) {
3305                 removeDirOk = removeDirectoryOperation(info.absoluteFilePath());
3306             } else {
3307                 removeDirOk = QFile::remove(info.absoluteFilePath());
3308             }
3309 
3310             if (!removeDirOk) {
3311                 return removeDirOk;
3312             }
3313         }
3314     removeDirOk = dir.rmdir(dirToDelete);
3315     return removeDirOk;
3316 }
3317 
cleanupCropDialog()3318 void Phototonic::cleanupCropDialog() {
3319     setInterfaceEnabled(true);
3320 }
3321 
cleanupResizeDialog()3322 void Phototonic::cleanupResizeDialog() {
3323     delete resizeDialog;
3324     resizeDialog = 0;
3325     setInterfaceEnabled(true);
3326 }
3327 
cleanupColorsDialog()3328 void Phototonic::cleanupColorsDialog() {
3329     Settings::colorsActive = false;
3330     setInterfaceEnabled(true);
3331 }
3332 
setInterfaceEnabled(bool enable)3333 void Phototonic::setInterfaceEnabled(bool enable) {
3334     // actions
3335     colorsAction->setEnabled(enable);
3336     renameAction->setEnabled(enable);
3337     removeMetadataAction->setEnabled(enable);
3338     cropAction->setEnabled(enable);
3339     resizeAction->setEnabled(enable);
3340     CloseImageAction->setEnabled(enable);
3341     nextImageAction->setEnabled(enable);
3342     prevImageAction->setEnabled(enable);
3343     firstImageAction->setEnabled(enable);
3344     lastImageAction->setEnabled(enable);
3345     randomImageAction->setEnabled(enable);
3346     slideShowAction->setEnabled(enable);
3347     copyToAction->setEnabled(enable);
3348     moveToAction->setEnabled(enable);
3349     deleteAction->setEnabled(enable);
3350     deletePermanentlyAction->setEnabled(enable);
3351     settingsAction->setEnabled(enable);
3352     viewImageAction->setEnabled(enable);
3353 
3354     // other
3355     thumbsViewer->setEnabled(enable);
3356     fileSystemTree->setEnabled(enable);
3357     bookmarks->setEnabled(enable);
3358     thumbsViewer->imageTags->setEnabled(enable);
3359     menuBar()->setEnabled(enable);
3360     editToolBar->setEnabled(enable);
3361     goToolBar->setEnabled(enable);
3362     viewToolBar->setEnabled(enable);
3363     interfaceDisabled = !enable;
3364 
3365     if (enable) {
3366         if (isFullScreen()) {
3367             imageViewer->setCursorHiding(true);
3368         }
3369     } else {
3370         imageViewer->setCursorHiding(false);
3371     }
3372 }
3373 
addNewBookmark()3374 void Phototonic::addNewBookmark() {
3375     addBookmark(getSelectedPath());
3376 }
3377 
addBookmark(QString path)3378 void Phototonic::addBookmark(QString path) {
3379     Settings::bookmarkPaths.insert(path);
3380     bookmarks->reloadBookmarks();
3381 }
3382