diff --git a/app/lattecorona.cpp b/app/lattecorona.cpp index 6696afaf..5f5dbb0f 100644 --- a/app/lattecorona.cpp +++ b/app/lattecorona.cpp @@ -1,1055 +1,1054 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "lattecorona.h" // local #include "alternativeshelper.h" #include "importer.h" #include "lattedockadaptor.h" #include "launcherssignals.h" #include "layoutmanager.h" #include "screenpool.h" #include "indicator/factory.h" #include "layout/activelayout.h" #include "layout/genericlayout.h" -#include "layout/toplayout.h" +#include "layout/sharedlayout.h" #include "shortcuts/globalshortcuts.h" #include "package/lattepackage.h" #include "plasma/extended/screenpool.h" #include "plasma/extended/theme.h" #include "settings/universalsettings.h" #include "view/view.h" #include "wm/abstractwindowinterface.h" #include "wm/waylandinterface.h" #include "wm/xwindowinterface.h" // Qt #include #include #include #include #include #include #include #include #include // Plasma #include #include #include #include // KDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Latte { Corona::Corona(bool defaultLayoutOnStartup, QString layoutNameOnStartUp, int userSetMemoryUsage, QObject *parent) : Plasma::Corona(parent), m_defaultLayoutOnStartup(defaultLayoutOnStartup), m_userSetMemoryUsage(userSetMemoryUsage), m_layoutNameOnStartUp(layoutNameOnStartUp), m_activityConsumer(new KActivities::Consumer(this)), m_screenPool(new ScreenPool(KSharedConfig::openConfig(), this)), m_indicatorFactory(new Indicator::Factory(this)), m_universalSettings(new UniversalSettings(KSharedConfig::openConfig(), this)), m_globalShortcuts(new GlobalShortcuts(this)), m_plasmaScreenPool(new PlasmaExtended::ScreenPool(this)), m_themeExtended(new PlasmaExtended::Theme(KSharedConfig::openConfig(), this)), m_layoutManager(new LayoutManager(this)) { //! create the window manager if (KWindowSystem::isPlatformWayland()) { m_wm = new WaylandInterface(this); } else { m_wm = new XWindowInterface(this); } setupWaylandIntegration(); KPackage::Package package(new Latte::Package(this)); m_screenPool->load(); if (!package.isValid()) { qWarning() << staticMetaObject.className() << "the package" << package.metadata().rawData() << "is invalid!"; return; } else { qDebug() << staticMetaObject.className() << "the package" << package.metadata().rawData() << "is valid!"; } setKPackage(package); //! universal settings / extendedtheme must be loaded after the package has been set m_universalSettings->load(); m_themeExtended->load(); qmlRegisterTypes(); if (m_activityConsumer && (m_activityConsumer->serviceStatus() == KActivities::Consumer::Running)) { load(); } connect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); m_viewsScreenSyncTimer.setSingleShot(true); m_viewsScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval()); connect(&m_viewsScreenSyncTimer, &QTimer::timeout, this, &Corona::syncLatteViewsToScreens); connect(m_universalSettings, &UniversalSettings::screenTrackerIntervalChanged, this, [this]() { m_viewsScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval()); }); //! initialize the background tracer for broadcasted backgrounds m_backgroundTracer = new KDeclarative::QmlObjectSharedEngine(this); m_backgroundTracer->setInitializationDelayed(true); m_backgroundTracer->setSource(kPackage().filePath("backgroundTracer")); m_backgroundTracer->completeInitialization(); //! Dbus adaptor initialization new LatteDockAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/Latte"), this); } Corona::~Corona() { //! BEGIN: Give the time to slide-out views when closing m_layoutManager->hideAllViews(); //! Don't delay the destruction under wayland in any case //! because it creates a crash with kwin effects //! https://bugs.kde.org/show_bug.cgi?id=392890 if (!KWindowSystem::isPlatformWayland()) { QTimer::singleShot(400, [this]() { m_quitTimedEnded = true; }); while (!m_quitTimedEnded) { QGuiApplication::processEvents(QEventLoop::AllEvents, 50); } } //! END: slide-out views when closing m_viewsScreenSyncTimer.stop(); if (m_layoutManager->memoryUsage() == Types::SingleLayout) { cleanConfig(); } qDebug() << "Latte Corona - unload: containments ..."; m_layoutManager->unload(); m_wm->deleteLater(); m_globalShortcuts->deleteLater(); m_layoutManager->deleteLater(); m_screenPool->deleteLater(); m_universalSettings->deleteLater(); m_plasmaScreenPool->deleteLater(); m_backgroundTracer->deleteLater(); m_themeExtended->deleteLater(); m_indicatorFactory->deleteLater(); disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); delete m_activityConsumer; qDebug() << "Latte Corona - deleted..."; } void Corona::load() { if (m_activityConsumer && (m_activityConsumer->serviceStatus() == KActivities::Consumer::Running) && m_activitiesStarting) { disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); m_layoutManager->load(); m_activitiesStarting = false; connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &Corona::primaryOutputChanged, Qt::UniqueConnection); connect(QApplication::desktop(), &QDesktopWidget::screenCountChanged, this, &Corona::screenCountChanged); connect(m_screenPool, &ScreenPool::primaryPoolChanged, this, &Corona::screenCountChanged); QString assignedLayout = m_layoutManager->shouldSwitchToLayout(m_activityConsumer->currentActivity()); QString loadLayoutName = ""; if (!m_defaultLayoutOnStartup && m_layoutNameOnStartUp.isEmpty()) { if (!assignedLayout.isEmpty() && assignedLayout != m_universalSettings->currentLayoutName()) { loadLayoutName = assignedLayout; } else { loadLayoutName = m_universalSettings->currentLayoutName(); } if (!m_layoutManager->layoutExists(loadLayoutName)) { loadLayoutName = m_layoutManager->defaultLayoutName(); m_layoutManager->importDefaultLayout(false); } } else if (m_defaultLayoutOnStartup) { loadLayoutName = m_layoutManager->importer()->uniqueLayoutName(m_layoutManager->defaultLayoutName()); m_layoutManager->importDefaultLayout(true); } else { loadLayoutName = m_layoutNameOnStartUp; } if (m_userSetMemoryUsage != -1 && !KWindowSystem::isPlatformWayland()) { Types::LayoutsMemoryUsage usage = static_cast(m_userSetMemoryUsage); m_universalSettings->setLayoutsMemoryUsage(usage); } if (KWindowSystem::isPlatformWayland()) { m_universalSettings->setLayoutsMemoryUsage(Types::SingleLayout); } m_layoutManager->loadLayoutOnStartup(loadLayoutName); //! load screens signals such screenGeometryChanged in order to support //! plasmoid.screenGeometry properly for (QScreen *screen : qGuiApp->screens()) { addOutput(screen); } connect(qGuiApp, &QGuiApplication::screenAdded, this, &Corona::addOutput, Qt::UniqueConnection); } } void Corona::unload() { qDebug() << "unload: removing containments..."; while (!containments().isEmpty()) { //deleting a containment will remove it from the list due to QObject::destroyed connect in Corona //this form doesn't crash, while qDeleteAll(containments()) does delete containments().first(); } } void Corona::setupWaylandIntegration() { if (!KWindowSystem::isPlatformWayland()) { return; } using namespace KWayland::Client; auto connection = ConnectionThread::fromApplication(this); if (!connection) { return; } Registry *registry{new Registry(this)}; registry->create(connection); connect(registry, &Registry::plasmaShellAnnounced, this , [this, registry](quint32 name, quint32 version) { m_waylandCorona = registry->createPlasmaShell(name, version, this); }); QObject::connect(registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced, [this, registry](quint32 name, quint32 version) { KWayland::Client::PlasmaWindowManagement *pwm = registry->createPlasmaWindowManagement(name, version, this); WaylandInterface *wI = qobject_cast(m_wm); if (wI) { wI->initWindowManagement(pwm); } }); registry->setup(); connection->roundtrip(); } KActivities::Consumer *Corona::activityConsumer() const { return m_activityConsumer; } KWayland::Client::PlasmaShell *Corona::waylandCoronaInterface() const { return m_waylandCorona; } void Corona::cleanConfig() { auto containmentsEntries = config()->group("Containments"); bool changed = false; for(const auto &cId : containmentsEntries.groupList()) { if (!containmentExists(cId.toUInt())) { //cleanup obsolete containments containmentsEntries.group(cId).deleteGroup(); changed = true; qDebug() << "obsolete containment configuration deleted:" << cId; } else { //cleanup obsolete applets of running containments auto appletsEntries = containmentsEntries.group(cId).group("Applets"); for(const auto &appletId : appletsEntries.groupList()) { if (!appletExists(cId.toUInt(), appletId.toUInt())) { appletsEntries.group(appletId).deleteGroup(); changed = true; qDebug() << "obsolete applet configuration deleted:" << appletId; } } } } if (changed) { config()->sync(); qDebug() << "configuration file cleaned..."; } } bool Corona::containmentExists(uint id) const { for(const auto containment : containments()) { if (id == containment->id()) { return true; } } return false; } bool Corona::appletExists(uint containmentId, uint appletId) const { Plasma::Containment *containment = nullptr; for(const auto cont : containments()) { if (containmentId == cont->id()) { containment = cont; break; } } if (!containment) { return false; } for(const auto applet : containment->applets()) { if (applet->id() == appletId) { return true; } } return false; } KActivities::Consumer *Corona::activitiesConsumer() const { return m_activityConsumer; } GlobalShortcuts *Corona::globalShortcuts() const { return m_globalShortcuts; } ScreenPool *Corona::screenPool() const { return m_screenPool; } UniversalSettings *Corona::universalSettings() const { return m_universalSettings; } LayoutManager *Corona::layoutManager() const { return m_layoutManager; } AbstractWindowInterface *Corona::wm() const { return m_wm; } Indicator::Factory *Corona::indicatorFactory() const { return m_indicatorFactory; } PlasmaExtended::ScreenPool *Corona::plasmaScreenPool() const { return m_plasmaScreenPool; } PlasmaExtended::Theme *Corona::themeExtended() const { return m_themeExtended; } int Corona::numScreens() const { return qGuiApp->screens().count(); } QRect Corona::screenGeometry(int id) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; QString screenName; if (m_screenPool->knownIds().contains(id)) screenName = m_screenPool->connector(id); for(const auto scr : screens) { if (scr->name() == screenName) { screen = scr; break; } } return screen->geometry(); } QRegion Corona::availableScreenRegion(int id) const { return availableScreenRegionWithCriteria(id); } QRegion Corona::availableScreenRegionWithCriteria(int id, QString forLayout) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; QString screenName; if (m_screenPool->knownIds().contains(id)) screenName = m_screenPool->connector(id); for(auto scr : screens) { if (scr->name() == screenName) { screen = scr; break; } } if (!screen) return QRegion(); QList views; if (forLayout.isEmpty()) { Latte::ActiveLayout *currentLayout = m_layoutManager->currentLayout(); views = currentLayout->latteViews(); } else { Layout::GenericLayout *generic = m_layoutManager->activeLayout(forLayout); if (!generic) { - //! identify best active layout to be used for metrics - //! active layouts are always considering their top layouts - //! for their metrics - TopLayout *topLayout = m_layoutManager->topLayout(forLayout); + //! Identify best active layout to be used for metrics calculations. + //! Active layouts are always take into account their shared layouts for their metrics + SharedLayout *sharedLayout = m_layoutManager->sharedLayout(forLayout); - if (topLayout) { - generic = topLayout->currentActiveLayout(); + if (sharedLayout) { + generic = sharedLayout->currentActiveLayout(); } } if (!generic) { generic = m_layoutManager->currentLayout(); } views = generic->latteViews(); } QRegion available(screen->geometry()); for (const auto *view : views) { if (view && view->containment() && view->screen() == screen && view->visibility() && (view->visibility()->mode() != Latte::Types::AutoHide)) { int realThickness = view->normalThickness() - view->effects()->innerShadow(); // Usually availableScreenRect is used by the desktop, // but Latte don't have desktop, then here just // need calculate available space for top and bottom location, // because the left and right are those who dodge others views switch (view->location()) { case Plasma::Types::TopEdge: if (view->behaveAsPlasmaPanel()) { available -= view->geometry(); } else { QRect realGeometry; int realWidth = view->maxLength() * view->width(); switch (view->alignment()) { case Latte::Types::Left: realGeometry = QRect(view->x(), view->y(), realWidth, realThickness); break; case Latte::Types::Center: case Latte::Types::Justify: realGeometry = QRect(qMax(view->geometry().x(), view->geometry().center().x() - realWidth / 2), view->y(), realWidth, realThickness); break; case Latte::Types::Right: realGeometry = QRect(view->geometry().right() - realWidth + 1, view->y(), realWidth, realThickness); break; } available -= realGeometry; } break; case Plasma::Types::BottomEdge: if (view->behaveAsPlasmaPanel()) { available -= view->geometry(); } else { QRect realGeometry; int realWidth = view->maxLength() * view->width(); int realY = view->geometry().bottom() - realThickness + 1; switch (view->alignment()) { case Latte::Types::Left: realGeometry = QRect(view->x(), realY, realWidth, realThickness); break; case Latte::Types::Center: case Latte::Types::Justify: realGeometry = QRect(qMax(view->geometry().x(), view->geometry().center().x() - realWidth / 2), realY, realWidth, realThickness); break; case Latte::Types::Right: realGeometry = QRect(view->geometry().right() - realWidth + 1, realY, realWidth, realThickness); break; } available -= realGeometry; } break; default: //! bypass clang warnings break; } } } /*qDebug() << "::::: FREE AREAS :::::"; for (int i = 0; i < available.rectCount(); ++i) { qDebug() << available.rects().at(i); } qDebug() << "::::: END OF FREE AREAS :::::";*/ return available; } QRect Corona::availableScreenRect(int id) const { return availableScreenRectWithCriteria(id); } QRect Corona::availableScreenRectWithCriteria(int id, QList modes, QList edges) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; if (m_screenPool->knownIds().contains(id)) { QString scrName = m_screenPool->connector(id); for(const auto scr : screens) { if (scr->name() == scrName) { screen = scr; break; } } } if (!screen) return {}; bool allModes = modes.isEmpty(); bool allEdges = edges.isEmpty(); auto available = screen->geometry(); Latte::ActiveLayout *currentLayout = m_layoutManager->currentLayout(); QList views; if (currentLayout) { views = currentLayout->latteViews(); } for (const auto *view : views) { if (view && view->containment() && view->screen() == screen && ((allEdges || edges.contains(view->location())) && (allModes || (view->visibility() && modes.contains(view->visibility()->mode()))))) { // Usually availableScreenRect is used by the desktop, // but Latte don't have desktop, then here just // need calculate available space for top and bottom location, // because the left and right are those who dodge others docks switch (view->location()) { case Plasma::Types::TopEdge: available.setTop(view->y() + view->normalThickness()); break; case Plasma::Types::BottomEdge: available.setBottom(view->y() + view->height() - view->normalThickness()); break; case Plasma::Types::LeftEdge: available.setLeft(view->x() + view->normalThickness()); break; case Plasma::Types::RightEdge: available.setRight(view->x() + view->width() - view->normalThickness()); break; default: //! bypass clang warnings break; } } } return available; } void Corona::addOutput(QScreen *screen) { Q_ASSERT(screen); int id = m_screenPool->id(screen->name()); if (id == -1) { int newId = m_screenPool->firstAvailableId(); m_screenPool->insertScreenMapping(newId, screen->name()); } connect(screen, &QScreen::geometryChanged, this, [ = ]() { const int id = m_screenPool->id(screen->name()); if (id >= 0) { emit screenGeometryChanged(id); emit availableScreenRegionChanged(); emit availableScreenRectChanged(); } }); emit availableScreenRectChanged(); emit screenAdded(m_screenPool->id(screen->name())); } void Corona::primaryOutputChanged() { m_viewsScreenSyncTimer.start(); } void Corona::screenRemoved(QScreen *screen) { Q_ASSERT(screen); } void Corona::screenCountChanged() { m_viewsScreenSyncTimer.start(); } //! the central functions that updates loading/unloading latteviews //! concerning screen changed (for multi-screen setups mainly) void Corona::syncLatteViewsToScreens() { m_layoutManager->syncLatteViewsToScreens(); } int Corona::primaryScreenId() const { return m_screenPool->id(qGuiApp->primaryScreen()->name()); } void Corona::closeApplication() { //! this code must be called asynchronously because it is called //! also from qml (Settings window). QTimer::singleShot(5, [this]() { m_layoutManager->hideLatteSettingsDialog(); m_layoutManager->hideAllViews(); }); //! give the time for the views to hide themselves QTimer::singleShot(500, [this]() { qGuiApp->quit(); }); } void Corona::aboutApplication() { if (aboutDialog) { aboutDialog->hide(); aboutDialog->deleteLater(); } aboutDialog = new KAboutApplicationDialog(KAboutData::applicationData()); connect(aboutDialog.data(), &QDialog::finished, aboutDialog.data(), &QObject::deleteLater); m_wm->skipTaskBar(*aboutDialog); m_wm->setKeepAbove(*aboutDialog, true); aboutDialog->show(); } int Corona::screenForContainment(const Plasma::Containment *containment) const { //FIXME: indexOf is not a proper way to support multi-screen // as for environment to environment the indexes change // also there is the following issue triggered // from latteView adaptToScreen() // // in a multi-screen environment that // primary screen is not set to 0 it was // created an endless showing loop at // startup (catch-up race) between // screen:0 and primaryScreen //case in which this containment is child of an applet, hello systray :) if (Plasma::Applet *parentApplet = qobject_cast(containment->parent())) { if (Plasma::Containment *cont = parentApplet->containment()) { return screenForContainment(cont); } else { return -1; } } Latte::ActiveLayout *currentLayout = m_layoutManager->currentLayout(); Latte::View *view = currentLayout->viewForContainment(containment); if (view && view->screen()) { return m_screenPool->id(view->screen()->name()); } //Failed? fallback on lastScreen() //lastScreen() is the correct screen for panels //It is also correct for desktops *that have the correct activity()* //a containment with lastScreen() == 0 but another activity, //won't be associated to a screen // qDebug() << "ShellCorona screenForContainment: " << containment << " Last screen is " << containment->lastScreen(); for (auto screen : qGuiApp->screens()) { // containment->lastScreen() == m_screenPool->id(screen->name()) to check if the lastScreen refers to a screen that exists/it's known if (containment->lastScreen() == m_screenPool->id(screen->name()) && (containment->activity() == m_activityConsumer->currentActivity() || containment->containmentType() == Plasma::Types::PanelContainment || containment->containmentType() == Plasma::Types::CustomPanelContainment)) { return containment->lastScreen(); } } return -1; } void Corona::showAlternativesForApplet(Plasma::Applet *applet) { const QString alternativesQML = kPackage().filePath("appletalternativesui"); if (alternativesQML.isEmpty()) { return; } Latte::ActiveLayout *currentLayout = m_layoutManager->currentLayout(); Latte::View *latteView = currentLayout->viewForContainment(applet->containment()); KDeclarative::QmlObjectSharedEngine *qmlObj{nullptr}; if (latteView) { latteView->setAlternativesIsShown(true); qmlObj = new KDeclarative::QmlObjectSharedEngine(latteView); } else { qmlObj = new KDeclarative::QmlObjectSharedEngine(this); } qmlObj->setInitializationDelayed(true); qmlObj->setSource(QUrl::fromLocalFile(alternativesQML)); AlternativesHelper *helper = new AlternativesHelper(applet, qmlObj); qmlObj->rootContext()->setContextProperty(QStringLiteral("alternativesHelper"), helper); m_alternativesObjects << qmlObj; qmlObj->completeInitialization(); //! Alternative dialog signals connect(helper, &QObject::destroyed, this, [latteView]() { latteView->setAlternativesIsShown(false); }); connect(qmlObj->rootObject(), SIGNAL(visibleChanged(bool)), this, SLOT(alternativesVisibilityChanged(bool))); connect(applet, &Plasma::Applet::destroyedChanged, this, [this, qmlObj](bool destroyed) { if (!destroyed) { return; } QMutableListIterator it(m_alternativesObjects); while (it.hasNext()) { KDeclarative::QmlObjectSharedEngine *obj = it.next(); if (obj == qmlObj) { it.remove(); obj->deleteLater(); } } }); } void Corona::alternativesVisibilityChanged(bool visible) { if (visible) { return; } QObject *root = sender(); QMutableListIterator it(m_alternativesObjects); while (it.hasNext()) { KDeclarative::QmlObjectSharedEngine *obj = it.next(); if (obj->rootObject() == root) { it.remove(); obj->deleteLater(); } } } void Corona::addViewForLayout(QString layoutName) { qDebug() << "loading default layout"; //! Setting mutable for create a containment setImmutability(Plasma::Types::Mutable); QVariantList args; auto defaultContainment = createContainmentDelayed("org.kde.latte.containment", args); defaultContainment->setContainmentType(Plasma::Types::PanelContainment); defaultContainment->init(); if (!defaultContainment || !defaultContainment->kPackage().isValid()) { qWarning() << "the requested containment plugin can not be located or loaded"; return; } auto config = defaultContainment->config(); defaultContainment->restore(config); using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; Layout::GenericLayout *currentLayout = m_layoutManager->layout(layoutName); if (currentLayout) { edges = currentLayout->freeEdges(defaultContainment->screen()); } if ((edges.count() > 0)) { defaultContainment->setLocation(edges.at(0)); } else { defaultContainment->setLocation(Plasma::Types::BottomEdge); } if (m_layoutManager->memoryUsage() == Latte::Types::MultipleLayouts) { config.writeEntry("layoutId", layoutName); } defaultContainment->updateConstraints(Plasma::Types::StartupCompletedConstraint); defaultContainment->save(config); requestConfigSync(); defaultContainment->flushPendingConstraintsEvents(); emit containmentAdded(defaultContainment); emit containmentCreated(defaultContainment); defaultContainment->createApplet(QStringLiteral("org.kde.latte.plasmoid")); defaultContainment->createApplet(QStringLiteral("org.kde.plasma.analogclock")); } void Corona::loadDefaultLayout() { addViewForLayout(m_layoutManager->currentLayoutName()); } QStringList Corona::containmentsIds() { QStringList ids; for(const auto containment : containments()) { ids << QString::number(containment->id()); } return ids; } QStringList Corona::appletsIds() { QStringList ids; for(const auto containment : containments()) { auto applets = containment->config().group("Applets"); ids << applets.groupList(); } return ids; } //! Activate launcher menu through dbus interface void Corona::activateLauncherMenu() { m_globalShortcuts->activateLauncherMenu(); } void Corona::windowColorScheme(QString windowIdAndScheme) { int firstSlash = windowIdAndScheme.indexOf("-"); QString windowIdStr = windowIdAndScheme.mid(0, firstSlash); QString schemeStr = windowIdAndScheme.mid(firstSlash + 1); if (KWindowSystem::isPlatformWayland()) { QTimer::singleShot(200, [this, schemeStr]() { //! [Wayland Case] - give the time to be informed correctly for the active window id //! otherwise the active window id may not be the same with the one trigerred //! the color scheme dbus signal QString windowIdStr = m_wm->activeWindow().toString(); m_wm->setColorSchemeForWindow(windowIdStr.toUInt(), schemeStr); }); } else { m_wm->setColorSchemeForWindow(windowIdStr.toUInt(), schemeStr); } } //! update badge for specific view item void Corona::updateDockItemBadge(QString identifier, QString value) { m_globalShortcuts->updateViewItemBadge(identifier, value); } void Corona::switchToLayout(QString layout) { m_layoutManager->switchToLayout(layout); } void Corona::showSettingsWindow(int page) { Types::LatteConfigPage p = Types::LayoutPage; if (page >= Types::LayoutPage && page <= Types::PreferencesPage) { p = static_cast(page); } m_layoutManager->showLatteSettingsDialog(p); } void Corona::setContextMenuView(int id) { //! set context menu view id m_contextMenuViewId = id; } QStringList Corona::contextMenuData() { QStringList data; Types::ViewType viewType{Types::DockView}; Latte::ActiveLayout *currentLayout = m_layoutManager->currentLayout(); if (currentLayout) { viewType = currentLayout->latteViewType(m_contextMenuViewId); } data << QString::number((int)m_layoutManager->memoryUsage()); data << m_layoutManager->currentLayoutName(); data << QString::number((int)viewType); for(const auto &layoutName : m_layoutManager->menuLayouts()) { if (m_layoutManager->activeLayout(layoutName)) { data << QString("1," + layoutName); } else { data << QString("0," + layoutName); } } //! reset context menu view id m_contextMenuViewId = -1; return data; } void Corona::setBackgroundFromBroadcast(QString activity, QString screenName, QString filename) { if (filename.startsWith("file://")) { filename = filename.remove(0,7); } QMetaObject::invokeMethod(m_backgroundTracer->rootObject(), "setBackgroundFromBroadcast", Q_ARG(QVariant, activity), Q_ARG(QVariant, screenName), Q_ARG(QVariant, filename)); } void Corona::setBroadcastedBackgroundsEnabled(QString activity, QString screenName, bool enabled) { QMetaObject::invokeMethod(m_backgroundTracer->rootObject(), "setBroadcastedBackgroundsEnabled", Q_ARG(QVariant, activity), Q_ARG(QVariant, screenName), Q_ARG(QVariant, enabled)); } inline void Corona::qmlRegisterTypes() const { qmlRegisterType(); } } diff --git a/app/layout/CMakeLists.txt b/app/layout/CMakeLists.txt index 9efc4029..d7fdabca 100644 --- a/app/layout/CMakeLists.txt +++ b/app/layout/CMakeLists.txt @@ -1,9 +1,9 @@ set(lattedock-app_SRCS ${lattedock-app_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/abstractlayout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/activelayout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/genericlayout.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sharedlayout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/storage.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/toplayout.cpp PARENT_SCOPE ) diff --git a/app/layout/activelayout.cpp b/app/layout/activelayout.cpp index d2831c54..c775476c 100644 --- a/app/layout/activelayout.cpp +++ b/app/layout/activelayout.cpp @@ -1,461 +1,461 @@ /* * Copyright 2017 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "activelayout.h" // local -#include "toplayout.h" +#include "sharedlayout.h" #include "../lattecorona.h" #include "../layoutmanager.h" #include "../screenpool.h" #include "../settings/universalsettings.h" #include "../view/view.h" #include "../../liblatte2/types.h" // Qt #include // KDE #include #include namespace Latte { ActiveLayout::ActiveLayout(QObject *parent, QString layoutFile, QString assignedName) : Layout::GenericLayout(parent, layoutFile, assignedName) { if (m_loadedCorrectly) { loadConfig(); init(); } } ActiveLayout::~ActiveLayout() { if (!m_layoutFile.isEmpty()) { m_layoutGroup.sync(); } } void ActiveLayout::unloadContainments() { Layout::GenericLayout::unloadContainments(); - if (m_topLayout) { - disconnect(m_topLayout, &Layout::GenericLayout::viewsCountChanged, this, &Layout::GenericLayout::viewsCountChanged); - m_topLayout->removeActiveLayout(this); + if (m_sharedLayout) { + disconnect(m_sharedLayout, &Layout::GenericLayout::viewsCountChanged, this, &Layout::GenericLayout::viewsCountChanged); + m_sharedLayout->removeActiveLayout(this); } } void ActiveLayout::init() { connect(this, &ActiveLayout::activitiesChanged, this, &ActiveLayout::saveConfig); connect(this, &ActiveLayout::disableBordersForMaximizedWindowsChanged, this, &ActiveLayout::saveConfig); + connect(this, &ActiveLayout::sharedLayoutNameChanged, this, &ActiveLayout::saveConfig); connect(this, &ActiveLayout::showInMenuChanged, this, &ActiveLayout::saveConfig); - connect(this, &ActiveLayout::topLayoutNameChanged, this, &ActiveLayout::saveConfig); } void ActiveLayout::initToCorona(Latte::Corona *corona) { if (GenericLayout::initToCorona(corona)) { connect(m_corona->universalSettings(), &UniversalSettings::canDisableBordersChanged, this, [&]() { if (m_corona->universalSettings()->canDisableBorders()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } else { kwin_setDisabledMaximizedBorders(false); } }); if (m_corona->layoutManager()->memoryUsage() == Types::SingleLayout && m_corona->universalSettings()->canDisableBorders()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } else if (m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts) { connect(m_corona->layoutManager(), &LayoutManager::currentLayoutNameChanged, this, [&]() { if (m_corona->universalSettings()->canDisableBorders() && m_corona->layoutManager()->currentLayoutName() == name()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } }); } - //! Request the TopLayout in case there is one and Latte is functioning in MultipleLayouts mode - if (m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts && !m_topLayoutName.isEmpty()) { - if (m_corona->layoutManager()->assignActiveToTopLayout(this, m_topLayoutName)) { - setTopLayout(m_corona->layoutManager()->topLayout(m_topLayoutName)); + //! Request the SharedLayout in case there is one and Latte is functioning in MultipleLayouts mode + if (m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts && !m_sharedLayoutName.isEmpty()) { + if (m_corona->layoutManager()->assignActiveToSharedLayout(this, m_sharedLayoutName)) { + setSharedLayout(m_corona->layoutManager()->sharedLayout(m_sharedLayoutName)); } } } } bool ActiveLayout::disableBordersForMaximizedWindows() const { return m_disableBordersForMaximizedWindows; } void ActiveLayout::setDisableBordersForMaximizedWindows(bool disable) { if (m_disableBordersForMaximizedWindows == disable) { return; } m_disableBordersForMaximizedWindows = disable; kwin_setDisabledMaximizedBorders(disable); emit disableBordersForMaximizedWindowsChanged(); } bool ActiveLayout::kwin_disabledMaximizedBorders() const { //! Identify Plasma Desktop version QProcess process; process.start("kreadconfig5 --file kwinrc --group Windows --key BorderlessMaximizedWindows"); process.waitForFinished(); QString output(process.readAllStandardOutput()); output = output.remove("\n"); return (output == "true"); } void ActiveLayout::kwin_setDisabledMaximizedBorders(bool disable) { if (kwin_disabledMaximizedBorders() == disable) { return; } QString disableText = disable ? "true" : "false"; QProcess process; QString commandStr = "kwriteconfig5 --file kwinrc --group Windows --key BorderlessMaximizedWindows --type bool " + disableText; process.start(commandStr); process.waitForFinished(); QDBusInterface iface("org.kde.KWin", "/KWin", "", QDBusConnection::sessionBus()); if (iface.isValid()) { iface.call("reconfigure"); } } bool ActiveLayout::showInMenu() const { return m_showInMenu; } void ActiveLayout::setShowInMenu(bool show) { if (m_showInMenu == show) { return; } m_showInMenu = show; emit showInMenuChanged(); } QStringList ActiveLayout::activities() const { return m_activities; } void ActiveLayout::setActivities(QStringList activities) { if (m_activities == activities) { return; } m_activities = activities; emit activitiesChanged(); } -QString ActiveLayout::topLayoutName() const +QString ActiveLayout::sharedLayoutName() const { - return m_topLayoutName; + return m_sharedLayoutName; } -void ActiveLayout::setTopLayoutName(QString name) +void ActiveLayout::setSharedLayoutName(QString name) { - if (m_topLayoutName == name) { + if (m_sharedLayoutName == name) { return; } - m_topLayoutName = name; - emit topLayoutNameChanged(); + m_sharedLayoutName = name; + emit sharedLayoutNameChanged(); } -void ActiveLayout::setTopLayout(TopLayout *layout) +void ActiveLayout::setSharedLayout(SharedLayout *layout) { - if (m_topLayout == layout) { + if (m_sharedLayout == layout) { return; } - disconnect(m_topLayout, &Layout::GenericLayout::viewsCountChanged, this, &Layout::GenericLayout::viewsCountChanged); + disconnect(m_sharedLayout, &Layout::GenericLayout::viewsCountChanged, this, &Layout::GenericLayout::viewsCountChanged); - m_topLayout = layout; + m_sharedLayout = layout; - connect(m_topLayout, &Layout::GenericLayout::viewsCountChanged, this, &Layout::GenericLayout::viewsCountChanged); + connect(m_sharedLayout, &Layout::GenericLayout::viewsCountChanged, this, &Layout::GenericLayout::viewsCountChanged); emit viewsCountChanged(); } bool ActiveLayout::isActiveLayout() const { if (!m_corona) { return false; } ActiveLayout *activeLayout = m_corona->layoutManager()->activeLayout(m_layoutName); if (activeLayout) { return true; } else { return false; } } bool ActiveLayout::isOriginalLayout() const { return m_layoutName != MultipleLayoutsName; } void ActiveLayout::loadConfig() { m_disableBordersForMaximizedWindows = m_layoutGroup.readEntry("disableBordersForMaximizedWindows", false); m_showInMenu = m_layoutGroup.readEntry("showInMenu", false); - m_topLayoutName = m_layoutGroup.readEntry("topLayoutName", QString()); + m_sharedLayoutName = m_layoutGroup.readEntry("sharedLayout", QString()); m_activities = m_layoutGroup.readEntry("activities", QStringList()); emit activitiesChanged(); } void ActiveLayout::saveConfig() { qDebug() << "active layout is saving... for layout:" << m_layoutName; m_layoutGroup.writeEntry("showInMenu", m_showInMenu); m_layoutGroup.writeEntry("disableBordersForMaximizedWindows", m_disableBordersForMaximizedWindows); - m_layoutGroup.writeEntry("topLayoutName", m_topLayoutName); + m_layoutGroup.writeEntry("sharedLayout", m_sharedLayoutName); m_layoutGroup.writeEntry("activities", m_activities); m_layoutGroup.sync(); } //! OVERRIDES void ActiveLayout::addView(Plasma::Containment *containment, bool forceOnPrimary, int explicitScreen, Layout::ViewsMap *occupied) { - if (m_topLayout) { - //! consider already occupied edges from TopLayout - Layout::ViewsMap ocMap = m_topLayout->validViewsMap(); + if (m_sharedLayout) { + //! consider already occupied edges from SharedLayout + Layout::ViewsMap ocMap = m_sharedLayout->validViewsMap(); Layout::GenericLayout::addView(containment, forceOnPrimary, explicitScreen, &ocMap); } else { Layout::GenericLayout::addView(containment, forceOnPrimary, explicitScreen, occupied); } } const QStringList ActiveLayout::appliedActivities() { if (!m_corona) { return {}; } if (m_corona->layoutManager()->memoryUsage() == Types::SingleLayout) { return {"0"}; } else if (m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts) { if (m_activities.isEmpty()) { return m_corona->layoutManager()->orphanedActivities(); } else { return m_activities; } } else { return {"0"}; } } QList ActiveLayout::latteViews() { - if (m_topLayout) { + if (m_sharedLayout) { QList views = Layout::GenericLayout::latteViews(); - views << m_topLayout->latteViews(); + views << m_sharedLayout->latteViews(); return views; } return Layout::GenericLayout::latteViews(); } int ActiveLayout::viewsCount(int screen) const { if (!m_corona) { return 0; } int views = Layout::GenericLayout::viewsCount(screen); - if (m_topLayout) { + if (m_sharedLayout) { QScreen *scr = m_corona->screenPool()->screenForId(screen); - for (const auto view : m_topLayout->latteViews()) { + for (const auto view : m_sharedLayout->latteViews()) { if (view && view->screen() == scr && !view->containment()->destroyed()) { ++views; } } } return views; } int ActiveLayout::viewsCount(QScreen *screen) const { if (!m_corona) { return 0; } int views = Layout::GenericLayout::viewsCount(screen); - if (m_topLayout) { - for (const auto view : m_topLayout->latteViews()) { + if (m_sharedLayout) { + for (const auto view : m_sharedLayout->latteViews()) { if (view && view->screen() == screen && !view->containment()->destroyed()) { ++views; } } } return views; } int ActiveLayout::viewsCount() const { if (!m_corona) { return 0; } int views = Layout::GenericLayout::viewsCount(); - if (m_topLayout) { - for (const auto view : m_topLayout->latteViews()) { + if (m_sharedLayout) { + for (const auto view : m_sharedLayout->latteViews()) { if (view && view->containment() && !view->containment()->destroyed()) { ++views; } } } return views; } QList ActiveLayout::availableEdgesForView(QScreen *scr, Latte::View *forView) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } edges = Layout::GenericLayout::availableEdgesForView(scr, forView); - if (m_topLayout) { - for (const auto view : m_topLayout->latteViews()) { + if (m_sharedLayout) { + for (const auto view : m_sharedLayout->latteViews()) { //! make sure that availabe edges takes into account only views that should be excluded, //! this is why the forView should not be excluded if (view && view != forView && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } } return edges; } QList ActiveLayout::freeEdges(QScreen *scr) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } edges = Layout::GenericLayout::freeEdges(scr); - if (m_topLayout) { - for (const auto view : m_topLayout->latteViews()) { + if (m_sharedLayout) { + for (const auto view : m_sharedLayout->latteViews()) { if (view && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } } return edges; } QList ActiveLayout::freeEdges(int screen) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } edges = Layout::GenericLayout::freeEdges(screen); QScreen *scr = m_corona->screenPool()->screenForId(screen); - if (m_topLayout) { - for (const auto view : m_topLayout->latteViews()) { + if (m_sharedLayout) { + for (const auto view : m_sharedLayout->latteViews()) { if (view && scr && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } } return edges; } QList ActiveLayout::sortedLatteViews(QList views) { QList vws = latteViews(); return Layout::GenericLayout::sortedLatteViews(vws); } QList ActiveLayout::viewsWithPlasmaShortcuts() { QList combined = Layout::GenericLayout::viewsWithPlasmaShortcuts(); - if (m_topLayout) { - combined << m_topLayout->viewsWithPlasmaShortcuts(); + if (m_sharedLayout) { + combined << m_sharedLayout->viewsWithPlasmaShortcuts(); } return combined; } void ActiveLayout::syncLatteViewsToScreens(Layout::ViewsMap *occupiedMap) { - if (m_topLayout) { - Layout::ViewsMap map = m_topLayout->validViewsMap(); + if (m_sharedLayout) { + Layout::ViewsMap map = m_sharedLayout->validViewsMap(); Layout::GenericLayout::syncLatteViewsToScreens(&map); } else { Layout::GenericLayout::syncLatteViewsToScreens(); } } } diff --git a/app/layout/activelayout.h b/app/layout/activelayout.h index 609d84e1..6f4a0239 100644 --- a/app/layout/activelayout.h +++ b/app/layout/activelayout.h @@ -1,117 +1,117 @@ /* * Copyright 2017 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ACTIVELAYOUT_H #define ACTIVELAYOUT_H // local #include "genericlayout.h" // Qt #include namespace Latte { class Corona; -class TopLayout; +class SharedLayout; } namespace Latte { //! This class is responsible to hold the settings for a specific layout. //! It also updates always the relevant layout configuration concerning //! its general settings (no the containments) class ActiveLayout : public Layout::GenericLayout { Q_OBJECT public: ActiveLayout(QObject *parent, QString layoutFile, QString layoutName = QString()); ~ActiveLayout() override; void initToCorona(Latte::Corona *corona); bool disableBordersForMaximizedWindows() const; void setDisableBordersForMaximizedWindows(bool disable); bool showInMenu() const; void setShowInMenu(bool show); //!this layout is loaded and running bool isActiveLayout() const; //!it is original layout compared to pseudo-layouts that are combinations of multiple-original layouts bool isOriginalLayout() const; - QString topLayoutName() const; - void setTopLayoutName(QString name); + QString sharedLayoutName() const; + void setSharedLayoutName(QString name); QStringList activities() const; void setActivities(QStringList activities); //! OVERRIDE GeneralLayout implementations void addView(Plasma::Containment *containment, bool forceOnPrimary = false, int explicitScreen = -1, Layout::ViewsMap *occupied = nullptr); void syncLatteViewsToScreens(Layout::ViewsMap *occupiedMap = nullptr) override; void unloadContainments() override; const QStringList appliedActivities() override; QList latteViews() override; int viewsCount(int screen) const override; int viewsCount(QScreen *screen) const override; int viewsCount() const override; //! Available edges for specific view in that screen QList availableEdgesForView(QScreen *scr, Latte::View *forView) const override; //! All free edges in that screen QList freeEdges(QScreen *scr) const override; QList freeEdges(int screen) const override; QList sortedLatteViews(QList views = QList()) override; QList viewsWithPlasmaShortcuts() override; signals: void activitiesChanged(); void disableBordersForMaximizedWindowsChanged(); void showInMenuChanged(); - void topLayoutNameChanged(); + void sharedLayoutNameChanged(); private slots: void loadConfig(); void saveConfig(); - void setTopLayout(TopLayout *layout); + void setSharedLayout(SharedLayout *layout); private: void init(); void importLocalLayout(QString file); bool kwin_disabledMaximizedBorders() const; void kwin_setDisabledMaximizedBorders(bool disable); private: bool m_disableBordersForMaximizedWindows{false}; bool m_showInMenu{false}; - QString m_topLayoutName; + QString m_sharedLayoutName; QStringList m_activities; - QPointer m_topLayout; + QPointer m_sharedLayout; }; } #endif //ACTIVELAYOUT_H diff --git a/app/layout/toplayout.cpp b/app/layout/sharedlayout.cpp similarity index 80% rename from app/layout/toplayout.cpp rename to app/layout/sharedlayout.cpp index 1e7773cc..b647a6cb 100644 --- a/app/layout/toplayout.cpp +++ b/app/layout/sharedlayout.cpp @@ -1,239 +1,239 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "toplayout.h" +#include "sharedlayout.h" // local #include "activelayout.h" #include "../lattecorona.h" #include "../layoutmanager.h" #include "../screenpool.h" #include "../view/view.h" namespace Latte { -TopLayout::TopLayout(ActiveLayout *assigned, QObject *parent, QString layoutFile, QString layoutName) +SharedLayout::SharedLayout(ActiveLayout *assigned, QObject *parent, QString layoutFile, QString layoutName) : Layout::GenericLayout (parent, layoutFile, layoutName) { initToCorona(assigned->corona()); - connect(m_corona->layoutManager(), &LayoutManager::currentLayoutNameChanged, this, &TopLayout::updateLastUsedActiveLayout); + connect(m_corona->layoutManager(), &LayoutManager::currentLayoutNameChanged, this, &SharedLayout::updateLastUsedActiveLayout); addActiveLayout(assigned); updateLastUsedActiveLayout(); } -TopLayout::~TopLayout() +SharedLayout::~SharedLayout() { } -bool TopLayout::isCurrent() const +bool SharedLayout::isCurrent() const { for (const auto &layout : m_activeLayouts) { if (layout->isCurrent()) { return true; } } return false; } -const QStringList TopLayout::appliedActivities() +const QStringList SharedLayout::appliedActivities() { if (!m_corona) { return {}; } QStringList activities; for (const auto &layout : m_activeLayouts) { activities << layout->appliedActivities(); } return activities; } -void TopLayout::updateLastUsedActiveLayout() +void SharedLayout::updateLastUsedActiveLayout() { for (const auto &layout : m_activeLayouts) { if (layout->isCurrent()) { m_lastUsedActiveLayout = layout->name(); break; } } } -ActiveLayout *TopLayout::currentActiveLayout() const +ActiveLayout *SharedLayout::currentActiveLayout() const { //! first the current active one for (const auto &layout : m_activeLayouts) { if (layout->isCurrent()) { return layout; } } //! the last used for (const auto &layout : m_activeLayouts) { if (layout->name() == m_lastUsedActiveLayout) { return layout; } } return nullptr; } -void TopLayout::addActiveLayout(ActiveLayout *layout) +void SharedLayout::addActiveLayout(ActiveLayout *layout) { if (layout != nullptr && !m_activeLayouts.contains(layout)) { m_activeLayouts.append(layout); connect(layout, &GenericLayout::activitiesChanged, this, &GenericLayout::activitiesChanged); emit activitiesChanged(); emit viewsCountChanged(); updateLastUsedActivity(); } } -void TopLayout::removeActiveLayout(ActiveLayout *layout) +void SharedLayout::removeActiveLayout(ActiveLayout *layout) { if (m_activeLayouts.contains(layout)) { - qDebug() << "TOPLAYOUT <" << name() << "> : Removing active layout, " << layout->name(); + qDebug() << "SHAREDLAYOUT <" << name() << "> : Removing active layout, " << layout->name(); m_activeLayouts.removeAll(layout); disconnect(layout, &GenericLayout::activitiesChanged, this, &GenericLayout::activitiesChanged); emit activitiesChanged(); //! viewsCount signal is not needed to be trigerred here because //! in such case the views number has not been changed for the rest //! active layouts } } //! OVERRIDE -int TopLayout::viewsCount(int screen) const +int SharedLayout::viewsCount(int screen) const { if (!m_corona) { return 0; } ActiveLayout *current = currentActiveLayout(); if (current) { return current->viewsCount(screen); } return Layout::GenericLayout::viewsCount(screen); } -int TopLayout::viewsCount(QScreen *screen) const +int SharedLayout::viewsCount(QScreen *screen) const { if (!m_corona) { return 0; } ActiveLayout *current = currentActiveLayout(); if (current) { return current->viewsCount(screen); } return Layout::GenericLayout::viewsCount(screen);; } -int TopLayout::viewsCount() const +int SharedLayout::viewsCount() const { if (!m_corona) { return 0; } ActiveLayout *current = currentActiveLayout(); if (current) { return current->viewsCount(); } return Layout::GenericLayout::viewsCount(); } -QList TopLayout::availableEdgesForView(QScreen *scr, Latte::View *forView) const +QList SharedLayout::availableEdgesForView(QScreen *scr, Latte::View *forView) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } return Layout::GenericLayout::availableEdgesForView(scr, forView); } -QList TopLayout::freeEdges(QScreen *scr) const +QList SharedLayout::freeEdges(QScreen *scr) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } ActiveLayout *current = currentActiveLayout(); if (current) { return current->freeEdges(scr); } return Layout::GenericLayout::freeEdges(scr); } -QList TopLayout::freeEdges(int screen) const +QList SharedLayout::freeEdges(int screen) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } ActiveLayout *current = currentActiveLayout(); if (current) { return current->freeEdges(screen); } return Layout::GenericLayout::freeEdges(screen); } -QList TopLayout::sortedLatteViews(QList views) +QList SharedLayout::sortedLatteViews(QList views) { ActiveLayout *current = currentActiveLayout(); if (current) { return current->sortedLatteViews(); } return Layout::GenericLayout::sortedLatteViews(); } } diff --git a/app/layout/toplayout.h b/app/layout/sharedlayout.h similarity index 86% rename from app/layout/toplayout.h rename to app/layout/sharedlayout.h index 7c8495e2..e48cdb12 100644 --- a/app/layout/toplayout.h +++ b/app/layout/sharedlayout.h @@ -1,83 +1,83 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef TOPLAYOUT_H -#define TOPLAYOUT_H +#ifndef SHAREDLAYOUT_H +#define SHAREDLAYOUT_H // local #include "genericlayout.h" // Qt #include namespace Latte { class ActiveLayout; } namespace Latte { -//! TopLayout is a layout that exists only as long as it belongs to one or +//! SharedLayout is a layout that exists only as long as it belongs to one or //! more ActiveLayout(s). It is a layer above an active or more layouts and can //! be used from ActiveLayouts to share Latte:View(s) . Much of its functionality //! is provided by the ActiveLayouts it belongs to. For example the activities //! that its views should be shown is identified only from the active layouts //! it belongs to -class TopLayout : public Layout::GenericLayout +class SharedLayout : public Layout::GenericLayout { public: - TopLayout(ActiveLayout *assigned, QObject *parent, QString layoutFile, QString layoutName = QString()); - ~TopLayout() override; + SharedLayout(ActiveLayout *assigned, QObject *parent, QString layoutFile, QString layoutName = QString()); + ~SharedLayout() override; const QStringList appliedActivities(); ActiveLayout *currentActiveLayout() const; //! OVERRIDE GeneralLayout implementations bool isCurrent() const override; int viewsCount(int screen) const override; int viewsCount(QScreen *screen) const override; int viewsCount() const override; //! Available edges for specific view in that screen QList availableEdgesForView(QScreen *scr, Latte::View *forView) const override; //! All free edges in that screen QList freeEdges(QScreen *scr) const override; QList freeEdges(int screen) const override; QList sortedLatteViews(QList views = QList()) override; public slots: void addActiveLayout(ActiveLayout *layout); void removeActiveLayout(ActiveLayout *layout); private slots: void updateLastUsedActiveLayout(); private: QString m_lastUsedActiveLayout; QList m_activeLayouts; }; } -#endif //TOPLAYOUT_H +#endif //SHAREDLAYOUT_H diff --git a/app/layoutmanager.cpp b/app/layoutmanager.cpp index 9497935e..e5205fcf 100644 --- a/app/layoutmanager.cpp +++ b/app/layoutmanager.cpp @@ -1,1234 +1,1234 @@ /* * Copyright 2017 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "layoutmanager.h" // local #include "importer.h" #include "infoview.h" #include "launcherssignals.h" #include "screenpool.h" #include "layout/abstractlayout.h" #include "layout/activelayout.h" #include "layout/genericlayout.h" -#include "layout/toplayout.h" +#include "layout/sharedlayout.h" #include "settings/settingsdialog.h" #include "settings/universalsettings.h" #include "view/view.h" // Qt #include #include #include #include #include // KDE #include #include #include #include namespace Latte { const int MultipleLayoutsPresetId = 10; LayoutManager::LayoutManager(QObject *parent) : QObject(parent), m_importer(new Importer(this)), m_launchersSignals(new LaunchersSignals(this)), m_activitiesController(new KActivities::Controller(this)) { m_corona = qobject_cast(parent); if (m_corona) { connect(m_corona->universalSettings(), &UniversalSettings::currentLayoutNameChanged, this, &LayoutManager::currentLayoutNameChanged); connect(m_corona->universalSettings(), &UniversalSettings::showInfoWindowChanged, this, &LayoutManager::showInfoWindowChanged); m_dynamicSwitchTimer.setSingleShot(true); showInfoWindowChanged(); connect(&m_dynamicSwitchTimer, &QTimer::timeout, this, &LayoutManager::confirmDynamicSwitch); } } LayoutManager::~LayoutManager() { m_importer->deleteLater(); m_launchersSignals->deleteLater(); unload(); m_activitiesController->deleteLater(); } void LayoutManager::load() { int configVer = m_corona->universalSettings()->version(); qDebug() << "Universal Settings version : " << configVer; if (configVer < 2 && QFile(QDir::homePath() + "/.config/lattedockrc").exists()) { qDebug() << "Latte must update its configuration..."; m_importer->updateOldConfiguration(); importPresets(false); } else if (!QFile(QDir::homePath() + "/.config/lattedockrc").exists()) { //startup create what is necessary.... QDir layoutDir(QDir::homePath() + "/.config/latte"); if (!layoutDir.exists()) { QDir(QDir::homePath() + "/.config").mkdir("latte"); } newLayout(i18n("My Layout")); importPresets(false); m_corona->universalSettings()->setCurrentLayoutName(i18n("My Layout")); m_corona->universalSettings()->setVersion(2); } //! Check if the multiple-layouts hidden file is present, add it if it isnt if (!QFile(QDir::homePath() + "/.config/latte/" + Layout::AbstractLayout::MultipleLayoutsName + ".layout.latte").exists()) { importPreset(MultipleLayoutsPresetId, false); } qDebug() << "Latte is loading its layouts..."; connect(m_corona->m_activityConsumer, &KActivities::Consumer::currentActivityChanged, this, &LayoutManager::currentActivityChanged); connect(m_corona->m_activityConsumer, &KActivities::Consumer::runningActivitiesChanged, this, [&]() { if (memoryUsage() == Types::MultipleLayouts) { syncMultipleLayoutsToActivities(); } }); loadLayouts(); } void LayoutManager::unload() { bool multipleMode{activeLayout(Layout::AbstractLayout::MultipleLayoutsName)}; //! Unload all ActiveLayouts while (!m_activeLayouts.isEmpty()) { ActiveLayout *layout = m_activeLayouts.at(0); m_activeLayouts.removeFirst(); if (layout->isOriginalLayout() && multipleMode) { layout->syncToLayoutFile(true); } layout->unloadContainments(); layout->unloadLatteViews(); if (layout->isOriginalLayout() && multipleMode) { clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds(), true); } delete layout; } - //! Unload all TopLayouts - while (!m_topLayouts.isEmpty()) { - TopLayout *layout = m_topLayouts.at(0); - m_topLayouts.removeFirst(); + //! Unload all SharedLayouts + while (!m_sharedLayouts.isEmpty()) { + SharedLayout *layout = m_sharedLayouts.at(0); + m_sharedLayouts.removeFirst(); if (multipleMode) { layout->syncToLayoutFile(true); } layout->unloadContainments(); layout->unloadLatteViews(); if (multipleMode) { clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds(), true); } delete layout; } //! Remove no-needed temp files QString temp1File = QDir::homePath() + "/.config/lattedock.copy1.bak"; QString temp2File = QDir::homePath() + "/.config/lattedock.copy2.bak"; QFile file1(temp1File); QFile file2(temp2File); if (file1.exists()) file1.remove(); if (file2.exists()) file2.remove(); } Latte::Corona *LayoutManager::corona() { return m_corona; } Importer *LayoutManager::importer() { return m_importer; } LaunchersSignals *LayoutManager::launchersSignals() { return m_launchersSignals; } QString LayoutManager::currentLayoutName() const { if (memoryUsage() == Types::SingleLayout) { return m_corona->universalSettings()->currentLayoutName(); } else if (memoryUsage() == Types::MultipleLayouts) { return m_currentLayoutNameInMultiEnvironment; } return QString(); } QString LayoutManager::defaultLayoutName() const { QByteArray presetNameOrig = QString("preset" + QString::number(1)).toUtf8(); QString presetPath = m_corona->kPackage().filePath(presetNameOrig); QString presetName = ActiveLayout::layoutName(presetPath); QByteArray presetNameChars = presetName.toUtf8(); presetName = i18n(presetNameChars); return presetName; } bool LayoutManager::layoutExists(QString layoutName) const { return m_layouts.contains(layoutName); } QStringList LayoutManager::layouts() const { return m_layouts; } QStringList LayoutManager::menuLayouts() const { QStringList fixedMenuLayouts = m_menuLayouts; //! in case the current layout isnt checked to be shown in the menus //! we must add it on top if (!fixedMenuLayouts.contains(currentLayoutName()) && memoryUsage() == Types::SingleLayout) { fixedMenuLayouts.prepend(currentLayoutName()); } else if (memoryUsage() == Types::MultipleLayouts) { for (const auto layout : m_activeLayouts) { if (layout->isOriginalLayout() && !fixedMenuLayouts.contains(layout->name())) { fixedMenuLayouts.prepend(layout->name()); } } } return fixedMenuLayouts; } void LayoutManager::setMenuLayouts(QStringList layouts) { if (m_menuLayouts == layouts) { return; } m_menuLayouts = layouts; emit menuLayoutsChanged(); } QStringList LayoutManager::activities() { return m_corona->m_activityConsumer->activities(); } QStringList LayoutManager::runningActivities() { return m_corona->m_activityConsumer->runningActivities(); } QStringList LayoutManager::orphanedActivities() { QStringList orphans; for (const auto &activity : activities()) { if (m_assignedLayouts[activity].isEmpty()) { orphans.append(activity); } } return orphans; } QStringList LayoutManager::presetsPaths() const { return m_presetsPaths; } QString LayoutManager::layoutPath(QString layoutName) { QString path = QDir::homePath() + "/.config/latte/" + layoutName + ".layout.latte"; if (!QFile(path).exists()) { path = ""; } return path; } Types::LayoutsMemoryUsage LayoutManager::memoryUsage() const { return m_corona->universalSettings()->layoutsMemoryUsage(); } int LayoutManager::layoutsMemoryUsage() { return (int)m_corona->universalSettings()->layoutsMemoryUsage(); } void LayoutManager::setMemoryUsage(Types::LayoutsMemoryUsage memoryUsage) { m_corona->universalSettings()->setLayoutsMemoryUsage(memoryUsage); } bool LayoutManager::latteViewExists(Latte::View *view) const { for (const auto layout : m_activeLayouts) { for (const auto &v : layout->latteViews()) { if (v == view) { return true; } } } return false; } QStringList LayoutManager::activeLayoutsNames() { QStringList names; if (memoryUsage() == Types::SingleLayout) { names << currentLayoutName(); } else { for (int i = 0; i < m_activeLayouts.size(); ++i) { ActiveLayout *layout = m_activeLayouts.at(i); if (layout->isOriginalLayout()) { names << layout->name(); } } } return names; } Layout::GenericLayout *LayoutManager::layout(QString id) const { Layout::GenericLayout *l = activeLayout(id); if (!l) { - l = topLayout(id); + l = sharedLayout(id); } return l; } ActiveLayout *LayoutManager::activeLayout(QString id) const { for (int i = 0; i < m_activeLayouts.size(); ++i) { ActiveLayout *layout = m_activeLayouts.at(i); if (layout->name() == id) { return layout; } } return nullptr; } int LayoutManager::activeLayoutPos(QString id) const { for (int i = 0; i < m_activeLayouts.size(); ++i) { ActiveLayout *layout = m_activeLayouts.at(i); if (layout->name() == id) { return i; } } return -1; } -TopLayout *LayoutManager::topLayout(QString id) const +SharedLayout *LayoutManager::sharedLayout(QString id) const { - for (int i = 0; i < m_topLayouts.size(); ++i) { - TopLayout *layout = m_topLayouts.at(i); + for (int i = 0; i < m_sharedLayouts.size(); ++i) { + SharedLayout *layout = m_sharedLayouts.at(i); if (layout->name() == id) { return layout; } } return nullptr; } -bool LayoutManager::assignActiveToTopLayout(ActiveLayout *active, QString id) +bool LayoutManager::assignActiveToSharedLayout(ActiveLayout *active, QString id) { if (memoryUsage() == Types::SingleLayout) { return false; } - for (int i = 0; i < m_topLayouts.size(); ++i) { - TopLayout *layout = m_topLayouts.at(i); + for (int i = 0; i < m_sharedLayouts.size(); ++i) { + SharedLayout *layout = m_sharedLayouts.at(i); if (layout->name() == id) { layout->addActiveLayout(active); // syncLatteViewsToScreens(); return true; } } - //! If TopLayout was not found, we must create it - TopLayout *top = new TopLayout(active, this, Importer::layoutFilePath(id)); - m_topLayouts.append(top); + //! If SharedLayout was not found, we must create it + SharedLayout *top = new SharedLayout(active, this, Importer::layoutFilePath(id)); + m_sharedLayouts.append(top); top->importToCorona(); // syncLatteViewsToScreens(); return true; } ActiveLayout *LayoutManager::currentLayout() const { if (memoryUsage() == Types::SingleLayout) { return m_activeLayouts.at(0); } else { for (auto layout : m_activeLayouts) { if (layout->activities().contains(m_corona->m_activityConsumer->currentActivity())) { return layout; } } for (auto layout : m_activeLayouts) { if ((layout->name() != Layout::AbstractLayout::MultipleLayoutsName) && (layout->activities().isEmpty())) { return layout; } } } return nullptr; } void LayoutManager::updateCurrentLayoutNameInMultiEnvironment() { for (const auto layout : m_activeLayouts) { if (layout->isOriginalLayout() && layout->activities().contains(m_corona->activitiesConsumer()->currentActivity())) { m_currentLayoutNameInMultiEnvironment = layout->name(); emit currentLayoutNameChanged(); return; } } for (const auto layout : m_activeLayouts) { if (layout->isOriginalLayout() && layout->activities().isEmpty()) { m_currentLayoutNameInMultiEnvironment = layout->name(); emit currentLayoutNameChanged(); return; } } } void LayoutManager::currentActivityChanged(const QString &id) { if (memoryUsage() == Types::SingleLayout) { qDebug() << "activity changed :: " << id; m_shouldSwitchToLayout = shouldSwitchToLayout(id); m_dynamicSwitchTimer.start(); } else if (memoryUsage() == Types::MultipleLayouts) { updateCurrentLayoutNameInMultiEnvironment(); } } void LayoutManager::showInfoWindowChanged() { if (m_corona->universalSettings()->showInfoWindow()) { m_dynamicSwitchTimer.setInterval(1800); } else { m_dynamicSwitchTimer.setInterval(2300); } } QString LayoutManager::shouldSwitchToLayout(QString activityId) { if (m_assignedLayouts.contains(activityId) && m_assignedLayouts[activityId] != currentLayoutName()) { return m_assignedLayouts[activityId]; } else if (!m_assignedLayouts.contains(activityId) && !m_corona->universalSettings()->lastNonAssignedLayoutName().isEmpty() && m_corona->universalSettings()->lastNonAssignedLayoutName() != currentLayoutName()) { return m_corona->universalSettings()->lastNonAssignedLayoutName(); } return QString(); } void LayoutManager::confirmDynamicSwitch() { QString tempShouldSwitch = shouldSwitchToLayout(m_corona->m_activityConsumer->currentActivity()); if (tempShouldSwitch.isEmpty()) { return; } if (m_shouldSwitchToLayout == tempShouldSwitch && m_shouldSwitchToLayout != currentLayoutName()) { qDebug() << "dynamic switch to layout :: " << m_shouldSwitchToLayout; emit currentLayoutIsSwitching(currentLayoutName()); if (m_corona->universalSettings()->showInfoWindow()) { showInfoWindow(i18n("Switching to layout %0 ...").arg(m_shouldSwitchToLayout), 4000); } QTimer::singleShot(500, [this, tempShouldSwitch]() { switchToLayout(tempShouldSwitch); }); } else { m_shouldSwitchToLayout = tempShouldSwitch; m_dynamicSwitchTimer.start(); } } void LayoutManager::loadLayouts() { m_layouts.clear(); m_menuLayouts.clear(); m_presetsPaths.clear(); m_assignedLayouts.clear(); QDir layoutDir(QDir::homePath() + "/.config/latte"); QStringList filter; filter.append(QString("*.layout.latte")); QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); for (const auto &layout : files) { ActiveLayout layoutSets(this, layoutDir.absolutePath() + "/" + layout); QStringList validActivityIds = validActivities(layoutSets.activities()); layoutSets.setActivities(validActivityIds); for (const auto &activity : validActivityIds) { m_assignedLayouts[activity] = layoutSets.name(); } m_layouts.append(layoutSets.name()); if (layoutSets.showInMenu()) { m_menuLayouts.append(layoutSets.name()); } } m_presetsPaths.append(m_corona->kPackage().filePath("preset1")); m_presetsPaths.append(m_corona->kPackage().filePath("preset2")); m_presetsPaths.append(m_corona->kPackage().filePath("preset3")); m_presetsPaths.append(m_corona->kPackage().filePath("preset4")); emit layoutsChanged(); emit menuLayoutsChanged(); } void LayoutManager::loadLayoutOnStartup(QString layoutName) { // if (memoryUsage() == Types::MultipleLayouts) { QStringList layouts = m_importer->checkRepairMultipleLayoutsLinkedFile(); //! Latte didn't close correctly, maybe a crash if (layouts.size() > 0) { QMessageBox *msg = new QMessageBox(); msg->setAttribute(Qt::WA_DeleteOnClose); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Multiple Layouts Warning")); msg->setText(i18n("Latte did not close properly in the previous session. The following layout(s) [%0] were updated for consistency!!!").arg(layouts.join(","))); msg->setStandardButtons(QMessageBox::Ok); msg->open(); } //} switchToLayout(layoutName); } void LayoutManager::loadLatteLayout(QString layoutPath) { qDebug() << " -------------------------------------------------------------------- "; qDebug() << " -------------------------------------------------------------------- "; if (m_corona->containments().size() > 0) { qDebug() << "LOAD LATTE LAYOUT ::: There are still containments present !!!! :: " << m_corona->containments().size(); } if (!layoutPath.isEmpty() && m_corona->containments().size() == 0) { cleanupOnStartup(layoutPath); qDebug() << "LOADING CORONA LAYOUT:" << layoutPath; m_corona->loadLayout(layoutPath); //! ~~~ ADDING LATTEVIEWS AND ENFORCE LOADING IF TASKS ARENT PRESENT BASED ON SCREENS ~~~ !// //! this is used to record the first dock having tasks in it. It is used //! to specify which dock will be loaded on startup if a case that no "dock //! with tasks" will be loaded otherwise. Currently the older one dock wins /*int firstContainmentWithTasks = -1; //! this is used to check if a dock with tasks in it will be loaded on startup bool tasksWillBeLoaded = heuresticForLoadingDockWithTasks(&firstContainmentWithTasks); qDebug() << "TASKS WILL BE PRESENT AFTER LOADING ::: " << tasksWillBeLoaded; for (const auto containment : m_corona->containments()) { //! forceDockLoading is used when a latte configuration based on the //! current running screens does not provide a dock containing tasks. //! in such case the lowest latte containment containing tasks is loaded //! and it forcefully becomes primary dock if (!tasksWillBeLoaded && firstContainmentWithTasks == containment->id()) { tasksWillBeLoaded = true; //this protects by loading more than one dock at startup addDock(containment, true); } else { addDock(containment); } }*/ } } void LayoutManager::cleanupOnStartup(QString path) { KSharedConfigPtr filePtr = KSharedConfig::openConfig(path); KConfigGroup actionGroups = KConfigGroup(filePtr, "ActionPlugins"); QStringList deprecatedActionGroup; for (const auto &actId : actionGroups.groupList()) { QString pluginId = actionGroups.group(actId).readEntry("RightButton;NoModifier", ""); if (pluginId == "org.kde.contextmenu") { deprecatedActionGroup << actId; } } for (const auto &pId : deprecatedActionGroup) { qDebug() << "!!!!!!!!!!!!!!!! !!!!!!!!!!!! !!!!!!! REMOVING :::: " << pId; actionGroups.group(pId).deleteGroup(); } KConfigGroup containmentGroups = KConfigGroup(filePtr, "Containments"); QStringList removeContaimentsList; for (const auto &cId : containmentGroups.groupList()) { QString pluginId = containmentGroups.group(cId).readEntry("plugin", ""); if (pluginId == "org.kde.desktopcontainment") { //!must remove ghost containments first removeContaimentsList << cId; } } for (const auto &cId : removeContaimentsList) { containmentGroups.group(cId).deleteGroup(); } actionGroups.sync(); containmentGroups.sync(); } void LayoutManager::showAboutDialog() { m_corona->aboutApplication(); } void LayoutManager::importLatteLayout(QString layoutPath) { //! This might not be needed as it is Layout responsibility } void LayoutManager::hideAllViews() { for (const auto layout : m_activeLayouts) { if (layout->isOriginalLayout()) { emit currentLayoutIsSwitching(layout->name()); } } } void LayoutManager::addLayout(ActiveLayout *layout) { if (!m_activeLayouts.contains(layout)) { m_activeLayouts.append(layout); layout->initToCorona(m_corona); } } bool LayoutManager::switchToLayout(QString layoutName, int previousMemoryUsage) { if (m_activeLayouts.size() > 0 && currentLayoutName() == layoutName && previousMemoryUsage == -1) { return false; } //! First Check If that Layout is already present and in that case //! we can just switch to the proper Activity if (memoryUsage() == Types::MultipleLayouts && previousMemoryUsage == -1) { ActiveLayout *layout = activeLayout(layoutName); if (layout) { QStringList appliedActivities = layout->appliedActivities(); QString nextActivity = !layout->lastUsedActivity().isEmpty() ? layout->lastUsedActivity() : appliedActivities[0]; //! it means we are at a foreign activity if (!appliedActivities.contains(m_corona->activitiesConsumer()->currentActivity())) { m_activitiesController->setCurrentActivity(nextActivity); return true; } } } //! When going from memory usage to different memory usage we first //! send the layouts that will be changed. This signal creates the //! nice animation that hides these docks/panels if (previousMemoryUsage != -1) { for (const auto layout : m_activeLayouts) { if (layout->isOriginalLayout()) { emit currentLayoutIsSwitching(layout->name()); } } - for (const auto layout : m_topLayouts) { + for (const auto layout : m_sharedLayouts) { emit currentLayoutIsSwitching(layout->name()); } } QString lPath = layoutPath(layoutName); if (lPath.isEmpty() && layoutName == i18n("Alternative")) { lPath = newLayout(i18n("Alternative"), i18n("Default")); } if (!lPath.isEmpty()) { if (memoryUsage() == Types::SingleLayout) { // emit currentLayoutIsSwitching(currentLayoutName()); } else if (memoryUsage() == Types::MultipleLayouts && layoutName != Layout::AbstractLayout::MultipleLayoutsName) { ActiveLayout toLayout(this, lPath); QStringList toActivities = toLayout.activities(); ActiveLayout *activeForOrphans{nullptr}; for (const auto fromLayout : m_activeLayouts) { if (fromLayout->isOriginalLayout() && fromLayout->activities().isEmpty()) { activeForOrphans = fromLayout; break; } } if (toActivities.isEmpty() && activeForOrphans && (toLayout.name() != activeForOrphans->name())) { emit currentLayoutIsSwitching(activeForOrphans->name()); } } //! this code must be called asynchronously because it is called //! also from qml (Tasks plasmoid). This change fixes a very important //! crash when switching sessions through the Tasks plasmoid Context menu //! Latte was unstable and was crashing very often during changing //! sessions. QTimer::singleShot(350, [this, layoutName, lPath, previousMemoryUsage]() { qDebug() << layoutName << " - " << lPath; QString fixedLPath = lPath; QString fixedLayoutName = layoutName; bool initializingMultipleLayouts{false}; if (memoryUsage() == Types::MultipleLayouts && !activeLayout(Layout::AbstractLayout::MultipleLayoutsName)) { initializingMultipleLayouts = true; } if (memoryUsage() == Types::SingleLayout || initializingMultipleLayouts || previousMemoryUsage == Types::MultipleLayouts) { unload(); if (initializingMultipleLayouts) { fixedLayoutName = QString(Layout::AbstractLayout::MultipleLayoutsName); fixedLPath = layoutPath(fixedLayoutName); } ActiveLayout *newLayout = new ActiveLayout(this, fixedLPath, fixedLayoutName); addLayout(newLayout); loadLatteLayout(fixedLPath); emit activeLayoutsChanged(); } if (memoryUsage() == Types::MultipleLayouts) { if (!initializingMultipleLayouts && !activeLayout(layoutName)) { //! When we are in Multiple Layouts Environment and the user activates //! a Layout that is assigned to specific activities but this //! layout isnt loaded (this means neither of its activities are running) //! is such case we just activate these Activities ActiveLayout layout(this, Importer::layoutFilePath(layoutName)); int i = 0; bool lastUsedActivityFound{false}; QString lastUsedActivity = layout.lastUsedActivity(); bool orphanedLayout = !layoutIsAssigned(layoutName); QStringList assignedActivities = orphanedLayout ? orphanedActivities() : layout.activities(); if (!orphanedLayout) { for (const auto &assignedActivity : assignedActivities) { //! Starting the activities must be done asynchronous because otherwise //! the activity manager cant close multiple activities QTimer::singleShot(i * 1000, [this, assignedActivity, lastUsedActivity]() { m_activitiesController->startActivity(assignedActivity); if (lastUsedActivity == assignedActivity) { m_activitiesController->setCurrentActivity(lastUsedActivity); } }); if (lastUsedActivity == assignedActivity) { lastUsedActivityFound = true; } i = i + 1; } } else { //! orphaned layout for (const auto &assignedActivity : assignedActivities) { if (lastUsedActivity == assignedActivity) { lastUsedActivityFound = true; } } if ((!lastUsedActivityFound && assignedActivities.count() == 0) || !assignedActivities.contains(m_corona->m_activityConsumer->currentActivity())) { //! Starting the activities must be done asynchronous because otherwise //! the activity manager cant close multiple activities QTimer::singleShot(1000, [this, lastUsedActivity, lastUsedActivityFound]() { m_activitiesController->startActivity(lastUsedActivity); m_activitiesController->setCurrentActivity(lastUsedActivity); }); } } if (orphanedLayout) { syncMultipleLayoutsToActivities(layoutName); } else if (!orphanedLayout && !lastUsedActivityFound) { m_activitiesController->setCurrentActivity(layout.activities()[0]); } } else { syncMultipleLayoutsToActivities(layoutName); } } m_corona->universalSettings()->setCurrentLayoutName(layoutName); if (!layoutIsAssigned(layoutName)) { m_corona->universalSettings()->setLastNonAssignedLayoutName(layoutName); } }); } else { qDebug() << "Layout : " << layoutName << " was not found..."; } return true; } void LayoutManager::syncMultipleLayoutsToActivities(QString layoutForOrphans) { qDebug() << " ---- --------- ------ syncMultipleLayoutsToActivities ------- "; qDebug() << " ---- --------- ------ ------------------------------- ------- "; QStringList layoutsToUnload; QStringList layoutsToLoad; layoutsToLoad << Layout::AbstractLayout::MultipleLayoutsName; bool allRunningActivitiesWillBeReserved{true}; if (layoutForOrphans.isEmpty() || m_assignedLayouts.values().contains(layoutForOrphans)) { layoutForOrphans = m_corona->universalSettings()->lastNonAssignedLayoutName(); } for (const auto &activity : runningActivities()) { if (!m_assignedLayouts[activity].isEmpty()) { if (!layoutsToLoad.contains(m_assignedLayouts[activity])) { layoutsToLoad.append(m_assignedLayouts[activity]); } } else { allRunningActivitiesWillBeReserved = false; } } for (const auto layout : m_activeLayouts) { QString tempLayoutName; if (!layoutsToLoad.contains(layout->name()) && layout->name() != layoutForOrphans) { tempLayoutName = layout->name(); } else if (layout->activities().isEmpty() && allRunningActivitiesWillBeReserved) { //! in such case the layout for the orphaned must be unloaded tempLayoutName = layout->name(); } if (!tempLayoutName.isEmpty() && !layoutsToUnload.contains(tempLayoutName)) { layoutsToUnload << tempLayoutName; } } //! Unload no needed Layouts for (const auto &layoutName : layoutsToUnload) { if (layoutName != Layout::AbstractLayout::MultipleLayoutsName) { ActiveLayout *layout = activeLayout(layoutName); int posLayout = activeLayoutPos(layoutName); if (posLayout >= 0) { qDebug() << "REMOVING LAYOUT ::::: " << layoutName; m_activeLayouts.removeAt(posLayout); if (layout->isOriginalLayout()) { layout->syncToLayoutFile(true); } layout->unloadContainments(); layout->unloadLatteViews(); clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds()); delete layout; } } } //! Add Layout for orphan activities if (!allRunningActivitiesWillBeReserved) { if (!activeLayout(layoutForOrphans)) { ActiveLayout *newLayout = new ActiveLayout(this, layoutPath(layoutForOrphans), layoutForOrphans); if (newLayout) { qDebug() << "ACTIVATING ORPHANED LAYOUT ::::: " << layoutForOrphans; addLayout(newLayout); newLayout->importToCorona(); } } } //! Add needed Layouts based on Activities for (const auto &layoutName : layoutsToLoad) { if (!activeLayout(layoutName)) { ActiveLayout *newLayout = new ActiveLayout(this, QString(layoutPath(layoutName)), layoutName); if (newLayout) { qDebug() << "ACTIVATING LAYOUT ::::: " << layoutName; addLayout(newLayout); newLayout->importToCorona(); if (newLayout->isOriginalLayout() && m_corona->universalSettings()->showInfoWindow()) { showInfoWindow(i18n("Activating layout: %0 ...").arg(newLayout->name()), 5000, newLayout->appliedActivities()); } } } } updateCurrentLayoutNameInMultiEnvironment(); emit activeLayoutsChanged(); } void LayoutManager::pauseLayout(QString layoutName) { if (memoryUsage() == Types::MultipleLayouts) { ActiveLayout *layout = activeLayout(layoutName); if (layout && !layout->activities().isEmpty()) { int i = 0; for (const auto &activityId : layout->activities()) { //! Stopping the activities must be done asynchronous because otherwise //! the activity manager cant close multiple activities QTimer::singleShot(i * 1000, [this, activityId]() { m_activitiesController->stopActivity(activityId); }); i = i + 1; } } } } void LayoutManager::syncActiveLayoutsToOriginalFiles() { if (memoryUsage() == Types::MultipleLayouts) { for (const auto layout : m_activeLayouts) { if (layout->isOriginalLayout()) { layout->syncToLayoutFile(); } } } } void LayoutManager::clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds, bool bypassChecks) { if (!m_corona || (memoryUsage() == Types::SingleLayout && !bypassChecks)) { return; } auto containments = m_corona->config()->group("Containments"); for (const auto &conId : containmentsIds) { qDebug() << "unloads ::: " << conId; KConfigGroup containment = containments.group(conId); containment.deleteGroup(); } containments.sync(); } void LayoutManager::syncLatteViewsToScreens() { - for (const auto layout : m_topLayouts) { + for (const auto layout : m_sharedLayouts) { layout->syncLatteViewsToScreens(); } for (const auto layout : m_activeLayouts) { layout->syncLatteViewsToScreens(); } } QString LayoutManager::newLayout(QString layoutName, QString preset) { QDir layoutDir(QDir::homePath() + "/.config/latte"); QStringList filter; filter.append(QString(layoutName + "*.layout.latte")); QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); //! if the newLayout already exists provide a newName that doesn't if (files.count() >= 1) { int newCounter = files.count() + 1; layoutName = layoutName + "-" + QString::number(newCounter); } QString newLayoutPath = layoutDir.absolutePath() + "/" + layoutName + ".layout.latte"; qDebug() << "adding layout : " << layoutName << " based on preset:" << preset; if (preset == i18n("Default") && !QFile(newLayoutPath).exists()) { qDebug() << "adding layout : succeed"; QFile(m_corona->kPackage().filePath("preset1")).copy(newLayoutPath); } return newLayoutPath; } //! This function figures in the beginning if a view with tasks //! in it will be loaded taking into account also the screens are present. bool LayoutManager::heuresticForLoadingViewWithTasks(int *firstContainmentWithTasks) { for (const auto containment : m_corona->containments()) { QString plugin = containment->pluginMetaData().pluginId(); if (plugin == "org.kde.latte.containment") { bool onPrimary = containment->config().readEntry("onPrimary", true); int lastScreen = containment->lastScreen(); qDebug() << "containment values: " << onPrimary << " - " << lastScreen; bool containsTasks = false; for (const auto applet : containment->applets()) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { containsTasks = true; break; } } if (containsTasks) { *firstContainmentWithTasks = containment->id(); if (onPrimary) { return true; } else { if (lastScreen >= 0) { QString connector = m_corona->screenPool()->connector(lastScreen); for (const auto scr : qGuiApp->screens()) { if (scr && scr->name() == connector) { return true; break; } } } } } } } return false; } void LayoutManager::importDefaultLayout(bool newInstanceIfPresent) { importPreset(1, newInstanceIfPresent); if (newInstanceIfPresent) { loadLayouts(); } } void LayoutManager::importPresets(bool includeDefault) { int start = 1; if (!includeDefault) { start = 2; } for (int i = start; i <= 4; ++i) { importPreset(i, false); } } void LayoutManager::importPreset(int presetNo, bool newInstanceIfPresent) { QDir configDir(QDir::homePath() + "/.config"); if (!QDir(configDir.absolutePath() + "/latte").exists()) { configDir.mkdir("latte"); } QByteArray presetNameOrig = QString("preset" + QString::number(presetNo)).toUtf8(); QString presetPath = m_corona->kPackage().filePath(presetNameOrig); QString presetName = ActiveLayout::layoutName(presetPath); QByteArray presetNameChars = presetName.toUtf8(); presetName = i18n(presetNameChars); //! hide the multiple layouts layout file from user presetName = (presetNo == MultipleLayoutsPresetId) ? "." + presetName : presetName; QString newLayoutFile = ""; if (newInstanceIfPresent) { newLayoutFile = QDir::homePath() + "/.config/latte/" + m_importer->uniqueLayoutName(presetName) + ".layout.latte"; } else { newLayoutFile = QDir::homePath() + "/.config/latte/" + presetName + ".layout.latte"; } if (!QFile(newLayoutFile).exists()) { QFile(presetPath).copy(newLayoutFile); QFileInfo newFileInfo(newLayoutFile); if (newFileInfo.exists() && !newFileInfo.isWritable()) { QFile(newLayoutFile).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } } } QStringList LayoutManager::validActivities(QStringList currentList) { QStringList validIds; for (const auto &activity : currentList) { if (activities().contains(activity)) { validIds.append(activity); } } return validIds; } bool LayoutManager::layoutIsAssigned(QString layoutName) { QHashIterator i(m_assignedLayouts); while (i.hasNext()) { i.next(); if (i.value() == layoutName) { return true; } } return false; } void LayoutManager::showLatteSettingsDialog(int page) { if (!m_latteSettingsDialog) { m_latteSettingsDialog = new SettingsDialog(nullptr, m_corona); } m_latteSettingsDialog->show(); if (m_latteSettingsDialog->isMinimized()) { m_latteSettingsDialog->showNormal(); } Types::LatteConfigPage configPage = static_cast(page); m_latteSettingsDialog->setCurrentPage(configPage); m_latteSettingsDialog->activateWindow(); } void LayoutManager::hideLatteSettingsDialog() { if (m_latteSettingsDialog) { m_latteSettingsDialog->deleteLater(); m_latteSettingsDialog = nullptr; } } void LayoutManager::showInfoWindow(QString info, int duration, QStringList activities) { for (const auto screen : qGuiApp->screens()) { InfoView *infoView = new InfoView(m_corona, info, screen); infoView->show(); infoView->setOnActivities(activities); QTimer::singleShot(duration, [this, infoView]() { infoView->deleteLater(); }); } } //! it is used just in order to provide translations for the presets void LayoutManager::ghostForTranslatedPresets() { QString preset1 = i18n("Default"); QString preset2 = i18n("Plasma"); QString preset3 = i18n("Unity"); QString preset4 = i18n("Extended"); } } diff --git a/app/layoutmanager.h b/app/layoutmanager.h index 4be6a4bd..bd9d0e7c 100644 --- a/app/layoutmanager.h +++ b/app/layoutmanager.h @@ -1,210 +1,210 @@ /* * Copyright 2017 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LAYOUTMANAGER_H #define LAYOUTMANAGER_H // local #include "launcherssignals.h" #include "settings/settingsdialog.h" // Qt #include #include #include // KDE #include namespace Plasma { class Containment; class Types; } namespace KActivities { class Controller; } namespace Latte { class Corona; class Importer; class ActiveLayout; class LaunchersSignals; -class TopLayout; +class SharedLayout; class View; namespace Layout { class GenericLayout; } } namespace Latte { //! This class is responsible to manipulate all layouts. //! add,remove,rename, update configurations etc. class LayoutManager : public QObject { Q_OBJECT Q_PROPERTY(QString currentLayoutName READ currentLayoutName NOTIFY currentLayoutNameChanged) Q_PROPERTY(QStringList layouts READ layouts NOTIFY layoutsChanged) Q_PROPERTY(QStringList menuLayouts READ menuLayouts NOTIFY menuLayoutsChanged) Q_PROPERTY(LaunchersSignals *launchersSignals READ launchersSignals NOTIFY launchersSignalsChanged) public: LayoutManager(QObject *parent = nullptr); ~LayoutManager() override; Latte::Corona *corona(); Importer *importer(); void load(); void loadLayoutOnStartup(QString layoutName); void unload(); void hideAllViews(); void pauseLayout(QString layoutName); void syncLatteViewsToScreens(); void syncActiveLayoutsToOriginalFiles(); bool latteViewExists(Latte::View *view) const; bool layoutExists(QString layoutName) const; QString shouldSwitchToLayout(QString activityId); QString currentLayoutName() const; QString defaultLayoutName() const; QStringList layouts() const; QStringList menuLayouts() const; QStringList presetsPaths() const; Types::LayoutsMemoryUsage memoryUsage() const; void setMemoryUsage(Types::LayoutsMemoryUsage memoryUsage); //! returns an active layout with that #id (name), it returns null if such //! layout cant be found ActiveLayout *activeLayout(QString id) const; int activeLayoutPos(QString id) const; - TopLayout *topLayout(QString id) const; - //! return an active or top layout with #id (name), it returns null if such + SharedLayout *sharedLayout(QString id) const; + //! return an active or shared layout with #id (name), it returns null if such //! loaded layout was not found Layout::GenericLayout *layout(QString id) const; //! returns the current and active layout based on activities and user preferences ActiveLayout *currentLayout() const; LaunchersSignals *launchersSignals(); QStringList activities(); QStringList runningActivities(); QStringList orphanedActivities(); //! These are activities that haven't been assigned to specific layout void importDefaultLayout(bool newInstanceIfPresent = false); void importPresets(bool includeDefault = false); - bool assignActiveToTopLayout(ActiveLayout *active, QString id); + bool assignActiveToSharedLayout(ActiveLayout *active, QString id); public slots: void showAboutDialog(); void hideLatteSettingsDialog(); Q_INVOKABLE void showLatteSettingsDialog(int page = Latte::Types::LayoutPage); //! switch to specified layout, default previousMemoryUsage means that it didn't change Q_INVOKABLE bool switchToLayout(QString layoutName, int previousMemoryUsage = -1); Q_INVOKABLE int layoutsMemoryUsage(); //! creates a new layout with layoutName based on the preset Q_INVOKABLE QString newLayout(QString layoutName, QString preset = i18n("Default")); Q_INVOKABLE QStringList activeLayoutsNames(); signals: void activeLayoutsChanged(); void currentLayoutChanged(); void currentLayoutNameChanged(); void launchersSignalsChanged(); void layoutsChanged(); void menuLayoutsChanged(); void currentLayoutIsSwitching(QString layoutName); private slots: void currentActivityChanged(const QString &id); void showInfoWindowChanged(); void syncMultipleLayoutsToActivities(QString layoutForOrphans = QString()); private: void addLayout(ActiveLayout *layout); void cleanupOnStartup(QString path); //!remove deprecated or oldstyle config options void clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds, bool bypassChecks = false); void confirmDynamicSwitch(); //! it is used just in order to provide translations for the presets void ghostForTranslatedPresets(); //! This function figures in the beginning if a view with tasks //! in it will be loaded taking into account also the screens are present. //! returns true if it will be loaded, false otherwise //! firstContainmentWithTasks = the first containment containing a taskmanager plasmoid bool heuresticForLoadingViewWithTasks(int *firstContainmentWithTasks); void importLatteLayout(QString layoutPath); void importPreset(int presetNo, bool newInstanceIfPresent = false); void loadLatteLayout(QString layoutPath); void loadLayouts(); void setMenuLayouts(QStringList layouts); void showInfoWindow(QString info, int duration, QStringList activities = {"0"}); void updateCurrentLayoutNameInMultiEnvironment(); bool layoutIsAssigned(QString layoutName); QString layoutPath(QString layoutName); QStringList validActivities(QStringList currentList); private: QString m_currentLayoutNameInMultiEnvironment; QString m_shouldSwitchToLayout; QStringList m_layouts; QStringList m_menuLayouts; QStringList m_presetsPaths; QHash m_assignedLayouts; QTimer m_dynamicSwitchTimer; QPointer m_latteSettingsDialog; Latte::Corona *m_corona{nullptr}; Importer *m_importer{nullptr}; LaunchersSignals *m_launchersSignals{nullptr}; QList m_activeLayouts; - QList m_topLayouts; + QList m_sharedLayouts; KActivities::Controller *m_activitiesController; friend class SettingsDialog; }; } #endif // LAYOUTMANAGER_H