File indexing completed on 2024-12-08 04:58:12
0001 /* 0002 SPDX-FileCopyrightText: 2019 Michail Vourlakos <mvourlakos@gmail.com> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "synchronizer.h" 0007 0008 //! local 0009 #include <config-latte.h> 0010 #include "importer.h" 0011 #include "manager.h" 0012 #include "../apptypes.h" 0013 #include "../screenpool.h" 0014 #include "../data/layoutdata.h" 0015 #include "../lattecorona.h" 0016 #include "../layout/centrallayout.h" 0017 #include "../layout/genericlayout.h" 0018 #include "../settings/universalsettings.h" 0019 #include "../templates/templatesmanager.h" 0020 #include "../view/view.h" 0021 0022 // Qt 0023 #include <QDir> 0024 #include <QFile> 0025 #include <QStringList> 0026 0027 // Plasma 0028 #include <Plasma/Containment> 0029 0030 // KDE 0031 #include <KActivities/Consumer> 0032 #include <KActivities/Controller> 0033 #include <KWindowSystem> 0034 0035 #define LAYOUTSINITINTERVAL 350 0036 0037 namespace Latte { 0038 namespace Layouts { 0039 0040 Synchronizer::Synchronizer(QObject *parent) 0041 : QObject(parent), 0042 m_activitiesController(new KActivities::Controller) 0043 { 0044 m_manager = qobject_cast<Manager *>(parent); 0045 0046 connect(this, &Synchronizer::layoutsChanged, this, &Synchronizer::reloadAssignedLayouts); 0047 0048 //! KWin update Disabled Borders 0049 connect(this, &Synchronizer::centralLayoutsChanged, this, &Synchronizer::updateBorderlessMaximizedAfterTimer); 0050 connect(m_manager->corona()->universalSettings(), &UniversalSettings::canDisableBordersChanged, this, &Synchronizer::updateKWinDisabledBorders); 0051 0052 m_updateBorderlessMaximized.setInterval(500); 0053 m_updateBorderlessMaximized.setSingleShot(true); 0054 connect(&m_updateBorderlessMaximized, &QTimer::timeout, this, &Synchronizer::updateKWinDisabledBorders); 0055 0056 //! KActivities tracking 0057 connect(m_manager->corona()->activitiesConsumer(), &KActivities::Consumer::activityRemoved, 0058 this, &Synchronizer::onActivityRemoved); 0059 0060 connect(m_manager->corona()->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, 0061 this, [&]() { 0062 if (m_manager->memoryUsage() == MemoryUsage::MultipleLayouts) { 0063 //! this signal is also triggered when runningactivities are changed and actually is received first 0064 //! this is why we need a timer here in order to delay that execution and not activate/deactivate 0065 //! maximizedborders faulty because syncMultipleLayoutsToActivities(); has not been executed yet 0066 updateBorderlessMaximizedAfterTimer(); 0067 } 0068 }); 0069 0070 connect(m_manager->corona()->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, 0071 this, [&]() { 0072 if (m_manager->memoryUsage() == MemoryUsage::MultipleLayouts) { 0073 syncMultipleLayoutsToActivities(); 0074 } 0075 }); 0076 } 0077 0078 Synchronizer::~Synchronizer() 0079 { 0080 m_activitiesController->deleteLater(); 0081 } 0082 0083 KActivities::Controller *Synchronizer::activitiesController() const 0084 { 0085 return m_activitiesController; 0086 } 0087 0088 bool Synchronizer::latteViewExists(Latte::View *view) const 0089 { 0090 for (const auto layout : m_centralLayouts) { 0091 for (const auto &v : layout->latteViews()) { 0092 if (v == view) { 0093 return true; 0094 } 0095 } 0096 } 0097 0098 return false; 0099 } 0100 0101 bool Synchronizer::layoutExists(QString layoutName) const 0102 { 0103 return m_layouts.containsName(layoutName); 0104 } 0105 0106 0107 bool Synchronizer::isAssigned(QString layoutName) const 0108 { 0109 for(auto activityid : m_assignedLayouts.keys()) { 0110 if (m_assignedLayouts[activityid].contains(layoutName)) { 0111 return true; 0112 } 0113 } 0114 0115 return false; 0116 } 0117 0118 int Synchronizer::centralLayoutPos(QString id) const 0119 { 0120 for (int i = 0; i < m_centralLayouts.size(); ++i) { 0121 CentralLayout *layout = m_centralLayouts.at(i); 0122 0123 if (layout->name() == id) { 0124 return i; 0125 } 0126 } 0127 0128 return -1; 0129 } 0130 0131 QString Synchronizer::layoutPath(QString layoutName) 0132 { 0133 QString path = Layouts::Importer::layoutUserFilePath(layoutName); 0134 0135 if (!QFile(path).exists()) { 0136 path = ""; 0137 } 0138 0139 return path; 0140 } 0141 0142 QStringList Synchronizer::activities() 0143 { 0144 return m_manager->corona()->activitiesConsumer()->activities(); 0145 } 0146 0147 QStringList Synchronizer::freeActivities() 0148 { 0149 QStringList frees = activities(); 0150 0151 for(auto assigned : m_assignedLayouts.keys()) { 0152 frees.removeAll(assigned); 0153 } 0154 0155 return frees; 0156 } 0157 0158 QStringList Synchronizer::runningActivities() 0159 { 0160 return m_manager->corona()->activitiesConsumer()->runningActivities(); 0161 } 0162 0163 QStringList Synchronizer::freeRunningActivities() 0164 { 0165 QStringList fActivities; 0166 0167 for (const auto &activity : runningActivities()) { 0168 if (!m_assignedLayouts.contains(activity)) { 0169 fActivities.append(activity); 0170 } 0171 } 0172 0173 return fActivities; 0174 } 0175 0176 QStringList Synchronizer::validActivities(const QStringList &layoutActivities) 0177 { 0178 QStringList valids; 0179 QStringList allactivities = activities(); 0180 0181 for(auto activity : layoutActivities) { 0182 if (allactivities.contains(activity)) { 0183 valids << activity; 0184 } 0185 } 0186 0187 return valids; 0188 } 0189 0190 QStringList Synchronizer::centralLayoutsNames() 0191 { 0192 QStringList names; 0193 0194 if (m_manager->memoryUsage() == MemoryUsage::SingleLayout) { 0195 if (m_centralLayouts.count() > 0) { 0196 names << m_centralLayouts.at(0)->name(); 0197 } 0198 } else { 0199 for (int i = 0; i < m_centralLayouts.size(); ++i) { 0200 CentralLayout *layout = m_centralLayouts.at(i); 0201 names << layout->name(); 0202 } 0203 } 0204 0205 return names; 0206 } 0207 0208 QStringList Synchronizer::currentLayoutsNames() const 0209 { 0210 QList<CentralLayout *> currents = currentLayouts(); 0211 QStringList currentNames; 0212 0213 for (int i = 0; i < currents.size(); ++i) { 0214 CentralLayout *layout = currents.at(i); 0215 currentNames << layout->name(); 0216 } 0217 0218 return currentNames; 0219 } 0220 0221 QStringList Synchronizer::layouts() const 0222 { 0223 return m_layouts.names(); 0224 } 0225 0226 QStringList Synchronizer::menuLayouts() const 0227 { 0228 QStringList menulayouts; 0229 0230 for (int i=0; i<m_layouts.rowCount(); ++i) { 0231 if (!m_layouts[i].isShownInMenu) { 0232 continue; 0233 } 0234 0235 menulayouts << m_layouts[i].name; 0236 } 0237 0238 for (const auto layout : m_centralLayouts) { 0239 if (!menulayouts.contains(layout->name())) { 0240 menulayouts.prepend(layout->name()); 0241 } 0242 } 0243 0244 menulayouts.sort(Qt::CaseInsensitive); 0245 0246 return menulayouts; 0247 } 0248 0249 void Synchronizer::setIsSingleLayoutInDeprecatedRenaming(const bool &enabled) 0250 { 0251 m_isSingleLayoutInDeprecatedRenaming = enabled; 0252 } 0253 0254 Data::Layout Synchronizer::data(const QString &storedLayoutName) const 0255 { 0256 Data::Layout l; 0257 0258 if (m_layouts.containsName(storedLayoutName)) { 0259 QString lid = m_layouts.idForName(storedLayoutName); 0260 return m_layouts[lid]; 0261 } 0262 0263 return l; 0264 } 0265 0266 Data::LayoutsTable Synchronizer::layoutsTable() const 0267 { 0268 return m_layouts; 0269 } 0270 0271 void Synchronizer::setLayoutsTable(const Data::LayoutsTable &table) 0272 { 0273 if (m_layouts == table) { 0274 return; 0275 } 0276 0277 m_layouts = table; 0278 emit layoutsChanged(); 0279 } 0280 0281 void Synchronizer::updateLayoutsTable() 0282 { 0283 for (int i = 0; i < m_centralLayouts.size(); ++i) { 0284 CentralLayout *layout = m_centralLayouts.at(i); 0285 0286 if (m_layouts.containsId(layout->file())) { 0287 m_layouts[layout->file()] = layout->data(); 0288 } 0289 } 0290 0291 for (int i = 0; i < m_layouts.rowCount(); ++i) { 0292 if ((m_layouts[i].errors>0 || m_layouts[i].warnings>0) && !m_layouts[i].isActive) { 0293 CentralLayout central(this, m_layouts[i].id); 0294 m_layouts[i].errors = central.errors().count(); 0295 m_layouts[i].warnings = central.warnings().count(); 0296 } 0297 } 0298 } 0299 0300 CentralLayout *Synchronizer::centralLayout(QString layoutname) const 0301 { 0302 for (int i = 0; i < m_centralLayouts.size(); ++i) { 0303 CentralLayout *layout = m_centralLayouts.at(i); 0304 0305 if (layout->name() == layoutname) { 0306 return layout; 0307 } 0308 } 0309 0310 return nullptr; 0311 } 0312 0313 QList<CentralLayout *> Synchronizer::currentLayouts() const 0314 { 0315 QList<CentralLayout *> layouts; 0316 layouts.clear(); 0317 0318 if (m_centralLayouts.isEmpty()) { 0319 return layouts; 0320 } 0321 0322 if (m_manager->memoryUsage() == MemoryUsage::SingleLayout) { 0323 layouts << m_centralLayouts[0]; 0324 } else { 0325 for (auto layout : m_centralLayouts) { 0326 if (layout->isOnAllActivities() || layout->appliedActivities().contains(m_manager->corona()->activitiesConsumer()->currentActivity())) { 0327 layouts << layout; 0328 } 0329 } 0330 } 0331 0332 return layouts; 0333 } 0334 0335 QList<CentralLayout *> Synchronizer::centralLayoutsForActivity(const QString activityid) const 0336 { 0337 QList<CentralLayout *> layouts; 0338 0339 if (m_manager->memoryUsage() == MemoryUsage::SingleLayout && m_centralLayouts.count() >= 1) { 0340 layouts << m_centralLayouts.at(0); 0341 } else { 0342 for (auto layout : m_centralLayouts) { 0343 if (layout->isOnAllActivities() || layout->appliedActivities().contains(activityid)) { 0344 layouts << layout; 0345 } 0346 } 0347 } 0348 0349 return layouts; 0350 } 0351 0352 QList<Latte::View *> Synchronizer::currentViews() const 0353 { 0354 QList<Latte::View *> views; 0355 0356 for(auto layout : currentLayouts()) { 0357 views << layout->latteViews(); 0358 } 0359 0360 return views; 0361 } 0362 0363 QList<Latte::View *> Synchronizer::currentOriginalViews() const 0364 { 0365 QList<Latte::View *> views; 0366 0367 for(auto layout : currentLayouts()) { 0368 views << layout->onlyOriginalViews(); 0369 } 0370 0371 return views; 0372 } 0373 0374 QList<Latte::View *> Synchronizer::currentViewsWithPlasmaShortcuts() const 0375 { 0376 QList<Latte::View *> views; 0377 0378 for(auto layout : currentLayouts()) { 0379 views << layout->viewsWithPlasmaShortcuts(); 0380 } 0381 0382 return views; 0383 } 0384 0385 QList<Latte::View *> Synchronizer::sortedCurrentViews() const 0386 { 0387 return Layout::GenericLayout::sortedLatteViews(currentViews(), m_manager->corona()->screenPool()->primaryScreen()); 0388 } 0389 0390 QList<Latte::View *> Synchronizer::sortedCurrentOriginalViews() const 0391 { 0392 return Layout::GenericLayout::sortedLatteViews(currentOriginalViews(), m_manager->corona()->screenPool()->primaryScreen()); 0393 } 0394 0395 QList<Latte::View *> Synchronizer::viewsBasedOnActivityId(const QString &id) const 0396 { 0397 QList<Latte::View *> views; 0398 0399 for(auto layout : centralLayoutsForActivity(id)) { 0400 if (m_centralLayouts.contains(layout)) { 0401 views << layout->latteViews(); 0402 } 0403 } 0404 0405 return views; 0406 } 0407 0408 Layout::GenericLayout *Synchronizer::layout(QString layoutname) const 0409 { 0410 Layout::GenericLayout *l = centralLayout(layoutname); 0411 0412 return l; 0413 } 0414 0415 int Synchronizer::screenForContainment(Plasma::Containment *containment) 0416 { 0417 for (auto layout : m_centralLayouts) { 0418 if (layout->contains(containment)) { 0419 return layout->screenForContainment(containment); 0420 } 0421 } 0422 0423 return -1; 0424 } 0425 0426 Latte::View *Synchronizer::viewForContainment(uint id) 0427 { 0428 for (auto layout : m_centralLayouts) { 0429 Latte::View *view = layout->viewForContainment(id); 0430 0431 if (view) { 0432 return view; 0433 } 0434 } 0435 0436 return nullptr; 0437 } 0438 0439 Latte::View *Synchronizer::viewForContainment(Plasma::Containment *containment) 0440 { 0441 for (auto layout : m_centralLayouts) { 0442 Latte::View *view = layout->viewForContainment(containment); 0443 0444 if (view) { 0445 return view; 0446 } 0447 } 0448 0449 return nullptr; 0450 } 0451 0452 void Synchronizer::addLayout(CentralLayout *layout) 0453 { 0454 if (!m_centralLayouts.contains(layout)) { 0455 m_centralLayouts.append(layout); 0456 } 0457 } 0458 0459 void Synchronizer::onActivityRemoved(const QString &activityid) 0460 { 0461 if (!m_assignedLayouts.contains(activityid)) { 0462 return; 0463 } 0464 0465 //! remove any other explicit set layouts for the current activity 0466 QStringList explicits = m_assignedLayouts[activityid]; 0467 0468 for(auto explicitlayoutname : explicits) { 0469 QString explicitlayoutid = m_layouts.idForName(explicitlayoutname); 0470 0471 m_layouts[explicitlayoutid].activities.removeAll(activityid); 0472 m_manager->setOnActivities(explicitlayoutname, m_layouts[explicitlayoutid].activities); 0473 emit layoutActivitiesChanged(m_layouts[explicitlayoutid]); 0474 } 0475 0476 QStringList freelayoutnames; 0477 0478 if (m_assignedLayouts.contains(Data::Layout::FREEACTIVITIESID)) { 0479 freelayoutnames = m_assignedLayouts[Data::Layout::FREEACTIVITIESID]; 0480 } 0481 0482 reloadAssignedLayouts(); 0483 0484 for(auto freelayoutname : freelayoutnames) { 0485 //! inform free activities layouts that their activities probably changed 0486 CentralLayout *central = centralLayout(freelayoutname); 0487 0488 if (central) { 0489 emit central->activitiesChanged(); 0490 } 0491 } 0492 } 0493 0494 void Synchronizer::updateBorderlessMaximizedAfterTimer() 0495 { 0496 //! this signal is also triggered when runningactivities are changed and actually is received first 0497 //! this is why we need a timer here in order to delay that execution and not activate/deactivate 0498 //! maximizedborders faulty because syncMultipleLayoutsToActivities(); has not been executed yet 0499 m_updateBorderlessMaximized.start(); 0500 } 0501 0502 void Synchronizer::hideAllViews() 0503 { 0504 for (const auto layout : m_centralLayouts) { 0505 emit currentLayoutIsSwitching(layout->name()); 0506 } 0507 } 0508 0509 void Synchronizer::pauseLayout(QString layoutName) 0510 { 0511 if (m_manager->memoryUsage() == MemoryUsage::MultipleLayouts) { 0512 CentralLayout *layout = centralLayout(layoutName); 0513 0514 if (layout->isOnAllActivities()) { 0515 return; 0516 } 0517 0518 QStringList appliedactivities = layout->appliedActivities(); 0519 0520 if (layout && !appliedactivities.isEmpty()) { 0521 int i = 0; 0522 0523 for (const auto &activityid : appliedactivities) { 0524 //! Stopping the activities must be done asynchronous because otherwise 0525 //! the activity manager cant close multiple activities 0526 QTimer::singleShot(i * 1000, [this, activityid]() { 0527 m_activitiesController->stopActivity(activityid); 0528 }); 0529 0530 i = i + 1; 0531 } 0532 } 0533 } 0534 } 0535 0536 void Synchronizer::syncActiveLayoutsToOriginalFiles() 0537 { 0538 if (m_manager->memoryUsage() == MemoryUsage::MultipleLayouts) { 0539 for (const auto layout : m_centralLayouts) { 0540 layout->syncToLayoutFile(); 0541 } 0542 } 0543 } 0544 0545 void Synchronizer::syncLatteViewsToScreens() 0546 { 0547 for (const auto layout : m_centralLayouts) { 0548 layout->syncLatteViewsToScreens(); 0549 } 0550 } 0551 0552 void Synchronizer::unloadCentralLayout(CentralLayout *layout) 0553 { 0554 int pos = m_centralLayouts.indexOf(layout); 0555 0556 if (pos>=0) { 0557 CentralLayout *central = m_centralLayouts.takeAt(pos); 0558 0559 if (m_multipleModeInitialized && !m_manager->corona()->inQuit()) { 0560 central->syncToLayoutFile(true); 0561 } 0562 0563 central->unloadLatteViews(); 0564 central->unloadContainments(); 0565 0566 if (m_multipleModeInitialized && !m_manager->corona()->inQuit()) { 0567 m_manager->clearUnloadedContainmentsFromLinkedFile(central->unloadedContainmentsIds(), true); 0568 } 0569 0570 delete central; 0571 } 0572 } 0573 0574 void Synchronizer::initLayouts() 0575 { 0576 m_layouts.clear(); 0577 0578 QDir layoutDir(Layouts::Importer::layoutUserDir()); 0579 QStringList filter; 0580 filter.append(QString("*.layout.latte")); 0581 QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); 0582 0583 for (const auto &layout : files) { 0584 if (layout.contains(Layout::MULTIPLELAYOUTSHIDDENNAME)) { 0585 //! IMPORTANT: DON'T ADD MultipleLayouts hidden file in layouts list 0586 continue; 0587 } 0588 0589 QString layoutpath = layoutDir.absolutePath() + "/" + layout; 0590 onLayoutAdded(layoutpath); 0591 } 0592 0593 emit layoutsChanged(); 0594 0595 if (!m_isLoaded) { 0596 m_isLoaded = true; 0597 connect(m_manager->corona()->templatesManager(), &Latte::Templates::Manager::newLayoutAdded, this, &Synchronizer::onLayoutAdded); 0598 connect(m_manager->importer(), &Latte::Layouts::Importer::newLayoutAdded, this, &Synchronizer::onLayoutAdded); 0599 } 0600 } 0601 0602 void Synchronizer::onLayoutAdded(const QString &layout) 0603 { 0604 CentralLayout centrallayout(this, layout); 0605 m_layouts.insertBasedOnName(centrallayout.data()); 0606 0607 if (m_isLoaded) { 0608 emit layoutsChanged(); 0609 } 0610 } 0611 0612 void Synchronizer::reloadAssignedLayouts() 0613 { 0614 m_assignedLayouts.clear(); 0615 0616 for (int i=0; i< m_layouts.rowCount(); ++i) { 0617 for (const auto &activity : m_layouts[i].activities) { 0618 if (m_assignedLayouts.contains(activity)) { 0619 m_assignedLayouts[activity] << m_layouts[i].name; 0620 } else { 0621 m_assignedLayouts[activity] = QStringList(m_layouts[i].name); 0622 } 0623 } 0624 } 0625 } 0626 0627 void Synchronizer::unloadLayouts() 0628 { 0629 //! Unload all CentralLayouts 0630 while (!m_centralLayouts.isEmpty()) { 0631 CentralLayout *layout = m_centralLayouts.at(0); 0632 unloadCentralLayout(layout); 0633 } 0634 0635 m_multipleModeInitialized = false; 0636 } 0637 0638 void Synchronizer::unloadPreloadedLayouts() 0639 { 0640 QStringList currentnames; 0641 QStringList preloadednames = Layouts::Storage::self()->storedLayoutsInMultipleFile(); 0642 0643 for(auto l : m_centralLayouts) { 0644 if (l) { 0645 currentnames << l->name(); 0646 } 0647 } 0648 0649 for(auto lname : preloadednames) { 0650 if (!currentnames.contains(lname)) { 0651 Layouts::Storage::self()->moveToLayoutFile(lname); 0652 } 0653 } 0654 } 0655 0656 bool Synchronizer::memoryInitialized() const 0657 { 0658 return ((m_manager->memoryUsage() == MemoryUsage::SingleLayout && m_centralLayouts.size()>0) 0659 || (m_manager->memoryUsage() == MemoryUsage::MultipleLayouts && m_multipleModeInitialized)); 0660 } 0661 0662 bool Synchronizer::initSingleMode(QString layoutName) 0663 { 0664 QString layoutpath = layoutName.isEmpty() ? layoutPath(m_manager->corona()->universalSettings()->singleModeLayoutName()) : layoutPath(layoutName); 0665 0666 if (layoutpath.isEmpty()) { 0667 qDebug() << "Layout : " << layoutName << " was not found..."; 0668 return false; 0669 } 0670 0671 if (m_centralLayouts.size() > 0) { 0672 emit currentLayoutIsSwitching(m_centralLayouts[0]->name()); 0673 } 0674 0675 //! this code must be called asynchronously because it can create crashes otherwise. 0676 //! Tasks plasmoid case that triggers layouts switching through its context menu 0677 QTimer::singleShot(LAYOUTSINITINTERVAL, [this, layoutName, layoutpath]() { 0678 qDebug() << " ... initializing layout in single mode : " << layoutName << " - " << layoutpath; 0679 unloadPreloadedLayouts(); 0680 unloadLayouts(); 0681 0682 //! load the main single layout/corona file 0683 CentralLayout *newLayout = new CentralLayout(this, layoutpath, layoutName); 0684 0685 //! Order of initialization steps is very important and guarantees correct startup initialization 0686 //! Step1: corona is set for the layout 0687 //! Step2: containments from file are loaded into main corona 0688 //! Step3: layout connects to corona signals and slots 0689 //! Step4: layout is added in manager and is accesible for others to find 0690 //! Step5: layout is attaching its initial containmens and is now considered ACTIVE 0691 newLayout->setCorona(m_manager->corona()); //step1 0692 m_manager->loadLatteLayout(layoutpath); //step2 0693 newLayout->initCorona(); //step3 0694 addLayout(newLayout); //step4 0695 newLayout->initContainments(); //step5 0696 0697 emit centralLayoutsChanged(); 0698 0699 if (m_isSingleLayoutInDeprecatedRenaming) { 0700 QString deprecatedlayoutpath = layoutPath(m_manager->corona()->universalSettings()->singleModeLayoutName()); 0701 0702 if (!deprecatedlayoutpath.isEmpty()) { 0703 qDebug() << "Removing Deprecated single layout after renaming:: " << m_manager->corona()->universalSettings()->singleModeLayoutName(); 0704 QFile(deprecatedlayoutpath).remove(); 0705 } 0706 0707 m_isSingleLayoutInDeprecatedRenaming = false; 0708 } 0709 0710 m_manager->corona()->universalSettings()->setSingleModeLayoutName(layoutName); 0711 m_manager->importer()->setMultipleLayoutsStatus(Latte::MultipleLayouts::Uninitialized); 0712 emit initializationFinished(); 0713 }); 0714 0715 return true; 0716 } 0717 0718 bool Synchronizer::initMultipleMode(QString layoutName) 0719 { 0720 if (m_multipleModeInitialized) { 0721 return false; 0722 } 0723 0724 for (const auto layout : m_centralLayouts) { 0725 emit currentLayoutIsSwitching(layout->name()); 0726 } 0727 0728 //! this code must be called asynchronously because it can create crashes otherwise. 0729 //! Tasks plasmoid case that triggers layouts switching through its context menu 0730 QTimer::singleShot(LAYOUTSINITINTERVAL, [this, layoutName]() { 0731 qDebug() << " ... initializing layout in multiple mode : " << layoutName ; 0732 unloadLayouts(); 0733 0734 QStringList layoutsinmultiplestorage = Layouts::Storage::self()->storedLayoutsInMultipleFile(); 0735 qDebug() << "Preloaded Multiple Layouts in Storage :: " << layoutsinmultiplestorage; 0736 0737 m_manager->loadLatteLayout(layoutPath(QString(Layout::MULTIPLELAYOUTSHIDDENNAME))); 0738 0739 m_multipleModeInitialized = true; 0740 0741 emit centralLayoutsChanged(); 0742 0743 if (!layoutName.isEmpty()) { 0744 switchToLayoutInMultipleModeBasedOnActivities(layoutName); 0745 } 0746 0747 syncMultipleLayoutsToActivities(layoutsinmultiplestorage); 0748 m_manager->importer()->setMultipleLayoutsStatus(Latte::MultipleLayouts::Running); 0749 emit initializationFinished(); 0750 }); 0751 0752 return true; 0753 } 0754 0755 bool Synchronizer::switchToLayoutInSingleMode(QString layoutName) 0756 { 0757 if (!memoryInitialized() || m_manager->memoryUsage() != MemoryUsage::SingleLayout) { 0758 return false; 0759 } 0760 0761 if (m_centralLayouts.size()>0 && m_centralLayouts[0]->name() == layoutName) { 0762 return true; 0763 } 0764 0765 return initSingleMode(layoutName); 0766 } 0767 0768 bool Synchronizer::switchToLayoutInMultipleModeBasedOnActivities(const QString &layoutName) 0769 { 0770 Data::Layout layoutdata; 0771 CentralLayout *central = centralLayout(layoutName); 0772 0773 if (central) { 0774 layoutdata = central->data(); 0775 } else if (m_layouts.containsName(layoutName)) { 0776 QString layoutid = m_layouts.idForName(layoutName); 0777 CentralLayout storagedlayout(this, layoutid); 0778 layoutdata = storagedlayout.data(); 0779 0780 m_layouts[layoutid] = layoutdata; 0781 } 0782 0783 if (layoutdata.isEmpty()) { 0784 return false; 0785 } 0786 0787 QString switchToActivity; 0788 0789 //! try to not remove activityids that belong to different machines that are not currently present 0790 QStringList validlayoutactivities = validActivities(layoutdata.activities); 0791 0792 if (layoutdata.isOnAllActivities()) { 0793 //! no reason to switch in any activity; 0794 } else if (layoutdata.isForFreeActivities()) { 0795 //! free-activities case 0796 QStringList freerunningactivities = freeRunningActivities(); 0797 0798 if (freerunningactivities.count() > 0) { 0799 if (freerunningactivities.contains(layoutdata.lastUsedActivity)) { 0800 switchToActivity = layoutdata.lastUsedActivity; 0801 } else { 0802 switchToActivity = freerunningactivities[0]; 0803 } 0804 } else { 0805 QStringList freepausedactivities = freeActivities(); 0806 0807 if (freepausedactivities.count() > 0) { 0808 switchToActivity = freepausedactivities[0]; 0809 } 0810 } 0811 } else if (!validlayoutactivities.isEmpty()) { 0812 //! set on-explicit activities 0813 QStringList allactivities = activities(); 0814 0815 if (validlayoutactivities.contains(layoutdata.lastUsedActivity)) { 0816 switchToActivity = layoutdata.lastUsedActivity; 0817 } else { 0818 switchToActivity = validlayoutactivities[0]; 0819 } 0820 } else if (validlayoutactivities.isEmpty() && m_layouts.containsName(layoutName)) { 0821 //! no-activities are set 0822 //! has not been set in any activities but nonetheless it is requested probably by the user 0823 //! requested layout is assigned explicitly in current activity and any remaining explicit layouts 0824 //! are removing current activity from their activities list 0825 QString layoutid = m_layouts.idForName(layoutName); 0826 QString currentactivityid = m_activitiesController->currentActivity(); 0827 0828 QStringList layoutIdsChanged; 0829 0830 m_layouts[layoutid].activities.append(currentactivityid); 0831 m_manager->setOnActivities(layoutName, m_layouts[layoutid].activities); 0832 emit layoutActivitiesChanged(m_layouts[layoutid]); 0833 0834 layoutIdsChanged << layoutid; 0835 0836 if (m_assignedLayouts.contains(currentactivityid)) { 0837 //! remove any other explicit set layouts for the current activity 0838 QStringList explicits = m_assignedLayouts[currentactivityid]; 0839 0840 for(auto explicitlayoutname : explicits) { 0841 QString explicitlayoutid = m_layouts.idForName(explicitlayoutname); 0842 0843 m_layouts[explicitlayoutid].activities.removeAll(currentactivityid); 0844 m_manager->setOnActivities(explicitlayoutname, m_layouts[explicitlayoutid].activities); 0845 emit layoutActivitiesChanged(m_layouts[explicitlayoutid]); 0846 } 0847 } 0848 0849 QStringList freelayoutnames; 0850 if (m_assignedLayouts.contains(Data::Layout::FREEACTIVITIESID)) { 0851 freelayoutnames = m_assignedLayouts[Data::Layout::FREEACTIVITIESID]; 0852 } 0853 0854 reloadAssignedLayouts(); 0855 0856 for(auto freelayoutname : freelayoutnames) { 0857 //! inform free activities layouts that their activities probably changed 0858 CentralLayout *central = centralLayout(freelayoutname); 0859 0860 if (central) { 0861 emit central->activitiesChanged(); 0862 } 0863 } 0864 } 0865 0866 if (!switchToActivity.isEmpty()) { 0867 if (!m_manager->corona()->activitiesConsumer()->runningActivities().contains(switchToActivity)) { 0868 m_activitiesController->startActivity(switchToActivity); 0869 } 0870 0871 m_activitiesController->setCurrentActivity(switchToActivity); 0872 } 0873 0874 return true; 0875 } 0876 0877 bool Synchronizer::switchToLayoutInMultipleMode(QString layoutName) 0878 { 0879 if (!memoryInitialized() || m_manager->memoryUsage() != MemoryUsage::MultipleLayouts) { 0880 return false; 0881 } 0882 0883 CentralLayout *layout = centralLayout(layoutName); 0884 0885 if (layout) { 0886 QStringList appliedActivities = layout->appliedActivities(); 0887 QString nextActivity = !layout->lastUsedActivity().isEmpty() ? layout->lastUsedActivity() : appliedActivities[0]; 0888 0889 if (!appliedActivities.contains(m_manager->corona()->activitiesConsumer()->currentActivity())) { 0890 //! it means we are at a foreign activity and we can switch to correct one 0891 m_activitiesController->setCurrentActivity(nextActivity); 0892 return true; 0893 } 0894 } else { 0895 if (!layoutName.isEmpty()) { 0896 switchToLayoutInMultipleModeBasedOnActivities(layoutName); 0897 } 0898 0899 syncMultipleLayoutsToActivities(); 0900 } 0901 0902 return true; 0903 } 0904 0905 0906 bool Synchronizer::switchToLayout(QString layoutName, MemoryUsage::LayoutsMemory newMemoryUsage) 0907 { 0908 qDebug() << " >>>>> SWITCHING >> " << layoutName << " __ from memory: " << m_manager->memoryUsage() << " to memory: " << newMemoryUsage; 0909 0910 if (newMemoryUsage == MemoryUsage::Current) { 0911 newMemoryUsage = m_manager->memoryUsage(); 0912 } 0913 0914 if (!memoryInitialized() || newMemoryUsage != m_manager->memoryUsage()) { 0915 //! Initiate Layouts memory properly 0916 m_manager->setMemoryUsage(newMemoryUsage); 0917 0918 return (newMemoryUsage == MemoryUsage::SingleLayout ? initSingleMode(layoutName) : initMultipleMode(layoutName)); 0919 } 0920 0921 if (m_manager->memoryUsage() == MemoryUsage::SingleLayout) { 0922 return switchToLayoutInSingleMode(layoutName); 0923 } else { 0924 return switchToLayoutInMultipleMode(layoutName); 0925 } 0926 } 0927 0928 void Synchronizer::syncMultipleLayoutsToActivities(QStringList preloadedLayouts) 0929 { 0930 qDebug() << " ---- --------- ------ syncMultipleLayoutsToActivities ------- "; 0931 qDebug() << " ---- --------- ------ ------------------------------- ------- "; 0932 0933 QStringList layoutNamesToUnload; 0934 QStringList layoutNamesToLoad; 0935 QStringList currentNames = centralLayoutsNames(); 0936 0937 //! discover OnAllActivities layouts 0938 if (m_assignedLayouts.contains(Data::Layout::ALLACTIVITIESID)) { 0939 layoutNamesToLoad << m_assignedLayouts[Data::Layout::ALLACTIVITIESID]; 0940 } 0941 0942 //! discover ForFreeActivities layouts 0943 if (m_assignedLayouts.contains(Data::Layout::FREEACTIVITIESID) && freeRunningActivities().count()>0) { 0944 layoutNamesToLoad << m_assignedLayouts[Data::Layout::FREEACTIVITIESID]; 0945 } 0946 0947 //! discover layouts assigned to explicit activities based on running activities 0948 for (const auto &activity : runningActivities()) { 0949 if (m_assignedLayouts.contains(activity)) { 0950 layoutNamesToLoad << m_assignedLayouts[activity]; 0951 } 0952 } 0953 0954 //! discover layouts that must be unloaded because of running activities changes 0955 for (const auto layout : m_centralLayouts) { 0956 if (!layoutNamesToLoad.contains(layout->name())) { 0957 layoutNamesToUnload << layout->name(); 0958 } 0959 } 0960 0961 for (const auto lname : preloadedLayouts) { 0962 if (!layoutNamesToLoad.contains(lname)) { 0963 layoutNamesToUnload << lname; 0964 } 0965 } 0966 0967 QString defaultForcedLayout; 0968 0969 //! Safety 0970 if (layoutNamesToLoad.isEmpty()) { 0971 //! If no layout is found then force loading Default Layout 0972 QString layoutPath = m_manager->corona()->templatesManager()->newLayout("", i18n(Templates::DEFAULTLAYOUTTEMPLATENAME)); 0973 layoutNamesToLoad << Layout::AbstractLayout::layoutName(layoutPath); 0974 m_manager->setOnAllActivities(layoutNamesToLoad[0]); 0975 defaultForcedLayout = layoutNamesToLoad[0]; 0976 } 0977 0978 QStringList newlyActivatedLayouts; 0979 0980 //! Add needed Layouts based on Activities settings 0981 for (const auto &layoutname : layoutNamesToLoad) { 0982 if (!centralLayout(layoutname)) { 0983 CentralLayout *newLayout = new CentralLayout(this, QString(layoutPath(layoutname)), layoutname); 0984 0985 if (newLayout) { 0986 qDebug() << "ACTIVATING LAYOUT ::::: " << layoutname; 0987 0988 //! Order of initialization steps is very important and guarantees correct startup initialization 0989 //! Step1: corona is set for the layout 0990 //! Step2: containments from the layout file are adjusted and are imported into main corona 0991 //! Step3: layout connects to corona signals and slots 0992 //! Step4: layout is added in manager and is accesible for others to find 0993 //! Step5: layout is attaching its initial containmens and is now considered ACTIVE 0994 newLayout->setCorona(m_manager->corona()); //step1 0995 if (!preloadedLayouts.contains(layoutname)) { 0996 newLayout->importToCorona(); //step2 0997 } 0998 newLayout->initCorona(); //step3 0999 addLayout(newLayout); //step4 1000 newLayout->initContainments(); //step5 1001 1002 if (!defaultForcedLayout.isEmpty() && defaultForcedLayout == layoutname) { 1003 emit newLayoutAdded(newLayout->data()); 1004 } 1005 1006 newlyActivatedLayouts << newLayout->name(); 1007 } 1008 } 1009 } 1010 1011 if (newlyActivatedLayouts.count()>0 && m_manager->corona()->universalSettings()->showInfoWindow()) { 1012 m_manager->showInfoWindow(i18np("Activating layout: <b>%2</b> ...", 1013 "Activating layouts: <b>%2</b> ...", 1014 newlyActivatedLayouts.count(), 1015 newlyActivatedLayouts.join(", ")), 1016 4000, QStringList(Data::Layout::ALLACTIVITIESID)); 1017 } 1018 1019 //! Unload no needed Layouts 1020 1021 //! hide layouts that will be removed in the end 1022 if (!layoutNamesToUnload.isEmpty()) { 1023 for (const auto layoutname : layoutNamesToUnload) { 1024 emit currentLayoutIsSwitching(layoutname); 1025 } 1026 1027 QTimer::singleShot(LAYOUTSINITINTERVAL, [this, layoutNamesToUnload, preloadedLayouts]() { 1028 unloadLayouts(layoutNamesToUnload, preloadedLayouts); 1029 }); 1030 } 1031 1032 currentNames.sort(); 1033 layoutNamesToLoad.sort(); 1034 1035 if (currentNames != layoutNamesToLoad) { 1036 emit centralLayoutsChanged(); 1037 } 1038 } 1039 1040 void Synchronizer::unloadLayouts(const QStringList &layoutNames, const QStringList &preloadedLayouts) 1041 { 1042 if (layoutNames.isEmpty()) { 1043 return; 1044 } 1045 1046 //! Unload no needed Layouts 1047 for (const auto &layoutname : layoutNames) { 1048 CentralLayout *layout = centralLayout(layoutname); 1049 int posLayout = centralLayoutPos(layoutname); 1050 1051 if (posLayout >= 0) { 1052 qDebug() << "REMOVING LAYOUT ::::: " << layoutname; 1053 m_centralLayouts.removeAt(posLayout); 1054 1055 if (!m_manager->corona()->inQuit()) { 1056 layout->syncToLayoutFile(true); 1057 } 1058 1059 layout->unloadContainments(); 1060 layout->unloadLatteViews(); 1061 if (!m_manager->corona()->inQuit()) { 1062 m_manager->clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds()); 1063 } 1064 delete layout; 1065 } else if (preloadedLayouts.contains(layoutname)) { 1066 Layouts::Storage::self()->moveToLayoutFile(layoutname); 1067 //! just make sure that 1068 } 1069 } 1070 1071 emit centralLayoutsChanged(); 1072 } 1073 1074 void Synchronizer::updateKWinDisabledBorders() 1075 { 1076 if (KWindowSystem::isPlatformWayland()) { 1077 // BUG: https://bugs.kde.org/show_bug.cgi?id=428202 1078 // KWin::reconfigure() function blocks/freezes Latte under wayland 1079 return; 1080 } 1081 1082 if (!m_manager->corona()->universalSettings()->canDisableBorders()) { 1083 m_manager->corona()->universalSettings()->kwin_setDisabledMaximizedBorders(false); 1084 } else { 1085 if (m_manager->corona()->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout && m_centralLayouts.size() > 0) { 1086 m_manager->corona()->universalSettings()->kwin_setDisabledMaximizedBorders(m_centralLayouts.at(0)->disableBordersForMaximizedWindows()); 1087 } else if (m_manager->corona()->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) { 1088 QList<CentralLayout *> centrals = centralLayoutsForActivity(m_manager->corona()->activitiesConsumer()->currentActivity()); 1089 1090 for (int i = 0; i < centrals.size(); ++i) { 1091 CentralLayout *layout = centrals.at(i); 1092 1093 if (layout->disableBordersForMaximizedWindows()) { 1094 m_manager->corona()->universalSettings()->kwin_setDisabledMaximizedBorders(true); 1095 return; 1096 } 1097 } 1098 1099 //! avoid initialization step for example during startup that no layouts have been loaded yet 1100 if (centrals.size() > 0) { 1101 m_manager->corona()->universalSettings()->kwin_setDisabledMaximizedBorders(false); 1102 } 1103 1104 } 1105 } 1106 } 1107 1108 } 1109 } // end of namespace