1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2021-08-27
7  * Description : Side Bar Widget for the Showfoto folder view.
8  *
9  * Copyright (C) 2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  *
11  * This program is free software; you can redistribute it
12  * and/or modify it under the terms of the GNU General
13  * Public License as published by the Free Software Foundation;
14  * either version 2, or (at your option)
15  * any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * ============================================================ */
23 
24 #include "showfotofolderviewsidebar.h"
25 
26 // Qt includes
27 
28 #include <QApplication>
29 #include <QStyle>
30 #include <QIcon>
31 #include <QUndoStack>
32 #include <QVBoxLayout>
33 #include <QFileInfo>
34 #include <QDir>
35 #include <QTimer>
36 #include <QSplitter>
37 
38 // KDE includes
39 
40 #include <kconfiggroup.h>
41 #include <klocalizedstring.h>
42 
43 // Local includes
44 
45 #include "digikam_debug.h"
46 #include "digikam_globals.h"
47 #include "showfoto.h"
48 #include "showfotofolderviewbar.h"
49 #include "showfotofolderviewundo.h"
50 #include "showfotofolderviewmodel.h"
51 #include "showfotofolderviewlist.h"
52 #include "showfotofolderviewbookmarks.h"
53 
54 namespace ShowFoto
55 {
56 
57 class Q_DECL_HIDDEN ShowfotoFolderViewSideBar::Private
58 {
59 
60 public:
61 
Private()62     explicit Private()
63       : fsmodel     (nullptr),
64         fsview      (nullptr),
65         fsbar       (nullptr),
66         fsmarks     (nullptr),
67         fsstack     (nullptr),
68         splitter    (nullptr),
69         parent      (nullptr),
70         fsSortOrder (Qt::AscendingOrder),
71         fsRole      (ShowfotoFolderViewList::FileName)
72     {
73     }
74 
75     static const QString                   configIconSizeEntry;
76     static const QString                   configLastFolderEntry;
77     static const QString                   configFolderViewModeEntry;
78     static const QString                   configFolderViewTypeMimeEntry;
79     static const QString                   configBookmarksVisibleEntry;
80     static const QString                   configSplitterStateEntry;
81 
82     ShowfotoFolderViewModel*               fsmodel;
83     ShowfotoFolderViewList*                fsview;
84     ShowfotoFolderViewBar*                 fsbar;
85     ShowfotoFolderViewBookmarks*           fsmarks;
86     QUndoStack*                            fsstack;
87     QSplitter*                             splitter;
88     Showfoto*                              parent;
89     QList<DPluginAction*>                  pluginActions;
90     Qt::SortOrder                          fsSortOrder;
91     ShowfotoFolderViewList::FolderViewRole fsRole;
92 };
93 
94 const QString ShowfotoFolderViewSideBar::Private::configIconSizeEntry(QLatin1String("Icon Size"));
95 const QString ShowfotoFolderViewSideBar::Private::configLastFolderEntry(QLatin1String("Last Folder"));
96 const QString ShowfotoFolderViewSideBar::Private::configFolderViewModeEntry(QLatin1String("Folder View Mode"));
97 const QString ShowfotoFolderViewSideBar::Private::configFolderViewTypeMimeEntry(QLatin1String("Folder View Type Mime"));
98 const QString ShowfotoFolderViewSideBar::Private::configBookmarksVisibleEntry(QLatin1String("Bookmarks Visible"));
99 const QString ShowfotoFolderViewSideBar::Private::configSplitterStateEntry(QLatin1String("Splitter State"));
100 
ShowfotoFolderViewSideBar(Showfoto * const parent)101 ShowfotoFolderViewSideBar::ShowfotoFolderViewSideBar(Showfoto* const parent)
102     : QWidget          (parent),
103       StateSavingObject(this),
104       d                (new Private)
105 {
106     setObjectName(QLatin1String("ShowfotoFolderView Sidebar"));
107 
108     d->parent                  = parent;
109     d->fsstack                 = new QUndoStack(this);
110 
111     // --- Populate the view
112 
113     d->fsbar                   = new ShowfotoFolderViewBar(this);
114     d->fsview                  = new ShowfotoFolderViewList(this, d->fsbar);
115     d->fsmodel                 = new ShowfotoFolderViewModel(d->fsview);
116     d->fsview->setModel(d->fsmodel);
117     d->fsview->setRootIndex(d->fsmodel->index(QDir::rootPath()));
118 
119     d->fsmarks                 = new ShowfotoFolderViewBookmarks(this);
120 
121     d->splitter                = new QSplitter(Qt::Vertical, this);
122     d->splitter->addWidget(d->fsview);
123     d->splitter->addWidget(d->fsmarks);
124     d->splitter->setStretchFactor(0, 10);
125     d->splitter->setStretchFactor(1, 3);
126 
127     QVBoxLayout* const layout  = new QVBoxLayout(this);
128     layout->addWidget(d->fsbar);
129     layout->addWidget(d->splitter);
130     layout->setContentsMargins(0, 0, 0, 0);
131 
132     // --- Setup connections
133 
134     connect(d->fsbar, SIGNAL(signalSetup()),
135             this, SIGNAL(signalSetup()));
136 
137     connect(d->fsbar, SIGNAL(signalShowBookmarks(bool)),
138             this, SLOT(slotShowBookmarks(bool)));
139 
140     connect(d->fsbar, SIGNAL(signalViewModeChanged(int)),
141             this, SLOT(slotViewModeChanged(int)));
142 
143     connect(d->fsbar, SIGNAL(signalIconSizeChanged(int)),
144             d->fsview, SLOT(slotIconSizeChanged(int)));
145 
146     connect(d->fsbar, SIGNAL(signalGoHome()),
147             this, SLOT(slotGoHome()));
148 
149     connect(d->fsbar, SIGNAL(signalGoUp()),
150             this, SLOT(slotGoUp()));
151 
152     connect(d->fsbar, SIGNAL(signalLoadContents()),
153             this, SLOT(slotLoadContents()));
154 
155     connect(d->fsbar, SIGNAL(signalAppendContents()),
156             this, SLOT(slotAppendContents()));
157 
158     connect(d->fsmarks, SIGNAL(signalLoadContents()),
159             this, SLOT(slotLoadContents()));
160 
161     connect(d->fsbar, SIGNAL(signalCustomPathChanged(QString)),
162             this, SLOT(slotCustomPathChanged(QString)));
163 
164     connect(d->fsbar, SIGNAL(signalTypeMimesChanged(QString)),
165             this, SLOT(slotTypeMimesChanged(QString)));
166 
167     connect(d->fsbar, SIGNAL(signalGoNext()),
168             this, SLOT(slotRedo()));
169 
170     connect(d->fsbar, SIGNAL(signalGoPrevious()),
171             this, SLOT(slotUndo()));
172 
173     connect(d->fsstack, SIGNAL(canUndoChanged(bool)),
174             d->fsbar, SLOT(slotPreviousEnabled(bool)));
175 
176     connect(d->fsstack, SIGNAL(canRedoChanged(bool)),
177             d->fsbar, SLOT(slotNextEnabled(bool)));
178 
179     connect(d->fsview, SIGNAL(signalAddBookmark()),
180             this, SIGNAL(signalAddBookmark()));
181 }
182 
~ShowfotoFolderViewSideBar()183 ShowfotoFolderViewSideBar::~ShowfotoFolderViewSideBar()
184 {
185     delete d;
186 }
187 
slotTypeMimesChanged(const QString & patterns)188 void ShowfotoFolderViewSideBar::slotTypeMimesChanged(const QString& patterns)
189 {
190     d->fsmodel->setNameFilters(patterns.split(QLatin1Char(' ')));
191 }
192 
slotLoadContents()193 void ShowfotoFolderViewSideBar::slotLoadContents()
194 {
195     QModelIndex index = d->fsmodel->index(currentPath());
196     loadContents(index);
197 }
198 
slotAppendContents()199 void ShowfotoFolderViewSideBar::slotAppendContents()
200 {
201     QModelIndex index = d->fsmodel->index(currentPath());
202     loadContents(index, true);
203 }
204 
loadContents(const QModelIndex & index,bool append)205 void ShowfotoFolderViewSideBar::loadContents(const QModelIndex& index, bool append)
206 {
207     if (!index.isValid())
208     {
209         return;
210     }
211 
212     QStringList lst;
213     QString currentFile;
214 
215     if      (d->fsmodel->isDir(index))
216     {
217         setCurrentPath(d->fsmodel->filePath(index));
218 
219         lst         = d->fsmodel->currentFilesPath();
220         currentFile = !lst.isEmpty() ? lst.first() : QString();
221     }
222     else if (d->fsmodel->fileInfo(index).isFile())
223     {
224         lst         = d->fsmodel->currentFilesPath();
225         currentFile = d->fsmodel->fileInfo(index).filePath();
226     }
227 
228     qCDebug(DIGIKAM_SHOWFOTO_LOG) << "Load Contents from:" << currentPath() << "Files:" << lst;
229 
230     if (!lst.isEmpty())
231     {
232         if (!append)
233         {
234             emit signalLoadContentsFromFiles(lst, currentFile);
235         }
236         else
237         {
238             emit signalAppendContentsFromFiles(lst, currentFile);
239         }
240     }
241 }
242 
slotCustomPathChanged(const QString & path)243 void ShowfotoFolderViewSideBar::slotCustomPathChanged(const QString& path)
244 {
245     setCurrentPath(path);
246 }
247 
slotUndo()248 void ShowfotoFolderViewSideBar::slotUndo()
249 {
250     d->fsstack->undo();
251 }
252 
slotRedo()253 void ShowfotoFolderViewSideBar::slotRedo()
254 {
255     d->fsstack->redo();
256 }
257 
slotGoHome()258 void ShowfotoFolderViewSideBar::slotGoHome()
259 {
260     setCurrentPath(QDir::homePath());
261 }
262 
slotGoUp()263 void ShowfotoFolderViewSideBar::slotGoUp()
264 {
265     QDir dir(currentFolder());
266     dir.cdUp();
267 
268     // Is this the same as going back?  If so just go back, so we can keep the view scroll position.
269 
270     if (d->fsstack->canUndo())
271     {
272         const ShowfotoFolderViewUndo* lastDir = static_cast<const ShowfotoFolderViewUndo*>
273                                                 (d->fsstack->command(d->fsstack->index() - 1));
274 
275         if (lastDir->undoPath() == dir.path())
276         {
277             d->fsstack->undo();
278             return;
279         }
280     }
281 
282     setCurrentPath(dir.absolutePath());
283 }
284 
currentFolder() const285 QString ShowfotoFolderViewSideBar::currentFolder() const
286 {
287     QString path = d->fsmodel->rootPath();
288 
289     if (!path.endsWith(QDir::separator()))
290     {
291         path.append(QDir::separator());
292     }
293 
294     return path;
295 }
296 
currentPath() const297 QString ShowfotoFolderViewSideBar::currentPath() const
298 {
299     QModelIndex index = d->fsview->currentIndex();
300 
301     if (index.isValid())
302     {
303         return (d->fsmodel->filePath(index));
304     }
305 
306     return currentFolder();
307 }
308 
setCurrentPath(const QString & newPathNative)309 void ShowfotoFolderViewSideBar::setCurrentPath(const QString& newPathNative)
310 {
311     QFileInfo infoNative(newPathNative);
312 
313     if (!infoNative.exists())
314     {
315         return;
316     }
317 
318     QString newPath = QDir::fromNativeSeparators(newPathNative);
319 
320     if (infoNative.isDir() && !newPath.endsWith(QDir::separator()))
321     {
322         newPath.append(QDir::separator());
323     }
324 
325     QString oldDir(d->fsmodel->rootPath());
326 
327     if (!oldDir.endsWith(QDir::separator()))
328     {
329         oldDir.append(QDir::separator());
330     }
331 
332     if (oldDir == newPath)
333     {
334         return;
335     }
336 
337     QFileInfo info(newPath);
338 
339     if (info.isDir())
340     {
341         QModelIndex index = d->fsmodel->index(newPath);
342 
343         if (index.isValid())
344         {
345             d->fsstack->push(new ShowfotoFolderViewUndo(this, newPath));
346             d->fsmodel->setRootPath(newPath);
347             d->fsview->setRootIndex(index);
348         }
349     }
350     else
351     {
352         QModelIndex index = d->fsmodel->index(newPath);
353 
354         if (index.isValid())
355         {
356             QString newDir = info.absolutePath();
357 
358             if (!newDir.endsWith(QDir::separator()))
359             {
360                 newDir.append(QDir::separator());
361             }
362 
363             if (newDir != oldDir)
364             {
365                 d->fsstack->push(new ShowfotoFolderViewUndo(this, newDir));
366                 d->fsmodel->setRootPath(newDir);
367             }
368 
369             d->fsview->setCurrentIndex(index);
370             d->fsview->scrollTo(index);
371         }
372     }
373 }
374 
setCurrentPathWithoutUndo(const QString & newPath)375 void ShowfotoFolderViewSideBar::setCurrentPathWithoutUndo(const QString& newPath)
376 {
377     QModelIndex index = d->fsmodel->setRootPath(newPath);
378 
379     if (index.isValid())
380     {
381         d->fsview->setRootIndex(index);
382         d->fsbar->setCurrentPath(currentFolder());
383     }
384 }
385 
getIcon()386 const QIcon ShowfotoFolderViewSideBar::getIcon()
387 {
388     return QIcon::fromTheme(QLatin1String("document-open-folder"));
389 }
390 
getCaption()391 const QString ShowfotoFolderViewSideBar::getCaption()
392 {
393     return i18nc("@title: file system tree", "Folders");
394 }
395 
doLoadState()396 void ShowfotoFolderViewSideBar::doLoadState()
397 {
398     KConfigGroup group = getConfigGroup();
399 
400     d->fsmarks->readSettings(group);
401     d->fsbar->setFolderViewMode(group.readEntry(entryName(d->configFolderViewModeEntry),         (int)ShowfotoFolderViewList::ShortView));
402     d->fsbar->setFolderViewTypeMime(group.readEntry(entryName(d->configFolderViewTypeMimeEntry), (int)ShowfotoFolderViewBar::TYPE_MIME_ALL));
403     d->fsbar->setBookmarksVisible(group.readEntry(entryName(d->configBookmarksVisibleEntry),     false));
404     slotViewModeChanged(d->fsbar->folderViewMode());
405     d->fsbar->setIconSize(group.readEntry(entryName(d->configIconSizeEntry),                     32));
406 
407     QByteArray state = group.readEntry(entryName(d->configSplitterStateEntry),                   QByteArray());
408 
409     if (!state.isEmpty())
410     {
411         d->splitter->restoreState(QByteArray::fromBase64(state));
412     }
413 
414     setCurrentPathWithoutUndo(group.readEntry(entryName(d->configLastFolderEntry),               QDir::rootPath()));
415     loadContents(d->fsview->currentIndex());
416 }
417 
doSaveState()418 void ShowfotoFolderViewSideBar::doSaveState()
419 {
420     KConfigGroup group = getConfigGroup();
421 
422     d->fsmarks->saveSettings(group);
423     group.writeEntry(entryName(d->configFolderViewModeEntry),       d->fsbar->folderViewMode());
424     group.writeEntry(entryName(d->configFolderViewTypeMimeEntry),   d->fsbar->folderViewTypeMime());
425     group.writeEntry(entryName(d->configBookmarksVisibleEntry),     d->fsbar->bookmarksVisible());
426     group.writeEntry(entryName(d->configIconSizeEntry),             d->fsbar->iconSize());
427     group.writeEntry(entryName(d->configLastFolderEntry),           currentFolder());
428     group.writeEntry(entryName(d->configSplitterStateEntry),        d->splitter->saveState().toBase64());
429     group.sync();
430 }
431 
slotViewModeChanged(int mode)432 void ShowfotoFolderViewSideBar::slotViewModeChanged(int mode)
433 {
434     switch (mode)
435     {
436         case ShowfotoFolderViewList::ShortView:
437         {
438             d->fsview->setColumnHidden(ShowfotoFolderViewList::FileSize, true);
439             d->fsview->setColumnHidden(ShowfotoFolderViewList::FileType, true);
440             d->fsview->setColumnHidden(ShowfotoFolderViewList::FileDate, true);
441             d->fsview->setHeaderHidden(true);
442             break;
443         }
444 
445         default:    // ShowfotoFolderViewList::DetailledView
446         {
447             d->fsview->setColumnHidden(ShowfotoFolderViewList::FileSize, false);
448             d->fsview->setColumnHidden(ShowfotoFolderViewList::FileType, false);
449             d->fsview->setColumnHidden(ShowfotoFolderViewList::FileDate, false);
450             d->fsview->setHeaderHidden(false);
451             break;
452         }
453     }
454 }
455 
setSortOrder(int order)456 void ShowfotoFolderViewSideBar::setSortOrder(int order)
457 {
458     d->fsSortOrder = (order == ShowfotoItemSortSettings::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder; // Inverted compared to Thumbbar.
459     d->fsmodel->sort(d->fsRole, d->fsSortOrder);
460 }
461 
setSortRole(int role)462 void ShowfotoFolderViewSideBar::setSortRole(int role)
463 {
464     d->fsRole = (ShowfotoFolderViewList::FolderViewRole)role;
465     d->fsmodel->sort(d->fsRole, d->fsSortOrder);
466 }
467 
slotShowBookmarks(bool visible)468 void ShowfotoFolderViewSideBar::slotShowBookmarks(bool visible)
469 {
470     d->fsmarks->setVisible(visible);
471 }
472 
registerPluginActions(const QList<DPluginAction * > & actions)473 void ShowfotoFolderViewSideBar::registerPluginActions(const QList<DPluginAction*>& actions)
474 {
475     d->pluginActions = actions;
476 
477     d->fsbar->registerPluginActions(d->pluginActions);
478 
479     connect(d->fsbar, SIGNAL(signalPluginActionTriggered(QAction*)),
480             this, SLOT(slotPluginActionTriggered(QAction*)));
481 }
482 
slotPluginActionTriggered(QAction * act)483 void ShowfotoFolderViewSideBar::slotPluginActionTriggered(QAction* act)
484 {
485     foreach (QAction* const dpact, d->pluginActions)
486     {
487         if (act->objectName() == dpact->objectName())
488         {
489             slotLoadContents();
490             QTimer::singleShot(1000, dpact, SLOT(trigger()));
491             return;
492         }
493     }
494 }
495 
pluginActions() const496 QList<QAction*> ShowfotoFolderViewSideBar::pluginActions() const
497 {
498     return d->fsbar->pluginActions();
499 }
500 
501 } // namespace ShowFoto
502