1 /*
2     SPDX-FileCopyrightText: 2017 Smith AR <audoban@openmailbox.org>
3     SPDX-FileCopyrightText: 2019 Michail Vourlakos <mvourlakos@gmail.com>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #include "manager.h"
9 
10 // local
11 #include "importer.h"
12 #include "syncedlaunchers.h"
13 #include "../infoview.h"
14 #include "../screenpool.h"
15 #include "../data/layoutdata.h"
16 #include "../data/generictable.h"
17 #include "../layout/abstractlayout.h"
18 #include "../layout/centrallayout.h"
19 #include "../settings/universalsettings.h"
20 #include "../templates/templatesmanager.h"
21 #include "../tools/commontools.h"
22 
23 // Qt
24 #include <QDir>
25 #include <QFile>
26 #include <QMessageBox>
27 #include <QLatin1String>
28 
29 // KDE
30 #include <KMessageBox>
31 #include <KLocalizedString>
32 #include <KNotification>
33 
34 namespace Latte {
35 namespace Layouts {
36 
Manager(QObject * parent)37 Manager::Manager(QObject *parent)
38     : QObject(parent),
39       m_importer(new Importer(this)),
40       m_syncedLaunchers(new SyncedLaunchers(this))
41 {
42     m_corona = qobject_cast<Latte::Corona *>(parent);
43     //! needs to be created AFTER corona assignment
44     m_synchronizer = new Synchronizer(this);
45 
46     if (m_corona) {
47         connect(m_synchronizer, &Synchronizer::centralLayoutsChanged, this, &Manager::centralLayoutsChanged);
48         connect(m_synchronizer, &Synchronizer::currentLayoutIsSwitching, this, &Manager::currentLayoutIsSwitching);
49     }
50 }
51 
~Manager()52 Manager::~Manager()
53 {
54     m_importer->deleteLater();
55     m_syncedLaunchers->deleteLater();
56 
57     //! no needed because Latte:Corona is calling it at better place
58     // unload();
59 
60     m_synchronizer->deleteLater();
61 }
62 
init()63 void Manager::init()
64 {
65     QDir layoutsDir(Layouts::Importer::layoutUserDir());
66     bool firstRun = !layoutsDir.exists();
67 
68     int configVer = m_corona->universalSettings()->version();
69     qDebug() << "Universal Settings version : " << configVer;
70 
71     if (firstRun) {
72         m_corona->universalSettings()->setVersion(2);
73         m_corona->universalSettings()->setSingleModeLayoutName(i18n("My Layout"));
74 
75         //startup create what is necessary....
76         if (!layoutsDir.exists()) {
77             QDir(Latte::configPath()).mkdir("latte");
78         }
79 
80         QString defpath = m_corona->templatesManager()->newLayout(i18n("My Layout"), i18n(Templates::DEFAULTLAYOUTTEMPLATENAME));
81         setOnAllActivities(Layout::AbstractLayout::layoutName(defpath));
82 
83         m_corona->templatesManager()->importSystemLayouts();
84     } else if (configVer < 2 && !firstRun) {
85         m_corona->universalSettings()->setVersion(2);
86 
87         bool isOlderVersion = m_importer->updateOldConfiguration();
88         if (isOlderVersion) {
89             qDebug() << "Latte is updating its older configuration...";
90             m_corona->templatesManager()->importSystemLayouts();
91         } else {
92             m_corona->universalSettings()->setSingleModeLayoutName(i18n("My Layout"));
93         }
94     }
95 
96     //! Custom Templates path creation
97     QDir localTemplatesDir(Latte::configPath() + "/latte/templates");
98 
99     if (!localTemplatesDir.exists()) {
100         QDir(Latte::configPath() + "/latte").mkdir("templates");
101     }
102 
103     //! Check if the multiple-layouts hidden file is present, add it if it isnt
104     if (!QFile(Layouts::Importer::layoutUserFilePath(Layout::MULTIPLELAYOUTSHIDDENNAME)).exists()) {
105         m_corona->templatesManager()->newLayout("", Layout::MULTIPLELAYOUTSHIDDENNAME);
106     }
107 
108     qDebug() << "Latte is loading  its layouts...";
109 
110     m_synchronizer->initLayouts();
111 }
112 
unload()113 void Manager::unload()
114 {
115     m_synchronizer->unloadLayouts();
116 }
117 
corona()118 Latte::Corona *Manager::corona()
119 {
120     return m_corona;
121 }
122 
importer()123 Importer *Manager::importer()
124 {
125     return m_importer;
126 }
127 
syncedLaunchers() const128 SyncedLaunchers *Manager::syncedLaunchers() const
129 {
130     return m_syncedLaunchers;
131 }
132 
synchronizer() const133 Synchronizer *Manager::synchronizer() const
134 {
135     return m_synchronizer;
136 }
137 
memoryUsage() const138 MemoryUsage::LayoutsMemory Manager::memoryUsage() const
139 {
140     return m_corona->universalSettings()->layoutsMemoryUsage();
141 }
142 
setMemoryUsage(MemoryUsage::LayoutsMemory memoryUsage)143 void Manager::setMemoryUsage(MemoryUsage::LayoutsMemory memoryUsage)
144 {
145     m_corona->universalSettings()->setLayoutsMemoryUsage(memoryUsage);
146 }
147 
centralLayoutsNames()148 QStringList Manager::centralLayoutsNames()
149 {
150     return m_synchronizer->centralLayoutsNames();
151 }
152 
currentLayoutsNames() const153 QStringList Manager::currentLayoutsNames() const
154 {
155     return m_synchronizer->currentLayoutsNames();
156 }
157 
viewTemplateNames() const158 QStringList Manager::viewTemplateNames() const
159 {
160     Latte::Data::GenericTable<Data::Generic> viewtemplates = m_corona->templatesManager()->viewTemplates();
161 
162     QStringList names;
163 
164     for(int i=0; i<viewtemplates.rowCount(); ++i) {
165         names << viewtemplates[i].name;
166     }
167 
168     return names;
169 }
170 
viewTemplateIds() const171 QStringList Manager::viewTemplateIds() const
172 {
173     Latte::Data::GenericTable<Data::Generic> viewtemplates = m_corona->templatesManager()->viewTemplates();
174 
175     QStringList ids;
176 
177     for(int i=0; i<viewtemplates.rowCount(); ++i) {
178         ids << viewtemplates[i].id;
179     }
180 
181     return ids;
182 }
183 
iconForLayout(const QString & storedLayoutName) const184 Latte::Data::LayoutIcon Manager::iconForLayout(const QString &storedLayoutName) const
185 {
186     Data::Layout l = m_synchronizer->data(storedLayoutName);
187     return iconForLayout(l);
188 }
189 
iconForLayout(const Data::Layout & layout) const190 Latte::Data::LayoutIcon Manager::iconForLayout(const Data::Layout &layout) const
191 {
192     Latte::Data::LayoutIcon _icon;
193 
194     if (!layout.icon.isEmpty()) {
195         //! if there is specific icon set from the user for this layout we draw only that icon
196         _icon.name = layout.icon;
197         _icon.isBackgroundFile = false;
198         return _icon;
199     }
200 
201     //! fallback icon: background image
202     if (_icon.isEmpty()) {
203         QString colorPath = m_corona->kPackage().path() + "../../shells/org.kde.latte.shell/contents/images/canvas/";
204 
205         if (layout.backgroundStyle == Layout::PatternBackgroundStyle && layout.background.isEmpty()) {
206             colorPath += "defaultcustomprint.jpg";
207         } else {
208             colorPath = layout.background.startsWith("/") ? layout.background : colorPath + layout.color + "print.jpg";
209         }
210 
211         if (QFileInfo(colorPath).exists()) {
212             _icon.isBackgroundFile = true;
213             _icon.name = colorPath;
214             return _icon;
215         }
216     }
217 
218     return Latte::Data::LayoutIcon();
219 }
220 
currentLayouts() const221 QList<CentralLayout *> Manager::currentLayouts() const
222 {
223     return m_synchronizer->currentLayouts();
224 }
225 
switchToLayout(QString layoutName,MemoryUsage::LayoutsMemory newMemoryUsage)226 bool Manager::switchToLayout(QString layoutName,  MemoryUsage::LayoutsMemory newMemoryUsage)
227 {
228     return m_synchronizer->switchToLayout(layoutName, newMemoryUsage);
229 }
230 
loadLayoutOnStartup(QString layoutName)231 void Manager::loadLayoutOnStartup(QString layoutName)
232 {
233     QStringList layouts = m_importer->checkRepairMultipleLayoutsLinkedFile();
234 
235     //! Latte didn't close correctly, maybe a crash
236     if (layouts.size() > 0) {
237         QDialog* dialog = new QDialog(nullptr);
238         dialog->setWindowTitle(i18n("Multiple Layouts Startup Warning"));
239         dialog->setObjectName("sorry");
240         dialog->setAttribute(Qt::WA_DeleteOnClose);
241 
242         auto buttonbox = new QDialogButtonBox(QDialogButtonBox::Ok);
243 
244         KMessageBox::createKMessageBox(dialog,
245                                        buttonbox,
246                                        QMessageBox::Warning,
247                                        i18np("<b>Multiple Layouts based on Activities</b> mode did not close properly during the last session.<br/><br/>The following layout <b>[ %2 ]</b> had to be updated for consistency!",
248                                              "<b>Multiple Layouts based on Activities</b> mode did not close properly during the last session.<br/><br/>The following layouts <b>[ %2 ]</b> had to be updated for consistency!",
249                                              layouts.count(),
250                                              layouts.join(", ")),
251                                        QStringList(),
252                                        QString(),
253                                        0,
254                                        KMessageBox::NoExec,
255                                        QString());
256         dialog->show();
257     }
258 
259     m_synchronizer->switchToLayout(layoutName);
260 }
261 
moveView(QString originLayoutName,uint originViewId,QString destinationLayoutName)262 void Manager::moveView(QString originLayoutName, uint originViewId, QString destinationLayoutName)
263 {
264     if (memoryUsage() != Latte::MemoryUsage::MultipleLayouts
265             || originLayoutName.isEmpty()
266             || destinationLayoutName.isEmpty()
267             || originViewId <= 0
268             || originLayoutName == destinationLayoutName) {
269         return;
270     }
271 
272     auto originlayout = m_synchronizer->layout(originLayoutName);
273     auto destinationlayout = m_synchronizer->layout(destinationLayoutName);
274 
275     if (!originlayout || !destinationlayout || originlayout == destinationlayout) {
276         return;
277     }
278 
279     Plasma::Containment *originviewcontainment = originlayout->containmentForId(originViewId);
280     Latte::View *originview = originlayout->viewForContainment(originViewId);
281 
282     if (!originviewcontainment) {
283         return;
284     }
285 
286     QList<Plasma::Containment *> origincontainments = originlayout->unassignFromLayout(originviewcontainment);
287 
288     if (origincontainments.size() > 0) {
289         destinationlayout->assignToLayout(originview, origincontainments);
290     }
291 }
292 
loadLatteLayout(QString layoutPath)293 void Manager::loadLatteLayout(QString layoutPath)
294 {
295     qDebug() << " -------------------------------------------------------------------- ";
296     qDebug() << " -------------------------------------------------------------------- ";
297 
298     if (m_corona->containments().size() > 0) {
299         qDebug() << "LOAD LATTE LAYOUT ::: There are still containments present !!!! :: " << m_corona->containments().size();
300     }
301 
302     if (!layoutPath.isEmpty() && m_corona->containments().size() == 0) {
303         cleanupOnStartup(layoutPath);
304         qDebug() << "LOADING CORONA LAYOUT:" << layoutPath;
305         m_corona->loadLayout(layoutPath);
306     }
307 }
308 
setOnAllActivities(QString layoutName)309 void Manager::setOnAllActivities(QString layoutName)
310 {
311     CentralLayout *central = m_synchronizer->centralLayout(layoutName);
312 
313     if (central) {
314         central->setActivities(QStringList(Data::Layout::ALLACTIVITIESID));
315     } else if (m_importer->layoutExists(layoutName)) {
316         CentralLayout storage(this, m_importer->layoutUserFilePath(layoutName));
317         storage.setActivities(QStringList(Data::Layout::ALLACTIVITIESID));
318     }
319 }
320 
setOnActivities(QString layoutName,QStringList activities)321 void Manager::setOnActivities(QString layoutName, QStringList activities)
322 {
323     CentralLayout *central = m_synchronizer->centralLayout(layoutName);
324 
325     if (central) {
326         central->setActivities(activities);
327     } else if (m_importer->layoutExists(layoutName)) {
328         CentralLayout storage(this, m_importer->layoutUserFilePath(layoutName));
329         storage.setActivities(activities);
330     }
331 }
332 
cleanupOnStartup(QString path)333 void Manager::cleanupOnStartup(QString path)
334 {
335     KSharedConfigPtr filePtr = KSharedConfig::openConfig(path);
336 
337     KConfigGroup actionGroups = KConfigGroup(filePtr, "ActionPlugins");
338 
339     QStringList deprecatedActionGroup;
340 
341     for (const auto &actId : actionGroups.groupList()) {
342         QString pluginId = actionGroups.group(actId).readEntry("RightButton;NoModifier", "");
343 
344         if (pluginId == QStringLiteral("org.kde.contextmenu")) {
345             deprecatedActionGroup << actId;
346         }
347     }
348 
349     for (const auto &pId : deprecatedActionGroup) {
350         qDebug() << "!!!!!!!!!!!!!!!!  !!!!!!!!!!!! !!!!!!! REMOVING :::: " << pId;
351         actionGroups.group(pId).deleteGroup();
352     }
353 
354     KConfigGroup containmentGroups = KConfigGroup(filePtr, "Containments");
355 
356     QStringList removeContaimentsList;
357 
358     for (const auto &cId : containmentGroups.groupList()) {
359         QString pluginId = containmentGroups.group(cId).readEntry("plugin", "");
360 
361         if (pluginId == QStringLiteral("org.kde.desktopcontainment")) { //!must remove ghost containments first
362             removeContaimentsList << cId;
363         }
364     }
365 
366     for (const auto &cId : removeContaimentsList) {
367         containmentGroups.group(cId).deleteGroup();
368     }
369 }
370 
371 
showAboutDialog()372 void Manager::showAboutDialog()
373 {
374     m_corona->aboutApplication();
375 }
376 
clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds,bool bypassChecks)377 void Manager::clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds, bool bypassChecks)
378 {
379     if (!m_corona || (memoryUsage() == MemoryUsage::SingleLayout && !bypassChecks)) {
380         return;
381     }
382 
383     auto containments = m_corona->config()->group("Containments");
384 
385     for (const auto &conId : containmentsIds) {
386         qDebug() << "unloads ::: " << conId;
387         KConfigGroup containment = containments.group(conId);
388         containment.deleteGroup();
389         containment.sync();
390     }
391 
392     containments.sync();
393 }
394 
showLatteSettingsDialog(int firstPage,bool toggleCurrentPage)395 void Manager::showLatteSettingsDialog(int firstPage, bool toggleCurrentPage)
396 {
397     if (!m_latteSettingsDialog) {
398         m_latteSettingsDialog = new Latte::Settings::Dialog::SettingsDialog(nullptr, m_corona);
399     }
400     m_latteSettingsDialog->show();
401 
402     if (m_latteSettingsDialog->isMinimized()) {
403         m_latteSettingsDialog->showNormal();
404     }
405 
406     if (toggleCurrentPage) {
407         m_latteSettingsDialog->toggleCurrentPage();
408     } else {
409         m_latteSettingsDialog->setCurrentPage(firstPage);
410     }
411 
412     m_latteSettingsDialog->activateWindow();
413 }
414 
hideLatteSettingsDialog()415 void Manager::hideLatteSettingsDialog()
416 {
417     if (m_latteSettingsDialog) {
418         m_latteSettingsDialog->deleteLater();
419         m_latteSettingsDialog = nullptr;
420     }
421 }
422 
showInfoWindow(QString info,int duration,QStringList activities)423 void Manager::showInfoWindow(QString info, int duration, QStringList activities)
424 {
425     for (const auto screen : qGuiApp->screens()) {
426         InfoView *infoView = new InfoView(m_corona, info, screen);
427 
428         infoView->show();
429         infoView->setOnActivities(activities);
430 
431         QTimer::singleShot(duration, [this, infoView]() {
432             infoView->deleteLater();
433         });
434     }
435 }
436 
437 }
438 }
439