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