1 /*
2 SPDX-FileCopyrightText: 2019 Michail Vourlakos <mvourlakos@gmail.com>
3 SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
6 #include "synchronizer.h"
7
8 //! local
9 #include <config-latte.h>
10 #include "importer.h"
11 #include "manager.h"
12 #include "../apptypes.h"
13 #include "../data/layoutdata.h"
14 #include "../lattecorona.h"
15 #include "../layout/centrallayout.h"
16 #include "../layout/genericlayout.h"
17 #include "../settings/universalsettings.h"
18 #include "../templates/templatesmanager.h"
19 #include "../view/view.h"
20
21 // Qt
22 #include <QDir>
23 #include <QFile>
24 #include <QStringList>
25
26 // Plasma
27 #include <Plasma/Containment>
28
29 // KDE
30 #include <KActivities/Consumer>
31 #include <KActivities/Controller>
32 #include <KWindowSystem>
33
34 #define LAYOUTSINITINTERVAL 350
35
36 namespace Latte {
37 namespace Layouts {
38
Synchronizer(QObject * parent)39 Synchronizer::Synchronizer(QObject *parent)
40 : QObject(parent),
41 m_activitiesController(new KActivities::Controller)
42 {
43 m_manager = qobject_cast<Manager *>(parent);
44
45 connect(this, &Synchronizer::layoutsChanged, this, &Synchronizer::reloadAssignedLayouts);
46
47 //! KWin update Disabled Borders
48 connect(this, &Synchronizer::centralLayoutsChanged, this, &Synchronizer::updateBorderlessMaximizedAfterTimer);
49 connect(m_manager->corona()->universalSettings(), &UniversalSettings::canDisableBordersChanged, this, &Synchronizer::updateKWinDisabledBorders);
50
51 m_updateBorderlessMaximized.setInterval(500);
52 m_updateBorderlessMaximized.setSingleShot(true);
53 connect(&m_updateBorderlessMaximized, &QTimer::timeout, this, &Synchronizer::updateKWinDisabledBorders);
54
55 //! KActivities tracking
56 connect(m_manager->corona()->activitiesConsumer(), &KActivities::Consumer::activityRemoved,
57 this, &Synchronizer::onActivityRemoved);
58
59 connect(m_manager->corona()->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged,
60 this, [&]() {
61 if (m_manager->memoryUsage() == MemoryUsage::MultipleLayouts) {
62 //! this signal is also triggered when runningactivities are changed and actually is received first
63 //! this is why we need a timer here in order to delay that execution and not activate/deactivate
64 //! maximizedborders faulty because syncMultipleLayoutsToActivities(); has not been executed yet
65 updateBorderlessMaximizedAfterTimer();
66 }
67 });
68
69 connect(m_manager->corona()->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged,
70 this, [&]() {
71 if (m_manager->memoryUsage() == MemoryUsage::MultipleLayouts) {
72 syncMultipleLayoutsToActivities();
73 }
74 });
75 }
76
~Synchronizer()77 Synchronizer::~Synchronizer()
78 {
79 m_activitiesController->deleteLater();
80 }
81
activitiesController() const82 KActivities::Controller *Synchronizer::activitiesController() const
83 {
84 return m_activitiesController;
85 }
86
latteViewExists(Latte::View * view) const87 bool Synchronizer::latteViewExists(Latte::View *view) const
88 {
89 for (const auto layout : m_centralLayouts) {
90 for (const auto &v : layout->latteViews()) {
91 if (v == view) {
92 return true;
93 }
94 }
95 }
96
97 return false;
98 }
99
layoutExists(QString layoutName) const100 bool Synchronizer::layoutExists(QString layoutName) const
101 {
102 return m_layouts.containsName(layoutName);
103 }
104
105
isAssigned(QString layoutName) const106 bool Synchronizer::isAssigned(QString layoutName) const
107 {
108 for(auto activityid : m_assignedLayouts.keys()) {
109 if (m_assignedLayouts[activityid].contains(layoutName)) {
110 return true;
111 }
112 }
113
114 return false;
115 }
116
centralLayoutPos(QString id) const117 int Synchronizer::centralLayoutPos(QString id) const
118 {
119 for (int i = 0; i < m_centralLayouts.size(); ++i) {
120 CentralLayout *layout = m_centralLayouts.at(i);
121
122 if (layout->name() == id) {
123 return i;
124 }
125 }
126
127 return -1;
128 }
129
layoutPath(QString layoutName)130 QString Synchronizer::layoutPath(QString layoutName)
131 {
132 QString path = Layouts::Importer::layoutUserFilePath(layoutName);
133
134 if (!QFile(path).exists()) {
135 path = "";
136 }
137
138 return path;
139 }
140
activities()141 QStringList Synchronizer::activities()
142 {
143 return m_manager->corona()->activitiesConsumer()->activities();
144 }
145
freeActivities()146 QStringList Synchronizer::freeActivities()
147 {
148 QStringList frees = activities();
149
150 for(auto assigned : m_assignedLayouts.keys()) {
151 frees.removeAll(assigned);
152 }
153
154 return frees;
155 }
156
runningActivities()157 QStringList Synchronizer::runningActivities()
158 {
159 return m_manager->corona()->activitiesConsumer()->runningActivities();
160 }
161
freeRunningActivities()162 QStringList Synchronizer::freeRunningActivities()
163 {
164 QStringList fActivities;
165
166 for (const auto &activity : runningActivities()) {
167 if (!m_assignedLayouts.contains(activity)) {
168 fActivities.append(activity);
169 }
170 }
171
172 return fActivities;
173 }
174
validActivities(const QStringList & layoutActivities)175 QStringList Synchronizer::validActivities(const QStringList &layoutActivities)
176 {
177 QStringList valids;
178 QStringList allactivities = activities();
179
180 for(auto activity : layoutActivities) {
181 if (allactivities.contains(activity)) {
182 valids << activity;
183 }
184 }
185
186 return valids;
187 }
188
centralLayoutsNames()189 QStringList Synchronizer::centralLayoutsNames()
190 {
191 QStringList names;
192
193 if (m_manager->memoryUsage() == MemoryUsage::SingleLayout) {
194 names << m_centralLayouts.at(0)->name();
195 } else {
196 for (int i = 0; i < m_centralLayouts.size(); ++i) {
197 CentralLayout *layout = m_centralLayouts.at(i);
198 names << layout->name();
199 }
200 }
201
202 return names;
203 }
204
currentLayoutsNames() const205 QStringList Synchronizer::currentLayoutsNames() const
206 {
207 QList<CentralLayout *> currents = currentLayouts();
208 QStringList currentNames;
209
210 for (int i = 0; i < currents.size(); ++i) {
211 CentralLayout *layout = currents.at(i);
212 currentNames << layout->name();
213 }
214
215 return currentNames;
216 }
217
layouts() const218 QStringList Synchronizer::layouts() const
219 {
220 return m_layouts.names();
221 }
222
menuLayouts() const223 QStringList Synchronizer::menuLayouts() const
224 {
225 QStringList menulayouts;
226
227 for (int i=0; i<m_layouts.rowCount(); ++i) {
228 if (!m_layouts[i].isShownInMenu) {
229 continue;
230 }
231
232 menulayouts << m_layouts[i].name;
233 }
234
235 for (const auto layout : m_centralLayouts) {
236 if (!menulayouts.contains(layout->name())) {
237 menulayouts.prepend(layout->name());
238 }
239 }
240
241 menulayouts.sort(Qt::CaseInsensitive);
242
243 return menulayouts;
244 }
245
setIsSingleLayoutInDeprecatedRenaming(const bool & enabled)246 void Synchronizer::setIsSingleLayoutInDeprecatedRenaming(const bool &enabled)
247 {
248 m_isSingleLayoutInDeprecatedRenaming = enabled;
249 }
250
data(const QString & storedLayoutName) const251 Data::Layout Synchronizer::data(const QString &storedLayoutName) const
252 {
253 Data::Layout l;
254
255 if (m_layouts.containsName(storedLayoutName)) {
256 QString lid = m_layouts.idForName(storedLayoutName);
257 return m_layouts[lid];
258 }
259
260 return l;
261 }
262
layoutsTable() const263 Data::LayoutsTable Synchronizer::layoutsTable() const
264 {
265 return m_layouts;
266 }
267
setLayoutsTable(const Data::LayoutsTable & table)268 void Synchronizer::setLayoutsTable(const Data::LayoutsTable &table)
269 {
270 if (m_layouts == table) {
271 return;
272 }
273
274 m_layouts = table;
275 emit layoutsChanged();
276 }
277
updateLayoutsTable()278 void Synchronizer::updateLayoutsTable()
279 {
280 for (int i = 0; i < m_centralLayouts.size(); ++i) {
281 CentralLayout *layout = m_centralLayouts.at(i);
282
283 if (m_layouts.containsId(layout->file())) {
284 m_layouts[layout->file()] = layout->data();
285 }
286 }
287
288 for (int i = 0; i < m_layouts.rowCount(); ++i) {
289 if ((m_layouts[i].errors>0 || m_layouts[i].warnings>0) && !m_layouts[i].isActive) {
290 CentralLayout central(this, m_layouts[i].id);
291 m_layouts[i].errors = central.errors().count();
292 m_layouts[i].warnings = central.warnings().count();
293 }
294 }
295 }
296
centralLayout(QString layoutname) const297 CentralLayout *Synchronizer::centralLayout(QString layoutname) const
298 {
299 for (int i = 0; i < m_centralLayouts.size(); ++i) {
300 CentralLayout *layout = m_centralLayouts.at(i);
301
302 if (layout->name() == layoutname) {
303 return layout;
304 }
305 }
306
307 return nullptr;
308 }
309
currentLayouts() const310 QList<CentralLayout *> Synchronizer::currentLayouts() const
311 {
312 QList<CentralLayout *> layouts;
313 layouts.clear();
314
315 if (m_centralLayouts.isEmpty()) {
316 return layouts;
317 }
318
319 if (m_manager->memoryUsage() == MemoryUsage::SingleLayout) {
320 layouts << m_centralLayouts[0];
321 } else {
322 for (auto layout : m_centralLayouts) {
323 if (layout->isOnAllActivities() || layout->appliedActivities().contains(m_manager->corona()->activitiesConsumer()->currentActivity())) {
324 layouts << layout;
325 }
326 }
327 }
328
329 return layouts;
330 }
331
centralLayoutsForActivity(const QString activityid) const332 QList<CentralLayout *> Synchronizer::centralLayoutsForActivity(const QString activityid) const
333 {
334 QList<CentralLayout *> layouts;
335
336 if (m_manager->memoryUsage() == MemoryUsage::SingleLayout && m_centralLayouts.count() >= 1) {
337 layouts << m_centralLayouts.at(0);
338 } else {
339 for (auto layout : m_centralLayouts) {
340 if (layout->isOnAllActivities() || layout->appliedActivities().contains(activityid)) {
341 layouts << layout;
342 }
343 }
344 }
345
346 return layouts;
347 }
348
currentViews() const349 QList<Latte::View *> Synchronizer::currentViews() const
350 {
351 QList<Latte::View *> views;
352
353 for(auto layout : currentLayouts()) {
354 views << layout->latteViews();
355 }
356
357 return views;
358 }
359
currentViewsWithPlasmaShortcuts() const360 QList<Latte::View *> Synchronizer::currentViewsWithPlasmaShortcuts() const
361 {
362 QList<Latte::View *> views;
363
364 for(auto layout : currentLayouts()) {
365 views << layout->viewsWithPlasmaShortcuts();
366 }
367
368 return views;
369 }
370
sortedCurrentViews() const371 QList<Latte::View *> Synchronizer::sortedCurrentViews() const
372 {
373 QList<Latte::View *> views = currentViews();
374
375 return Layout::GenericLayout::sortedLatteViews(views);
376 }
377
viewsBasedOnActivityId(const QString & id) const378 QList<Latte::View *> Synchronizer::viewsBasedOnActivityId(const QString &id) const
379 {
380 QList<Latte::View *> views;
381
382 for(auto layout : centralLayoutsForActivity(id)) {
383 if (m_centralLayouts.contains(layout)) {
384 views << layout->latteViews();
385 }
386 }
387
388 return views;
389 }
390
layout(QString layoutname) const391 Layout::GenericLayout *Synchronizer::layout(QString layoutname) const
392 {
393 Layout::GenericLayout *l = centralLayout(layoutname);
394
395 return l;
396 }
397
screenForContainment(Plasma::Containment * containment)398 int Synchronizer::screenForContainment(Plasma::Containment *containment)
399 {
400 for (auto layout : m_centralLayouts) {
401 if (layout->contains(containment)) {
402 return layout->screenForContainment(containment);
403 }
404 }
405
406 return -1;
407 }
408
viewForContainment(uint id)409 Latte::View *Synchronizer::viewForContainment(uint id)
410 {
411 for (auto layout : m_centralLayouts) {
412 Latte::View *view = layout->viewForContainment(id);
413
414 if (view) {
415 return view;
416 }
417 }
418
419 return nullptr;
420 }
421
viewForContainment(Plasma::Containment * containment)422 Latte::View *Synchronizer::viewForContainment(Plasma::Containment *containment)
423 {
424 for (auto layout : m_centralLayouts) {
425 Latte::View *view = layout->viewForContainment(containment);
426
427 if (view) {
428 return view;
429 }
430 }
431
432 return nullptr;
433 }
434
addLayout(CentralLayout * layout)435 void Synchronizer::addLayout(CentralLayout *layout)
436 {
437 if (!m_centralLayouts.contains(layout)) {
438 m_centralLayouts.append(layout);
439 layout->initToCorona(m_manager->corona());
440 }
441 }
442
onActivityRemoved(const QString & activityid)443 void Synchronizer::onActivityRemoved(const QString &activityid)
444 {
445 if (!m_assignedLayouts.contains(activityid)) {
446 return;
447 }
448
449 //! remove any other explicit set layouts for the current activity
450 QStringList explicits = m_assignedLayouts[activityid];
451
452 for(auto explicitlayoutname : explicits) {
453 QString explicitlayoutid = m_layouts.idForName(explicitlayoutname);
454
455 m_layouts[explicitlayoutid].activities.removeAll(activityid);
456 m_manager->setOnActivities(explicitlayoutname, m_layouts[explicitlayoutid].activities);
457 emit layoutActivitiesChanged(m_layouts[explicitlayoutid]);
458 }
459
460 QStringList freelayoutnames;
461
462 if (m_assignedLayouts.contains(Data::Layout::FREEACTIVITIESID)) {
463 freelayoutnames = m_assignedLayouts[Data::Layout::FREEACTIVITIESID];
464 }
465
466 reloadAssignedLayouts();
467
468 for(auto freelayoutname : freelayoutnames) {
469 //! inform free activities layouts that their activities probably changed
470 CentralLayout *central = centralLayout(freelayoutname);
471
472 if (central) {
473 emit central->activitiesChanged();
474 }
475 }
476 }
477
updateBorderlessMaximizedAfterTimer()478 void Synchronizer::updateBorderlessMaximizedAfterTimer()
479 {
480 //! this signal is also triggered when runningactivities are changed and actually is received first
481 //! this is why we need a timer here in order to delay that execution and not activate/deactivate
482 //! maximizedborders faulty because syncMultipleLayoutsToActivities(); has not been executed yet
483 m_updateBorderlessMaximized.start();
484 }
485
hideAllViews()486 void Synchronizer::hideAllViews()
487 {
488 for (const auto layout : m_centralLayouts) {
489 emit currentLayoutIsSwitching(layout->name());
490 }
491 }
492
pauseLayout(QString layoutName)493 void Synchronizer::pauseLayout(QString layoutName)
494 {
495 if (m_manager->memoryUsage() == MemoryUsage::MultipleLayouts) {
496 CentralLayout *layout = centralLayout(layoutName);
497
498 if (layout->isOnAllActivities()) {
499 return;
500 }
501
502 QStringList appliedactivities = layout->appliedActivities();
503
504 if (layout && !appliedactivities.isEmpty()) {
505 int i = 0;
506
507 for (const auto &activityid : appliedactivities) {
508 //! Stopping the activities must be done asynchronous because otherwise
509 //! the activity manager cant close multiple activities
510 QTimer::singleShot(i * 1000, [this, activityid]() {
511 m_activitiesController->stopActivity(activityid);
512 });
513
514 i = i + 1;
515 }
516 }
517 }
518 }
519
syncActiveLayoutsToOriginalFiles()520 void Synchronizer::syncActiveLayoutsToOriginalFiles()
521 {
522 if (m_manager->memoryUsage() == MemoryUsage::MultipleLayouts) {
523 for (const auto layout : m_centralLayouts) {
524 layout->syncToLayoutFile();
525 }
526 }
527 }
528
syncLatteViewsToScreens()529 void Synchronizer::syncLatteViewsToScreens()
530 {
531 for (const auto layout : m_centralLayouts) {
532 layout->syncLatteViewsToScreens();
533 }
534 }
535
unloadCentralLayout(CentralLayout * layout)536 void Synchronizer::unloadCentralLayout(CentralLayout *layout)
537 {
538 int pos = m_centralLayouts.indexOf(layout);
539
540 if (pos>=0) {
541 CentralLayout *central = m_centralLayouts.takeAt(pos);
542
543 if (m_multipleModeInitialized) {
544 central->syncToLayoutFile(true);
545 }
546
547 central->unloadLatteViews();
548 central->unloadContainments();
549
550 if (m_multipleModeInitialized) {
551 m_manager->clearUnloadedContainmentsFromLinkedFile(central->unloadedContainmentsIds(), true);
552 }
553
554 delete central;
555 }
556 }
557
initLayouts()558 void Synchronizer::initLayouts()
559 {
560 m_layouts.clear();
561
562 QDir layoutDir(Layouts::Importer::layoutUserDir());
563 QStringList filter;
564 filter.append(QString("*.layout.latte"));
565 QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
566
567 for (const auto &layout : files) {
568 if (layout.contains(Layout::MULTIPLELAYOUTSHIDDENNAME)) {
569 //! IMPORTANT: DON'T ADD MultipleLayouts hidden file in layouts list
570 continue;
571 }
572
573 QString layoutpath = layoutDir.absolutePath() + "/" + layout;
574 onLayoutAdded(layoutpath);
575 }
576
577 emit layoutsChanged();
578
579 if (!m_isLoaded) {
580 m_isLoaded = true;
581 connect(m_manager->corona()->templatesManager(), &Latte::Templates::Manager::newLayoutAdded, this, &Synchronizer::onLayoutAdded);
582 connect(m_manager->importer(), &Latte::Layouts::Importer::newLayoutAdded, this, &Synchronizer::onLayoutAdded);
583 }
584 }
585
onLayoutAdded(const QString & layout)586 void Synchronizer::onLayoutAdded(const QString &layout)
587 {
588 CentralLayout centrallayout(this, layout);
589 m_layouts.insertBasedOnName(centrallayout.data());
590
591 if (m_isLoaded) {
592 emit layoutsChanged();
593 }
594 }
595
reloadAssignedLayouts()596 void Synchronizer::reloadAssignedLayouts()
597 {
598 m_assignedLayouts.clear();
599
600 for (int i=0; i< m_layouts.rowCount(); ++i) {
601 for (const auto &activity : m_layouts[i].activities) {
602 if (m_assignedLayouts.contains(activity)) {
603 m_assignedLayouts[activity] << m_layouts[i].name;
604 } else {
605 m_assignedLayouts[activity] = QStringList(m_layouts[i].name);
606 }
607 }
608 }
609 }
610
unloadLayouts()611 void Synchronizer::unloadLayouts()
612 {
613 //! Unload all CentralLayouts
614 while (!m_centralLayouts.isEmpty()) {
615 CentralLayout *layout = m_centralLayouts.at(0);
616 unloadCentralLayout(layout);
617 }
618
619 m_multipleModeInitialized = false;
620 }
621
memoryInitialized() const622 bool Synchronizer::memoryInitialized() const
623 {
624 return ((m_manager->memoryUsage() == MemoryUsage::SingleLayout && m_centralLayouts.size()>0)
625 || (m_manager->memoryUsage() == MemoryUsage::MultipleLayouts && m_multipleModeInitialized));
626 }
627
initSingleMode(QString layoutName)628 bool Synchronizer::initSingleMode(QString layoutName)
629 {
630 QString layoutpath = layoutName.isEmpty() ? layoutPath(m_manager->corona()->universalSettings()->singleModeLayoutName()) : layoutPath(layoutName);
631
632 if (layoutpath.isEmpty()) {
633 qDebug() << "Layout : " << layoutName << " was not found...";
634 return false;
635 }
636
637 if (m_centralLayouts.size() > 0) {
638 emit currentLayoutIsSwitching(m_centralLayouts[0]->name());
639 }
640
641 //! this code must be called asynchronously because it can create crashes otherwise.
642 //! Tasks plasmoid case that triggers layouts switching through its context menu
643 QTimer::singleShot(LAYOUTSINITINTERVAL, [this, layoutName, layoutpath]() {
644 qDebug() << " ... initializing layout in single mode : " << layoutName << " - " << layoutpath;
645 unloadLayouts();
646
647 //! load the main layout/corona file
648 CentralLayout *newLayout = new CentralLayout(this, layoutpath, layoutName);
649 addLayout(newLayout);
650
651 m_manager->loadLatteLayout(layoutpath);
652
653 emit centralLayoutsChanged();
654
655 if (m_isSingleLayoutInDeprecatedRenaming) {
656 QString deprecatedlayoutpath = layoutPath(m_manager->corona()->universalSettings()->singleModeLayoutName());
657
658 if (!deprecatedlayoutpath.isEmpty()) {
659 qDebug() << "Removing Deprecated single layout after renaming:: " << m_manager->corona()->universalSettings()->singleModeLayoutName();
660 QFile(deprecatedlayoutpath).remove();
661 }
662
663 m_isSingleLayoutInDeprecatedRenaming = false;
664 }
665
666 m_manager->corona()->universalSettings()->setSingleModeLayoutName(layoutName);
667
668 emit initializationFinished();
669 });
670
671 return true;
672 }
673
initMultipleMode(QString layoutName)674 bool Synchronizer::initMultipleMode(QString layoutName)
675 {
676 if (m_multipleModeInitialized) {
677 return false;
678 }
679
680 for (const auto layout : m_centralLayouts) {
681 emit currentLayoutIsSwitching(layout->name());
682 }
683
684 //! this code must be called asynchronously because it can create crashes otherwise.
685 //! Tasks plasmoid case that triggers layouts switching through its context menu
686 QTimer::singleShot(LAYOUTSINITINTERVAL, [this, layoutName]() {
687 qDebug() << " ... initializing layout in multiple mode : " << layoutName ;
688 unloadLayouts();
689
690 m_manager->loadLatteLayout(layoutPath(QString(Layout::MULTIPLELAYOUTSHIDDENNAME)));
691
692 m_multipleModeInitialized = true;
693
694 emit centralLayoutsChanged();
695
696 if (!layoutName.isEmpty()) {
697 switchToLayoutInMultipleModeBasedOnActivities(layoutName);
698 }
699
700 syncMultipleLayoutsToActivities();
701
702 emit initializationFinished();
703 });
704
705 return true;
706 }
707
switchToLayoutInSingleMode(QString layoutName)708 bool Synchronizer::switchToLayoutInSingleMode(QString layoutName)
709 {
710 if (!memoryInitialized() || m_manager->memoryUsage() != MemoryUsage::SingleLayout) {
711 return false;
712 }
713
714 if (m_centralLayouts.size()>0 && m_centralLayouts[0]->name() == layoutName) {
715 return true;
716 }
717
718 return initSingleMode(layoutName);
719 }
720
switchToLayoutInMultipleModeBasedOnActivities(const QString & layoutName)721 bool Synchronizer::switchToLayoutInMultipleModeBasedOnActivities(const QString &layoutName)
722 {
723 Data::Layout layoutdata;
724 CentralLayout *central = centralLayout(layoutName);
725
726 if (central) {
727 layoutdata = central->data();
728 } else if (m_layouts.containsName(layoutName)) {
729 QString layoutid = m_layouts.idForName(layoutName);
730 CentralLayout storagedlayout(this, layoutid);
731 layoutdata = storagedlayout.data();
732
733 m_layouts[layoutid] = layoutdata;
734 }
735
736 if (layoutdata.isEmpty()) {
737 return false;
738 }
739
740 QString switchToActivity;
741
742 //! try to not remove activityids that belong to different machines that are not currently present
743 QStringList validlayoutactivities = validActivities(layoutdata.activities);
744
745 if (layoutdata.isOnAllActivities()) {
746 //! no reason to switch in any activity;
747 } else if (layoutdata.isForFreeActivities()) {
748 //! free-activities case
749 QStringList freerunningactivities = freeRunningActivities();
750
751 if (freerunningactivities.count() > 0) {
752 if (freerunningactivities.contains(layoutdata.lastUsedActivity)) {
753 switchToActivity = layoutdata.lastUsedActivity;
754 } else {
755 switchToActivity = freerunningactivities[0];
756 }
757 } else {
758 QStringList freepausedactivities = freeActivities();
759
760 if (freepausedactivities.count() > 0) {
761 switchToActivity = freepausedactivities[0];
762 }
763 }
764 } else if (!validlayoutactivities.isEmpty()) {
765 //! set on-explicit activities
766 QStringList allactivities = activities();
767
768 if (validlayoutactivities.contains(layoutdata.lastUsedActivity)) {
769 switchToActivity = layoutdata.lastUsedActivity;
770 } else {
771 switchToActivity = validlayoutactivities[0];
772 }
773 } else if (validlayoutactivities.isEmpty() && m_layouts.containsName(layoutName)) {
774 //! no-activities are set
775 //! has not been set in any activities but nonetheless it is requested probably by the user
776 //! requested layout is assigned explicitly in current activity and any remaining explicit layouts
777 //! are removing current activity from their activities list
778 QString layoutid = m_layouts.idForName(layoutName);
779 QString currentactivityid = m_activitiesController->currentActivity();
780
781 QStringList layoutIdsChanged;
782
783 m_layouts[layoutid].activities.append(currentactivityid);
784 m_manager->setOnActivities(layoutName, m_layouts[layoutid].activities);
785 emit layoutActivitiesChanged(m_layouts[layoutid]);
786
787 layoutIdsChanged << layoutid;
788
789 if (m_assignedLayouts.contains(currentactivityid)) {
790 //! remove any other explicit set layouts for the current activity
791 QStringList explicits = m_assignedLayouts[currentactivityid];
792
793 for(auto explicitlayoutname : explicits) {
794 QString explicitlayoutid = m_layouts.idForName(explicitlayoutname);
795
796 m_layouts[explicitlayoutid].activities.removeAll(currentactivityid);
797 m_manager->setOnActivities(explicitlayoutname, m_layouts[explicitlayoutid].activities);
798 emit layoutActivitiesChanged(m_layouts[explicitlayoutid]);
799 }
800 }
801
802 QStringList freelayoutnames;
803 if (m_assignedLayouts.contains(Data::Layout::FREEACTIVITIESID)) {
804 freelayoutnames = m_assignedLayouts[Data::Layout::FREEACTIVITIESID];
805 }
806
807 reloadAssignedLayouts();
808
809 for(auto freelayoutname : freelayoutnames) {
810 //! inform free activities layouts that their activities probably changed
811 CentralLayout *central = centralLayout(freelayoutname);
812
813 if (central) {
814 emit central->activitiesChanged();
815 }
816 }
817 }
818
819 if (!switchToActivity.isEmpty()) {
820 if (!m_manager->corona()->activitiesConsumer()->runningActivities().contains(switchToActivity)) {
821 m_activitiesController->startActivity(switchToActivity);
822 }
823
824 m_activitiesController->setCurrentActivity(switchToActivity);
825 }
826
827 return true;
828 }
829
switchToLayoutInMultipleMode(QString layoutName)830 bool Synchronizer::switchToLayoutInMultipleMode(QString layoutName)
831 {
832 if (!memoryInitialized() || m_manager->memoryUsage() != MemoryUsage::MultipleLayouts) {
833 return false;
834 }
835
836 CentralLayout *layout = centralLayout(layoutName);
837
838 if (layout) {
839 QStringList appliedActivities = layout->appliedActivities();
840 QString nextActivity = !layout->lastUsedActivity().isEmpty() ? layout->lastUsedActivity() : appliedActivities[0];
841
842 if (!appliedActivities.contains(m_manager->corona()->activitiesConsumer()->currentActivity())) {
843 //! it means we are at a foreign activity and we can switch to correct one
844 m_activitiesController->setCurrentActivity(nextActivity);
845 return true;
846 }
847 } else {
848 if (!layoutName.isEmpty()) {
849 switchToLayoutInMultipleModeBasedOnActivities(layoutName);
850 }
851
852 syncMultipleLayoutsToActivities();
853 }
854
855 return true;
856 }
857
858
switchToLayout(QString layoutName,MemoryUsage::LayoutsMemory newMemoryUsage)859 bool Synchronizer::switchToLayout(QString layoutName, MemoryUsage::LayoutsMemory newMemoryUsage)
860 {
861 qDebug() << " >>>>> SWITCHING >> " << layoutName << " __ from memory: " << m_manager->memoryUsage() << " to memory: " << newMemoryUsage;
862
863 if (newMemoryUsage == MemoryUsage::Current) {
864 newMemoryUsage = m_manager->memoryUsage();
865 }
866
867 if (!memoryInitialized() || newMemoryUsage != m_manager->memoryUsage()) {
868 //! Initiate Layouts memory properly
869 m_manager->setMemoryUsage(newMemoryUsage);
870
871 return (newMemoryUsage == MemoryUsage::SingleLayout ? initSingleMode(layoutName) : initMultipleMode(layoutName));
872 }
873
874 if (m_manager->memoryUsage() == MemoryUsage::SingleLayout) {
875 return switchToLayoutInSingleMode(layoutName);
876 } else {
877 return switchToLayoutInMultipleMode(layoutName);
878 }
879 }
880
syncMultipleLayoutsToActivities()881 void Synchronizer::syncMultipleLayoutsToActivities()
882 {
883 qDebug() << " ---- --------- ------ syncMultipleLayoutsToActivities ------- ";
884 qDebug() << " ---- --------- ------ ------------------------------- ------- ";
885
886 QStringList layoutNamesToUnload;
887 QStringList layoutNamesToLoad;
888 QStringList currentNames = centralLayoutsNames();
889
890 //! discover OnAllActivities layouts
891 if (m_assignedLayouts.contains(Data::Layout::ALLACTIVITIESID)) {
892 layoutNamesToLoad << m_assignedLayouts[Data::Layout::ALLACTIVITIESID];
893 }
894
895 //! discover ForFreeActivities layouts
896 if (m_assignedLayouts.contains(Data::Layout::FREEACTIVITIESID) && freeRunningActivities().count()>0) {
897 layoutNamesToLoad << m_assignedLayouts[Data::Layout::FREEACTIVITIESID];
898 }
899
900 //! discover layouts assigned to explicit activities based on running activities
901 for (const auto &activity : runningActivities()) {
902 #if KF5_VERSION_MINOR < 81
903 if (KWindowSystem::isPlatformWayland() && (m_activitiesController->currentActivity() != activity)){
904 //! Wayland Protection: Plasma wayland does not support Activities for windows before kde frameworks 5.81
905 //! In that scenario we can load the layouts that belong OnAllActivities + (ForFreeActivities OR SpecificActivity)
906 continue;
907 }
908 #endif
909
910 if (m_assignedLayouts.contains(activity)) {
911 layoutNamesToLoad << m_assignedLayouts[activity];
912 }
913 }
914
915 //! discover layouts that must be unloaded because of running activities changes
916 for (const auto layout : m_centralLayouts) {
917 if (!layoutNamesToLoad.contains(layout->name())) {
918 layoutNamesToUnload << layout->name();
919 }
920 }
921
922 QString defaultForcedLayout;
923
924 //! Safety
925 if (layoutNamesToLoad.isEmpty()) {
926 //! If no layout is found then force loading Default Layout
927 QString layoutPath = m_manager->corona()->templatesManager()->newLayout("", i18n(Templates::DEFAULTLAYOUTTEMPLATENAME));
928 layoutNamesToLoad << Layout::AbstractLayout::layoutName(layoutPath);
929 m_manager->setOnAllActivities(layoutNamesToLoad[0]);
930 defaultForcedLayout = layoutNamesToLoad[0];
931 }
932
933 QStringList newlyActivatedLayouts;
934
935 //! Add needed Layouts based on Activities settings
936 for (const auto &layoutname : layoutNamesToLoad) {
937 if (!centralLayout(layoutname)) {
938 CentralLayout *newLayout = new CentralLayout(this, QString(layoutPath(layoutname)), layoutname);
939
940 if (newLayout) {
941 qDebug() << "ACTIVATING LAYOUT ::::: " << layoutname;
942 addLayout(newLayout);
943 newLayout->importToCorona();
944
945 if (!defaultForcedLayout.isEmpty() && defaultForcedLayout == layoutname) {
946 emit newLayoutAdded(newLayout->data());
947 }
948
949 newlyActivatedLayouts << newLayout->name();
950 }
951 }
952 }
953
954 if (newlyActivatedLayouts.count()>0 && m_manager->corona()->universalSettings()->showInfoWindow()) {
955 m_manager->showInfoWindow(i18np("Activating layout: <b>%2</b> ...",
956 "Activating layouts: <b>%2</b> ...",
957 newlyActivatedLayouts.count(),
958 newlyActivatedLayouts.join(", ")),
959 4000, QStringList(Data::Layout::ALLACTIVITIESID));
960 }
961
962 //! Unload no needed Layouts
963
964 //! hide layouts that will be removed in the end
965 if (!layoutNamesToUnload.isEmpty()) {
966 for (const auto layoutname : layoutNamesToUnload) {
967 emit currentLayoutIsSwitching(layoutname);
968 }
969
970 QTimer::singleShot(LAYOUTSINITINTERVAL, [this, layoutNamesToUnload]() {
971 unloadLayouts(layoutNamesToUnload);
972 });
973 }
974
975 currentNames.sort();
976 layoutNamesToLoad.sort();
977
978 if (currentNames != layoutNamesToLoad) {
979 emit centralLayoutsChanged();
980 }
981 }
982
unloadLayouts(const QStringList & layoutNames)983 void Synchronizer::unloadLayouts(const QStringList &layoutNames)
984 {
985 if (layoutNames.isEmpty()) {
986 return;
987 }
988
989 //! Unload no needed Layouts
990 for (const auto &layoutname : layoutNames) {
991 CentralLayout *layout = centralLayout(layoutname);
992 int posLayout = centralLayoutPos(layoutname);
993
994 if (posLayout >= 0) {
995 qDebug() << "REMOVING LAYOUT ::::: " << layoutname;
996 m_centralLayouts.removeAt(posLayout);
997
998 layout->syncToLayoutFile(true);
999 layout->unloadContainments();
1000 layout->unloadLatteViews();
1001 m_manager->clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds());
1002 delete layout;
1003 }
1004 }
1005
1006 emit centralLayoutsChanged();
1007 }
1008
updateKWinDisabledBorders()1009 void Synchronizer::updateKWinDisabledBorders()
1010 {
1011 if (KWindowSystem::isPlatformWayland()) {
1012 // BUG: https://bugs.kde.org/show_bug.cgi?id=428202
1013 // KWin::reconfigure() function blocks/freezes Latte under wayland
1014 return;
1015 }
1016
1017 if (!m_manager->corona()->universalSettings()->canDisableBorders()) {
1018 m_manager->corona()->universalSettings()->kwin_setDisabledMaximizedBorders(false);
1019 } else {
1020 if (m_manager->corona()->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout) {
1021 m_manager->corona()->universalSettings()->kwin_setDisabledMaximizedBorders(m_centralLayouts.at(0)->disableBordersForMaximizedWindows());
1022 } else if (m_manager->corona()->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
1023 QList<CentralLayout *> centrals = centralLayoutsForActivity(m_manager->corona()->activitiesConsumer()->currentActivity());
1024
1025 for (int i = 0; i < centrals.size(); ++i) {
1026 CentralLayout *layout = centrals.at(i);
1027
1028 if (layout->disableBordersForMaximizedWindows()) {
1029 m_manager->corona()->universalSettings()->kwin_setDisabledMaximizedBorders(true);
1030 return;
1031 }
1032 }
1033
1034 //! avoid initialization step for example during startup that no layouts have been loaded yet
1035 if (centrals.size() > 0) {
1036 m_manager->corona()->universalSettings()->kwin_setDisabledMaximizedBorders(false);
1037 }
1038
1039 }
1040 }
1041 }
1042
1043 }
1044 } // end of namespace
1045