1 /*
2     SPDX-FileCopyrightText: 2019 Michail Vourlakos <mvourlakos@gmail.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "genericlayout.h"
8 
9 // local
10 #include "abstractlayout.h"
11 #include "../apptypes.h"
12 #include "../lattecorona.h"
13 #include "../screenpool.h"
14 #include "../layouts/importer.h"
15 #include "../layouts/manager.h"
16 #include "../layouts/storage.h"
17 #include "../layouts/synchronizer.h"
18 #include "../shortcuts/shortcutstracker.h"
19 #include "../templates/templatesmanager.h"
20 #include "../view/view.h"
21 #include "../view/positioner.h"
22 
23 // Qt
24 #include <QDebug>
25 #include <QScreen>
26 
27 // Plasma
28 #include <Plasma>
29 #include <Plasma/Applet>
30 #include <Plasma/Containment>
31 
32 // KDE
33 #include <KActionCollection>
34 #include <KConfigGroup>
35 
36 namespace Latte {
37 namespace Layout {
38 
GenericLayout(QObject * parent,QString layoutFile,QString assignedName)39 GenericLayout::GenericLayout(QObject *parent, QString layoutFile, QString assignedName)
40     : AbstractLayout (parent, layoutFile, assignedName)
41 {
42 }
43 
~GenericLayout()44 GenericLayout::~GenericLayout()
45 {
46 }
47 
type() const48 Type GenericLayout::type() const
49 {
50     return Type::Generic;
51 }
52 
unloadContainments()53 void GenericLayout::unloadContainments()
54 {
55     if (!m_corona) {
56         return;
57     }
58 
59     qDebug() << "Layout - " + name() + " : [unloadContainments]"
60              << "containments ::: " << m_containments.size()
61              << " ,latteViews in memory ::: " << m_latteViews.size()
62              << " ,hidden latteViews in memory :::  " << m_waitingLatteViews.size();
63 
64     for (const auto view : m_latteViews) {
65         view->disconnectSensitiveSignals();
66     }
67 
68     for (const auto view : m_waitingLatteViews) {
69         view->disconnectSensitiveSignals();
70     }
71 
72     m_unloadedContainmentsIds.clear();
73 
74     QList<Plasma::Containment *> subcontainments;
75 
76     //!identify subcontainments and unload them first
77     for (const auto containment : m_containments) {
78         if (Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent())) {
79             subcontainments.append(containment);
80         }
81     }
82 
83     while (!subcontainments.isEmpty()) {
84         Plasma::Containment *sub = subcontainments.at(0);
85         m_unloadedContainmentsIds << QString::number(sub->id());
86         subcontainments.removeFirst();
87         m_containments.removeAll(sub);
88         delete sub;
89     }
90 
91     while (!m_containments.isEmpty()) {
92         Plasma::Containment *containment = m_containments.at(0);
93         m_unloadedContainmentsIds << QString::number(containment->id());
94         m_containments.removeFirst();
95         delete containment;
96     }
97 }
98 
unloadLatteViews()99 void GenericLayout::unloadLatteViews()
100 {
101     if (!m_corona) {
102         return;
103     }
104 
105     qDebug() << "Layout - " + name() + " : [unloadLatteViews]"
106              << "containments ::: " << m_containments.size()
107              << " ,latteViews in memory ::: " << m_latteViews.size()
108              << " ,hidden latteViews in memory :::  " << m_waitingLatteViews.size();
109 
110     //!disconnect signals in order to avoid crashes when the layout is unloading
111     disconnect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged);
112     disconnect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged);
113     disconnect(this, &GenericLayout::activitiesChanged, this, &GenericLayout::updateLastUsedActivity);
114     disconnect(m_corona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, &GenericLayout::updateLastUsedActivity);
115 
116     for (const auto view : m_latteViews) {
117         view->disconnectSensitiveSignals();
118     }
119 
120     for (const auto view : m_waitingLatteViews) {
121         view->disconnectSensitiveSignals();
122     }
123 
124     qDeleteAll(m_latteViews);
125     qDeleteAll(m_waitingLatteViews);
126     m_latteViews.clear();
127     m_waitingLatteViews.clear();
128 }
129 
blockAutomaticLatteViewCreation() const130 bool GenericLayout::blockAutomaticLatteViewCreation() const
131 {
132     return m_blockAutomaticLatteViewCreation;
133 }
134 
setBlockAutomaticLatteViewCreation(bool block)135 void GenericLayout::setBlockAutomaticLatteViewCreation(bool block)
136 {
137     if (m_blockAutomaticLatteViewCreation == block) {
138         return;
139     }
140 
141     m_blockAutomaticLatteViewCreation = block;
142 }
143 
isActive() const144 bool GenericLayout::isActive() const
145 {
146     return m_corona && (m_corona->layoutsManager()->synchronizer()->layout(m_layoutName) != nullptr);
147 }
148 
isCurrent()149 bool GenericLayout::isCurrent()
150 {
151     if (!m_corona) {
152         return false;
153     }
154 
155     return m_corona->layoutsManager()->currentLayoutsNames().contains(name());
156 }
157 
background() const158 QString GenericLayout::background() const
159 {
160     QString colorsPath = m_corona->kPackage().path() + "../../shells/org.kde.latte.shell/contents/images/canvas/";
161 
162     if (backgroundStyle() == Layout::PatternBackgroundStyle) {
163         if (customBackground().isEmpty()) {
164 
165             return colorsPath + "defaultcustomprint.jpg";
166         } else {
167             return AbstractLayout::customBackground();
168         }
169     }
170 
171     return colorsPath + AbstractLayout::color() + "print.jpg";
172 }
173 
textColor() const174 QString GenericLayout::textColor() const
175 {
176     if (backgroundStyle() == Layout::PatternBackgroundStyle && customBackground().isEmpty() && customTextColor().isEmpty()) {
177         return AbstractLayout::defaultCustomTextColor();
178     }
179 
180     return AbstractLayout::textColor();
181 }
182 
viewsCount(int screen) const183 int GenericLayout::viewsCount(int screen) const
184 {
185     if (!m_corona) {
186         return 0;
187     }
188 
189     QScreen *scr = m_corona->screenPool()->screenForId(screen);
190 
191     int views{0};
192 
193     for (const auto view : m_latteViews) {
194         if (view && view->screen() == scr && !view->containment()->destroyed()) {
195             ++views;
196         }
197     }
198 
199     return views;
200 }
201 
viewsCount(QScreen * screen) const202 int GenericLayout::viewsCount(QScreen *screen) const
203 {
204     if (!m_corona) {
205         return 0;
206     }
207 
208     int views{0};
209 
210     for (const auto view : m_latteViews) {
211         if (view && view->screen() == screen && !view->containment()->destroyed()) {
212             ++views;
213         }
214     }
215 
216     return views;
217 }
218 
viewsCount() const219 int GenericLayout::viewsCount() const
220 {
221     if (!m_corona) {
222         return 0;
223     }
224 
225     int views{0};
226 
227     for (const auto view : m_latteViews) {
228         if (view && view->containment() && !view->containment()->destroyed()) {
229             ++views;
230         }
231     }
232 
233     return views;
234 }
235 
qmlFreeEdges(int screen) const236 QList<int> GenericLayout::qmlFreeEdges(int screen) const
237 {
238     if (!m_corona) {
239         const QList<int> emptyEdges;
240         return emptyEdges;
241     }
242 
243     const auto edges = freeEdges(screen);
244     QList<int> edgesInt;
245 
246     for (const Plasma::Types::Location &edge : edges) {
247         edgesInt.append(static_cast<int>(edge));
248     }
249 
250     return edgesInt;
251 }
252 
freeEdges(QScreen * scr) const253 QList<Plasma::Types::Location> GenericLayout::freeEdges(QScreen *scr) const
254 {
255     using Plasma::Types;
256     QList<Types::Location> edges{Types::BottomEdge, Types::LeftEdge,
257                 Types::TopEdge, Types::RightEdge};
258 
259     if (!m_corona) {
260         return edges;
261     }
262 
263     for (const auto view : m_latteViews) {
264         if (view && view->positioner()->currentScreenName() == scr->name()) {
265             edges.removeOne(view->location());
266         }
267     }
268 
269     return edges;
270 }
271 
freeEdges(int screen) const272 QList<Plasma::Types::Location> GenericLayout::freeEdges(int screen) const
273 {
274     using Plasma::Types;
275     QList<Types::Location> edges{Types::BottomEdge, Types::LeftEdge,
276                 Types::TopEdge, Types::RightEdge};
277 
278     if (!m_corona) {
279         return edges;
280     }
281 
282     QScreen *scr = m_corona->screenPool()->screenForId(screen);
283 
284     for (const auto view : m_latteViews) {
285         if (view && scr && view->positioner()->currentScreenName() == scr->name()) {
286             edges.removeOne(view->location());
287         }
288     }
289 
290     return edges;
291 }
292 
viewsWithTasks() const293 int GenericLayout::viewsWithTasks() const
294 {
295     if (!m_corona) {
296         return 0;
297     }
298 
299     int result = 0;
300 
301     for (const auto view : m_latteViews) {
302         if (view->extendedInterface()->hasLatteTasks() || view->extendedInterface()->hasPlasmaTasks()) {
303             result++;
304         }
305     }
306 
307     return result;
308 }
309 
unloadedContainmentsIds()310 QStringList GenericLayout::unloadedContainmentsIds()
311 {
312     return m_unloadedContainmentsIds;
313 }
314 
corona() const315 Latte::Corona *GenericLayout::corona() const
316 {
317     return m_corona;
318 }
319 
latteViewType(uint containmentId) const320 Types::ViewType GenericLayout::latteViewType(uint containmentId) const
321 {
322     for (const auto view : m_latteViews) {
323         if (view->containment() && view->containment()->id() == containmentId) {
324             return view->type();
325         }
326     }
327 
328     return Types::DockView;
329 }
330 
highestPriorityView()331 Latte::View *GenericLayout::highestPriorityView()
332 {
333     QList<Latte::View *> views = sortedLatteViews();
334 
335     return (views.count() > 0 ? views[0] : nullptr);
336 }
337 
lastConfigViewFor()338 Latte::View *GenericLayout::lastConfigViewFor()
339 {
340     return m_lastConfigViewFor;
341 }
342 
setLastConfigViewFor(Latte::View * view)343 void GenericLayout::setLastConfigViewFor(Latte::View *view)
344 {
345     if (m_lastConfigViewFor == view) {
346         return;
347     }
348 
349     m_lastConfigViewFor = view;
350 
351     if (view) {
352         emit lastConfigViewForChanged(view);
353     }
354 }
355 
onLastConfigViewChangedFrom(Latte::View * view)356 void GenericLayout::onLastConfigViewChangedFrom(Latte::View *view)
357 {
358     if (!m_latteViews.values().contains(view)) {
359         setLastConfigViewFor(nullptr);
360     }
361 }
362 
viewForContainment(uint id) const363 Latte::View *GenericLayout::viewForContainment(uint id) const
364 {
365     for(auto view : m_latteViews) {
366         if (view && view->containment()->id() == id) {
367             return view;
368         }
369     }
370 
371     return nullptr;
372 }
373 
containmentForId(uint id) const374 Plasma::Containment *GenericLayout::containmentForId(uint id) const
375 {
376     for(auto containment : m_containments) {
377         if (containment->id() == id) {
378             return containment;
379         }
380     }
381 
382     return nullptr;
383 }
384 
contains(Plasma::Containment * containment) const385 bool GenericLayout::contains(Plasma::Containment *containment) const
386 {
387     return m_containments.contains(containment);
388 }
389 
screenForContainment(Plasma::Containment * containment)390 int GenericLayout::screenForContainment(Plasma::Containment *containment)
391 {
392     if (!containment) {
393         return -1;
394     }
395 
396     //! there is a pending update
397     QString containmentid = QString::number(containment->id());
398     if (m_pendingContainmentUpdates.containsId(containmentid)) {
399         if (m_corona && m_pendingContainmentUpdates[containmentid].onPrimary) {
400             return m_corona->screenPool()->primaryScreenId();
401         } else {
402             return m_pendingContainmentUpdates[containmentid].screen;
403         }
404     }
405 
406     //! there is a view present
407     Latte::View *view{nullptr};
408 
409     if (m_latteViews.contains(containment)) {
410         view = m_latteViews[containment];
411     } else if (m_waitingLatteViews.contains(containment)) {
412         view = m_waitingLatteViews[containment];
413     }
414 
415     if (view && view->screen()) {
416         return m_corona->screenPool()->id(view->screen()->name());
417     }
418 
419     //! fallback scenario
420     return containment->lastScreen();
421 }
422 
containsView(const int & containmentId) const423 bool GenericLayout::containsView(const int &containmentId) const
424 {
425     if (!isActive()) {
426         return Layouts::Storage::self()->containsView(file(), containmentId);
427     }
428 
429     for(auto containment : m_containments) {
430         if ((int)containment->id() == containmentId && Layouts::Storage::self()->isLatteContainment(containment)) {
431             return true;
432         }
433     }
434 
435     return false;
436 }
437 
viewForContainment(Plasma::Containment * containment) const438 Latte::View *GenericLayout::viewForContainment(Plasma::Containment *containment) const
439 {
440     if (m_containments.contains(containment) && m_latteViews.contains(containment)) {
441         return m_latteViews[containment];
442     }
443 
444     return nullptr;
445 }
446 
latteViews()447 QList<Latte::View *> GenericLayout::latteViews()
448 {
449     return m_latteViews.values();
450 }
451 
sortedLatteViews()452 QList<Latte::View *> GenericLayout::sortedLatteViews()
453 {
454     return sortedLatteViews(latteViews());
455 }
456 
sortedLatteViews(QList<Latte::View * > views)457 QList<Latte::View *> GenericLayout::sortedLatteViews(QList<Latte::View *> views)
458 {
459     QList<Latte::View *> sortedViews = views;
460 
461     qDebug() << " -------- ";
462 
463     for (int i = 0; i < sortedViews.count(); ++i) {
464         qDebug() << i << ". " << sortedViews[i]->screen()->name() << " - " << sortedViews[i]->location();
465     }
466 
467     //! sort the views based on screens and edges priorities
468     //! views on primary screen have higher priority and
469     //! for views in the same screen the priority goes to
470     //! Bottom,Left,Top,Right
471     for (int i = 0; i < sortedViews.size(); ++i) {
472         for (int j = 0; j < sortedViews.size() - i - 1; ++j) {
473             if (viewAtLowerScreenPriority(sortedViews[j], sortedViews[j + 1])
474                     || (sortedViews[j]->screen() == sortedViews[j + 1]->screen()
475                         && viewAtLowerEdgePriority(sortedViews[j], sortedViews[j + 1]))) {
476                 Latte::View *temp = sortedViews[j + 1];
477                 sortedViews[j + 1] = sortedViews[j];
478                 sortedViews[j] = temp;
479             }
480         }
481     }
482 
483     Latte::View *highestPriorityView{nullptr};
484 
485     for (int i = 0; i < sortedViews.size(); ++i) {
486         if (sortedViews[i]->isPreferredForShortcuts()) {
487             highestPriorityView = sortedViews[i];
488             sortedViews.removeAt(i);
489             break;
490         }
491     }
492 
493     if (highestPriorityView) {
494         sortedViews.prepend(highestPriorityView);
495     }
496 
497     qDebug() << " -------- sorted -----";
498 
499     for (int i = 0; i < sortedViews.count(); ++i) {
500         qDebug() << i << ". " << sortedViews[i]->isPreferredForShortcuts() << " - " << sortedViews[i]->screen()->name() << " - " << sortedViews[i]->location();
501     }
502 
503     return sortedViews;
504 }
505 
viewAtLowerScreenPriority(Latte::View * test,Latte::View * base)506 bool GenericLayout::viewAtLowerScreenPriority(Latte::View *test, Latte::View *base)
507 {
508     if (!base || ! test) {
509         return true;
510     }
511 
512     if (base->screen() == test->screen()) {
513         return false;
514     } else if (base->screen() != qGuiApp->primaryScreen() && test->screen() == qGuiApp->primaryScreen()) {
515         return false;
516     } else if (base->screen() == qGuiApp->primaryScreen() && test->screen() != qGuiApp->primaryScreen()) {
517         return true;
518     } else {
519         int basePriority = -1;
520         int testPriority = -1;
521 
522         for (int i = 0; i < qGuiApp->screens().count(); ++i) {
523             if (base->screen() == qGuiApp->screens()[i]) {
524                 basePriority = i;
525             }
526 
527             if (test->screen() == qGuiApp->screens()[i]) {
528                 testPriority = i;
529             }
530         }
531 
532         if (testPriority <= basePriority) {
533             return true;
534         } else {
535             return false;
536         }
537 
538     }
539 
540     qDebug() << "viewAtLowerScreenPriority : shouldn't had reached here...";
541     return false;
542 }
543 
viewAtLowerEdgePriority(Latte::View * test,Latte::View * base)544 bool GenericLayout::viewAtLowerEdgePriority(Latte::View *test, Latte::View *base)
545 {
546     if (!base || ! test) {
547         return true;
548     }
549 
550     QList<Plasma::Types::Location> edges{Plasma::Types::RightEdge, Plasma::Types::TopEdge,
551                 Plasma::Types::LeftEdge, Plasma::Types::BottomEdge};
552 
553     int testPriority = -1;
554     int basePriority = -1;
555 
556     for (int i = 0; i < edges.count(); ++i) {
557         if (edges[i] == base->location()) {
558             basePriority = i;
559         }
560 
561         if (edges[i] == test->location()) {
562             testPriority = i;
563         }
564     }
565 
566     if (testPriority < basePriority) {
567         return true;
568     } else {
569         return false;
570     }
571 }
572 
viewDataAtLowerScreenPriority(const Latte::Data::View & test,const Latte::Data::View & base) const573 bool GenericLayout::viewDataAtLowerScreenPriority(const Latte::Data::View &test, const Latte::Data::View &base) const
574 {
575     if (test.onPrimary && base.onPrimary) {
576         return false;
577     } else if (!base.onPrimary && test.onPrimary) {
578         return false;
579     } else if (base.onPrimary && !test.onPrimary) {
580         return true;
581     } else {
582         return test.screen <= base.screen;
583     }
584 }
585 
viewDataAtLowerStatePriority(const Latte::Data::View & test,const Latte::Data::View & base) const586 bool GenericLayout::viewDataAtLowerStatePriority(const Latte::Data::View &test, const Latte::Data::View &base) const
587 {
588     if (test.isActive == base.isActive) {
589         return false;
590     } else if (!base.isActive && test.isActive) {
591         return false;
592     } else if (base.isActive && !test.isActive) {
593         return true;
594     }
595 
596     return false;
597 }
598 
viewDataAtLowerEdgePriority(const Latte::Data::View & test,const Latte::Data::View & base) const599 bool GenericLayout::viewDataAtLowerEdgePriority(const Latte::Data::View &test, const Latte::Data::View &base) const
600 {
601     QList<Plasma::Types::Location> edges{Plasma::Types::RightEdge, Plasma::Types::TopEdge,
602                 Plasma::Types::LeftEdge, Plasma::Types::BottomEdge};
603 
604     int testPriority = -1;
605     int basePriority = -1;
606 
607     for (int i = 0; i < edges.count(); ++i) {
608         if (edges[i] == base.edge) {
609             basePriority = i;
610         }
611 
612         if (edges[i] == test.edge) {
613             testPriority = i;
614         }
615     }
616 
617     if (testPriority < basePriority) {
618         return true;
619     } else {
620         return false;
621     }
622 }
623 
sortedViewsData(const QList<Latte::Data::View> & viewsData)624 QList<Latte::Data::View> GenericLayout::sortedViewsData(const QList<Latte::Data::View> &viewsData)
625 {
626     QList<Latte::Data::View> sortedData = viewsData;
627 
628     //! sort the views based on screens and edges priorities
629     //! views on primary screen have higher priority and
630     //! for views in the same screen the priority goes to
631     //! Bottom,Left,Top,Right
632     for (int i = 0; i < sortedData.size(); ++i) {
633         for (int j = 0; j < sortedData.size() - i - 1; ++j) {
634             if (viewDataAtLowerStatePriority(sortedData[j], sortedData[j + 1])
635                     || viewDataAtLowerScreenPriority(sortedData[j], sortedData[j + 1])
636                     || (!viewDataAtLowerScreenPriority(sortedData[j], sortedData[j + 1])
637                         && viewDataAtLowerEdgePriority(sortedData[j], sortedData[j + 1])) ) {
638                 Latte::Data::View temp = sortedData[j + 1];
639                 sortedData[j + 1] = sortedData[j];
640                 sortedData[j] = temp;
641             }
642         }
643     }
644 
645     return sortedData;
646 }
647 
648 
containments() const649 const QList<Plasma::Containment *> *GenericLayout::containments() const
650 {
651     return &m_containments;
652 }
653 
viewsWithPlasmaShortcuts()654 QList<Latte::View *> GenericLayout::viewsWithPlasmaShortcuts()
655 {
656     QList<Latte::View *> views;
657 
658     if (!m_corona) {
659         return views;
660     }
661 
662     QList<uint> appletsWithShortcuts = m_corona->globalShortcuts()->shortcutsTracker()->appletsWithPlasmaShortcuts();
663 
664     for (const auto &appletId : appletsWithShortcuts) {
665         for (const auto view : m_latteViews) {
666             bool found{false};
667             for (const auto applet : view->containment()->applets()) {
668                 if (appletId == applet->id()) {
669                     if (!views.contains(view)) {
670                         views.append(view);
671                         found = true;
672                         break;
673                     }
674                 }
675             }
676 
677             if (found) {
678                 break;
679             }
680         }
681     }
682 
683     return views;
684 }
685 
686 
687 //! Containments Actions
addContainment(Plasma::Containment * containment)688 void GenericLayout::addContainment(Plasma::Containment *containment)
689 {
690     if (!containment || m_containments.contains(containment)) {
691         return;
692     }
693 
694     bool containmentInLayout{false};
695 
696     if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout) {
697         m_containments.append(containment);
698         containmentInLayout = true;
699     } else if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
700         QString layoutId = containment->config().readEntry("layoutId", QString());
701 
702         if (!layoutId.isEmpty() && (layoutId == m_layoutName)) {
703             m_containments.append(containment);
704             containmentInLayout = true;
705         }
706     }
707 
708     if (containmentInLayout) {
709         if (!blockAutomaticLatteViewCreation()) {
710             addView(containment);
711         } else {
712             qDebug() << "delaying LatteView creation for containment :: " << containment->id();
713         }
714 
715         connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed);
716     }
717 }
718 
appletCreated(Plasma::Applet * applet)719 void GenericLayout::appletCreated(Plasma::Applet *applet)
720 {
721     //! In Multiple Layout the orphaned subcontainments must be assigned to layouts
722     //! when the user adds them
723     KConfigGroup appletSettings = applet->containment()->config().group("Applets").group(QString::number(applet->id()));
724 
725     int subId = Layouts::Storage::self()->subContainmentId(appletSettings);
726 
727     if (Layouts::Storage::isValid(subId)) {
728         uint sId = (uint)subId;
729 
730         for (const auto containment : m_corona->containments()) {
731             if (containment->id() == sId) {
732                 containment->config().writeEntry("layoutId", m_layoutName);
733             }
734 
735             addContainment(containment);
736         }
737     }
738 }
739 
containmentDestroyed(QObject * cont)740 void GenericLayout::containmentDestroyed(QObject *cont)
741 {
742     if (!m_corona) {
743         return;
744     }
745 
746     Plasma::Containment *containment = static_cast<Plasma::Containment *>(cont);
747 
748     if (containment) {
749         int containmentIndex = m_containments.indexOf(containment);
750 
751         if (containmentIndex >= 0) {
752             m_containments.removeAt(containmentIndex);
753         }
754 
755         qDebug() << "Layout " << name() << " :: containment destroyed!!!!";
756         auto view = m_latteViews.take(containment);
757 
758         if (!view) {
759             view = m_waitingLatteViews.take(containment);
760         }
761 
762         if (view) {
763             view->disconnectSensitiveSignals();
764             view->positioner()->slideOutDuringExit(containment->location());
765             view->deleteLater();
766 
767             emit viewEdgeChanged();
768             emit viewsCountChanged();
769         }
770     }
771 }
772 
destroyedChanged(bool destroyed)773 void GenericLayout::destroyedChanged(bool destroyed)
774 {
775     if (!m_corona) {
776         return;
777     }
778 
779     qDebug() << "dock containment destroyed changed!!!!";
780     Plasma::Containment *sender = qobject_cast<Plasma::Containment *>(QObject::sender());
781 
782     if (!sender) {
783         return;
784     }
785 
786     Latte::View *view;
787 
788     if (destroyed) {
789         view = m_latteViews.take(static_cast<Plasma::Containment *>(sender));
790         m_waitingLatteViews[sender] = view;
791     } else {
792         view = m_waitingLatteViews.take(static_cast<Plasma::Containment *>(sender));
793         m_latteViews[sender] =view;
794     }
795 
796     if (view) {
797         emit m_corona->availableScreenRectChangedFrom(view);
798         emit m_corona->availableScreenRegionChangedFrom(view);
799         emit viewEdgeChanged();
800         emit viewsCountChanged();
801     }
802 }
803 
renameLayout(QString newName)804 void GenericLayout::renameLayout(QString newName)
805 {
806     if (!m_corona || m_corona->layoutsManager()->memoryUsage() != MemoryUsage::MultipleLayouts) {
807         return;
808     }
809 
810     if (m_layoutFile != Layouts::Importer::layoutUserFilePath(newName)) {
811         setFile(Layouts::Importer::layoutUserFilePath(newName));
812     }
813 
814     setName(newName);
815 
816     for (const auto containment : m_containments) {
817         qDebug() << "Cont ID :: " << containment->id();
818         containment->config().writeEntry("layoutId", m_layoutName);
819     }
820 }
821 
addView(Plasma::Containment * containment,bool forceOnPrimary,int explicitScreen,Layout::ViewsMap * occupied)822 void GenericLayout::addView(Plasma::Containment *containment, bool forceOnPrimary, int explicitScreen, Layout::ViewsMap *occupied)
823 {
824     qDebug() << "Layout :::: " << m_layoutName << " ::: addView was called... m_containments :: " << m_containments.size();
825 
826     if (!containment || !m_corona || !containment->kPackage().isValid()) {
827         qWarning() << "the requested containment plugin can not be located or loaded";
828         return;
829     }
830 
831     qDebug() << "step 1...";
832 
833     if (!Layouts::Storage::self()->isLatteContainment(containment)) {
834         return;
835     }
836 
837     qDebug() << "step 2...";
838 
839     for (auto *dock : m_latteViews) {
840         if (dock->containment() == containment)
841             return;
842     }
843 
844     qDebug() << "step 3...";
845 
846     QScreen *nextScreen{qGuiApp->primaryScreen()};
847 
848     bool onPrimary = containment->config().readEntry("onPrimary", true);
849     int id = containment->screen();
850 
851     if (!Layouts::Storage::isValid(id) && !Layouts::Storage::isValid(explicitScreen)) {
852         id = containment->lastScreen();
853     }
854 
855     if (onPrimary) {
856         id = m_corona->screenPool()->primaryScreenId();
857     } else if (Layouts::Storage::isValid(explicitScreen)) {
858         id = explicitScreen;
859     }
860 
861     Plasma::Types::Location edge = containment->location();
862 
863     QString connector = m_corona->screenPool()->hasScreenId(id) ? m_corona->screenPool()->connector(id) : "";
864 
865     qDebug() << "Adding view - containment id:" << containment->id() << " ,screen :" << id << " - " << connector
866              << " ,onprimary:" << onPrimary << " - "  << " edge:" << edge << " ,screenName:" << qGuiApp->primaryScreen()->name() << " ,forceOnPrimary:" << forceOnPrimary;
867 
868     if (occupied && m_corona->screenPool()->hasScreenId(id) && (*occupied).contains(connector) && (*occupied)[connector].contains(edge)) {
869         qDebug() << "Rejected : adding view because the edge is already occupied by a higher priority view ! : " << (*occupied)[connector][edge];
870         return;
871     }
872 
873     if (Layouts::Storage::isValid(id) && !onPrimary && !forceOnPrimary) {
874         qDebug() << "Add view - connector : " << connector;
875         bool found{false};
876 
877         if (m_corona->screenPool()->hasScreenId(id)) {
878             for (const auto scr : qGuiApp->screens()) {
879                 if (scr && scr->name() == connector) {
880                     found = true;
881                     nextScreen = scr;
882                     break;
883                 }
884             }
885         }
886 
887         if (!found) {
888             qDebug() << "Rejected : adding explicit view, screen not available ! : " << connector;
889             return;
890         }
891 
892         //! explicit dock can not be added at explicit screen when that screen is the same with
893         //! primary screen and that edge is already occupied by a primary dock
894         //! CAN BE REMOVED because we now accept primary and explicit views on ACTIVE SCREENS at ALL CASES
895         //if (nextScreen == qGuiApp->primaryScreen() && primaryDockOccupyEdge(containment->location())) {
896         //    qDebug() << "Rejected : adding explicit view, primary dock occupies edge at screen ! : " << connector;
897         //    return;
898         // }
899     }
900 
901     if (Layouts::Storage::isValid(id) && onPrimary) {
902         qDebug() << "add dock - connector : " << connector;
903 
904         for (const Plasma::Containment *testContainment : m_latteViews.keys()) {
905             int testScreenId = testContainment->screen();
906 
907             if (!Layouts::Storage::isValid(testScreenId)) {
908                 testScreenId = testContainment->lastScreen();
909             }
910 
911             bool testOnPrimary = testContainment->config().readEntry("onPrimary", true);
912             Plasma::Types::Location testLocation = static_cast<Plasma::Types::Location>((int)testContainment->config().readEntry("location", (int)Plasma::Types::BottomEdge));
913 
914             if (!testOnPrimary && m_corona->screenPool()->primaryScreenId() == testScreenId && testLocation == containment->location()) {
915                 qDebug() << "Rejected explicit latteView and removing it in order add an onPrimary with higher priority at screen: " << connector;
916                 auto viewToDelete = m_latteViews.take(testContainment);
917                 viewToDelete->disconnectSensitiveSignals();
918                 viewToDelete->deleteLater();
919             }
920         }
921     }
922 
923     qDebug() << "Adding view passed ALL checks" << " ,onPrimary:" << onPrimary << " ,screen:" << nextScreen->name() << " !!!";
924 
925     //! it is used to set the correct flag during the creation
926     //! of the window... This of course is also used during
927     //! recreations of the window between different visibility modes
928     auto mode = static_cast<Types::Visibility>(containment->config().readEntry("visibility", static_cast<int>(Types::DodgeActive)));
929     bool byPassWM{false};
930 
931     if (mode == Types::AlwaysVisible
932             || mode == Types::WindowsGoBelow
933             || mode == Types::WindowsCanCover
934             || mode == Types::WindowsAlwaysCover) {
935         byPassWM = false;
936     } else {
937         byPassWM = containment->config().readEntry("byPassWM", false);
938     }
939 
940     auto latteView = new Latte::View(m_corona, nextScreen, byPassWM);
941 
942     latteView->init(containment);
943     latteView->setContainment(containment);
944 
945     //! force this special dock case to become primary
946     //! even though it isnt
947     if (forceOnPrimary) {
948         qDebug() << "Enforcing onPrimary:true as requested for LatteView...";
949         latteView->setOnPrimary(true);
950     }
951 
952     latteView->setLayout(this);
953 
954 
955     //! Qt 5.9 creates a crash for this in wayland, that is why the check is used
956     //! but on the other hand we need this for copy to work correctly and show
957     //! the copied dock under X11
958     //if (!KWindowSystem::isPlatformWayland()) {
959     latteView->show();
960     //}
961 
962     m_latteViews[containment] = latteView;
963 
964     emit viewsCountChanged();
965 }
966 
toggleHiddenState(QString viewName,QString screenName,Plasma::Types::Location edge)967 void GenericLayout::toggleHiddenState(QString viewName, QString screenName, Plasma::Types::Location edge)
968 {
969     if (!m_corona) {
970         return;
971     }
972 
973     QString validScreenName = qGuiApp->primaryScreen()->name();
974     if (!screenName.isEmpty()) {
975         validScreenName = screenName;
976     }
977 
978     int viewsOnEdge{0};
979 
980     for(const auto view : latteViews()) {
981         if ((viewName.isEmpty() || (!viewName.isEmpty() && viewName == view->name()))
982                 && view->positioner()->currentScreenName() == validScreenName
983                 && (edge == Plasma::Types::Floating || ((edge != Plasma::Types::Floating) && view->location() == edge))) {
984             viewsOnEdge++;
985         }
986     }
987 
988     if (viewsOnEdge >= 1) {
989         for(const auto view : latteViews()) {
990             if ((viewName.isEmpty() || (!viewName.isEmpty() && viewName == view->name()))
991                     && view->positioner()->currentScreenName() == validScreenName
992                     && (edge == Plasma::Types::Floating || ((edge != Plasma::Types::Floating) && view->location() == edge))) {
993                 view->visibility()->toggleHiddenState();
994             }
995         }
996     }
997 }
998 
initToCorona(Latte::Corona * corona)999 bool GenericLayout::initToCorona(Latte::Corona *corona)
1000 {
1001     if (m_corona) {
1002         return false;
1003     }
1004 
1005     m_corona = corona;
1006 
1007     for (const auto containment : m_corona->containments()) {
1008         if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout) {
1009             addContainment(containment);
1010         } else if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
1011             QString layoutId = containment->config().readEntry("layoutId", QString());
1012 
1013             if (!layoutId.isEmpty() && (layoutId == m_layoutName)) {
1014                 addContainment(containment);
1015             }
1016         }
1017     }
1018 
1019     qDebug() << "Layout ::::: " << name() << " added containments ::: " << m_containments.size();
1020 
1021     updateLastUsedActivity();
1022 
1023     //! signals
1024     connect(this, &GenericLayout::activitiesChanged, this, &GenericLayout::updateLastUsedActivity);
1025     connect(m_corona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, &GenericLayout::updateLastUsedActivity);
1026     connect(m_corona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, &GenericLayout::updateLastUsedActivity);
1027 
1028     connect(m_corona, &Plasma::Corona::containmentAdded, this, &GenericLayout::addContainment);
1029 
1030     connect(this, &GenericLayout::lastConfigViewForChanged, m_corona->layoutsManager(), &Layouts::Manager::lastConfigViewChangedFrom);
1031     connect(m_corona->layoutsManager(), &Layouts::Manager::lastConfigViewChangedFrom, this, &GenericLayout::onLastConfigViewChangedFrom);
1032 
1033     //!connect signals after adding the containment
1034     connect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged);
1035     connect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged);
1036 
1037     emit viewsCountChanged();
1038 
1039     return true;
1040 }
1041 
updateLastUsedActivity()1042 void GenericLayout::updateLastUsedActivity()
1043 {
1044     if (!m_corona) {
1045         return;
1046     }
1047 
1048     QString currentId = m_corona->activitiesConsumer()->currentActivity();
1049     QStringList appliedActivitiesIds = appliedActivities();
1050 
1051     if (appliedActivitiesIds.contains(Data::Layout::ALLACTIVITIESID)
1052             || (m_lastUsedActivity != currentId && appliedActivitiesIds.contains(currentId))) {
1053         m_lastUsedActivity = currentId;
1054         emit lastUsedActivityChanged();
1055     }
1056 }
1057 
assignToLayout(Latte::View * latteView,QList<Plasma::Containment * > containments)1058 void GenericLayout::assignToLayout(Latte::View *latteView, QList<Plasma::Containment *> containments)
1059 {
1060     if (!m_corona || containments.isEmpty()) {
1061         return;
1062     }
1063 
1064     if (latteView) {
1065         m_latteViews[latteView->containment()] = latteView;
1066     }
1067 
1068     m_containments << containments;
1069 
1070     for (const auto containment : containments) {
1071         containment->config().writeEntry("layoutId", name());
1072 
1073         if (!latteView || (latteView && latteView->containment() != containment)) {
1074             //! assign signals only to subcontainments
1075             //! the View::setLayout() is responsible for the View::Containment signals
1076             connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed);
1077             connect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged);
1078             connect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated);
1079         }
1080     }
1081 
1082     if (latteView) {
1083         latteView->setLayout(this);
1084     }
1085 
1086     emit viewsCountChanged();
1087 
1088     //! sync the original layout file for integrity
1089     if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
1090         Layouts::Storage::self()->syncToLayoutFile(this, false);
1091     }
1092 }
1093 
unassignFromLayout(Plasma::Containment * latteContainment)1094 QList<Plasma::Containment *> GenericLayout::unassignFromLayout(Plasma::Containment *latteContainment)
1095 {
1096     QList<Plasma::Containment *> containments;
1097 
1098     if (!m_corona || !latteContainment || !contains(latteContainment)) {
1099         return containments;
1100     }
1101 
1102     containments << latteContainment;
1103 
1104     for (const auto containment : m_containments) {
1105         Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
1106 
1107         //! add subcontainments from that latteView
1108         if (parentApplet && parentApplet->containment() && parentApplet->containment() == latteContainment) {
1109             containments << containment;
1110             //! unassign signals only to subcontainments
1111             //! the View::setLayout() is responsible for the View::Containment signals
1112             disconnect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed);
1113             disconnect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged);
1114             disconnect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated);
1115         }
1116     }
1117 
1118     for (const auto containment : containments) {
1119         m_containments.removeAll(containment);
1120     }
1121 
1122     if (containments.size() > 0) {
1123         m_latteViews.remove(latteContainment);
1124     }
1125 
1126     //! sync the original layout file for integrity
1127     if (m_corona && m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
1128         Layouts::Storage::self()->syncToLayoutFile(this, false);
1129     }
1130 
1131     return containments;
1132 }
1133 
recreateView(Plasma::Containment * containment,bool delayed)1134 void GenericLayout::recreateView(Plasma::Containment *containment, bool delayed)
1135 {
1136     if (!m_corona || m_viewsToRecreate.contains(containment) || !containment || !m_latteViews.contains(containment)) {
1137         return;
1138     }
1139 
1140     int delay = delayed ? 350 : 0;
1141     m_viewsToRecreate << containment;
1142 
1143     //! give the time to config window to close itself first and then recreate the dock
1144     //! step:1 remove the latteview
1145     QTimer::singleShot(delay, [this, containment]() {
1146         auto view = m_latteViews[containment];
1147         view->disconnectSensitiveSignals();
1148 
1149         //! step:2 add the new latteview
1150         connect(view, &QObject::destroyed, this, [this, containment]() {
1151             auto view = m_latteViews.take(containment);
1152             QTimer::singleShot(250, this, [this, containment]() {
1153                 if (!m_latteViews.contains(containment)) {
1154                     qDebug() << "recreate - step 2: adding dock for containment:" << containment->id();
1155                     addView(containment);
1156                     m_viewsToRecreate.removeAll(containment);
1157                 }
1158             });
1159         });
1160 
1161         view->deleteLater();
1162     });
1163 }
1164 
1165 
latteViewExists(Plasma::Containment * containment)1166 bool GenericLayout::latteViewExists(Plasma::Containment *containment)
1167 {
1168     if (!m_corona) {
1169         return false;
1170     }
1171 
1172     return m_latteViews.keys().contains(containment);
1173 }
1174 
availableEdgesForView(QScreen * scr,Latte::View * forView) const1175 QList<Plasma::Types::Location> GenericLayout::availableEdgesForView(QScreen *scr, Latte::View *forView) const
1176 {
1177     using Plasma::Types;
1178     QList<Types::Location> edges{Types::BottomEdge, Types::LeftEdge,
1179                 Types::TopEdge, Types::RightEdge};
1180 
1181     if (!m_corona) {
1182         return edges;
1183     }
1184 
1185     for (const auto view : m_latteViews) {
1186         //! make sure that availabe edges takes into account only views that should be excluded,
1187         //! this is why the forView should not be excluded
1188         if (view && view != forView && view->positioner()->currentScreenName() == scr->name()) {
1189             edges.removeOne(view->location());
1190         }
1191     }
1192 
1193     return edges;
1194 }
1195 
explicitDockOccupyEdge(int screen,Plasma::Types::Location location) const1196 bool GenericLayout::explicitDockOccupyEdge(int screen, Plasma::Types::Location location) const
1197 {
1198     if (!m_corona) {
1199         return false;
1200     }
1201 
1202     for (const auto containment : m_containments) {
1203         if (Layouts::Storage::self()->isLatteContainment(containment)) {
1204             bool onPrimary = containment->config().readEntry("onPrimary", true);
1205             int id = containment->lastScreen();
1206             Plasma::Types::Location contLocation = containment->location();
1207 
1208             if (!onPrimary && id == screen && contLocation == location) {
1209                 return true;
1210             }
1211         }
1212     }
1213 
1214     return false;
1215 }
1216 
primaryDockOccupyEdge(Plasma::Types::Location location) const1217 bool GenericLayout::primaryDockOccupyEdge(Plasma::Types::Location location) const
1218 {
1219     if (!m_corona) {
1220         return false;
1221     }
1222 
1223     for (const auto containment : m_containments) {
1224         if (Layouts::Storage::self()->isLatteContainment(containment)) {
1225             bool onPrimary{false};
1226 
1227             if (m_latteViews.contains(containment)) {
1228                 onPrimary = m_latteViews[containment]->onPrimary();
1229             } else {
1230                 onPrimary = containment->config().readEntry("onPrimary", true);
1231             }
1232 
1233             Plasma::Types::Location contLocation = containment->location();
1234 
1235             if (onPrimary && contLocation == location) {
1236                 return true;
1237             }
1238         }
1239     }
1240 
1241     return false;
1242 }
1243 
mapContainsId(const Layout::ViewsMap * map,uint viewId) const1244 bool GenericLayout::mapContainsId(const Layout::ViewsMap *map, uint viewId) const
1245 {
1246     for(const auto &scr : map->keys()) {
1247         for(const auto &edge : (*map)[scr].keys()) {
1248             if ((*map)[scr][edge].contains(viewId)) {
1249                 return true;
1250             }
1251         }
1252     }
1253 
1254     return false;
1255 }
1256 
1257 //! screen name, location, containmentId
validViewsMap(Layout::ViewsMap * occupiedMap)1258 Layout::ViewsMap GenericLayout::validViewsMap(Layout::ViewsMap *occupiedMap)
1259 {
1260     //! Shared Views occupy the screen edge first
1261     //! Primary Views occupy the screen edge if Shared Views do not exist already on that screen edge
1262     //! Explicity Views occypy the screen edge if Shared Views and Primary Views do not exist already on that screen edge
1263     Layout::ViewsMap map;
1264 
1265     if (!m_corona) {
1266         return map;
1267     }
1268 
1269     if (occupiedMap != nullptr) {
1270         map = (*occupiedMap);
1271     }
1272 
1273     QString prmScreenName = qGuiApp->primaryScreen()->name();
1274 
1275     //! first step: primary docks must be placed in primary screen free edges
1276     for (const auto containment : m_containments) {
1277         if (Layouts::Storage::self()->isLatteContainment(containment)) {
1278             int screenId{Layouts::Storage::IDNULL};
1279 
1280             //! valid screen id
1281             if (latteViewExists(containment)) {
1282                 screenId = m_latteViews[containment]->positioner()->currentScreenId();
1283             } else {
1284                 screenId = containment->screen();
1285 
1286                 if (!Layouts::Storage::isValid(screenId)) {
1287                     screenId = containment->lastScreen();
1288                 }
1289             }
1290 
1291             bool onPrimary{true};
1292 
1293             //! valid onPrimary flag
1294             if (latteViewExists(containment)) {
1295                 onPrimary = m_latteViews[containment]->onPrimary();
1296             } else {
1297                 onPrimary = containment->config().readEntry("onPrimary", true);
1298             }
1299 
1300             //! valid location
1301             Plasma::Types::Location location = containment->location();
1302 
1303             if (onPrimary) {
1304                 map[prmScreenName][location] << containment->id();
1305             } else {
1306                 QString expScreenName = m_corona->screenPool()->connector(screenId);
1307 
1308                 if (m_corona->screenPool()->isScreenActive(screenId)) {
1309                     map[expScreenName][location] << containment->id();
1310                 }
1311             }
1312         }
1313     }
1314 
1315     /*
1316     //! CAN BE REMOVED because we now accept primary and explicit views on ACTIVE SCREENS at ALL CASES
1317     Layout::ViewsMap explicitMap;
1318 
1319     //! second step: explicit docks must be placed in their screens if the screen edge is free
1320     for (const auto containment : m_containments) {
1321         if (Layouts::Storage::self()->isLatteContainment(containment)) {
1322             int screenId{Layouts::Storage::IDNULL};
1323 
1324             //! valid screen id
1325             if (latteViewExists(containment)) {
1326                 screenId = m_latteViews[containment]->positioner()->currentScreenId();
1327             } else {
1328                 screenId = containment->screen();
1329 
1330                 if (!Layouts::Storage::isValid(screenId)) {
1331                     screenId = containment->lastScreen();
1332                 }
1333             }
1334 
1335             bool onPrimary{true};
1336 
1337             //! valid onPrimary flag
1338             if (latteViewExists(containment)) {
1339                 onPrimary = m_latteViews[containment]->onPrimary();
1340             } else {
1341                 onPrimary = containment->config().readEntry("onPrimary", true);
1342             }
1343 
1344             //! valid location
1345             Plasma::Types::Location location = containment->location();
1346 
1347             if (!onPrimary) {
1348                 QString expScreenName = m_corona->screenPool()->connector(screenId);
1349 
1350                 if (m_corona->screenPool()->isScreenActive(screenId) && !map[expScreenName].contains(location)) {
1351                     explicitMap[expScreenName][location] << containment->id();
1352                 }
1353             }
1354         }
1355     }
1356 
1357     for(const QString &expScreenName : explicitMap.keys()) {
1358         for(const Plasma::Types::Location &expLocation : explicitMap[expScreenName].keys()) {
1359             map[expScreenName][expLocation] << explicitMap[expScreenName][expLocation];
1360         }
1361     }*/
1362 
1363     return map;
1364 }
1365 
1366 
1367 //! the central functions that updates loading/unloading latteviews
1368 //! concerning screen changed (for multi-screen setups mainly)
syncLatteViewsToScreens(Layout::ViewsMap * occupiedMap)1369 void GenericLayout::syncLatteViewsToScreens(Layout::ViewsMap *occupiedMap)
1370 {
1371     if (!m_corona) {
1372         return;
1373     }
1374 
1375     qDebug() << "START of SyncLatteViewsToScreens ....";
1376     qDebug() << "LAYOUT ::: " << name();
1377     qDebug() << "screen count changed -+-+ " << qGuiApp->screens().size();
1378 
1379     //! Clear up pendingContainmentUpdates when no-needed any more
1380     QStringList clearpendings;
1381     for(int i=0; i<m_pendingContainmentUpdates.rowCount(); ++i) {
1382         auto viewdata = m_pendingContainmentUpdates[i];
1383         auto containment = containmentForId(viewdata.id.toUInt());
1384 
1385         if (containment) {
1386             if ((viewdata.onPrimary && containment->lastScreen() == m_corona->screenPool()->primaryScreenId())
1387                     || (!viewdata.onPrimary && containment->lastScreen() == viewdata.screen)) {
1388                 clearpendings << viewdata.id;
1389             }
1390         }
1391     }
1392 
1393     for(auto pendingid : clearpendings) {
1394         m_pendingContainmentUpdates.remove(pendingid);
1395     }
1396 
1397     if (m_pendingContainmentUpdates.rowCount() > 0) {
1398         qDebug () << "  Pending View updates still valid : ";
1399         m_pendingContainmentUpdates.print();
1400     }
1401 
1402     //! use valid views map based on active screens
1403     Layout::ViewsMap viewsMap = validViewsMap(occupiedMap);
1404 
1405     if (occupiedMap != nullptr) {
1406         qDebug() << "Occupied map used :: " << *occupiedMap;
1407     }
1408 
1409     QString prmScreenName = qGuiApp->primaryScreen()->name();
1410 
1411     qDebug() << "PRIMARY SCREEN :: " << prmScreenName;
1412     qDebug() << "LATTEVIEWS MAP :: " << viewsMap;
1413 
1414     //! add views
1415     for (const auto containment : m_containments) {
1416         int screenId = containment->screen();
1417 
1418         if (!Layouts::Storage::isValid(screenId)) {
1419             screenId = containment->lastScreen();
1420         }
1421 
1422         if (!latteViewExists(containment) && mapContainsId(&viewsMap, containment->id())) {
1423             qDebug() << "syncLatteViewsToScreens: view must be added... for containment:" << containment->id() << " at screen:" << m_corona->screenPool()->connector(screenId);
1424             addView(containment);
1425         }
1426     }
1427 
1428     //! remove views
1429     QList<Plasma::Containment *> viewsToDelete;
1430 
1431     for (auto view : m_latteViews) {
1432         auto containment = view->containment();
1433         if (containment && !mapContainsId(&viewsMap, containment->id())) {
1434             viewsToDelete << containment;
1435         }
1436     }
1437 
1438     while(!viewsToDelete.isEmpty()) {
1439         auto containment = viewsToDelete.takeFirst();
1440         auto view = m_latteViews.take(containment);
1441         qDebug() << "syncLatteViewsToScreens: view must be deleted... for containment:" << containment->id() << " at screen:" << view->positioner()->currentScreenName();
1442         view->disconnectSensitiveSignals();
1443         view->deleteLater();
1444     }
1445 
1446     //! reconsider views
1447     for (const auto view : m_latteViews) {
1448         if (view->containment() && mapContainsId(&viewsMap, view->containment()->id())) {
1449             //! if the dock will not be deleted its a very good point to reconsider
1450             //! if the screen in which is running is the correct one
1451             qDebug() << "syncLatteViewsToScreens: view must consider its screen... for containment:" << view->containment()->id() << " at screen:" << view->positioner()->currentScreenName();
1452             view->reconsiderScreen();
1453         }
1454     }
1455 
1456     qDebug() << "end of, syncLatteViewsToScreens ....";
1457 }
1458 
subContainmentsOf(uint id) const1459 QList<Plasma::Containment *> GenericLayout::subContainmentsOf(uint id) const
1460 {
1461     QList<Plasma::Containment *> subs;
1462 
1463     auto containment = containmentForId(id);
1464 
1465     if (!containment || !Layouts::Storage::self()->isLatteContainment(containment)) {
1466         return subs;
1467     }
1468 
1469     auto applets = containment->config().group("Applets");
1470 
1471     for (const auto &applet : applets.groupList()) {
1472         int tSubId = Layouts::Storage::self()->subContainmentId(applets.group(applet));
1473 
1474         if (Layouts::Storage::isValid(tSubId)) {
1475             auto subcontainment = containmentForId(tSubId);
1476 
1477             if (subcontainment) {
1478                 subs << subcontainment;
1479             }
1480         }
1481     }
1482 
1483     return subs;
1484 }
1485 
subContainmentsOf(Plasma::Containment * containment) const1486 QList<int> GenericLayout::subContainmentsOf(Plasma::Containment *containment) const
1487 {
1488     QList<int> subs;
1489 
1490     if (Layouts::Storage::self()->isLatteContainment(containment)) {
1491         auto applets = containment->config().group("Applets");
1492 
1493         for (const auto &applet : applets.groupList()) {
1494             int tSubId = Layouts::Storage::self()->subContainmentId(applets.group(applet));
1495 
1496             if (Layouts::Storage::isValid(tSubId)) {
1497                 subs << tSubId;
1498             }
1499         }
1500     }
1501 
1502     return subs;
1503 }
1504 
viewsExplicitScreens()1505 QList<int> GenericLayout::viewsExplicitScreens()
1506 {
1507     Data::ViewsTable views = viewsTable();
1508     QList<int> screens;
1509 
1510     for (int i=0; i<views.rowCount(); ++i) {
1511         if (!views[i].onPrimary && !screens.contains(views[i].screen)) {
1512             screens << views[i].screen;
1513         }
1514     }
1515 
1516     return screens;
1517 }
1518 
1519 //! STORAGE
1520 
isWritable() const1521 bool GenericLayout::isWritable() const
1522 {
1523     return Layouts::Storage::self()->isWritable(this);
1524 }
1525 
lock()1526 void GenericLayout::lock()
1527 {
1528     Layouts::Storage::self()->lock(this);
1529 }
1530 
unlock()1531 void GenericLayout::unlock()
1532 {
1533     Layouts::Storage::self()->unlock(this);
1534 }
1535 
syncToLayoutFile(bool removeLayoutId)1536 void GenericLayout::syncToLayoutFile(bool removeLayoutId)
1537 {
1538     syncSettings();
1539     Layouts::Storage::self()->syncToLayoutFile(this, removeLayoutId);
1540 }
1541 
newView(const QString & templateName)1542 bool GenericLayout::newView(const QString &templateName)
1543 {
1544     if (!isActive() || !m_corona->templatesManager()->hasViewTemplate(templateName)) {
1545         return false;
1546     }
1547 
1548     QString templatefilepath = m_corona->templatesManager()->viewTemplateFilePath(templateName);
1549     Data::ViewsTable templateviews = Layouts::Storage::self()->views(templatefilepath);
1550 
1551     if (templateviews.rowCount() <= 0) {
1552         return false;
1553     }
1554 
1555     Data::View nextdata = templateviews[0];
1556     int scrId = m_corona->screenPool()->primaryScreenId();
1557 
1558     QList<Plasma::Types::Location> freeedges = freeEdges(scrId);
1559 
1560     if (!freeedges.contains(nextdata.edge)) {
1561         nextdata.edge = (freeedges.count() > 0 ? freeedges[0] : Plasma::Types::BottomEdge);
1562     }
1563 
1564     nextdata.setState(Data::View::OriginFromViewTemplate, templatefilepath);
1565 
1566     newView(nextdata);
1567 
1568     return true;
1569 }
1570 
newView(const Latte::Data::View & nextViewData)1571 Data::View GenericLayout::newView(const Latte::Data::View &nextViewData)
1572 {
1573     if (nextViewData.state() == Data::View::IsInvalid) {
1574         return Data::View();
1575     }
1576 
1577     Data::View result = Layouts::Storage::self()->newView(this, nextViewData);
1578     emit viewEdgeChanged();
1579 
1580     return result;
1581 }
1582 
updateView(const Latte::Data::View & viewData)1583 void GenericLayout::updateView(const Latte::Data::View &viewData)
1584 {
1585     //! storage -> storage [view scenario]
1586     if (!isActive()) {
1587         Layouts::Storage::self()->updateView(this, viewData);
1588         return;
1589     }
1590 
1591     //! active -> active [view scenario]
1592     Latte::View *view = viewForContainment(viewData.id.toUInt());
1593     bool viewMustBeDeleted = (view && !viewData.onPrimary && !m_corona->screenPool()->isScreenActive(viewData.screen));
1594 
1595     QString nextactivelayoutname = (viewData.state() == Data::View::OriginFromLayout && !viewData.originLayout().isEmpty() ? viewData.originLayout() : QString());
1596 
1597     if (view) {
1598         if (!viewMustBeDeleted) {
1599             QString scrName = Latte::Data::Screen::ONPRIMARYNAME;
1600 
1601             if (!viewData.onPrimary) {
1602                 if (m_corona->screenPool()->hasScreenId(viewData.screen)) {
1603                     scrName = m_corona->screenPool()->connector(viewData.screen);
1604                 } else {
1605                     scrName = "";
1606                 }
1607             }
1608 
1609             view->setName(viewData.name);
1610             view->positioner()->setNextLocation(nextactivelayoutname, scrName, viewData.edge, viewData.alignment);
1611             return;
1612         } else {
1613             //! viewMustBeDeleted
1614             m_latteViews.remove(view->containment());
1615             view->disconnectSensitiveSignals();
1616             delete view;
1617         }
1618     }
1619 
1620     //! inactiveinmemory -> active/inactiveinmemory [viewscenario]
1621     //! active -> inactiveinmemory                  [viewscenario]
1622     auto containment = containmentForId(viewData.id.toUInt());
1623     if (containment) {
1624         Layouts::Storage::self()->updateView(this, viewData);
1625 
1626         //! by using pendingContainmentUpdates we make sure that when containment->screen() will be
1627         //! called though reactToScreenChange() the proper screen will be returned
1628         if (!m_pendingContainmentUpdates.containsId(viewData.id)) {
1629             m_pendingContainmentUpdates << viewData;
1630         } else {
1631             m_pendingContainmentUpdates[viewData.id] = viewData;
1632         }
1633         containment->reactToScreenChange();
1634     }
1635 
1636     if (!nextactivelayoutname.isEmpty()) {
1637         m_corona->layoutsManager()->moveView(name(), viewData.id.toUInt(), nextactivelayoutname);
1638     }
1639 
1640     //! complete update circle and inform the others about the changes
1641     if (viewMustBeDeleted) {
1642         emit viewEdgeChanged();
1643         emit viewsCountChanged();
1644     }
1645 
1646     syncLatteViewsToScreens();
1647 }
1648 
removeView(const Latte::Data::View & viewData)1649 void GenericLayout::removeView(const Latte::Data::View &viewData)
1650 {
1651     if (!containsView(viewData.id.toInt())) {
1652         return;
1653     }
1654 
1655     if (!isActive()) {
1656         Layouts::Storage::self()->removeView(file(), viewData);
1657         return;
1658     }
1659 
1660     Plasma::Containment *viewcontainment = containmentForId(viewData.id.toUInt());
1661     destroyContainment(viewcontainment);
1662 }
1663 
removeOrphanedSubContainment(const int & containmentId)1664 void GenericLayout::removeOrphanedSubContainment(const int &containmentId)
1665 {
1666     Data::ViewsTable views = viewsTable();
1667     QString cidstr = QString::number(containmentId);
1668 
1669     if (views.hasContainmentId(cidstr)) {
1670         return;
1671     }
1672 
1673     if (!isActive()) {
1674         Layouts::Storage::self()->removeContainment(file(), cidstr);
1675         return;
1676     }
1677 
1678     Plasma::Containment *orphanedcontainment = containmentForId(cidstr.toUInt());
1679     destroyContainment(orphanedcontainment);
1680 }
1681 
destroyContainment(Plasma::Containment * containment)1682 void GenericLayout::destroyContainment(Plasma::Containment *containment)
1683 {
1684     if (!containment || !m_corona || !contains(containment)) {
1685         return;
1686     }
1687 
1688     containment->setImmutability(Plasma::Types::Mutable);
1689     containment->destroy();
1690 }
1691 
storedView(const int & containmentId)1692 QString GenericLayout::storedView(const int &containmentId)
1693 {
1694     return Layouts::Storage::self()->storedView(this, containmentId);
1695 }
1696 
importToCorona()1697 void GenericLayout::importToCorona()
1698 {
1699     Layouts::Storage::self()->importToCorona(this);
1700 }
1701 
errors() const1702 Data::ErrorsList GenericLayout::errors() const
1703 {
1704     return Layouts::Storage::self()->errors(this);
1705 }
1706 
warnings() const1707 Data::WarningsList GenericLayout::warnings() const
1708 {
1709     return Layouts::Storage::self()->warnings(this);
1710 }
1711 
viewsTable() const1712 Latte::Data::ViewsTable GenericLayout::viewsTable() const
1713 {
1714     return Layouts::Storage::self()->views(this);
1715 }
1716 
1717 }
1718 }
1719