File indexing completed on 2024-09-15 12:59:03
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 ¢ralName : 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 ¢ralName : 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