File indexing completed on 2024-04-14 15:37:12

0001 /*
0002 *  Copyright 2019  Michail Vourlakos <mvourlakos@gmail.com>
0003 *
0004 *  This file is part of Latte-Dock
0005 *
0006 *  Latte-Dock is free software; you can redistribute it and/or
0007 *  modify it under the terms of the GNU General Public License as
0008 *  published by the Free Software Foundation; either version 2 of
0009 *  the License, or (at your option) any later version.
0010 *
0011 *  Latte-Dock is distributed in the hope that it will be useful,
0012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 *  GNU General Public License for more details.
0015 *
0016 *  You should have received a copy of the GNU General Public License
0017 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018 */
0019 
0020 #include "genericlayout.h"
0021 
0022 // local
0023 #include "abstractlayout.h"
0024 #include "storage.h"
0025 #include "../lattecorona.h"
0026 #include "../screenpool.h"
0027 #include "../layouts/importer.h"
0028 #include "../layouts/manager.h"
0029 #include "../layouts/synchronizer.h"
0030 #include "../shortcuts/shortcutstracker.h"
0031 #include "../view/view.h"
0032 #include "../view/positioner.h"
0033 
0034 // Qt
0035 #include <QDebug>
0036 #include <QScreen>
0037 
0038 // Plasma
0039 #include <Plasma>
0040 #include <Plasma/Applet>
0041 #include <Plasma/Containment>
0042 
0043 // KDE
0044 #include <KConfigGroup>
0045 
0046 namespace Latte {
0047 namespace Layout {
0048 
0049 GenericLayout::GenericLayout(QObject *parent, QString layoutFile, QString assignedName)
0050     : AbstractLayout (parent, layoutFile, assignedName),
0051       m_storage(new Storage(this))
0052 {
0053 }
0054 
0055 GenericLayout::~GenericLayout()
0056 {
0057 }
0058 
0059 Type GenericLayout::type() const
0060 {
0061     return Type::Generic;
0062 }
0063 
0064 void GenericLayout::unloadContainments()
0065 {
0066     if (!m_corona) {
0067         return;
0068     }
0069 
0070     qDebug() << "Layout - " + name() + " : [unloadContainments]"
0071              << "containments ::: " << m_containments.size()
0072              << " ,latteViews in memory ::: " << m_latteViews.size()
0073              << " ,hidden latteViews in memory :::  " << m_waitingLatteViews.size();
0074 
0075     for (const auto view : m_latteViews) {
0076         view->disconnectSensitiveSignals();
0077     }
0078 
0079     for (const auto view : m_waitingLatteViews) {
0080         view->disconnectSensitiveSignals();
0081     }
0082 
0083     m_unloadedContainmentsIds.clear();
0084 
0085     QList<Plasma::Containment *> systrays;
0086 
0087     //!identify systrays and unload them first
0088     for (const auto containment : m_containments) {
0089         if (Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent())) {
0090             systrays.append(containment);
0091         }
0092     }
0093 
0094     while (!systrays.isEmpty()) {
0095         Plasma::Containment *systray = systrays.at(0);
0096         m_unloadedContainmentsIds << QString::number(systray->id());
0097         systrays.removeFirst();
0098         m_containments.removeAll(systray);
0099         delete systray;
0100     }
0101 
0102     while (!m_containments.isEmpty()) {
0103         Plasma::Containment *containment = m_containments.at(0);
0104         m_unloadedContainmentsIds << QString::number(containment->id());
0105         m_containments.removeFirst();
0106         delete containment;
0107     }
0108 }
0109 
0110 void GenericLayout::unloadLatteViews()
0111 {
0112     if (!m_corona) {
0113         return;
0114     }
0115 
0116     qDebug() << "Layout - " + name() + " : [unloadLatteViews]"
0117              << "containments ::: " << m_containments.size()
0118              << " ,latteViews in memory ::: " << m_latteViews.size()
0119              << " ,hidden latteViews in memory :::  " << m_waitingLatteViews.size();
0120 
0121     //!disconnect signals in order to avoid crashes when the layout is unloading
0122     disconnect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged);
0123     disconnect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged);
0124     disconnect(m_corona->activityConsumer(), &KActivities::Consumer::currentActivityChanged, this, &GenericLayout::updateLastUsedActivity);
0125 
0126     for (const auto view : m_latteViews) {
0127         view->disconnectSensitiveSignals();
0128     }
0129 
0130     for (const auto view : m_waitingLatteViews) {
0131         view->disconnectSensitiveSignals();
0132     }
0133 
0134     qDeleteAll(m_latteViews);
0135     qDeleteAll(m_waitingLatteViews);
0136     m_latteViews.clear();
0137     m_waitingLatteViews.clear();
0138 }
0139 
0140 bool GenericLayout::blockAutomaticLatteViewCreation() const
0141 {
0142     return m_blockAutomaticLatteViewCreation;
0143 }
0144 
0145 void GenericLayout::setBlockAutomaticLatteViewCreation(bool block)
0146 {
0147     if (m_blockAutomaticLatteViewCreation == block) {
0148         return;
0149     }
0150 
0151     m_blockAutomaticLatteViewCreation = block;
0152 }
0153 
0154 bool GenericLayout::configViewIsShown() const
0155 {
0156     for (const auto view : m_latteViews) {
0157         if (view && view->settingsWindowIsShown()) {
0158             return true;
0159         }
0160     }
0161 
0162     return false;
0163 }
0164 
0165 bool GenericLayout::isActive() const
0166 {
0167     if (!m_corona) {
0168         return false;
0169     }
0170 
0171     GenericLayout *generic = m_corona->layoutsManager()->synchronizer()->layout(m_layoutName);
0172 
0173     if (generic) {
0174         return true;
0175     } else {
0176         return false;
0177     }
0178 }
0179 
0180 bool GenericLayout::isCurrent() const
0181 {
0182     if (!m_corona) {
0183         return false;
0184     }
0185 
0186     return name() == m_corona->layoutsManager()->currentLayoutName();
0187 }
0188 
0189 bool GenericLayout::isInternalContainment(Plasma::Applet *applet) const
0190 {
0191     if (!applet) {
0192         return false;
0193     }
0194 
0195     for (const auto containment : m_containments) {
0196         Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
0197         if (parentApplet && parentApplet == applet) {
0198             return true;
0199         }
0200     }
0201 
0202     return false;
0203 }
0204 
0205 Plasma::Containment *GenericLayout::internalContainmentOf(Plasma::Applet *applet) const
0206 {
0207     if (!applet) {
0208         return nullptr;
0209     }
0210 
0211     if (isInternalContainment(applet)) {
0212         for (const auto containment : m_containments) {
0213             Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
0214             if (parentApplet && parentApplet == applet) {
0215                 return containment;
0216             }
0217         }
0218     }
0219 
0220     return nullptr;
0221 }
0222 
0223 int GenericLayout::viewsCount(int screen) const
0224 {
0225     if (!m_corona) {
0226         return 0;
0227     }
0228 
0229     QScreen *scr = m_corona->screenPool()->screenForId(screen);
0230 
0231     int views{0};
0232 
0233     for (const auto view : m_latteViews) {
0234         if (view && view->screen() == scr && !view->containment()->destroyed()) {
0235             ++views;
0236         }
0237     }
0238 
0239     return views;
0240 }
0241 
0242 int GenericLayout::viewsCount(QScreen *screen) const
0243 {
0244     if (!m_corona) {
0245         return 0;
0246     }
0247 
0248     int views{0};
0249 
0250     for (const auto view : m_latteViews) {
0251         if (view && view->screen() == screen && !view->containment()->destroyed()) {
0252             ++views;
0253         }
0254     }
0255 
0256     return views;
0257 }
0258 
0259 int GenericLayout::viewsCount() const
0260 {
0261     if (!m_corona) {
0262         return 0;
0263     }
0264 
0265     int views{0};
0266 
0267     for (const auto view : m_latteViews) {
0268         if (view && view->containment() && !view->containment()->destroyed()) {
0269             ++views;
0270         }
0271     }
0272 
0273     return views;
0274 }
0275 
0276 QList<int> GenericLayout::qmlFreeEdges(int screen) const
0277 {
0278     if (!m_corona) {
0279         const QList<int> emptyEdges;
0280         return emptyEdges;
0281     }
0282 
0283     const auto edges = freeEdges(screen);
0284     QList<int> edgesInt;
0285 
0286     for (const Plasma::Types::Location &edge : edges) {
0287         edgesInt.append(static_cast<int>(edge));
0288     }
0289 
0290     return edgesInt;
0291 }
0292 
0293 QList<Plasma::Types::Location> GenericLayout::freeEdges(QScreen *scr) const
0294 {
0295     using Plasma::Types;
0296     QList<Types::Location> edges{Types::BottomEdge, Types::LeftEdge,
0297                 Types::TopEdge, Types::RightEdge};
0298 
0299     if (!m_corona) {
0300         return edges;
0301     }
0302 
0303     for (const auto view : m_latteViews) {
0304         if (view && view->positioner()->currentScreenName() == scr->name()) {
0305             edges.removeOne(view->location());
0306         }
0307     }
0308 
0309     return edges;
0310 }
0311 
0312 QList<Plasma::Types::Location> GenericLayout::freeEdges(int screen) const
0313 {
0314     using Plasma::Types;
0315     QList<Types::Location> edges{Types::BottomEdge, Types::LeftEdge,
0316                 Types::TopEdge, Types::RightEdge};
0317 
0318     if (!m_corona) {
0319         return edges;
0320     }
0321 
0322     QScreen *scr = m_corona->screenPool()->screenForId(screen);
0323 
0324     for (const auto view : m_latteViews) {
0325         if (view && scr && view->positioner()->currentScreenName() == scr->name()) {
0326             edges.removeOne(view->location());
0327         }
0328     }
0329 
0330     return edges;
0331 }
0332 
0333 int GenericLayout::viewsWithTasks() const
0334 {
0335     if (!m_corona) {
0336         return 0;
0337     }
0338 
0339     int result = 0;
0340 
0341     for (const auto view : m_latteViews) {
0342         if (view->tasksPresent()) {
0343             result++;
0344         }
0345     }
0346 
0347     return result;
0348 }
0349 
0350 QStringList GenericLayout::unloadedContainmentsIds()
0351 {
0352     return m_unloadedContainmentsIds;
0353 }
0354 
0355 Latte::Corona *GenericLayout::corona()
0356 {
0357     return m_corona;
0358 }
0359 
0360 Types::ViewType GenericLayout::latteViewType(int containmentId) const
0361 {
0362     for (const auto view : m_latteViews) {
0363         if (view->containment() && view->containment()->id() == containmentId) {
0364             return view->type();
0365         }
0366     }
0367 
0368     return Types::DockView;
0369 }
0370 
0371 Latte::View *GenericLayout::highestPriorityView()
0372 {
0373     QList<Latte::View *> views = sortedLatteViews();
0374 
0375     return (views.count() > 0 ? views[0] : nullptr);
0376 }
0377 
0378 Latte::View *GenericLayout::lastConfigViewFor()
0379 {
0380     if (!latteViews().contains(m_lastConfigViewFor)) {
0381         m_lastConfigViewFor = nullptr;
0382         return nullptr;
0383     }
0384 
0385     return m_lastConfigViewFor;
0386 }
0387 
0388 void GenericLayout::setLastConfigViewFor(Latte::View *view)
0389 {
0390     if (m_lastConfigViewFor == view) {
0391         return;
0392     }
0393 
0394     m_lastConfigViewFor = view;
0395     emit lastConfigViewForChanged(view);
0396 }
0397 
0398 Latte::View *GenericLayout::viewForContainment(Plasma::Containment *containment)
0399 {
0400     if (m_containments.contains(containment) && m_latteViews.contains(containment)) {
0401         return m_latteViews[containment];
0402     }
0403 
0404     return nullptr;
0405 }
0406 
0407 QList<Latte::View *> GenericLayout::latteViews()
0408 {
0409     return m_latteViews.values();
0410 }
0411 
0412 QList<Latte::View *> GenericLayout::sortedLatteViews(QList<Latte::View *> views)
0413 {
0414     QList<Latte::View *> sortedViews = views.isEmpty() ? latteViews() : views;
0415 
0416     qDebug() << " -------- ";
0417 
0418     for (int i = 0; i < sortedViews.count(); ++i) {
0419         qDebug() << i << ". " << sortedViews[i]->screen()->name() << " - " << sortedViews[i]->location();
0420     }
0421 
0422     //! sort the views based on screens and edges priorities
0423     //! views on primary screen have higher priority and
0424     //! for views in the same screen the priority goes to
0425     //! Bottom,Left,Top,Right
0426     for (int i = 0; i < sortedViews.size(); ++i) {
0427         for (int j = 0; j < sortedViews.size() - i - 1; ++j) {
0428             if (viewAtLowerScreenPriority(sortedViews[j], sortedViews[j + 1])
0429                     || (sortedViews[j]->screen() == sortedViews[j + 1]->screen()
0430                         && viewAtLowerEdgePriority(sortedViews[j], sortedViews[j + 1]))) {
0431                 Latte::View *temp = sortedViews[j + 1];
0432                 sortedViews[j + 1] = sortedViews[j];
0433                 sortedViews[j] = temp;
0434             }
0435         }
0436     }
0437 
0438     Latte::View *highestPriorityView{nullptr};
0439 
0440     for (int i = 0; i < sortedViews.size(); ++i) {
0441         if (sortedViews[i]->isPreferredForShortcuts()) {
0442             highestPriorityView = sortedViews[i];
0443             sortedViews.removeAt(i);
0444             break;
0445         }
0446     }
0447 
0448     if (highestPriorityView) {
0449         sortedViews.prepend(highestPriorityView);
0450     }
0451 
0452     qDebug() << " -------- sorted -----";
0453 
0454     for (int i = 0; i < sortedViews.count(); ++i) {
0455         qDebug() << i << ". " << sortedViews[i]->isPreferredForShortcuts() << " - " << sortedViews[i]->screen()->name() << " - " << sortedViews[i]->location();
0456     }
0457 
0458     return sortedViews;
0459 }
0460 
0461 bool GenericLayout::viewAtLowerScreenPriority(Latte::View *test, Latte::View *base)
0462 {
0463     if (!base || ! test) {
0464         return true;
0465     }
0466 
0467     if (base->screen() == test->screen()) {
0468         return false;
0469     } else if (base->screen() != qGuiApp->primaryScreen() && test->screen() == qGuiApp->primaryScreen()) {
0470         return false;
0471     } else if (base->screen() == qGuiApp->primaryScreen() && test->screen() != qGuiApp->primaryScreen()) {
0472         return true;
0473     } else {
0474         int basePriority = -1;
0475         int testPriority = -1;
0476 
0477         for (int i = 0; i < qGuiApp->screens().count(); ++i) {
0478             if (base->screen() == qGuiApp->screens()[i]) {
0479                 basePriority = i;
0480             }
0481 
0482             if (test->screen() == qGuiApp->screens()[i]) {
0483                 testPriority = i;
0484             }
0485         }
0486 
0487         if (testPriority <= basePriority) {
0488             return true;
0489         } else {
0490             return false;
0491         }
0492 
0493     }
0494 
0495     qDebug() << "viewAtLowerScreenPriority : shouldn't had reached here...";
0496     return false;
0497 }
0498 
0499 bool GenericLayout::viewAtLowerEdgePriority(Latte::View *test, Latte::View *base)
0500 {
0501     if (!base || ! test) {
0502         return true;
0503     }
0504 
0505     QList<Plasma::Types::Location> edges{Plasma::Types::RightEdge, Plasma::Types::TopEdge,
0506                 Plasma::Types::LeftEdge, Plasma::Types::BottomEdge};
0507 
0508     int testPriority = -1;
0509     int basePriority = -1;
0510 
0511     for (int i = 0; i < edges.count(); ++i) {
0512         if (edges[i] == base->location()) {
0513             basePriority = i;
0514         }
0515 
0516         if (edges[i] == test->location()) {
0517             testPriority = i;
0518         }
0519     }
0520 
0521     if (testPriority < basePriority) {
0522         return true;
0523     } else {
0524         return false;
0525     }
0526 }
0527 
0528 bool GenericLayout::viewDataAtLowerScreenPriority(const ViewData &test, const ViewData &base) const
0529 {
0530     if (test.onPrimary && base.onPrimary) {
0531         return false;
0532     } else if (!base.onPrimary && test.onPrimary) {
0533         return false;
0534     } else if (base.onPrimary && !test.onPrimary) {
0535         return true;
0536     } else {
0537         return test.screenId <= base.screenId;
0538     }
0539 }
0540 
0541 bool GenericLayout::viewDataAtLowerStatePriority(const ViewData &test, const ViewData &base) const
0542 {
0543     if (test.active == base.active) {
0544         return false;
0545     } else if (!base.active && test.active) {
0546         return false;
0547     } else if (base.active && !test.active) {
0548         return true;
0549     }
0550 
0551     return false;
0552 }
0553 
0554 bool GenericLayout::viewDataAtLowerEdgePriority(const ViewData &test, const ViewData &base) const
0555 {
0556     QList<Plasma::Types::Location> edges{Plasma::Types::RightEdge, Plasma::Types::TopEdge,
0557                 Plasma::Types::LeftEdge, Plasma::Types::BottomEdge};
0558 
0559     int testPriority = -1;
0560     int basePriority = -1;
0561 
0562     for (int i = 0; i < edges.count(); ++i) {
0563         if (edges[i] == base.location) {
0564             basePriority = i;
0565         }
0566 
0567         if (edges[i] == test.location) {
0568             testPriority = i;
0569         }
0570     }
0571 
0572     if (testPriority < basePriority) {
0573         return true;
0574     } else {
0575         return false;
0576     }
0577 }
0578 
0579 QList<ViewData> GenericLayout::sortedViewsData(const QList<ViewData> &viewsData)
0580 {
0581     QList<ViewData> sortedData = viewsData;
0582 
0583     //! sort the views based on screens and edges priorities
0584     //! views on primary screen have higher priority and
0585     //! for views in the same screen the priority goes to
0586     //! Bottom,Left,Top,Right
0587     for (int i = 0; i < sortedData.size(); ++i) {
0588         for (int j = 0; j < sortedData.size() - i - 1; ++j) {
0589             if (viewDataAtLowerStatePriority(sortedData[j], sortedData[j + 1])
0590                     || viewDataAtLowerScreenPriority(sortedData[j], sortedData[j + 1])
0591                     || (!viewDataAtLowerScreenPriority(sortedData[j], sortedData[j + 1])
0592                         && viewDataAtLowerEdgePriority(sortedData[j], sortedData[j + 1])) ) {
0593                 ViewData temp = sortedData[j + 1];
0594                 sortedData[j + 1] = sortedData[j];
0595                 sortedData[j] = temp;
0596             }
0597         }
0598     }
0599 
0600     return sortedData;
0601 }
0602 
0603 
0604 const QList<Plasma::Containment *> *GenericLayout::containments()
0605 {
0606     return &m_containments;
0607 }
0608 
0609 QList<Latte::View *> GenericLayout::viewsWithPlasmaShortcuts()
0610 {
0611     QList<Latte::View *> views;
0612 
0613     if (!m_corona) {
0614         return views;
0615     }
0616 
0617     QList<int> appletsWithShortcuts = m_corona->globalShortcuts()->shortcutsTracker()->appletsWithPlasmaShortcuts();
0618 
0619     for (const auto &appletId : appletsWithShortcuts) {
0620         for (const auto view : m_latteViews) {
0621             bool found{false};
0622             for (const auto applet : view->containment()->applets()) {
0623                 if (appletId == applet->id()) {
0624                     if (!views.contains(view)) {
0625                         views.append(view);
0626                         found = true;
0627                         break;
0628                     }
0629                 }
0630             }
0631 
0632             if (found) {
0633                 break;
0634             }
0635         }
0636     }
0637 
0638     return views;
0639 }
0640 
0641 
0642 //! Containments Actions
0643 void GenericLayout::addContainment(Plasma::Containment *containment)
0644 {
0645     if (!containment || m_containments.contains(containment)) {
0646         return;
0647     }
0648 
0649     bool containmentInLayout{false};
0650 
0651     if (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout) {
0652         m_containments.append(containment);
0653         containmentInLayout = true;
0654     } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
0655         QString layoutId = containment->config().readEntry("layoutId", QString());
0656 
0657         if (!layoutId.isEmpty() && (layoutId == m_layoutName)) {
0658             m_containments.append(containment);
0659             containmentInLayout = true;
0660         }
0661     }
0662 
0663     if (containmentInLayout) {
0664         if (!blockAutomaticLatteViewCreation()) {
0665             addView(containment);
0666         } else {
0667             qDebug() << "delaying LatteView creation for containment :: " << containment->id();
0668         }
0669 
0670         connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed);
0671     }
0672 }
0673 
0674 void GenericLayout::appletCreated(Plasma::Applet *applet)
0675 {
0676     //! In Multiple Layout the orphaned systrays must be assigned to layouts
0677     //! when the user adds them
0678     KConfigGroup appletSettings = applet->containment()->config().group("Applets").group(QString::number(applet->id())).group("Configuration");
0679 
0680     int systrayId = appletSettings.readEntry("SystrayContainmentId", -1);
0681 
0682     if (systrayId != -1) {
0683         uint sId = (uint)systrayId;
0684 
0685         for (const auto containment : m_corona->containments()) {
0686             if (containment->id() == sId) {
0687                 containment->config().writeEntry("layoutId", m_layoutName);
0688             }
0689 
0690             addContainment(containment);
0691         }
0692     }
0693 }
0694 
0695 void GenericLayout::containmentDestroyed(QObject *cont)
0696 {
0697     if (!m_corona) {
0698         return;
0699     }
0700 
0701     Plasma::Containment *containment = static_cast<Plasma::Containment *>(cont);
0702 
0703     if (containment) {
0704         int containmentIndex = m_containments.indexOf(containment);
0705 
0706         if (containmentIndex >= 0) {
0707             m_containments.removeAt(containmentIndex);
0708         }
0709 
0710         qDebug() << "Layout " << name() << " :: containment destroyed!!!!";
0711         auto view = m_latteViews.take(containment);
0712 
0713         if (!view) {
0714             view = m_waitingLatteViews.take(containment);
0715         }
0716 
0717         if (view) {
0718             view->disconnectSensitiveSignals();
0719 
0720             view->deleteLater();
0721 
0722             emit viewEdgeChanged();
0723             emit viewsCountChanged();
0724         }
0725     }
0726 }
0727 
0728 void GenericLayout::destroyedChanged(bool destroyed)
0729 {
0730     if (!m_corona) {
0731         return;
0732     }
0733 
0734     qDebug() << "dock containment destroyed changed!!!!";
0735     Plasma::Containment *sender = qobject_cast<Plasma::Containment *>(QObject::sender());
0736 
0737     if (!sender) {
0738         return;
0739     }
0740 
0741     if (destroyed) {
0742         m_waitingLatteViews[sender] = m_latteViews.take(static_cast<Plasma::Containment *>(sender));
0743     } else {
0744         m_latteViews[sender] = m_waitingLatteViews.take(static_cast<Plasma::Containment *>(sender));
0745     }
0746 
0747     emit viewEdgeChanged();
0748     emit viewsCountChanged();
0749 }
0750 
0751 void GenericLayout::renameLayout(QString newName)
0752 {
0753     if (!m_corona || m_corona->layoutsManager()->memoryUsage() != Types::MultipleLayouts) {
0754         return;
0755     }
0756 
0757     if (m_layoutFile != Layouts::Importer::layoutFilePath(newName)) {
0758         setFile(Layouts::Importer::layoutFilePath(newName));
0759     }
0760 
0761     setName(newName);
0762 
0763     for (const auto containment : m_containments) {
0764         qDebug() << "Cont ID :: " << containment->id();
0765         containment->config().writeEntry("layoutId", m_layoutName);
0766     }
0767 }
0768 
0769 void GenericLayout::addNewView()
0770 {
0771     if (!m_corona) {
0772         return;
0773     }
0774 
0775     m_corona->addViewForLayout(name());
0776     emit viewEdgeChanged();
0777 }
0778 
0779 void GenericLayout::addView(Plasma::Containment *containment, bool forceOnPrimary, int explicitScreen, Layout::ViewsMap *occupied)
0780 {
0781     qDebug() << "Layout :::: " << m_layoutName << " ::: addView was called... m_containments :: " << m_containments.size();
0782 
0783     if (!containment || !m_corona || !containment->kPackage().isValid()) {
0784         qWarning() << "the requested containment plugin can not be located or loaded";
0785         return;
0786     }
0787 
0788     qDebug() << "step 1...";
0789 
0790     if (!m_storage->isLatteContainment(containment))
0791         return;
0792 
0793     qDebug() << "step 2...";
0794 
0795     for (auto *dock : m_latteViews) {
0796         if (dock->containment() == containment)
0797             return;
0798     }
0799 
0800     qDebug() << "step 3...";
0801 
0802     QScreen *nextScreen{qGuiApp->primaryScreen()};
0803 
0804     bool onPrimary = containment->config().readEntry("onPrimary", true);
0805     int id = containment->screen();
0806 
0807     if (id == -1 && explicitScreen == -1) {
0808         id = containment->lastScreen();
0809     }
0810 
0811     if (onPrimary) {
0812         id = m_corona->screenPool()->primaryScreenId();
0813     } else if (explicitScreen > -1) {
0814         id = explicitScreen;
0815     }
0816 
0817     Plasma::Types::Location edge = containment->location();
0818 
0819     QString connector = m_corona->screenPool()->hasId(id) ? m_corona->screenPool()->connector(id) : "";
0820 
0821     qDebug() << "Adding view - containment id:" << containment->id() << " ,screen :" << id << " - " << connector
0822              << " ,onprimary:" << onPrimary << " - "  << " edge:" << edge << " ,screenName:" << qGuiApp->primaryScreen()->name() << " ,forceOnPrimary:" << forceOnPrimary;
0823 
0824     if (occupied && m_corona->screenPool()->hasId(id) && (*occupied).contains(connector) && (*occupied)[connector].contains(edge)) {
0825         qDebug() << "Rejected : adding view because the edge is already occupied by a higher priority view ! : " << (*occupied)[connector][edge];
0826         return;
0827     }
0828 
0829     if (id >= 0 && !onPrimary && !forceOnPrimary) {
0830         qDebug() << "Add view - connector : " << connector;
0831         bool found{false};
0832 
0833         if (m_corona->screenPool()->hasId(id)) {
0834             for (const auto scr : qGuiApp->screens()) {
0835                 if (scr && scr->name() == connector) {
0836                     found = true;
0837                     nextScreen = scr;
0838                     break;
0839                 }
0840             }
0841         }
0842 
0843         if (!found) {
0844             qDebug() << "Rejected : adding explicit view, screen not available ! : " << connector;
0845             return;
0846         }
0847 
0848         //! explicit dock can not be added at explicit screen when that screen is the same with
0849         //! primary screen and that edge is already occupied by a primary dock
0850         if (nextScreen == qGuiApp->primaryScreen() && primaryDockOccupyEdge(containment->location())) {
0851             qDebug() << "Rejected : adding explicit view, primary dock occupies edge at screen ! : " << connector;
0852             return;
0853         }
0854     }
0855 
0856     if (id >= 0 && onPrimary) {
0857         qDebug() << "add dock - connector : " << connector;
0858 
0859         for (const auto view : m_latteViews) {
0860             auto testContainment = view->containment();
0861 
0862             int testScreenId = testContainment->screen();
0863 
0864             if (testScreenId == -1) {
0865                 testScreenId = testContainment->lastScreen();
0866             }
0867 
0868             bool testOnPrimary = testContainment->config().readEntry("onPrimary", true);
0869             Plasma::Types::Location testLocation = static_cast<Plasma::Types::Location>((int)testContainment->config().readEntry("location", (int)Plasma::Types::BottomEdge));
0870 
0871             if (!testOnPrimary && m_corona->screenPool()->primaryScreenId() == testScreenId && testLocation == containment->location()) {
0872                 qDebug() << "Rejected explicit latteView and removing it in order add an onPrimary with higher priority at screen: " << connector;
0873                 auto viewToDelete = m_latteViews.take(testContainment);
0874                 viewToDelete->disconnectSensitiveSignals();
0875                 viewToDelete->deleteLater();
0876             }
0877         }
0878     }
0879 
0880     qDebug() << "Adding view passed ALL checks" << " ,onPrimary:" << onPrimary << " ,screen:" << nextScreen->name() << " !!!";
0881 
0882     //! it is used to set the correct flag during the creation
0883     //! of the window... This of course is also used during
0884     //! recreations of the window between different visibility modes
0885     auto mode = static_cast<Types::Visibility>(containment->config().readEntry("visibility", static_cast<int>(Types::DodgeActive)));
0886     bool byPassWM{false};
0887 
0888     if (mode == Types::AlwaysVisible || mode == Types::WindowsGoBelow) {
0889         byPassWM = false;
0890     } else {
0891         byPassWM = containment->config().readEntry("byPassWM", false);
0892     }
0893 
0894     auto latteView = new Latte::View(m_corona, nextScreen, byPassWM);
0895 
0896     latteView->init();
0897     latteView->setContainment(containment);
0898 
0899     //! force this special dock case to become primary
0900     //! even though it isnt
0901     if (forceOnPrimary) {
0902         qDebug() << "Enforcing onPrimary:true as requested for LatteView...";
0903         latteView->setOnPrimary(true);
0904     }
0905 
0906     latteView->setLayout(this);
0907 
0908     //! Qt 5.9 creates a crash for this in wayland, that is why the check is used
0909     //! but on the other hand we need this for copy to work correctly and show
0910     //! the copied dock under X11
0911     //if (!KWindowSystem::isPlatformWayland()) {
0912     latteView->show();
0913     //}
0914 
0915     m_latteViews[containment] = latteView;
0916 
0917     emit viewsCountChanged();
0918 }
0919 
0920 
0921 bool GenericLayout::initToCorona(Latte::Corona *corona)
0922 {
0923     if (m_corona) {
0924         return false;
0925     }
0926 
0927     m_corona = corona;
0928 
0929     for (const auto containment : m_corona->containments()) {
0930         if (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout) {
0931             addContainment(containment);
0932         } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
0933             QString layoutId = containment->config().readEntry("layoutId", QString());
0934 
0935             if (!layoutId.isEmpty() && (layoutId == m_layoutName)) {
0936                 addContainment(containment);
0937             }
0938         }
0939     }
0940 
0941     qDebug() << "Layout ::::: " << name() << " added containments ::: " << m_containments.size();
0942 
0943     updateLastUsedActivity();
0944 
0945     //! signals
0946     connect(m_corona->activityConsumer(), &KActivities::Consumer::currentActivityChanged,
0947             this, &GenericLayout::updateLastUsedActivity);
0948 
0949     connect(m_corona, &Plasma::Corona::containmentAdded, this, &GenericLayout::addContainment);
0950 
0951     //!connect signals after adding the containment
0952     connect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged);
0953     connect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged);
0954 
0955     emit viewsCountChanged();
0956 
0957     return true;
0958 }
0959 
0960 void GenericLayout::updateLastUsedActivity()
0961 {
0962     if (!m_corona) {
0963         return;
0964     }
0965 
0966     if (!m_lastUsedActivity.isEmpty() && !m_corona->layoutsManager()->synchronizer()->activities().contains(m_lastUsedActivity)) {
0967         clearLastUsedActivity();
0968     }
0969 
0970     QString currentId = m_corona->activitiesConsumer()->currentActivity();
0971 
0972     QStringList appliedActivitiesIds = appliedActivities();
0973 
0974     if (m_lastUsedActivity != currentId
0975             && (appliedActivitiesIds.contains(currentId)
0976                 || m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout)) {
0977         m_lastUsedActivity = currentId;
0978 
0979         emit lastUsedActivityChanged();
0980     }
0981 }
0982 
0983 void GenericLayout::assignToLayout(Latte::View *latteView, QList<Plasma::Containment *> containments)
0984 {
0985     if (!m_corona) {
0986         return;
0987     }
0988 
0989     if (latteView) {
0990         m_latteViews[latteView->containment()] = latteView;
0991         m_containments << containments;
0992 
0993         for (const auto containment : containments) {
0994             containment->config().writeEntry("layoutId", name());
0995 
0996             if (latteView->containment() != containment) {
0997                 //! assign signals only to systrays
0998                 //! the View::setLayout() is responsible for the View::Containment signals
0999                 connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed);
1000                 connect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged);
1001                 connect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated);
1002             }
1003         }
1004 
1005         latteView->setLayout(this);
1006 
1007         emit viewsCountChanged();
1008     }
1009 
1010     //! sync the original layout file for integrity
1011     if (m_corona && m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
1012         m_storage->syncToLayoutFile(false);
1013     }
1014 }
1015 
1016 QList<Plasma::Containment *> GenericLayout::unassignFromLayout(Latte::View *latteView)
1017 {
1018     QList<Plasma::Containment *> containments;
1019 
1020     if (!m_corona) {
1021         return containments;
1022     }
1023 
1024     containments << latteView->containment();
1025 
1026     for (const auto containment : m_containments) {
1027         Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
1028 
1029         //! add systrays from that latteView
1030         if (parentApplet && parentApplet->containment() && parentApplet->containment() == latteView->containment()) {
1031             containments << containment;
1032             //! unassign signals only to systrays
1033             //! the View::setLayout() is responsible for the View::Containment signals
1034             disconnect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed);
1035             disconnect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged);
1036             disconnect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated);
1037         }
1038     }
1039 
1040     for (const auto containment : containments) {
1041         m_containments.removeAll(containment);
1042     }
1043 
1044     if (containments.size() > 0) {
1045         m_latteViews.remove(latteView->containment());
1046     }
1047 
1048     //! sync the original layout file for integrity
1049     if (m_corona && m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
1050         m_storage->syncToLayoutFile(false);
1051     }
1052 
1053     return containments;
1054 }
1055 
1056 void GenericLayout::recreateView(Plasma::Containment *containment, bool delayed)
1057 {
1058     if (!m_corona || m_viewsToRecreate.contains(containment) || !containment || !m_latteViews.contains(containment)) {
1059         return;
1060     }
1061 
1062     int delay = delayed ? 350 : 0;
1063     m_viewsToRecreate << containment;
1064 
1065     //! give the time to config window to close itself first and then recreate the dock
1066     //! step:1 remove the latteview
1067     QTimer::singleShot(delay, [this, containment]() {
1068         auto view = m_latteViews[containment];
1069         view->disconnectSensitiveSignals();
1070 
1071         //! step:2 add the new latteview
1072         connect(view, &QObject::destroyed, this, [this, containment]() {
1073             auto view = m_latteViews.take(containment);
1074             QTimer::singleShot(250, this, [this, containment]() {
1075                 if (!m_latteViews.contains(containment)) {
1076                     qDebug() << "recreate - step 2: adding dock for containment:" << containment->id();
1077                     addView(containment);
1078                     m_viewsToRecreate.removeAll(containment);
1079                 }
1080             });
1081         });
1082 
1083         view->deleteLater();
1084     });
1085 }
1086 
1087 
1088 bool GenericLayout::latteViewExists(Plasma::Containment *containment)
1089 {
1090     if (!m_corona) {
1091         return false;
1092     }
1093 
1094     return m_latteViews.keys().contains(containment);
1095 }
1096 
1097 QList<Plasma::Types::Location> GenericLayout::availableEdgesForView(QScreen *scr, Latte::View *forView) const
1098 {
1099     using Plasma::Types;
1100     QList<Types::Location> edges{Types::BottomEdge, Types::LeftEdge,
1101                 Types::TopEdge, Types::RightEdge};
1102 
1103     if (!m_corona) {
1104         return edges;
1105     }
1106 
1107     for (const auto view : m_latteViews) {
1108         //! make sure that availabe edges takes into account only views that should be excluded,
1109         //! this is why the forView should not be excluded
1110         if (view && view != forView && view->positioner()->currentScreenName() == scr->name()) {
1111             edges.removeOne(view->location());
1112         }
1113     }
1114 
1115     return edges;
1116 }
1117 
1118 bool GenericLayout::explicitDockOccupyEdge(int screen, Plasma::Types::Location location) const
1119 {
1120     if (!m_corona) {
1121         return false;
1122     }
1123 
1124     for (const auto containment : m_containments) {
1125         if (m_storage->isLatteContainment(containment)) {
1126             bool onPrimary = containment->config().readEntry("onPrimary", true);
1127             int id = containment->lastScreen();
1128             Plasma::Types::Location contLocation = containment->location();
1129 
1130             if (!onPrimary && id == screen && contLocation == location) {
1131                 return true;
1132             }
1133         }
1134     }
1135 
1136     return false;
1137 }
1138 
1139 bool GenericLayout::primaryDockOccupyEdge(Plasma::Types::Location location) const
1140 {
1141     if (!m_corona) {
1142         return false;
1143     }
1144 
1145     for (const auto containment : m_containments) {
1146         if (m_storage->isLatteContainment(containment)) {
1147             bool onPrimary = containment->config().readEntry("onPrimary", true);
1148             Plasma::Types::Location contLocation = containment->location();
1149 
1150             if (onPrimary && contLocation == location) {
1151                 return true;
1152             }
1153         }
1154     }
1155 
1156     return false;
1157 }
1158 
1159 bool GenericLayout::mapContainsId(const Layout::ViewsMap *map, uint viewId) const
1160 {
1161     for(const auto &scr : map->keys()) {
1162         for(const auto &edge : (*map)[scr].keys()) {
1163             if ((*map)[scr][edge] == viewId) {
1164                 return true;
1165             }
1166         }
1167     }
1168 
1169     return false;
1170 }
1171 
1172 //! screen name, location, containmentId
1173 Layout::ViewsMap GenericLayout::validViewsMap(Layout::ViewsMap *occupiedMap)
1174 {
1175     Layout::ViewsMap map;
1176 
1177     if (!m_corona) {
1178         return map;
1179     }
1180 
1181     if (occupiedMap != nullptr) {
1182         map = (*occupiedMap);
1183     }
1184 
1185     QString prmScreenName = qGuiApp->primaryScreen()->name();
1186 
1187     //! first step: primary docks must be placed in primary screen free edges
1188     for (const auto containment : m_containments) {
1189         if (m_storage->isLatteContainment(containment)) {
1190             int screenId = 0;
1191 
1192             //! valid screen id
1193             if (latteViewExists(containment)) {
1194                 screenId = m_latteViews[containment]->positioner()->currentScreenId();
1195             } else {
1196                 screenId = containment->screen();
1197 
1198                 if (screenId == -1) {
1199                     screenId = containment->lastScreen();
1200                 }
1201             }
1202 
1203             bool onPrimary{true};
1204 
1205             //! valid onPrimary flag
1206             if (latteViewExists(containment)) {
1207                 onPrimary = m_latteViews[containment]->onPrimary();
1208             } else {
1209                 onPrimary = containment->config().readEntry("onPrimary", true);
1210             }
1211 
1212             //! valid location
1213             Plasma::Types::Location location = containment->location();
1214 
1215             if (onPrimary && !map[prmScreenName].contains(location)) {
1216                 map[prmScreenName][location] = containment->id();
1217             }
1218         }
1219     }
1220 
1221     //! second step: explicit docks must be placed in their screens if the screen edge is free
1222     for (const auto containment : m_containments) {
1223         if (m_storage->isLatteContainment(containment)) {
1224             int screenId = 0;
1225 
1226             //! valid screen id
1227             if (latteViewExists(containment)) {
1228                 screenId = m_latteViews[containment]->positioner()->currentScreenId();
1229             } else {
1230                 screenId = containment->screen();
1231 
1232                 if (screenId == -1) {
1233                     screenId = containment->lastScreen();
1234                 }
1235             }
1236 
1237             bool onPrimary{true};
1238 
1239             //! valid onPrimary flag
1240             if (latteViewExists(containment)) {
1241                 onPrimary = m_latteViews[containment]->onPrimary();
1242             } else {
1243                 onPrimary = containment->config().readEntry("onPrimary", true);
1244             }
1245 
1246             //! valid location
1247             Plasma::Types::Location location = containment->location();
1248 
1249             if (!onPrimary) {
1250                 QString expScreenName = m_corona->screenPool()->connector(screenId);
1251 
1252                 if (m_corona->screenPool()->screenExists(screenId) && !map[expScreenName].contains(location)) {
1253                     map[expScreenName][location] = containment->id();
1254                 }
1255             }
1256         }
1257     }
1258 
1259     return map;
1260 }
1261 
1262 
1263 //! the central functions that updates loading/unloading latteviews
1264 //! concerning screen changed (for multi-screen setups mainly)
1265 void GenericLayout::syncLatteViewsToScreens(Layout::ViewsMap *occupiedMap)
1266 {
1267     if (!m_corona) {
1268         return;
1269     }
1270 
1271     qDebug() << "START of SyncLatteViewsToScreens ....";
1272     qDebug() << "LAYOUT ::: " << name();
1273     qDebug() << "screen count changed -+-+ " << qGuiApp->screens().size();
1274 
1275     Layout::ViewsMap viewsMap = validViewsMap(occupiedMap);
1276 
1277     if (occupiedMap != nullptr) {
1278         qDebug() << "Occupied map used :: " << *occupiedMap;
1279     }
1280 
1281     QString prmScreenName = qGuiApp->primaryScreen()->name();
1282 
1283     qDebug() << "PRIMARY SCREEN :: " << prmScreenName;
1284     qDebug() << "LATTEVIEWS MAP :: " << viewsMap;
1285 
1286     //! add views
1287     for (const auto containment : m_containments) {
1288         int screenId = containment->screen();
1289 
1290         if (screenId == -1) {
1291             screenId = containment->lastScreen();
1292         }
1293 
1294         if (!latteViewExists(containment) && mapContainsId(&viewsMap, containment->id())) {
1295             qDebug() << "syncLatteViewsToScreens: view must be added... for containment:" << containment->id() << " at screen:" << m_corona->screenPool()->connector(screenId);
1296             addView(containment);
1297         }
1298     }
1299 
1300     //! remove views
1301     QList<Plasma::Containment *> viewsToDelete;
1302 
1303     for (auto view : m_latteViews) {
1304         auto containment = view->containment();
1305         if (containment && !mapContainsId(&viewsMap, containment->id())) {
1306             viewsToDelete << containment;
1307         }
1308     }
1309 
1310     while(!viewsToDelete.isEmpty()) {
1311         auto containment = viewsToDelete.takeFirst();
1312         auto view = m_latteViews.take(containment);
1313         qDebug() << "syncLatteViewsToScreens: view must be deleted... for containment:" << containment->id() << " at screen:" << view->positioner()->currentScreenName();
1314         view->disconnectSensitiveSignals();
1315         view->deleteLater();
1316     }
1317 
1318     //! reconsider views
1319     for (const auto view : m_latteViews) {
1320         if (view->containment() && mapContainsId(&viewsMap, view->containment()->id())) {
1321             //! if the dock will not be deleted its a very good point to reconsider
1322             //! if the screen in which is running is the correct one
1323             qDebug() << "syncLatteViewsToScreens: view must consider its screen... for containment:" << view->containment()->id() << " at screen:" << view->positioner()->currentScreenName();
1324             view->reconsiderScreen();
1325         }
1326     }
1327 
1328     qDebug() << "end of, syncLatteViewsToScreens ....";
1329 }
1330 
1331 QList<int> GenericLayout::containmentSystrays(Plasma::Containment *containment) const
1332 {
1333     QList<int> trays;
1334 
1335     if (m_storage->isLatteContainment(containment)) {
1336         auto applets = containment->config().group("Applets");
1337 
1338         for (const auto &applet : applets.groupList()) {
1339             KConfigGroup appletSettings = applets.group(applet).group("Configuration");
1340             int tSysId = appletSettings.readEntry("SystrayContainmentId", -1);
1341 
1342             if (tSysId != -1) {
1343                 trays << tSysId;
1344             }
1345         }
1346     }
1347 
1348     return trays;
1349 }
1350 
1351 
1352 QString GenericLayout::reportHtml(const ScreenPool *screenPool)
1353 {
1354     //qDebug() << "DBUS CALL ::: " << identifier << " - " << value;
1355     auto locationText = [this](const int &location) {
1356         switch (location) {
1357         case Plasma::Types::BottomEdge: return i18nc("bottom edge", "Bottom");
1358         case Plasma::Types::LeftEdge: return i18nc("left edge", "Left");
1359         case Plasma::Types::TopEdge: return i18nc("top edge", "Top");
1360         case Plasma::Types::RightEdge: return i18nc("right edge", "Right");
1361         }
1362 
1363         return QString();
1364     };
1365 
1366     auto idsLineStr = [this](const QList<int> list) {
1367         QString line;
1368 
1369         for(int i=0; i<list.count(); ++i) {
1370             if(i!=0) {
1371                 line += ", ";
1372             }
1373             line += "["+QString::number(list[i]) + "]";
1374         }
1375 
1376         return line;
1377     };
1378 
1379     ///////!  main report layout code
1380 
1381     QString report;
1382 
1383     int activeViews = m_latteViews.count();
1384 
1385     report += "<table cellspacing='8'>";
1386     report += "<tr>";
1387     report += "<td><b>" + i18nc("active docks panels","Active Views:") +"</b></td>";
1388     if (activeViews == 0) {
1389         report += "<td><b> -- </b></td>";
1390     } else {
1391         report += "<td><b><font color='blue'>" + QString::number(activeViews) +"</font></b></td>";
1392     }
1393     report += "</tr>";
1394 
1395     //! latte containment ids, systrays
1396     QHash<int, QList<int>> systrays;
1397     QList<int> assignedSystrays;
1398     QList<int> orphanSystrays;
1399 
1400     if (isActive()) {
1401         //! organize systrays
1402         for (const auto containment : m_containments) {
1403             QList<int> trays = containmentSystrays(containment);
1404             if (trays.count() > 0) {
1405                 systrays[containment->id()] = trays;
1406                 assignedSystrays << trays;
1407             }
1408         }
1409 
1410         //! orphan systrays
1411         for (const auto containment : m_containments) {
1412             if (!m_storage->isLatteContainment(containment) && !assignedSystrays.contains(containment->id())) {
1413                 orphanSystrays << containment->id();
1414             }
1415         }
1416     } else {
1417         m_storage->systraysInformation(systrays, assignedSystrays, orphanSystrays);
1418     }
1419 
1420     report += "<tr>";
1421     report += "<td><b>" + i18n("Orphan Systrays:") +"</b></td>";
1422     if (orphanSystrays.count() == 0) {
1423         report += "<td><b> -- </b></td>";
1424     } else {
1425         report += "<td><b><font color='red'>" + idsLineStr(orphanSystrays) +"</font></b></td>";
1426     }
1427     report += "</tr>";
1428     report += "</table>";
1429 
1430     report += "<table cellspacing='14'>";
1431     report += "<tr><td align='center'><b>" + i18nc("view id","ID") + "</b></td>" +
1432             "<td align='center'><b>" + i18n("Screen") + "</b></td>" +
1433             "<td align='center'><b>" + i18nc("screen edge","Edge") + "</b></td>" +
1434             "<td align='center'><b>" + i18nc("active dock/panel","Active") + "</b></td>" +
1435             "<td align='center'><b>" + i18n("Systrays") + "</b></td>";
1436 
1437     report += "<tr><td colspan='5'><hr></td></tr>";
1438 
1439     QList<ViewData> viewsData;
1440 
1441     if (isActive()) {
1442         //! collect viewData results
1443         for (const auto containment : m_containments) {
1444             if (m_storage->isLatteContainment(containment)) {
1445                 ViewData vData;
1446                 vData.id = containment->id();
1447                 vData.active = latteViewExists(containment);
1448                 vData.location = containment->location();
1449 
1450                 //! onPrimary / Screen Id
1451                 int screenId = -1;
1452                 bool onPrimary = true;
1453 
1454                 if (latteViewExists(containment)) {
1455                     screenId = m_latteViews[containment]->positioner()->currentScreenId();
1456                     onPrimary = m_latteViews[containment]->onPrimary();
1457                 } else {
1458                     screenId = containment->screen();
1459                     onPrimary = containment->config().readEntry("onPrimary", true);
1460 
1461                     if (screenId == -1) {
1462                         screenId = containment->lastScreen();
1463                     }
1464                 }
1465 
1466                 vData.onPrimary = onPrimary;
1467                 vData.screenId = screenId;
1468                 vData.systrays = containmentSystrays(containment);
1469 
1470                 viewsData << vData;
1471             }
1472         }
1473     } else {
1474         viewsData = m_storage->viewsData(systrays);
1475     }
1476 
1477     //! sort views data
1478     viewsData = sortedViewsData(viewsData);
1479 
1480     QStringList unknownScreens;
1481 
1482     //! print viewData results
1483     for (int i=0; i<viewsData.count(); ++i) {
1484         report += "<tr>";
1485 
1486         //! view id
1487         QString idStr = "[" + QString::number(viewsData[i].id) + "]";
1488         if(viewsData[i].active) {
1489             idStr = "<b>" + idStr + "</b>";
1490         }
1491         report += "<td align='center'>" + idStr + "</td>";
1492 
1493         //! screen
1494         QString screenStr = "[" + i18nc("primary screen","Primary") + "]";
1495         if (viewsData[i].active && viewsData[i].onPrimary) {
1496             screenStr = "<font color='green'>" + screenStr + "</font>";
1497         }
1498         if (!viewsData[i].onPrimary) {
1499             if (!screenPool->hasId(viewsData[i].screenId)) {
1500                 screenStr = "<font color='red'><i>[" + QString::number(viewsData[i].screenId) + "]</i></font>";
1501 
1502                 unknownScreens << QString("[" + QString::number(viewsData[i].screenId) + "]");
1503             } else {
1504                 screenStr = screenPool->connector(viewsData[i].screenId);
1505             }
1506         }
1507         if(viewsData[i].active) {
1508             screenStr = "<b>" + screenStr + "</b>";
1509         }
1510         report += "<td align='center'>" + screenStr + "</td>";
1511 
1512         //! edge
1513         QString edgeStr = locationText(viewsData[i].location);
1514         if(viewsData[i].active) {
1515             edgeStr = "<b>" + edgeStr + "</b>";
1516         }
1517         report += "<td align='center'>" + edgeStr + "</td>" ;
1518 
1519         //! active
1520         QString activeStr = " -- ";
1521         if(viewsData[i].active) {
1522             activeStr = "<b>" + i18n("Yes") + "</b>";
1523         }
1524         report += "<td align='center'>" + activeStr + "</td>" ;
1525 
1526         //! systrays
1527         QString systraysStr = " -- ";
1528         if (viewsData[i].systrays.count() > 0) {
1529             systraysStr = idsLineStr(viewsData[i].systrays);
1530         }
1531         if(viewsData[i].active) {
1532             systraysStr = "<b>" + systraysStr + "</b>";
1533         }
1534         report += "<td align='center'>" + systraysStr + "</td>";
1535 
1536         report += "</tr>";
1537     }
1538 
1539     report += "</table>";
1540 
1541     report += "<br/><hr>";
1542 
1543     QStringList errorsList;
1544     bool broken = m_storage->layoutIsBroken(errorsList);
1545 
1546     if (!broken && unknownScreens.count() == 0) {
1547         report += "<font color='green'>" + i18n("No errors were identified for this layout...") + "</font><br/>";
1548     } else {
1549         report += "<font color='red'><b>" + i18n("Errors:") + "</b></font><br/>";
1550     }
1551 
1552     if (broken){
1553         for(int i=0; i<errorsList.count(); ++i) {
1554             report += "<font color='red'><b>[" + QString::number(i) + "] - " + errorsList[i] + "</b></font><br/>";
1555         }
1556     }
1557 
1558     if (unknownScreens.count() > 0) {
1559         report += "<font color='red'><b>" + i18n("Unknown screens: ") + unknownScreens.join(", ") + "</b></font><br/>";
1560     }
1561 
1562     return report;
1563 }
1564 
1565 QList<int> GenericLayout::viewsScreens()
1566 {
1567     QList<int> screens;
1568 
1569     if (isActive()) {
1570         for (const auto containment : m_containments) {
1571             if (m_storage->isLatteContainment(containment)) {
1572                 int screenId = -1;
1573 
1574                 //! valid screen id
1575                 if (latteViewExists(containment)) {
1576                     screenId = m_latteViews[containment]->positioner()->currentScreenId();
1577                 } else {
1578                     screenId = containment->screen();
1579 
1580                     if (screenId == -1) {
1581                         screenId = containment->lastScreen();
1582                     }
1583                 }
1584 
1585                 if (screenId!=-1 &&!screens.contains(screenId)) {
1586                     screens << screenId;
1587                 }
1588             }
1589         }
1590 
1591         return screens;
1592     } else {
1593         return m_storage->viewsScreens();
1594     }
1595 }
1596 
1597 
1598 //! STORAGE
1599 
1600 bool GenericLayout::isWritable() const
1601 {
1602     return m_storage->isWritable();
1603 }
1604 
1605 void GenericLayout::lock()
1606 {
1607     m_storage->lock();
1608 }
1609 
1610 void GenericLayout::unlock()
1611 {
1612     m_storage->unlock();
1613 }
1614 
1615 void GenericLayout::syncToLayoutFile(bool removeLayoutId)
1616 {
1617     m_storage->syncToLayoutFile(removeLayoutId);
1618 }
1619 
1620 void GenericLayout::copyView(Plasma::Containment *containment)
1621 {
1622     m_storage->copyView(containment);
1623     emit viewEdgeChanged();
1624 }
1625 
1626 void GenericLayout::importToCorona()
1627 {
1628     m_storage->importToCorona();
1629 }
1630 
1631 bool GenericLayout::layoutIsBroken() const
1632 {
1633     QStringList errors;
1634     return m_storage->layoutIsBroken(errors);
1635 }
1636 
1637 }
1638 }