1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2018-07-30
7  * Description : a plugin to render slideshow.
8  *
9  * Copyright (C) 2018-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  * Copyright (C) 2019-2020 by Minh Nghia Duong <minhnghiaduong997 at gmail dot com>
11  *
12  * This program is free software; you can redistribute it
13  * and/or modify it under the terms of the GNU General
14  * Public License as published by the Free Software Foundation;
15  * either version 2, or (at your option) 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 "slideshowplugin.h"
25 
26 // Qt includes
27 
28 #include <QPointer>
29 #include <QMenu>
30 #include <QDebug>
31 #include <QString>
32 #include <QApplication>
33 
34 // KDE includes
35 
36 #include <klocalizedstring.h>
37 #include <kactioncollection.h>
38 #include <kxmlguiwindow.h>
39 
40 // Local includes
41 
42 #include "metaenginesettings.h"
43 #include "setupslideshow_dialog.h"
44 #include "slideshowloader.h"
45 
46 using namespace Digikam;
47 
48 namespace DigikamGenericSlideShowPlugin
49 {
50 
SlideShowPlugin(QObject * const parent)51 SlideShowPlugin::SlideShowPlugin(QObject* const parent)
52     : DPluginGeneric(parent)
53 {
54 }
55 
~SlideShowPlugin()56 SlideShowPlugin::~SlideShowPlugin()
57 {
58 }
59 
name() const60 QString SlideShowPlugin::name() const
61 {
62     return i18n("SlideShow");
63 }
64 
iid() const65 QString SlideShowPlugin::iid() const
66 {
67     return QLatin1String(DPLUGIN_IID);
68 }
69 
icon() const70 QIcon SlideShowPlugin::icon() const
71 {
72     return QIcon::fromTheme(QLatin1String("view-presentation"));
73 }
74 
description() const75 QString SlideShowPlugin::description() const
76 {
77     return i18n("A tool to render slideshow");
78 }
79 
details() const80 QString SlideShowPlugin::details() const
81 {
82     return i18n("<p>This tool render a series of items as a simple slide-show.</p>"
83                 "<p>Plenty of items properties can be displayed as overlay while running.</p>"
84                 "<p>This tool can play album contents in recursive mode with children albums if any.</p>"
85                 "<p><u>Usage:</u></p>"
86 
87                 "<table>"
88 
89                     "<tr>"
90                         "<td colspan=\"2\"><nobr><center>"
91                             "<b><h1>Item Access</h1></b>"
92                         "</center></nobr></td>"
93                     "</tr>"
94 
95                     "<tr><td>Previous Item:</td>"        "<td><i>Up</i> key</td></tr>"
96                     "<tr><td></td>"                      "<td><i>PgUp</i> key</td></tr>"
97                     "<tr><td></td>"                      "<td><i>Left</i> key</td></tr>"
98                     "<tr><td></td>"                      "<td>Mouse wheel up</td></tr>"
99                     "<tr><td></td>"                      "<td>Left mouse button</td></tr>"
100                     "<tr><td>Next Item:</td>"            "<td><i>Down</i> key</td></tr>"
101                     "<tr><td></td>"                      "<td><i>PgDown</i> key</td></tr>"
102                     "<tr><td></td>"                      "<td><i>Right</i> key</td></tr>"
103                     "<tr><td></td>"                      "<td>Mouse wheel down</td></tr>"
104                     "<tr><td></td>"                      "<td>Right mouse button</td></tr>"
105                     "<tr><td>Pause/Start:</td>"          "<td><i>Space</i> key</td></tr>"
106                     "<tr><td>Slideshow Settings:</td>"   "<td><i>F2</i> key</td></tr>"
107                     "<tr><td>Hide/Show Properties:</td>" "<td><i>F4</i> key</td></tr>"
108                     "<tr><td>Quit:</td>"                 "<td><i>Esc</i> key</td></tr>"
109 
110                     "<tr>"
111                         "<td colspan=\"2\"><nobr><center>"
112                             "<b><h1>Item Properties</h1></b>"
113                         "</center></nobr></td>"
114                     "</tr>"
115 
116                     "<tr><td>Change Tags:</td>"          "<td>Use Tags keyboard shortcuts</td></tr>"
117                     "<tr><td>Change Rating:</td>"        "<td>Use Rating keyboard shortcuts</td></tr>"
118                     "<tr><td>Change Color Label:</td>"   "<td>Use Color label keyboard shortcuts</td></tr>"
119                     "<tr><td>Change Pick Label:</td>"    "<td>Use Pick label keyboard shortcuts</td></tr>"
120 
121                     "<tr>"
122                         "<td colspan=\"2\"><nobr><center>"
123                             "<b><h1>Others</h1></b>"
124                         "</center></nobr></td>"
125                     "</tr>"
126 
127                     "<tr><td>Show this help:</td>"       "<td><i>F1</i> key</td></tr>"
128 
129                 "</table>");
130 }
131 
authors() const132 QList<DPluginAuthor> SlideShowPlugin::authors() const
133 {
134     return QList<DPluginAuthor>()
135             << DPluginAuthor(QString::fromUtf8("Enrico Ros"),
136                              QString::fromUtf8("eros dot kde at email dot it"),
137                              QString::fromUtf8("(C) 2004"))
138             << DPluginAuthor(QString::fromUtf8("Renchi Raju"),
139                              QString::fromUtf8("renchi dot raju at gmail dot com"),
140                              QString::fromUtf8("(C) 2004-2005"))
141             << DPluginAuthor(QString::fromUtf8("Gilles Caulier"),
142                              QString::fromUtf8("caulier dot gilles at gmail dot com"),
143                              QString::fromUtf8("(C) 2005-2021"))
144             << DPluginAuthor(QString::fromUtf8("Minh Nghia Duong"),
145                              QString::fromUtf8("minhnghiaduong997 at gmail dot com"),
146                              QString::fromUtf8("(C) 2019-2021"))
147             << DPluginAuthor(QString::fromUtf8("Phuoc Khanh Le"),
148                              QString::fromUtf8("phuockhanhnk94 at gmail dot com"),
149                              QString::fromUtf8("(C) 2021"))
150             << DPluginAuthor(QString::fromUtf8("Fady Khalaf"),
151                              QString::fromUtf8("fadykhalaf01 at gmail dot com"),
152                              QString::fromUtf8("(C) 2019"))
153             ;
154 }
setup(QObject * const parent)155 void SlideShowPlugin::setup(QObject* const parent)
156 {
157     DPluginAction* const ac     = new DPluginAction(parent);
158     ac->setIcon(icon());
159     ac->setText(i18nc("@action", "Slideshow"));
160     ac->setObjectName(QLatin1String("slideshow_plugin"));
161     ac->setActionCategory(DPluginAction::GenericView);
162 
163     DInfoInterface* const iface = infoIface(ac);
164 
165     if (iface && (parent->objectName() == QLatin1String("Digikam")))
166     {
167 
168         QMenu* const slideShowActions = new QMenu(i18n("Slideshow"));
169         slideShowActions->setIcon(icon());
170         ac->setMenu(slideShowActions);
171 
172         // Action show all
173 
174         QAction* const slideShowAllAction = new QAction(i18n("Play All"), ac);
175         slideShowAllAction->setObjectName(QLatin1String("slideshow_all"));
176         slideShowActions->addAction(slideShowAllAction);
177 
178         connect(slideShowAllAction, SIGNAL(triggered()),
179                 this, SLOT(slotMenuSlideShowAll()));
180 
181         // Action show selection
182 
183         QAction* const slideShowSelectionAction = new QAction(i18n("Play Selection"), ac);
184         slideShowSelectionAction->setObjectName(QLatin1String("slideshow_selected"));
185         slideShowActions->addAction(slideShowSelectionAction);
186 
187         connect(slideShowSelectionAction, SIGNAL(triggered()),
188                 this, SLOT(slotMenuSlideShowSelection()));
189 
190         // Action show recursive
191 
192         QAction* const slideShowRecursiveAction = new QAction(i18n("Play With Sub-Albums"), ac);
193         slideShowRecursiveAction->setObjectName(QLatin1String("slideshow_recursive"));
194         slideShowActions->addAction(slideShowRecursiveAction);
195 
196         connect(slideShowRecursiveAction, SIGNAL(triggered()),
197                 this, SLOT(slotMenuSlideShowRecursive()));
198 
199         connect(ac, SIGNAL(triggered(bool)),
200                 this, SLOT(slotShowManual()));
201 
202         // See bug #425425: register all sub-actions to collection instance to be able to edit keyboard shorcuts
203 
204         KXmlGuiWindow* const gui = dynamic_cast<KXmlGuiWindow*>(parent);
205 
206         if (gui)
207         {
208             KActionCollection* const collection = gui->actionCollection();
209             collection->setShortcutsConfigurable(slideShowActions->menuAction(), false);
210 
211             collection->addAction(slideShowAllAction->objectName(),       slideShowAllAction);
212             collection->addAction(slideShowSelectionAction->objectName(), slideShowSelectionAction);
213             collection->addAction(slideShowRecursiveAction->objectName(), slideShowRecursiveAction);
214 
215             collection->setDefaultShortcut(slideShowAllAction,       Qt::Key_F9);
216             collection->setDefaultShortcut(slideShowSelectionAction, Qt::ALT + Qt::Key_F9);
217             collection->setDefaultShortcut(slideShowRecursiveAction, Qt::SHIFT + Qt::Key_F9);
218         }
219     }
220     else
221     {
222         ac->setShortcut(Qt::Key_F9);
223 
224         connect(ac, SIGNAL(triggered(bool)),
225                 this, SLOT(slotMenuSlideShow()));
226     }
227 
228     addAction(ac);
229 }
230 
addConnectionSlideEnd(QObject * const obj)231 void SlideShowPlugin::addConnectionSlideEnd(QObject* const obj)
232 {
233     Q_UNUSED(obj);
234 }
235 
slotMenuSlideShow()236 void SlideShowPlugin::slotMenuSlideShow()
237 {
238     QUrl startFrom;
239     DPluginAction* const ac = dynamic_cast<DPluginAction*>(sender());
240 
241     if (ac)
242     {
243         startFrom = ac->data().toUrl();
244         ac->setData(QVariant());
245     }
246 
247     SlideShowSettings* const settings = new SlideShowSettings();
248     settings->iface                   = infoIface(ac);
249     settings->readFromConfig();
250     settings->exifRotate              = MetaEngineSettings::instance()->settings().exifRotate;
251     settings->fileList                = settings->iface->currentAlbumItems();
252 
253     slideshow(settings, true, startFrom);
254 }
255 
slotMenuSlideShowAll()256 void SlideShowPlugin::slotMenuSlideShowAll()
257 {
258     SlideShowSettings* const settings = new SlideShowSettings();
259     settings->iface                   = infoIface(sender()->parent());
260     settings->readFromConfig();
261     settings->fileList                = settings->iface->currentAlbumItems();
262 
263     slideshow(settings);
264 }
265 
slotMenuSlideShowSelection()266 void SlideShowPlugin::slotMenuSlideShowSelection()
267 {
268     SlideShowSettings* const settings = new SlideShowSettings();
269     settings->iface                   = infoIface(sender()->parent());
270     settings->readFromConfig();
271     settings->fileList                = settings->iface->currentSelectedItems();
272 
273     slideshow(settings);
274 }
275 
slotMenuSlideShowRecursive()276 void SlideShowPlugin::slotMenuSlideShowRecursive()
277 {
278     DInfoInterface* const iface = infoIface(sender()->parent());
279 
280     connect(iface, SIGNAL(signalAlbumItemsRecursiveCompleted(QList<QUrl>)),
281             this, SLOT(slotShowRecursive(QList<QUrl>)));
282 
283     iface->parseAlbumItemsRecursive();
284 }
285 
slotShowRecursive(const QList<QUrl> & imageList)286 void SlideShowPlugin::slotShowRecursive(const QList<QUrl>& imageList)
287 {
288     SlideShowSettings* const settings = new SlideShowSettings();
289     settings->iface                   = dynamic_cast<DInfoInterface*>(sender());
290     settings->readFromConfig();
291     settings->fileList                = imageList;
292 
293     slideshow(settings);
294 }
295 
slotShowManual()296 void SlideShowPlugin::slotShowManual()
297 {
298     DPluginAction* const ac = dynamic_cast<DPluginAction*>(sender());
299 
300     if (!ac)
301     {
302         return;
303     }
304 
305     QUrl startFrom(ac->data().toUrl());
306     ac->setData(QVariant());
307 
308     SlideShowSettings* const settings = new SlideShowSettings();
309     settings->iface                   = infoIface(ac);
310     settings->readFromConfig();
311     settings->exifRotate              = MetaEngineSettings::instance()->settings().exifRotate;
312     settings->fileList                = settings->iface->currentAlbumItems();
313 
314     slideshow(settings, !startFrom.isValid(), startFrom);
315 }
316 
slideshow(SlideShowSettings * const settings,bool autoPlayEnabled,const QUrl & startFrom)317 void SlideShowPlugin::slideshow(SlideShowSettings* const settings, bool autoPlayEnabled, const QUrl& startFrom)
318 {
319     settings->autoPlayEnabled = autoPlayEnabled;
320     settings->plugin          = this;
321 
322     // TODO: preview settings for digikam
323     //settings.previewSettings = ApplicationSettings::instance()->getPreviewSettings();
324 
325     if (startFrom.isValid())
326     {
327         settings->imageUrl = startFrom;
328     }
329 
330     SlideShowLoader* const slide = new SlideShowLoader(settings);
331     slide->setShortCutPrefixes(settings->iface->passShortcutActionsToWidget(slide));
332 
333     if      (settings->imageUrl.isValid())
334     {
335         slide->setCurrentItem(settings->imageUrl);
336     }
337     else if (settings->startWithCurrent)
338     {
339         if (!settings->iface->currentSelectedItems().isEmpty())
340         {
341             slide->setCurrentItem(settings->iface->currentSelectedItems()[0]);
342         }
343         else
344         {
345             // no current selection, do nothing.
346 
347             return;
348         }
349     }
350 
351     connect(slide, SIGNAL(signalLastItemUrl(QUrl)),
352             settings->iface, SIGNAL(signalLastItemUrl(QUrl)));
353 
354     connect(settings->iface, SIGNAL(signalShortcutPressed(QString,int)),
355             slide, SLOT(slotHandleShortcut(QString,int)));
356 
357     slide->show();
358 }
359 
360 } // namespace DigikamGenericSlideShowPlugin
361