File indexing completed on 2024-04-07 15:23:34

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 "synchronizer.h"
0021 
0022 //! local
0023 #include "importer.h"
0024 #include "manager.h"
0025 #include "../lattecorona.h"
0026 #include "../layout/centrallayout.h"
0027 #include "../layout/genericlayout.h"
0028 #include "../layout/sharedlayout.h"
0029 #include "../settings/universalsettings.h"
0030 #include "../view/view.h"
0031 
0032 // Qt
0033 #include <QDir>
0034 #include <QFile>
0035 
0036 // Plasma
0037 #include <Plasma/Containment>
0038 
0039 // KDE
0040 #include <KActivities/Consumer>
0041 #include <KActivities/Controller>
0042 
0043 namespace Latte {
0044 namespace Layouts {
0045 
0046 Synchronizer::Synchronizer(QObject *parent)
0047     : QObject(parent),
0048       m_activitiesController(new KActivities::Controller)
0049 {
0050     m_manager = qobject_cast<Manager *>(parent);
0051 
0052     //! Dynamic Switching
0053     m_dynamicSwitchTimer.setSingleShot(true);
0054     updateDynamicSwitchInterval();
0055     connect(m_manager->corona()->universalSettings(), &UniversalSettings::showInfoWindowChanged, this, &Synchronizer::updateDynamicSwitchInterval);
0056     connect(&m_dynamicSwitchTimer, &QTimer::timeout, this, &Synchronizer::confirmDynamicSwitch);
0057 
0058     //! KActivities tracking
0059     connect(m_manager->corona()->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged,
0060             this, &Synchronizer::currentActivityChanged);
0061 
0062     connect(m_manager->corona()->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged,
0063             this, [&]() {
0064         if (m_manager->memoryUsage() == Types::MultipleLayouts) {
0065             syncMultipleLayoutsToActivities();
0066         }
0067     });
0068 }
0069 
0070 Synchronizer::~Synchronizer()
0071 {
0072     m_activitiesController->deleteLater();
0073 }
0074 
0075 bool Synchronizer::latteViewExists(Latte::View *view) const
0076 {
0077     for (const auto layout : m_centralLayouts) {
0078         for (const auto &v : layout->latteViews()) {
0079             if (v == view) {
0080                 return true;
0081             }
0082         }
0083     }
0084 
0085     return false;
0086 }
0087 
0088 bool Synchronizer::layoutExists(QString layoutName) const
0089 {
0090     return m_layouts.contains(layoutName);
0091 }
0092 
0093 
0094 bool Synchronizer::layoutIsAssigned(QString layoutName)
0095 {
0096     QHashIterator<const QString, QString> i(m_assignedLayouts);
0097 
0098     while (i.hasNext()) {
0099         i.next();
0100 
0101         if (i.value() == layoutName) {
0102             return true;
0103         }
0104     }
0105 
0106     return false;
0107 }
0108 
0109 bool Synchronizer::mapHasRecord(const QString &record, SharesMap &map)
0110 {
0111     for (SharesMap::iterator i=map.begin(); i!=map.end(); ++i) {
0112         if (i.value().contains(record)) {
0113             return true;
0114         }
0115     }
0116 
0117     return false;
0118 }
0119 
0120 bool Synchronizer::registerAtSharedLayout(CentralLayout *central, QString id)
0121 {
0122     if (m_manager->memoryUsage() == Types::SingleLayout || centralLayout(id)) {
0123         //! if memory is functioning to SINGLE mode OR shared layout has already
0124         //! been loaded as CentralLayout
0125         return false;
0126     }
0127 
0128     for (int i = 0; i < m_sharedLayouts.size(); ++i) {
0129         SharedLayout *layout = m_sharedLayouts.at(i);
0130 
0131         if (layout->name() == id) {
0132             layout->addCentralLayout(central);
0133             return true;
0134         }
0135     }
0136 
0137     //! If SharedLayout was not found, we must create it
0138     SharedLayout *top = new SharedLayout(central, this, Importer::layoutFilePath(id));
0139     m_sharedLayouts.append(top);
0140     top->importToCorona();
0141 
0142     connect(top, &SharedLayout::layoutDestroyed, this, &Synchronizer::unloadSharedLayout);
0143 
0144     return true;
0145 }
0146 
0147 int Synchronizer::centralLayoutPos(QString id) const
0148 {
0149     for (int i = 0; i < m_centralLayouts.size(); ++i) {
0150         CentralLayout *layout = m_centralLayouts.at(i);
0151 
0152         if (layout->name() == id) {
0153 
0154             return i;
0155         }
0156     }
0157 
0158     return -1;
0159 }
0160 
0161 QString Synchronizer::currentLayoutName() const
0162 {
0163     if (m_manager->memoryUsage() == Types::SingleLayout) {
0164         return m_manager->corona()->universalSettings()->currentLayoutName();
0165     } else if (m_manager->memoryUsage() == Types::MultipleLayouts) {
0166         return currentLayoutNameInMultiEnvironment();
0167     }
0168 
0169     return QString();
0170 }
0171 
0172 QString Synchronizer::currentLayoutNameInMultiEnvironment() const
0173 {
0174     return m_currentLayoutNameInMultiEnvironment;
0175 }
0176 
0177 QString Synchronizer::layoutPath(QString layoutName)
0178 {
0179     QString path = QDir::homePath() + "/.config/latte/" + layoutName + ".layout.latte";
0180 
0181     if (!QFile(path).exists()) {
0182         path = "";
0183     }
0184 
0185     return path;
0186 }
0187 
0188 QStringList Synchronizer::activities()
0189 {
0190     return m_manager->corona()->activitiesConsumer()->activities();
0191 }
0192 
0193 QStringList Synchronizer::runningActivities()
0194 {
0195     return m_manager->corona()->activitiesConsumer()->runningActivities();
0196 }
0197 
0198 QStringList Synchronizer::orphanedActivities()
0199 {
0200     QStringList orphans;
0201 
0202     for (const auto &activity : activities()) {
0203         if (m_assignedLayouts[activity].isEmpty()) {
0204             orphans.append(activity);
0205         }
0206     }
0207 
0208     return orphans;
0209 }
0210 
0211 QStringList Synchronizer::centralLayoutsNames()
0212 {
0213     QStringList names;
0214 
0215     if (m_manager->memoryUsage() == Types::SingleLayout) {
0216         names << currentLayoutName();
0217     } else {
0218         for (int i = 0; i < m_centralLayouts.size(); ++i) {
0219             CentralLayout *layout = m_centralLayouts.at(i);
0220             names << layout->name();
0221         }
0222     }
0223 
0224     return names;
0225 }
0226 
0227 QStringList Synchronizer::layouts() const
0228 {
0229     return m_layouts;
0230 }
0231 
0232 QStringList Synchronizer::menuLayouts() const
0233 {
0234     QStringList fixedMenuLayouts = m_menuLayouts;
0235 
0236     //! in case the current layout isnt checked to be shown in the menus
0237     //! we must add it on top
0238     if (!fixedMenuLayouts.contains(currentLayoutName()) && m_manager->memoryUsage() == Types::SingleLayout) {
0239         fixedMenuLayouts.prepend(currentLayoutName());
0240     } else if (m_manager->memoryUsage() == Types::MultipleLayouts) {
0241         for (const auto layout : m_centralLayouts) {
0242             if (!fixedMenuLayouts.contains(layout->name())) {
0243                 fixedMenuLayouts.prepend(layout->name());
0244             }
0245         }
0246     }
0247 
0248     return fixedMenuLayouts;
0249 }
0250 
0251 void Synchronizer::setMenuLayouts(QStringList layouts)
0252 {
0253     if (m_menuLayouts == layouts) {
0254         return;
0255     }
0256 
0257     m_menuLayouts = layouts;
0258     emit menuLayoutsChanged();
0259 }
0260 
0261 QString Synchronizer::shouldSwitchToLayout(QString activityId)
0262 {
0263     if (m_assignedLayouts.contains(activityId) && m_assignedLayouts[activityId] != currentLayoutName()) {
0264         return m_assignedLayouts[activityId];
0265     } else if (!m_assignedLayouts.contains(activityId) && !m_manager->corona()->universalSettings()->lastNonAssignedLayoutName().isEmpty()
0266                && m_manager->corona()->universalSettings()->lastNonAssignedLayoutName() != currentLayoutName()) {
0267         return m_manager->corona()->universalSettings()->lastNonAssignedLayoutName();
0268     }
0269 
0270     return QString();
0271 }
0272 
0273 QStringList Synchronizer::sharedLayoutsNames()
0274 {
0275     QStringList names;
0276 
0277     for (int i = 0; i < m_sharedLayouts.size(); ++i) {
0278         SharedLayout *layout = m_sharedLayouts.at(i);
0279         names << layout->name();
0280     }
0281 
0282     return names;
0283 }
0284 
0285 QStringList Synchronizer::storedSharedLayouts() const
0286 {
0287     return m_sharedLayoutIds;
0288 }
0289 
0290 QStringList Synchronizer::validActivities(QStringList currentList)
0291 {
0292     QStringList validIds;
0293 
0294     for (const auto &activity : currentList) {
0295         if (activities().contains(activity)) {
0296             validIds.append(activity);
0297         }
0298     }
0299 
0300     return validIds;
0301 }
0302 
0303 CentralLayout *Synchronizer::centralLayout(QString id) const
0304 {
0305     for (int i = 0; i < m_centralLayouts.size(); ++i) {
0306         CentralLayout *layout = m_centralLayouts.at(i);
0307 
0308         if (layout->name() == id) {
0309 
0310             return layout;
0311         }
0312     }
0313 
0314     return nullptr;
0315 }
0316 
0317 CentralLayout *Synchronizer::currentLayout() const
0318 {
0319     if (m_manager->memoryUsage() == Types::SingleLayout) {
0320         return m_centralLayouts.at(0);
0321     } else {
0322         for (auto layout : m_centralLayouts) {
0323             if (layout->activities().contains(m_manager->corona()->activitiesConsumer()->currentActivity())) {
0324                 return layout;
0325             }
0326         }
0327 
0328         for (auto layout : m_centralLayouts) {
0329             if (layout->activities().isEmpty()) {
0330                 return layout;
0331             }
0332         }
0333     }
0334 
0335     return nullptr;
0336 }
0337 
0338 Layout::GenericLayout *Synchronizer::layout(QString id) const
0339 {
0340     Layout::GenericLayout *l = centralLayout(id);
0341 
0342     if (!l) {
0343         l = sharedLayout(id);
0344     }
0345 
0346     return l;
0347 }
0348 
0349 SharedLayout *Synchronizer::sharedLayout(QString id) const
0350 {
0351     for (int i = 0; i < m_sharedLayouts.size(); ++i) {
0352         SharedLayout *layout = m_sharedLayouts.at(i);
0353 
0354         if (layout->name() == id) {
0355             return layout;
0356         }
0357     }
0358 
0359     return nullptr;
0360 }
0361 
0362 Latte::View *Synchronizer::viewForContainment(Plasma::Containment *containment)
0363 {
0364     for (auto layout : m_centralLayouts) {
0365         Latte::View *view = layout->viewForContainment(containment);
0366 
0367         if (view) {
0368             return view;
0369         }
0370     }
0371 
0372     for (auto layout : m_sharedLayouts) {
0373         Latte::View *view = layout->viewForContainment(containment);
0374 
0375         if (view) {
0376             return view;
0377         }
0378     }
0379 
0380     return nullptr;
0381 }
0382 
0383 void Synchronizer::addLayout(CentralLayout *layout)
0384 {
0385     if (!m_centralLayouts.contains(layout)) {
0386         m_centralLayouts.append(layout);
0387         layout->initToCorona(m_manager->corona());
0388     }
0389 }
0390 
0391 void Synchronizer::clearSharedLayoutsFromCentralLists()
0392 {
0393     QStringList unassign;
0394 
0395     for(const QString &name : m_sharedLayoutIds) {
0396         //! remove from ContextMenu
0397         m_menuLayouts.removeAll(name);
0398 
0399         //! remove from layouts assigned to activities
0400         QHashIterator<const QString, QString> i(m_assignedLayouts);
0401 
0402         while (i.hasNext()) {
0403             i.next();
0404 
0405             if (i.value() == name) {
0406                 unassign << i.key();
0407             }
0408         }
0409     }
0410 
0411     for(const QString &activity : unassign) {
0412         m_assignedLayouts.remove(activity);
0413     }
0414 }
0415 
0416 void Synchronizer::confirmDynamicSwitch()
0417 {
0418     QString tempShouldSwitch = shouldSwitchToLayout(m_manager->corona()->activitiesConsumer()->currentActivity());
0419 
0420     if (tempShouldSwitch.isEmpty()) {
0421         return;
0422     }
0423 
0424     if (m_shouldSwitchToLayout == tempShouldSwitch && m_shouldSwitchToLayout != currentLayoutName()) {
0425         qDebug() << "dynamic switch to layout :: " << m_shouldSwitchToLayout;
0426 
0427         emit currentLayoutIsSwitching(currentLayoutName());
0428 
0429         if (m_manager->corona()->universalSettings()->showInfoWindow()) {
0430             m_manager->showInfoWindow(i18n("Switching to layout <b>%0</b> ...").arg(m_shouldSwitchToLayout), 4000);
0431         }
0432 
0433         QTimer::singleShot(500, [this, tempShouldSwitch]() {
0434             switchToLayout(tempShouldSwitch);
0435         });
0436     } else {
0437         m_shouldSwitchToLayout = tempShouldSwitch;
0438         m_dynamicSwitchTimer.start();
0439     }
0440 }
0441 
0442 void Synchronizer::currentActivityChanged(const QString &id)
0443 {
0444     if (m_manager->memoryUsage() == Types::SingleLayout) {
0445         qDebug() << "activity changed :: " << id;
0446 
0447         m_shouldSwitchToLayout = shouldSwitchToLayout(id);
0448 
0449         m_dynamicSwitchTimer.start();
0450     } else if (m_manager->memoryUsage() == Types::MultipleLayouts) {
0451         updateCurrentLayoutNameInMultiEnvironment();
0452     }
0453 }
0454 
0455 void Synchronizer::hideAllViews()
0456 {
0457     for (const auto layout : m_centralLayouts) {
0458         emit currentLayoutIsSwitching(layout->name());
0459     }
0460 }
0461 
0462 void Synchronizer::pauseLayout(QString layoutName)
0463 {
0464     if (m_manager->memoryUsage() == Types::MultipleLayouts) {
0465         CentralLayout *layout = centralLayout(layoutName);
0466 
0467         if (layout && !layout->activities().isEmpty()) {
0468             int i = 0;
0469 
0470             for (const auto &activityId : layout->activities()) {
0471                 //! Stopping the activities must be done asynchronous because otherwise
0472                 //! the activity manager cant close multiple activities
0473                 QTimer::singleShot(i * 1000, [this, activityId]() {
0474                     m_activitiesController->stopActivity(activityId);
0475                 });
0476 
0477                 i = i + 1;
0478             }
0479         }
0480     }
0481 }
0482 
0483 void Synchronizer::syncActiveLayoutsToOriginalFiles()
0484 {
0485     if (m_manager->memoryUsage() == Types::MultipleLayouts) {
0486         for (const auto layout : m_centralLayouts) {
0487             layout->syncToLayoutFile();
0488         }
0489 
0490         for (const auto layout : m_sharedLayouts) {
0491             layout->syncToLayoutFile();
0492         }
0493     }
0494 }
0495 
0496 void Synchronizer::syncLatteViewsToScreens()
0497 {
0498     for (const auto layout : m_sharedLayouts) {
0499         layout->syncLatteViewsToScreens();
0500     }
0501 
0502     for (const auto layout : m_centralLayouts) {
0503         layout->syncLatteViewsToScreens();
0504     }
0505 }
0506 
0507 void Synchronizer::unloadCentralLayout(CentralLayout *layout)
0508 {
0509     int pos = m_centralLayouts.indexOf(layout);
0510 
0511     if (pos>=0) {
0512         CentralLayout *central = m_centralLayouts.takeAt(0);
0513 
0514         if (m_multipleModeInitialized) {
0515             central->syncToLayoutFile(true);
0516         }
0517 
0518         central->unloadLatteViews();
0519         central->unloadContainments();
0520 
0521         if (m_multipleModeInitialized) {
0522             m_manager->clearUnloadedContainmentsFromLinkedFile(central->unloadedContainmentsIds(), true);
0523         }
0524 
0525         delete central;
0526     }
0527 }
0528 
0529 void Synchronizer::unloadSharedLayout(SharedLayout *layout)
0530 {
0531     if (m_sharedLayouts.contains(layout)) {
0532         disconnect(layout, &SharedLayout::layoutDestroyed, this, &Synchronizer::unloadSharedLayout);
0533         int pos = m_sharedLayouts.indexOf(layout);
0534         SharedLayout *shared = m_sharedLayouts.takeAt(pos);
0535         shared->syncToLayoutFile(true);
0536         shared->unloadLatteViews();
0537         shared->unloadContainments();
0538         m_manager->clearUnloadedContainmentsFromLinkedFile(shared->unloadedContainmentsIds(), true);
0539 
0540         delete layout;
0541     }
0542 }
0543 
0544 
0545 void Synchronizer::loadLayouts()
0546 {
0547     m_layouts.clear();
0548     m_menuLayouts.clear();
0549     m_assignedLayouts.clear();
0550     m_sharedLayoutIds.clear();
0551 
0552     QDir layoutDir(QDir::homePath() + "/.config/latte");
0553     QStringList filter;
0554     filter.append(QString("*.layout.latte"));
0555     QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
0556 
0557     for (const auto &layout : files) {
0558         if (layout.contains(Layout::AbstractLayout::MultipleLayoutsName)) {
0559             //! IMPORTANT: DON'T ADD MultipleLayouts hidden file in layouts list
0560             continue;
0561         }
0562 
0563         CentralLayout centralLayout(this, layoutDir.absolutePath() + "/" + layout);
0564 
0565         QStringList validActivityIds = validActivities(centralLayout.activities());
0566         centralLayout.setActivities(validActivityIds);
0567 
0568         for (const auto &activity : validActivityIds) {
0569             m_assignedLayouts[activity] = centralLayout.name();
0570         }
0571 
0572         m_layouts.append(centralLayout.name());
0573 
0574         if (centralLayout.showInMenu()) {
0575             m_menuLayouts.append(centralLayout.name());
0576         }
0577 
0578         QString sharedName = centralLayout.sharedLayoutName();
0579 
0580         if (!sharedName.isEmpty() && !m_sharedLayoutIds.contains(sharedName)) {
0581             m_sharedLayoutIds << sharedName;
0582         }
0583     }
0584 
0585     //! Shared Layouts should not be used for Activities->Layouts assignments or published lists
0586     clearSharedLayoutsFromCentralLists();
0587 
0588     emit layoutsChanged();
0589     emit menuLayoutsChanged();
0590 }
0591 
0592 
0593 void Synchronizer::unloadLayouts()
0594 {
0595     //! Unload all CentralLayouts
0596     while (!m_centralLayouts.isEmpty()) {
0597         CentralLayout *layout = m_centralLayouts.at(0);
0598         unloadCentralLayout(layout);
0599     }
0600 
0601     m_multipleModeInitialized = false;
0602 }
0603 
0604 void Synchronizer::updateCurrentLayoutNameInMultiEnvironment()
0605 {
0606     for (const auto layout : m_centralLayouts) {
0607         if (layout->activities().contains(m_manager->corona()->activitiesConsumer()->currentActivity())) {
0608             m_currentLayoutNameInMultiEnvironment = layout->name();
0609             emit currentLayoutNameChanged();
0610             return;
0611         }
0612     }
0613 
0614     for (const auto layout : m_centralLayouts) {
0615         if (layout->activities().isEmpty()) {
0616             m_currentLayoutNameInMultiEnvironment = layout->name();
0617             emit currentLayoutNameChanged();
0618             return;
0619         }
0620     }
0621 }
0622 
0623 void Synchronizer::updateDynamicSwitchInterval()
0624 {
0625     if (m_manager->corona()->universalSettings()->showInfoWindow()) {
0626         m_dynamicSwitchTimer.setInterval(1800);
0627     } else {
0628         m_dynamicSwitchTimer.setInterval(2300);
0629     }
0630 }
0631 
0632 bool Synchronizer::switchToLayout(QString layoutName, int previousMemoryUsage)
0633 {
0634     if (m_centralLayouts.size() > 0 && currentLayoutName() == layoutName && previousMemoryUsage == -1) {
0635         return false;
0636     }
0637 
0638     //! First Check If that Layout is already present and in that case
0639     //! we can just switch to the proper Activity
0640     if (m_manager->memoryUsage() == Types::MultipleLayouts && previousMemoryUsage == -1) {
0641         CentralLayout *layout = centralLayout(layoutName);
0642 
0643         if (layout) {
0644 
0645             QStringList appliedActivities = layout->appliedActivities();
0646             QString nextActivity = !layout->lastUsedActivity().isEmpty() ? layout->lastUsedActivity() : appliedActivities[0];
0647 
0648             //! it means we are at a foreign activity
0649             if (!appliedActivities.contains(m_manager->corona()->activitiesConsumer()->currentActivity())) {
0650                 m_activitiesController->setCurrentActivity(nextActivity);
0651                 return true;
0652             }
0653         }
0654     }
0655 
0656     //! When going from memory usage to different memory usage we first
0657     //! send the layouts that will be changed. This signal creates the
0658     //! nice animation that hides these docks/panels
0659     if (previousMemoryUsage != -1) {
0660         for (const auto layout : m_centralLayouts) {
0661             emit currentLayoutIsSwitching(layout->name());
0662         }
0663 
0664         for (const auto layout : m_sharedLayouts) {
0665             emit currentLayoutIsSwitching(layout->name());
0666         }
0667     }
0668 
0669     QString lPath = layoutPath(layoutName);
0670 
0671     if (lPath.isEmpty() && layoutName == i18n("Alternative")) {
0672         lPath = m_manager->newLayout(i18n("Alternative"), i18n("Default"));
0673     }
0674 
0675     if (!lPath.isEmpty()) {
0676         if (m_manager->memoryUsage() == Types::SingleLayout) {
0677             //  emit currentLayoutIsSwitching(currentLayoutName());
0678         } else if (m_manager->memoryUsage() == Types::MultipleLayouts && layoutName != Layout::AbstractLayout::MultipleLayoutsName) {
0679             CentralLayout toLayout(this, lPath);
0680 
0681             QStringList toActivities = toLayout.activities();
0682 
0683             CentralLayout *centralForOrphans{nullptr};
0684 
0685             for (const auto fromLayout : m_centralLayouts) {
0686                 if (fromLayout->activities().isEmpty()) {
0687                     centralForOrphans = fromLayout;
0688                     break;
0689                 }
0690             }
0691 
0692             if (toActivities.isEmpty() && centralForOrphans && (toLayout.name() != centralForOrphans->name())) {
0693                 emit currentLayoutIsSwitching(centralForOrphans->name());
0694             }
0695         }
0696 
0697         //! this code must be called asynchronously because it is called
0698         //! also from qml (Tasks plasmoid). This change fixes a very important
0699         //! crash when switching sessions through the Tasks plasmoid Context menu
0700         //! Latte was unstable and was crashing very often during changing
0701         //! sessions.
0702         QTimer::singleShot(350, [this, layoutName, lPath, previousMemoryUsage]() {
0703             qDebug() << layoutName << " - " << lPath;
0704             QString fixedLPath = lPath;
0705             QString fixedLayoutName = layoutName;
0706 
0707             bool initializingMultipleLayouts{false};
0708 
0709             if (m_manager->memoryUsage() == Types::MultipleLayouts && !m_multipleModeInitialized) {
0710                 initializingMultipleLayouts = true;
0711             }
0712 
0713             if (m_manager->memoryUsage() == Types::SingleLayout || initializingMultipleLayouts || previousMemoryUsage == Types::MultipleLayouts) {
0714                 unloadLayouts();
0715 
0716                 if (initializingMultipleLayouts) {
0717                     fixedLayoutName = QString(Layout::AbstractLayout::MultipleLayoutsName);
0718                     fixedLPath = layoutPath(fixedLayoutName);
0719                 }
0720 
0721                 if (fixedLayoutName != Layout::AbstractLayout::MultipleLayoutsName) {
0722                     CentralLayout *newLayout = new CentralLayout(this, fixedLPath, fixedLayoutName);
0723                     addLayout(newLayout);
0724                 }
0725 
0726                 m_manager->loadLatteLayout(fixedLPath);
0727 
0728                 if (initializingMultipleLayouts) {
0729                     m_multipleModeInitialized = true;
0730                 }
0731 
0732                 emit centralLayoutsChanged();
0733             }
0734 
0735             if (m_manager->memoryUsage() == Types::MultipleLayouts) {
0736                 if (!initializingMultipleLayouts && !centralLayout(layoutName)) {
0737                     //! When we are in Multiple Layouts Environment and the user activates
0738                     //! a Layout that is assigned to specific activities but this
0739                     //! layout isnt loaded (this means neither of its activities are running)
0740                     //! is such case we just activate these Activities
0741                     CentralLayout layout(this, Importer::layoutFilePath(layoutName));
0742 
0743                     int i = 0;
0744                     bool lastUsedActivityFound{false};
0745                     QString lastUsedActivity = layout.lastUsedActivity();
0746 
0747                     bool orphanedLayout = !layoutIsAssigned(layoutName);
0748 
0749                     QStringList assignedActivities = orphanedLayout ? orphanedActivities() : layout.activities();
0750 
0751                     if (!orphanedLayout) {
0752                         for (const auto &assignedActivity : assignedActivities) {
0753                             //! Starting the activities must be done asynchronous because otherwise
0754                             //! the activity manager cant close multiple activities
0755                             QTimer::singleShot(i * 1000, [this, assignedActivity, lastUsedActivity]() {
0756                                 m_activitiesController->startActivity(assignedActivity);
0757 
0758                                 if (lastUsedActivity == assignedActivity) {
0759                                     m_activitiesController->setCurrentActivity(lastUsedActivity);
0760                                 }
0761                             });
0762 
0763                             if (lastUsedActivity == assignedActivity) {
0764                                 lastUsedActivityFound = true;
0765                             }
0766 
0767                             i = i + 1;
0768                         }
0769                     } else {
0770                         //! orphaned layout
0771                         for (const auto &assignedActivity : assignedActivities) {
0772                             if (lastUsedActivity == assignedActivity) {
0773                                 lastUsedActivityFound = true;
0774                             }
0775                         }
0776 
0777                         if ((!lastUsedActivityFound && assignedActivities.count() == 0)
0778                                 || !assignedActivities.contains(m_manager->corona()->activitiesConsumer()->currentActivity())) {
0779 
0780                             //! Starting the activities must be done asynchronous because otherwise
0781                             //! the activity manager cant close multiple activities
0782                             QTimer::singleShot(1000, [this, lastUsedActivity, lastUsedActivityFound]() {
0783                                 m_activitiesController->startActivity(lastUsedActivity);
0784                                 m_activitiesController->setCurrentActivity(lastUsedActivity);
0785                             });
0786                         }
0787                     }
0788 
0789                     if (orphanedLayout) {
0790                         syncMultipleLayoutsToActivities(layoutName);
0791                     } else if (!orphanedLayout && !lastUsedActivityFound) {
0792                         m_activitiesController->setCurrentActivity(layout.activities()[0]);
0793                     }
0794                 } else {
0795                     syncMultipleLayoutsToActivities(layoutName);
0796                 }
0797             }
0798 
0799             m_manager->corona()->universalSettings()->setCurrentLayoutName(layoutName);
0800 
0801             if (!layoutIsAssigned(layoutName)) {
0802                 m_manager->corona()->universalSettings()->setLastNonAssignedLayoutName(layoutName);
0803             }
0804         });
0805     } else {
0806         qDebug() << "Layout : " << layoutName << " was not found...";
0807     }
0808 
0809     return true;
0810 }
0811 
0812 void Synchronizer::syncMultipleLayoutsToActivities(QString layoutForOrphans)
0813 {
0814     qDebug() << "   ----  --------- ------    syncMultipleLayoutsToActivities       -------   ";
0815     qDebug() << "   ----  --------- ------    -------------------------------       -------   ";
0816 
0817     QStringList layoutsToUnload;
0818     QStringList layoutsToLoad;
0819 
0820     bool allRunningActivitiesWillBeReserved{true};
0821 
0822     if (layoutForOrphans.isEmpty() || m_assignedLayouts.values().contains(layoutForOrphans)) {
0823         layoutForOrphans = m_manager->corona()->universalSettings()->lastNonAssignedLayoutName();
0824     }
0825 
0826     for (const auto &activity : runningActivities()) {
0827         if (!m_assignedLayouts[activity].isEmpty()) {
0828             if (!layoutsToLoad.contains(m_assignedLayouts[activity])) {
0829                 layoutsToLoad.append(m_assignedLayouts[activity]);
0830             }
0831         } else {
0832             allRunningActivitiesWillBeReserved = false;
0833         }
0834     }
0835 
0836     for (const auto layout : m_centralLayouts) {
0837         QString tempLayoutName;
0838 
0839         if (!layoutsToLoad.contains(layout->name()) && layout->name() != layoutForOrphans) {
0840             tempLayoutName = layout->name();
0841         } else if (layout->activities().isEmpty() && allRunningActivitiesWillBeReserved) {
0842             //! in such case the layout for the orphaned must be unloaded
0843             tempLayoutName = layout->name();
0844         }
0845 
0846         if (!tempLayoutName.isEmpty() && !layoutsToUnload.contains(tempLayoutName)) {
0847             layoutsToUnload << tempLayoutName;
0848         }
0849     }
0850 
0851     //! Unload no needed Layouts
0852     for (const auto &layoutName : layoutsToUnload) {
0853         CentralLayout *layout = centralLayout(layoutName);
0854         int posLayout = centralLayoutPos(layoutName);
0855 
0856         if (posLayout >= 0) {
0857             qDebug() << "REMOVING LAYOUT ::::: " << layoutName;
0858             m_centralLayouts.removeAt(posLayout);
0859 
0860             layout->syncToLayoutFile(true);
0861             layout->unloadContainments();
0862             layout->unloadLatteViews();
0863             m_manager->clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds());
0864             delete layout;
0865         }
0866     }
0867 
0868     //! Add needed Layouts based on Activities
0869     for (const auto &layoutName : layoutsToLoad) {
0870         if (!centralLayout(layoutName)) {
0871             CentralLayout *newLayout = new CentralLayout(this, QString(layoutPath(layoutName)), layoutName);
0872 
0873             if (newLayout) {
0874                 qDebug() << "ACTIVATING LAYOUT ::::: " << layoutName;
0875                 addLayout(newLayout);
0876                 newLayout->importToCorona();
0877 
0878                 if (m_manager->corona()->universalSettings()->showInfoWindow()) {
0879                     m_manager->showInfoWindow(i18n("Activating layout: <b>%0</b> ...").arg(newLayout->name()), 5000, newLayout->appliedActivities());
0880                 }
0881             }
0882         }
0883     }
0884 
0885     //! Add Layout for orphan activities
0886     if (!allRunningActivitiesWillBeReserved) {
0887         if (!centralLayout(layoutForOrphans) && !sharedLayout(layoutForOrphans)) {
0888             //! CENTRAL Layout for Orphaned Activities is not loaded and at the same time
0889             //! that layout is not already configured as SHARED for other CENTRAL layouts
0890             CentralLayout *newLayout = new CentralLayout(this, layoutPath(layoutForOrphans), layoutForOrphans);
0891 
0892             if (newLayout) {
0893                 qDebug() << "ACTIVATING ORPHANED LAYOUT ::::: " << layoutForOrphans;
0894                 addLayout(newLayout);
0895                 newLayout->importToCorona();
0896             }
0897         }
0898     }
0899 
0900     updateCurrentLayoutNameInMultiEnvironment();
0901     emit centralLayoutsChanged();
0902 }
0903 
0904 void Synchronizer::syncActiveShares(SharesMap &sharesMap, QStringList &deprecatedShares)
0905 {
0906     if (m_manager->memoryUsage() != Types::MultipleLayouts) {
0907         return;
0908     }
0909 
0910     qDebug() << " CURRENT SHARES MAP :: " << sharesMap;
0911     qDebug() << " DEPRECATED SHARES :: " << deprecatedShares;
0912 
0913     QHash<CentralLayout *, SharedLayout *> unassign;
0914 
0915     //! CENTRAL (inactive) layouts that must update their SharedLayoutName because they
0916     //! were unassigned from a Shared Layout
0917     for (const auto &share : deprecatedShares) {
0918         CentralLayout *central = centralLayout(share);
0919         if (!central) {
0920             //! Central Layout is not loaded
0921             CentralLayout centralInStorage(this, Importer::layoutFilePath(share));
0922             centralInStorage.setSharedLayoutName(QString());
0923         }
0924     }
0925 
0926     //! CENTRAL (active) layouts that will become SHARED must be unloaded first
0927     for (SharesMap::iterator i=sharesMap.begin(); i!=sharesMap.end(); ++i) {
0928         CentralLayout *central = centralLayout(i.key());
0929         if (central) {
0930             unloadCentralLayout(central);
0931         }
0932     }
0933 
0934     //! CENTRAL (active) layouts that update their (active) SHARED layouts
0935     //! AND load SHARED layouts that are NOT ACTIVE
0936     for (SharesMap::iterator i=sharesMap.begin(); i!=sharesMap.end(); ++i) {
0937         SharedLayout *shared = sharedLayout(i.key());
0938         qDebug() << " SHARED :: " << i.key();
0939         for (const auto &centralName : i.value()) {
0940             CentralLayout *central = centralLayout(centralName);
0941             qDebug() << " CENTRAL NAME :: " << centralName;
0942             if (central) {
0943                 //! Assign this Central Layout at a different Shared Layout
0944                 SharedLayout *oldShared = central->sharedLayout();
0945 
0946                 if (!shared) {
0947                     //Shared not loaded and it must be loaded before proceed
0948                     registerAtSharedLayout(central, i.key());
0949                     shared = sharedLayout(i.key());
0950                 }
0951 
0952                 if (shared != oldShared) {
0953                     shared->addCentralLayout(central);
0954                     central->setSharedLayout(shared);
0955                     if (oldShared) {
0956                         //! CENTRAL layout that changed from one ACTIVESHARED layout to another
0957                         unassign[central] = shared;
0958                     }
0959                 }
0960             } else {
0961                 //! Central Layout is not loaded
0962                 CentralLayout centralInStorage(this, Importer::layoutFilePath(centralName));
0963                 centralInStorage.setSharedLayoutName(i.key());
0964             }
0965         }
0966     }
0967 
0968     //! CENTRAL Layouts that wont have any SHARED Layout any more
0969     for (const auto &centralName : centralLayoutsNames()) {
0970         if (!mapHasRecord(centralName, sharesMap)) {
0971             CentralLayout *central = centralLayout(centralName);
0972             if (central && central->sharedLayout()) {
0973                 central->sharedLayout()->removeCentralLayout(central);
0974                 central->setSharedLayoutName(QString());
0975                 central->setSharedLayout(nullptr);
0976             }
0977         }
0978     }
0979 
0980     //! Unassing from Shared Layouts Central ones that are not assigned any more
0981     //! IMPORTANT: This must be done after all the ASSIGNMENTS in order to avoid
0982     //! to unload a SharedLayout that it should not
0983     for (QHash<CentralLayout *, SharedLayout *>::iterator i=unassign.begin(); i!=unassign.end(); ++i) {
0984         i.value()->removeCentralLayout(i.key());
0985     }
0986 }
0987 
0988 }
0989 } // end of namespace