1 /*******************************************************************************
2 **
3 ** Photivo
4 **
5 ** Copyright (C) 2011-2013 Bernd Schoeler <brjohn@brother-john.net>
6 ** Copyright (C) 2011-2013 Michael Munzert <mail@mm-log.com>
7 ** Copyright (C) 2013 Alexander Tzyganenko <tz@fast-report.com>
8 **
9 ** This file is part of Photivo.
10 **
11 ** Photivo is free software: you can redistribute it and/or modify
12 ** it under the terms of the GNU General Public License version 3
13 ** as published by the Free Software Foundation.
14 **
15 ** Photivo is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ** GNU General Public License for more details.
19 **
20 ** You should have received a copy of the GNU General Public License
21 ** along with Photivo.  If not, see <http://www.gnu.org/licenses/>.
22 **
23 *******************************************************************************/
24 
25 #include "ptFileMgrWindow.h"
26 #include "ptGraphicsSceneEmitter.h"
27 #include "ptRowGridThumbnailLayouter.h"
28 #include "ptColumnGridThumbnailLayouter.h"
29 #include "../ptDefines.h"
30 #include "../ptSettings.h"
31 #include "../ptTheme.h"
32 #include "../ptImageHelper.h"
33 #include "../ptMessageBox.h"
34 #include "../ptImage8.h"
35 #include <QVBoxLayout>
36 #include <QFontMetrics>
37 #include <QList>
38 #include <QDir>
39 #include <QMenu>
40 #include <QAction>
41 #include <QLabel>
42 #include <QScrollBar>
43 #include <QFileDialog>
44 #include <cassert>
45 
46 extern void CB_MenuFileOpen(const short HaveFile);
47 
48 extern ptSettings*  Settings;
49 extern ptTheme*     Theme;
50 extern QString      ImageFileToOpen;
51 extern short        InStartup;
52 extern QString      SaveBitmapPattern;
53 
54 //------------------------------------------------------------------------------
55 /*!
56   Creates a ptFileMgrWindow instance.
57   \param parent
58     The file manager’s parent window.
59 */
ptFileMgrWindow(QWidget * parent)60 ptFileMgrWindow::ptFileMgrWindow(QWidget* parent)
61 : QWidget(parent),
62   FDataModel(new ptFileMgrDM(this)),  // setup data model first because several UI elements need it
63   FIsFirstShow(true),
64   FThumbCount(-1),
65   FThumbListIdx(0)
66 {
67   // Main UI init
68   setupUi(this);
69   setMouseTracking(true);
70 
71   ptGraphicsSceneEmitter::ConnectThumbnailAction(
72       this, SLOT(execThumbnailAction(ptThumbnailAction,QString)));
73   ptGraphicsSceneEmitter::ConnectFocusChanged(this, SLOT(thumbFocusChanged()));
74 
75   //-------------------------------------
76 
77   // sidebar
78   FMSidebar->setVisible(Settings->GetInt("FileMgrShowSidebar"));
79 
80   // Folder list
81 #ifdef Q_OS_WIN
82   DirListLabel->setText(tr("Folders"));
83 #else
84   DirListLabel->setText(tr("Directories"));
85 #endif
86   m_DirList->setModel(FDataModel->dirModel());
87   connect(m_DirList, SIGNAL(activated(QModelIndex)), this, SLOT(changeListDir(QModelIndex)));
88 
89 
90 #ifdef Q_OS_WIN
91   QString BookmarkTooltip = tr("Bookmark current folder (Ctrl+B)");
92 #else
93   QString BookmarkTooltip = tr("Bookmark current directory (Ctrl+B)");
94 #endif
95 
96   // bookmark list in sidebar
97   FTagList = new ptTagList(this);
98   FTagList->setModel(FDataModel->tagModel());
99   connect(FDataModel->tagModel(), SIGNAL(itemChanged(QStandardItem*)),
100           this, SLOT(bookmarkDataChanged(QStandardItem*)));
101   m_TagPaneLayout->addWidget(FTagList);
102   connect(FTagList, SIGNAL(activated(QModelIndex)), this, SLOT(changeToBookmark(QModelIndex)));
103   m_AddBookmarkButton->setToolTip(BookmarkTooltip);
104   connect(m_AddBookmarkButton, SIGNAL(clicked()), this, SLOT(bookmarkCurrentDir()));
105 
106   //-------------------------------------
107 
108   //bookmark menu
109   FTagMenu = new QMenu(this);
110   FTagMenu->hide();
111   FTagMenu->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
112   FTagMenu->setObjectName("FMTagMenu");
113 
114   QLabel* label = new QLabel("<b>" + tr("Bookmarks") + "</b>", FTagMenu);
115   QToolButton* addButton = new QToolButton(FTagMenu);
116   addButton->setIcon(QIcon(Theme->IconAddBookmark));
117   addButton->setToolTip(BookmarkTooltip);
118   connect(addButton, SIGNAL(clicked()), this, SLOT(bookmarkCurrentDir()));
119 
120   QHBoxLayout* headerLayout = new QHBoxLayout();
121   headerLayout->setContentsMargins(0,0,0,0);
122   headerLayout->addWidget(addButton);
123   headerLayout->addWidget(label);
124 
125   FTagMenuList = new ptTagList(FTagMenu);
126   FTagMenuList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
127   FTagMenuList->setModel(FDataModel->tagModel());
128   connect(FTagMenuList, SIGNAL(activated(QModelIndex)),
129           this, SLOT(changeToBookmarkFromMenu(QModelIndex)));
130 
131   QVBoxLayout* layout = new QVBoxLayout(FTagMenu);
132   layout->addLayout(headerLayout);
133   layout->addWidget(FTagMenuList);
134   FTagMenu->setLayout(layout);
135 
136   //-------------------------------------
137 
138   // Setup the graphics view/scene
139   FFilesScene = new QGraphicsScene(m_FilesView);
140   FFilesScene->setStickyFocus(true);
141   FFilesScene->installEventFilter(this);
142   m_FilesView->setOptimizationFlags(QGraphicsView::DontSavePainterState);
143   m_FilesView->installEventFilter(this);
144   m_FilesView->verticalScrollBar()->installEventFilter(this);
145   m_FilesView->horizontalScrollBar()->installEventFilter(this);
146   m_FilesView->setScene(FFilesScene);
147   FDataModel->connectThumbGen(this, SLOT(receiveThumb(uint,TThumbPtr)));
148   setLayouter((ptThumbnailLayout)Settings->GetInt("FileMgrThumbLayoutType"));
149 
150   FPathBar = new ptPathBar(m_PathContainer);
151   FPathBar->setObjectName("FMPathBar");
152   connect(FPathBar, SIGNAL(changedPath(QString)), this, SLOT(changeDir(QString)));
153   m_PathLayout->addWidget(FPathBar);
154   m_Progressbar->hide();
155 
156   //-------------------------------------
157 
158   // construct the image view
159   FImageView = new ptImageView(FMImageViewPane);
160   FImageView->installEventFilter(this);
161   FMImageViewPane->setVisible((bool)Settings->GetInt("FileMgrShowImageView"));
162 
163   //-------------------------------------
164 
165   // Filemgr main splitter layout
166   if (Settings->m_IniSettings->contains("FileMgrMainSplitter")) {
167     FMMainSplitter->restoreState(
168         Settings->m_IniSettings->value("FileMgrMainSplitter").toByteArray());
169   } else {
170     FMMainSplitter->setSizes(QList<int>() << 150 << 500 << 500);
171   }
172   FMMainSplitter->setStretchFactor(1,1);
173 
174   // sidebar splitter layout
175   if (Settings->m_IniSettings->contains("FileMgrSidebarSplitter")) {
176     FMSidebarSplitter->restoreState(
177         Settings->m_IniSettings->value("FileMgrSidebarSplitter").toByteArray());
178   } else {
179     FMSidebarSplitter->setSizes(QList<int>() << 400 << 400);
180   }
181   FMSidebarSplitter->setStretchFactor(1,1);
182 
183   //-------------------------------------
184 
185   constructContextMenu();
186 }
187 
188 //------------------------------------------------------------------------------
189 /*! Destroys a \c ptFileMgrWindow instance. */
~ptFileMgrWindow()190 ptFileMgrWindow::~ptFileMgrWindow() {
191   Settings->SetValue("LastFileMgrLocation", FDataModel->currentDir());
192   Settings->m_IniSettings->
193       setValue("FileMgrMainSplitter", FMMainSplitter->saveState());
194   Settings->m_IniSettings->
195       setValue("FileMgrSidebarSplitter", FMSidebarSplitter->saveState());
196 
197   DelAndNull(FLayouter);
198   DelAndNull(FPathBar);
199 
200   // Make sure to destroy all thumbnail related things before the singletons!
201   ptGraphicsSceneEmitter::DestroyInstance();
202 
203   // context menu actions
204   DelAndNull(FVerticalThumbsAct);
205   DelAndNull(FHorizontalThumbsAct);
206   DelAndNull(FDetailedThumbsAct);
207   DelAndNull(FDirThumbsAct);
208   DelAndNull(FThumbLayoutGroupAct);
209   DelAndNull(FToggleSidebarAct);
210   DelAndNull(FToggleImageViewAct);
211   DelAndNull(FCloseFileMgrAct);
212   DelAndNull(FSaveThumbAct);
213 
214   DelAndNull(FImageView);
215 }
216 
217 //------------------------------------------------------------------------------
setLayouter(const ptThumbnailLayout layout)218 void ptFileMgrWindow::setLayouter(const ptThumbnailLayout layout) {
219   bool RestartThumbnailer = false;
220   if (!InStartup) {
221     if ((ptThumbnailLayout)Settings->GetInt("FileMgrThumbLayoutType") == layout)
222       return;
223     RestartThumbnailer = FDataModel->thumbGenRunning();
224     FDataModel->abortThumbGen();
225     DelAndNull(FLayouter);
226     Settings->SetValue("FileMgrThumbLayoutType", layout);
227   }
228 
229   switch (layout) {
230     case tlVerticalByRow:
231       m_FilesView->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
232       FLayouter = new ptRowGridThumbnailLayouter(m_FilesView);
233       break;
234 
235     case tlHorizontalByColumn:
236       m_FilesView->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
237       FLayouter = new ptColumnGridThumbnailLayouter(m_FilesView);
238       break;
239 
240     case tlDetailedList:
241       m_FilesView->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
242 //TODO: re-enable      m_Layouter = new ptDetailedThumbnailLayouter(m_FilesView);
243       break;
244 
245     default:
246       assert(!"Unhandled thumbnail layouter type!");
247       break;
248   }
249 
250   if (RestartThumbnailer) {
251     this->displayThumbnails();
252   } else {
253     this->layoutAll();
254   }
255 }
256 
257 //------------------------------------------------------------------------------
changeListDir(const QModelIndex & index)258 void ptFileMgrWindow::changeListDir(const QModelIndex& index) {
259   FDataModel->dirModel()->ChangeDir(index);
260   displayThumbnails(FDataModel->dirModel()->absolutePath(), FDataModel->dirModel()->pathType());
261 }
262 
263 //------------------------------------------------------------------------------
changeToBookmark(const QModelIndex & index)264 void ptFileMgrWindow::changeToBookmark(const QModelIndex& index) {
265   changeDir(FDataModel->tagModel()->path(index));
266 }
267 
268 //------------------------------------------------------------------------------
changeToBookmarkFromMenu(const QModelIndex & index)269 void ptFileMgrWindow::changeToBookmarkFromMenu(const QModelIndex& index) {
270   FTagMenu->hide();
271   changeToBookmark(index);
272 }
273 
274 //------------------------------------------------------------------------------
changeDir(const QString & path)275 void ptFileMgrWindow::changeDir(const QString& path) {
276   FDataModel->dirModel()->ChangeAbsoluteDir(path);
277   displayThumbnails(path, FDataModel->dirModel()->pathType());
278 }
279 
280 //------------------------------------------------------------------------------
281 /*!
282   Creates or refreshes the thumbnail display.
283   \param path
284     The path to the desired directory. Must be an absolute path. \c path can be
285     empty. Then it defaults to the currently set thumbnail directory.
286   \param fsoType
287     Only relevant on Windows to indicate if the folder is “My Computer”. If that is
288     the case, set to \c fsoRoot. Then \c path will be ignored and no thumbnails
289     displayed. Do \b not use as a general flag to prevent thumbnail display!
290 */
291 #ifdef Q_OS_UNIX
292 // Linux does not need parameter fsoType. Disable compiler warning.
293 #pragma GCC diagnostic push
294 #pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
295 #endif
displayThumbnails(QString path,ptFSOType fsoType)296 void ptFileMgrWindow::displayThumbnails(QString path /*= ""*/, ptFSOType fsoType /*= fsoDir*/) {
297   if (path.isEmpty()) {
298     path = FDataModel->dirModel()->absolutePath();
299     fsoType = FDataModel->dirModel()->pathType();
300   }
301 
302 #ifdef Q_OS_WIN
303   if (fsoType == fsoRoot) {
304     // We are in “My Computer”
305     path = MyComputerIdString;
306   }
307 #endif
308 
309   m_FilesView->horizontalScrollBar()->setValue(0);
310   m_FilesView->verticalScrollBar()->setValue(0);
311   FThumbListIdx = 0;
312   FThumbCount = FDataModel->setThumDir(path);
313   FThumbsReceived = 0;
314   FPathBar->setPath(path);
315 
316   if (FThumbCount == 0) {
317     // setting scene to null dimensions disappears unneeded scrollbars
318     FFilesScene->setSceneRect(0,0,0,0);
319 
320   } else {
321     FLayouter->LazyInit(FThumbCount);
322     this->initProgressbar();
323     FDataModel->populateThumbs(FFilesScene);  // non-blocking, returns almost immediately
324     this->layoutAll();
325   }
326 }
327 #ifdef Q_OS_UNIX
328 #pragma GCC diagnostic pop
329 #endif
330 
331 //------------------------------------------------------------------------------
332 // Slot to receive thumbnail images from the generator and dispatch them to their thumb group.
333 // Also updates the progress bar.
receiveThumb(uint AReceiverId,TThumbPtr AImage)334 void ptFileMgrWindow::receiveThumb(uint AReceiverId, TThumbPtr AImage) {
335   for (ptGraphicsThumbGroup* hThumbGroup: *FDataModel->thumbGroupList()) {
336     if (hThumbGroup->id() == AReceiverId) {
337       hThumbGroup->addImage(AImage);
338       ++FThumbsReceived;
339       this->updateProgressbar();
340       break;
341     }
342   }
343 }
344 
345 //------------------------------------------------------------------------------
346 // Sets progress bar range to current FThumbCount, shows the progressbar and hides the path bar.
initProgressbar()347 void ptFileMgrWindow::initProgressbar() {
348   m_Progressbar->setValue(0);
349   m_Progressbar->setMaximum(FThumbCount);
350   m_Progressbar->show();
351   m_PathContainer->hide();
352 }
353 
354 //------------------------------------------------------------------------------
355 // Updates the progress bar’s value. Switches display back to path bar when last thumbnail is reached.
updateProgressbar()356 void ptFileMgrWindow::updateProgressbar() {
357   if (FThumbsReceived < FThumbCount) {
358     m_Progressbar->setValue(FThumbsReceived);
359   } else {
360     m_Progressbar->hide();
361     m_PathContainer->show();
362 
363     FFilesScene->setFocus();
364   }
365 }
366 
367 //------------------------------------------------------------------------------
layoutAll()368 void ptFileMgrWindow::layoutAll() {
369   FLayouter->Init(FDataModel->thumbGroupList()->count(), m_FilesView->font());
370   QListIterator<ptGraphicsThumbGroup*> i(*FDataModel->thumbGroupList());
371 
372   while (i.hasNext()) {
373     FLayouter->Layout(i.next());
374   }
375 }
376 
377 //------------------------------------------------------------------------------
showEvent(QShowEvent * event)378 void ptFileMgrWindow::showEvent(QShowEvent* event) {
379   if (FIsFirstShow) {
380     // Execute once when the file manager is opened for the first time.
381 
382     // Theme and layout stuff (wouldn’t work in constructor)
383     updateTheme();
384 
385     // set initally selected directory
386     QString lastDir = Settings->GetString("LastFileMgrLocation");
387     ptFSOType lastDirType = fsoDir;
388 #ifdef Q_OS_WIN
389     if (lastDir == MyComputerIdString || lastDir.isEmpty()) {
390       lastDir = MyComputerIdString;
391       lastDirType = fsoRoot;
392     } else {
393 #endif
394       if (lastDir.isEmpty() || !QDir(lastDir).exists()) {
395         // dir from ini is broken, default to homePath
396         lastDir = QDir::homePath();
397       }
398 #ifdef Q_OS_WIN
399     }
400 #endif
401     FDataModel->dirModel()->ChangeAbsoluteDir(lastDir);
402     FDataModel->setCurrentDir(lastDir);
403     FPathBar->setPath(lastDir);
404 
405     // First call base class showEvent, then start thumbnail loading to ensure
406     // the file manager is visible before ressource heavy actions begin.
407     QWidget::showEvent(event);
408     if (!InStartup) {
409       this->displayThumbnails(lastDir, lastDirType);
410     }
411 
412     FIsFirstShow = false;
413     return;
414   }
415 
416   focusThumbnail(FDataModel->focusedThumb());
417   if (!event->spontaneous()) {
418     QWidget::showEvent(event);
419     // Thumbnails are cleared to free memory when the fm window is closed,
420     // i.e. we need to refresh the display when opening it again.
421 // temporarily disabled. should become a user option
422 //    displayThumbnails();
423   }
424 }
425 
426 //------------------------------------------------------------------------------
eventFilter(QObject * obj,QEvent * event)427 bool ptFileMgrWindow::eventFilter(QObject* obj, QEvent* event) {
428   if (obj == m_FilesView && event->type() == QEvent::Resize) {
429     // Resize event: Rearrange thumbnails when size of viewport changes
430     layoutAll();
431     return false;   // handle event further
432   }
433 
434 
435   else if ((obj == qobject_cast<QObject*>(m_FilesView->verticalScrollBar()) ||
436             obj == qobject_cast<QObject*>(m_FilesView->horizontalScrollBar())) &&
437             event->type() == QEvent::Wheel)
438   { // Wheel event
439     int dir = ((QWheelEvent*)event)->delta() > 0 ? -1 : 1;
440     if (m_FilesView->verticalScrollBar()->isVisible()) {
441       m_FilesView->verticalScrollBar()->setValue(
442             m_FilesView->verticalScrollBar()->value() + FLayouter->Step()*dir);
443 
444     } else if (m_FilesView->horizontalScrollBar()->isVisible()) {
445       m_FilesView->horizontalScrollBar()->setValue(
446             m_FilesView->horizontalScrollBar()->value() + FLayouter->Step()*dir);
447     }
448     return true;    // prevent further event handling
449   }
450 
451 
452   else if (obj == FFilesScene && (event->type() == QEvent::GraphicsSceneDragEnter ||
453                                    event->type() == QEvent::GraphicsSceneDrop))
454   { // Make sure drag&drop events are passed on to MainWindow
455     event->ignore();
456     return true;
457   }
458 
459 
460   else if (obj == FFilesScene && event->type() == QEvent::KeyPress) {
461     // Keyboard navigation in thumbnail list
462     int newIdx = FLayouter->MoveIndex(FDataModel->focusedThumb(), (QKeyEvent*)event);
463     if (newIdx >= 0) {
464       focusThumbnail(newIdx);
465       return true;
466     }
467   }
468 
469 
470   // unhandled events fall through to here
471   // make sure parent event filters are executed
472   return QWidget::eventFilter(obj, event);
473 }
474 
475 //------------------------------------------------------------------------------
focusThumbnail(int index)476 void ptFileMgrWindow::focusThumbnail(int index) {
477   if (index >= 0) {
478     // focus new thumb
479     ptGraphicsThumbGroup* thumb = FDataModel->moveFocus(index);
480     FFilesScene->setFocusItem(thumb);
481     m_FilesView->ensureVisible(thumb, 0, 0);
482     m_FilesView->setFocus();
483 
484     // if a different thumb is focused update ImageView
485     if (thumb->fsoType() == fsoFile) {
486       this->loadForImageView(thumb->fullPath());
487     }
488 
489   } else {
490     FFilesScene->clearFocus();
491   }
492 }
493 
494 //------------------------------------------------------------------------------
loadForImageView(const QString & AFilePath)495 void ptFileMgrWindow::loadForImageView(const QString& AFilePath) {
496   if (FImageView->isVisible()) {
497     FImageView->showImage(AFilePath);
498   }
499 }
500 
501 //------------------------------------------------------------------------------
thumbFocusChanged()502 void ptFileMgrWindow::thumbFocusChanged() {
503   focusThumbnail(FDataModel->focusedThumb(FFilesScene->focusItem()));
504 }
505 
506 //------------------------------------------------------------------------------
saveThumbnail()507 void ptFileMgrWindow::saveThumbnail() {
508   int hThumbIdx = FDataModel->focusedThumb();
509 
510   if (hThumbIdx > -1 && (hThumbIdx < FDataModel->thumbGroupList()->count())) {
511     QString   hFileName = FDataModel->thumbGroupList()->at(hThumbIdx)->fullPath();
512     auto      hImage = FDataModel->getThumb(hFileName, Settings->GetInt("FileMgrThumbSaveSize"));
513 
514     if (!hImage) {
515       ptMessageBox::warning(0, QObject::tr("Error"), QObject::tr("Thumbnail could not be saved."));
516       return;
517     }
518 
519     QFileInfo hPathInfo(hFileName);
520     QString   hSuggestedFileName = hPathInfo.dir().path() + "/" + hPathInfo.completeBaseName() + "-thumb.jpg";
521 
522     auto hOutputName = QFileDialog::getSaveFileName(nullptr,
523                                                     QObject::tr("Save File"),
524                                                     hSuggestedFileName,
525                                                     SaveBitmapPattern);
526 
527     if (hOutputName.isEmpty()) {
528       return; // Operation cancelled.
529     }
530 
531     if (!(hImage->DumpImage(hOutputName.toLocal8Bit().data(), true))) {
532       ptMessageBox::warning(0, QObject::tr("Error"), QObject::tr("Thumbnail could not be saved."));
533     }
534 
535     if (!ptImageHelper::TransferExif(hFileName, hOutputName)) {
536       ptMessageBox::warning(0, QObject::tr("Exif error"), QObject::tr("Exif data could not be written."));
537     }
538   }
539 }
540 
541 //------------------------------------------------------------------------------
execThumbnailAction(const ptThumbnailAction action,const QString location)542 void ptFileMgrWindow::execThumbnailAction(const ptThumbnailAction action, const QString location) {
543   if (action == tnaLoadImage) {
544     closeWindow();
545     ImageFileToOpen = location;
546     CB_MenuFileOpen(1);
547 
548   } else if (action == tnaChangeDir) {
549     FDataModel->dirModel()->ChangeAbsoluteDir(location);
550     displayThumbnails(location, FDataModel->dirModel()->pathType());
551 
552   } else if (action == tnaViewImage) {
553     this->loadForImageView(location);
554   }
555 }
556 
557 //------------------------------------------------------------------------------
558 /*! Updates the file manager’s visual appearance. Call this once every time Photivo’s theme changes. */
updateTheme()559 void ptFileMgrWindow::updateTheme() {
560   // Update file manager window appearance
561   setStyle(Theme->style());
562   setStyleSheet(Theme->stylesheet());
563 
564   // Thumbgroups don’t need to be updated because Photivo theme can only be changed
565   // while fm is closed = thumbnail display cleared
566 }
567 
568 //------------------------------------------------------------------------------
closeWindow()569 void ptFileMgrWindow::closeWindow() {
570   // File manager can’t close its window itself. That’s handled by the main window.
571   // Signal tells main window to perform necessary closing actions.
572   // FM internal cleanup is done in ptFileMgrWindow::hideEvent().
573   emit fileMgrWindowClosed();
574 }
575 
576 //------------------------------------------------------------------------------
hideEvent(QHideEvent *)577 void ptFileMgrWindow::hideEvent(QHideEvent* /*event*/) {
578   // temporarily disabled. should become a user-option
579 //  if (!event->spontaneous()) {
580 //    event->accept();
581 //    // free memory occupied by thumbnails and thumb cache
582 //    // clear() includes stopping thumbnail generation
583 //    FDataModel->clear();
584 //    FImageView->clear();
585 //    this->clearScene();
586 //  } else {
587 //    event->ignore();
588 //  }
589 }
590 
591 //------------------------------------------------------------------------------
clearScene()592 void ptFileMgrWindow::clearScene() {
593   FDataModel->thumbGroupList()->clear();
594   FFilesScene->clear();
595 }
596 
597 //------------------------------------------------------------------------------
keyPressEvent(QKeyEvent * event)598 void ptFileMgrWindow::keyPressEvent(QKeyEvent* event) {
599   // Esc: close file manager
600   if (event->key() == Qt::Key_Escape && event->modifiers() == Qt::NoModifier) {
601     closeWindow();
602   }
603   // Ctrl+M: close file manager
604   else if (event->key() == Qt::Key_M && event->modifiers() == Qt::ControlModifier) {
605     closeWindow();
606   }
607   // F5: refresh thumbnails
608   else if (event->key() == Qt::Key_F5 && event->modifiers() == Qt::NoModifier) {
609     displayThumbnails();
610   }
611   // Shift+F5: clear cache and refresh thumbnails
612   else if (event->key() == Qt::Key_F5 && event->modifiers() == Qt::ShiftModifier) {
613     FDataModel->clear();
614     displayThumbnails();
615   }
616   // Ctrl+B: bookmark current folder
617   else if (event->key() == Qt::Key_B && event->modifiers() == Qt::ControlModifier) {
618     bookmarkCurrentDir();
619   }
620   // F11: toggles fullscreen (handled by main window)
621   else if (event->key() == Qt::Key_F11 && event->modifiers() == Qt::NoModifier) {
622     event->ignore();
623   }
624   // Alt+1: vertical thumbnail view
625   else if (event->key() == Qt::Key_1 && event->modifiers() == Qt::AltModifier) {
626     setLayouter(tlVerticalByRow);
627   }
628   // Alt+2: horizontal thumbnail view
629   else if (event->key() == Qt::Key_2 && event->modifiers() == Qt::AltModifier) {
630     setLayouter(tlHorizontalByColumn);
631   }
632   // Alt+3: detailed thumbnail view
633 //TODO: re-enable  else if (event->key() == Qt::Key_3 && event->modifiers() == Qt::AltModifier) {
634 //    setLayouter(tlDetailedList);
635 //  }
636   // F3: toggles: ImageView
637   else if (event->key() == Qt::Key_F3 && event->modifiers() == Qt::NoModifier) {
638     toggleImageView();
639   }
640   // F4: toggles sidebar
641   else if (event->key() == Qt::Key_F4 && event->modifiers() == Qt::NoModifier) {
642     toggleSidebar();
643   }
644 
645   else if (event->modifiers() == Qt::NoModifier) {
646     // Keyboard actions for image viewer
647     switch (event->key()) {
648       case Qt::Key_1: FImageView->zoomIn();  break;
649       case Qt::Key_2: FImageView->zoom100(); break;
650       case Qt::Key_3: FImageView->zoomOut(); break;
651       case Qt::Key_4: FImageView->zoomFit(); break;
652       default: break;
653     }
654   }
655 }
656 
657 //------------------------------------------------------------------------------
constructContextMenu()658 void ptFileMgrWindow::constructContextMenu() {
659   // Actions for thumbnail view submenu
660   FVerticalThumbsAct = new QAction(tr("&Vertical") + "\t" + tr("Alt+1"), this);
661   FVerticalThumbsAct->setCheckable(true);
662   connect(FVerticalThumbsAct, SIGNAL(triggered()), this, SLOT(verticalThumbs()));
663 
664   FHorizontalThumbsAct = new QAction(tr("&Horizontal") + "\t" + tr("Alt+2"), this);
665   FHorizontalThumbsAct->setCheckable(true);
666   connect(FHorizontalThumbsAct, SIGNAL(triggered()), this, SLOT(horizontalThumbs()));
667 
668   FDetailedThumbsAct = new QAction(tr("&Details") + "\t" + tr("Alt+3"), this);
669   FDetailedThumbsAct->setCheckable(true);
670   connect(FDetailedThumbsAct, SIGNAL(triggered()), this, SLOT(detailedThumbs()));
671 
672 #ifdef Q_OS_WIN
673   FDirThumbsAct = new QAction(tr("Show &folder thumbnails"), this);
674 #else
675   FDirThumbsAct = new QAction(tr("Show &directory thumbnails"), this);
676 #endif
677   FDirThumbsAct->setCheckable(true);
678   connect(FDirThumbsAct, SIGNAL(triggered()), this, SLOT(toggleDirThumbs()));
679 
680   FThumbLayoutGroupAct = new QActionGroup(this);
681   FThumbLayoutGroupAct->setExclusive(true);
682   FThumbLayoutGroupAct->addAction(FVerticalThumbsAct);
683   FThumbLayoutGroupAct->addAction(FHorizontalThumbsAct);
684 //TODO: re-enable  ac_ThumbLayoutGroup->addAction(ac_DetailedThumbs);
685 
686   // actions for main context menu
687   FToggleImageViewAct = new QAction(tr("Show &image preview") + "\t" + tr("F3"), this);
688   FToggleImageViewAct->setCheckable(true);
689   connect(FToggleImageViewAct, SIGNAL(triggered()), this, SLOT(toggleImageView()));
690 
691   FToggleSidebarAct = new QAction(tr("Show &sidebar") + "\t" + tr("F4"), this);
692   FToggleSidebarAct->setCheckable(true);
693   connect(FToggleSidebarAct, SIGNAL(triggered()), this, SLOT(toggleSidebar()));
694 
695   FSaveThumbAct = new QAction(tr("&Save thumbnail"), this);
696   connect(FSaveThumbAct, SIGNAL(triggered()), this, SLOT(saveThumbnail()));
697 
698   FCloseFileMgrAct = new QAction(tr("&Close file manager") + "\t" + tr("Esc"), this);
699   connect(FCloseFileMgrAct, SIGNAL(triggered()), this, SLOT(closeWindow()));
700 
701   FToggleShowRAWsAct = new QAction(tr("Show RAWs"), this);
702   FToggleShowRAWsAct->setCheckable(true);
703   connect(FToggleShowRAWsAct, SIGNAL(triggered()), this, SLOT(toggleShowRAWs()));
704 
705   FToggleShowBitmapsAct = new QAction(tr("Show bitmaps"), this);
706   FToggleShowBitmapsAct->setCheckable(true);
707   connect(FToggleShowBitmapsAct, SIGNAL(triggered()), this, SLOT(toggleShowBitmaps()));
708 }
709 
710 //------------------------------------------------------------------------------
contextMenuEvent(QContextMenuEvent * event)711 void ptFileMgrWindow::contextMenuEvent(QContextMenuEvent* event) {
712   ptThumbnailLayout currLayout = (ptThumbnailLayout)Settings->GetInt("FileMgrThumbLayoutType");
713 
714   // thumbnail view submenu
715   QMenu MenuThumbLayout(tr("Thumbnail &view"));
716   MenuThumbLayout.setPalette(Theme->menuPalette());
717   MenuThumbLayout.setStyle(Theme->style());
718   MenuThumbLayout.addActions(FThumbLayoutGroupAct->actions());
719   MenuThumbLayout.addSeparator();
720   MenuThumbLayout.addAction(FDirThumbsAct);
721   FVerticalThumbsAct->setChecked(currLayout == tlVerticalByRow);
722   FHorizontalThumbsAct->setChecked(currLayout == tlHorizontalByColumn);
723   FDetailedThumbsAct->setChecked(currLayout == tlDetailedList);
724   FDirThumbsAct->setChecked(Settings->GetInt("FileMgrShowDirThumbs"));
725 
726   // main context menu
727   QMenu Menu(NULL);
728   Menu.setPalette(Theme->menuPalette());
729   Menu.setStyle(Theme->style());
730   Menu.addMenu(&MenuThumbLayout);
731 
732   Menu.addSeparator();
733   Menu.addAction(FToggleImageViewAct);
734   FToggleImageViewAct->setChecked(FMImageViewPane->isVisible());
735   Menu.addAction(FToggleSidebarAct);
736   FToggleSidebarAct->setChecked(FMSidebar->isVisible());
737 
738   Menu.addSeparator();
739   Menu.addAction(FToggleShowRAWsAct);
740   FToggleShowRAWsAct->setChecked(Settings->GetInt("FileMgrShowRAWs"));
741   Menu.addAction(FToggleShowBitmapsAct);
742   FToggleShowBitmapsAct->setChecked(Settings->GetInt("FileMgrShowBitmaps"));
743 
744   Menu.addSeparator();
745   Menu.addAction(FSaveThumbAct);
746 
747   Menu.addSeparator();
748   Menu.addAction(FCloseFileMgrAct);
749 
750   Menu.exec(event->globalPos());
751 }
752 
753 //------------------------------------------------------------------------------
verticalThumbs()754 void ptFileMgrWindow::verticalThumbs() {
755   setLayouter(tlVerticalByRow);
756 }
757 
758 //------------------------------------------------------------------------------
horizontalThumbs()759 void ptFileMgrWindow::horizontalThumbs() {
760   setLayouter(tlHorizontalByColumn);
761 }
762 
763 //------------------------------------------------------------------------------
detailedThumbs()764 void ptFileMgrWindow::detailedThumbs() {
765   setLayouter(tlDetailedList);
766 }
767 
768 //------------------------------------------------------------------------------
toggleSidebar()769 void ptFileMgrWindow::toggleSidebar() {
770   FMSidebar->setVisible(!FMSidebar->isVisible());
771   Settings->SetValue("FileMgrShowSidebar", (int)FMSidebar->isVisible());
772 }
773 
774 //------------------------------------------------------------------------------
toggleImageView()775 void ptFileMgrWindow::toggleImageView() {
776   FMImageViewPane->setVisible(!FMImageViewPane->isVisible());
777   Settings->SetValue("FileMgrShowImageView", (int)FMImageViewPane->isVisible());
778 }
779 
780 //------------------------------------------------------------------------------
toggleDirThumbs()781 void ptFileMgrWindow::toggleDirThumbs() {
782   Settings->SetValue("FileMgrShowDirThumbs", 1 - Settings->GetInt("FileMgrShowDirThumbs"));
783   displayThumbnails();
784 }
785 
toggleShowRAWs()786 void ptFileMgrWindow::toggleShowRAWs() {
787   Settings->SetValue("FileMgrShowRAWs", 1 - Settings->GetInt("FileMgrShowRAWs"));
788   displayThumbnails();
789 }
790 
toggleShowBitmaps()791 void ptFileMgrWindow::toggleShowBitmaps() {
792   Settings->SetValue("FileMgrShowBitmaps", 1 - Settings->GetInt("FileMgrShowBitmaps"));
793   displayThumbnails();
794 }
795 
796 //------------------------------------------------------------------------------
bookmarkCurrentDir()797 void ptFileMgrWindow::bookmarkCurrentDir() {
798   FDataModel->tagModel()->appendRow(QDir::toNativeSeparators(FDataModel->currentDir()),
799                                      FDataModel->currentDir());
800   if (FTagMenu->isVisible()) {
801     adjustBookmarkMenuSize();
802   }
803 }
804 
805 //------------------------------------------------------------------------------
on_m_BookmarkButton_clicked()806 void ptFileMgrWindow::on_m_BookmarkButton_clicked() {
807   FTagMenu->move(m_BookmarkButton->mapToGlobal(QPoint(0, m_BookmarkButton->height())));
808   FTagMenu->setPalette(Theme->menuPalette());
809   FTagMenu->setStyle(Theme->style());
810   FTagMenu->show();  // must be first or adjust size won’t work correctly
811   adjustBookmarkMenuSize();
812 
813   FTagMenuList->setFocus();
814 }
815 
816 //------------------------------------------------------------------------------
adjustBookmarkMenuSize()817 void ptFileMgrWindow::adjustBookmarkMenuSize() {
818   QSize MenuSize(0, 0);
819   QPoint MenuTopleft = m_BookmarkButton->mapTo(this, QPoint(0, m_BookmarkButton->height()));
820   QSize MaxSize((this->width() - MenuTopleft.x()) * 0.85,
821                 (this->height() - MenuTopleft.y()) * 0.85);
822 
823   QFontMetrics metrics(FTagMenuList->font());
824   for (int i = 0; i < FDataModel->tagModel()->rowCount(); i++) {
825     QModelIndex curIndex = FDataModel->tagModel()->index(i, 0);
826     int width = metrics.width(curIndex.data().toString());
827     if (width > MenuSize.width()) MenuSize.setWidth(width);
828     MenuSize.setHeight(MenuSize.height() + FTagMenuList->visualRect(curIndex).height());
829   }
830 
831   FTagMenu->setFixedSize(qBound(150,
832                                  MenuSize.width() + 20 + FTagMenuList->verticalScrollBar()->width(),
833                                  MaxSize.width()),
834                           qBound(FTagMenuList->y() + 50,
835                                  MenuSize.height() + FTagMenuList->y() + 30,
836                                  MaxSize.height()) );
837 }
838 
839 //------------------------------------------------------------------------------
bookmarkDataChanged(QStandardItem *)840 void ptFileMgrWindow::bookmarkDataChanged(QStandardItem*) {
841   if (FTagMenu->isVisible()) {
842     adjustBookmarkMenuSize();
843   }
844 }
845 
846