diff --git a/app/lattecorona.cpp b/app/lattecorona.cpp index 392764ff..9914f062 100644 --- a/app/lattecorona.cpp +++ b/app/lattecorona.cpp @@ -1,1060 +1,1061 @@ /* * 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 "lattedockadaptor.h" #include "screenpool.h" #include "indicator/factory.h" #include "layout/centrallayout.h" #include "layout/genericlayout.h" #include "layout/sharedlayout.h" #include "layouts/importer.h" #include "layouts/manager.h" #include "layouts/launcherssignals.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_layoutsManager(new Layouts::Manager(this)) + m_themeExtended(new PlasmaExtended::Theme(KSharedConfig::openConfig(), 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_layoutsManager->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_layoutsManager->memoryUsage() == Types::SingleLayout) { cleanConfig(); } qDebug() << "Latte Corona - unload: containments ..."; m_layoutsManager->unload(); m_wm->deleteLater(); m_globalShortcuts->deleteLater(); m_layoutsManager->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) { + m_activitiesStarting = false; + disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); - m_layoutsManager->load(); - m_activitiesStarting = false; + m_layoutsManager = new Layouts::Manager(this); + m_layoutsManager->load(); connect(this, &Corona::availableScreenRectChangedFrom, this, &Plasma::Corona::availableScreenRectChanged); connect(this, &Corona::availableScreenRegionChangedFrom, this, &Plasma::Corona::availableScreenRegionChanged); 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_layoutsManager->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_layoutsManager->layoutExists(loadLayoutName)) { loadLayoutName = m_layoutsManager->defaultLayoutName(); m_layoutsManager->importDefaultLayout(false); } } else if (m_defaultLayoutOnStartup) { loadLayoutName = m_layoutsManager->importer()->uniqueLayoutName(m_layoutsManager->defaultLayoutName()); m_layoutsManager->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_layoutsManager->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; } AbstractWindowInterface *Corona::wm() const { return m_wm; } Indicator::Factory *Corona::indicatorFactory() const { return m_indicatorFactory; } Layouts::Manager *Corona::layoutsManager() const { return m_layoutsManager; } 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->hasId(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->hasId(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::CentralLayout *currentLayout = m_layoutsManager->currentLayout(); views = currentLayout->latteViews(); } else { Layout::GenericLayout *generic = m_layoutsManager->centralLayout(forLayout); if (!generic) { //! 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_layoutsManager->sharedLayout(forLayout); if (sharedLayout) { generic = sharedLayout->currentCentralLayout(); } } if (!generic) { generic = m_layoutsManager->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(); // 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->hasId(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::CentralLayout *currentLayout = m_layoutsManager->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_layoutsManager->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_layoutsManager->hideLatteSettingsDialog(); m_layoutsManager->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::CentralLayout *currentLayout = m_layoutsManager->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::CentralLayout *currentLayout = m_layoutsManager->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_layoutsManager->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_layoutsManager->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_layoutsManager->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_layoutsManager->switchToLayout(layout); } void Corona::showSettingsWindow(int page) { Types::LatteConfigPage p = Types::LayoutPage; if (page >= Types::LayoutPage && page <= Types::PreferencesPage) { p = static_cast(page); } m_layoutsManager->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::CentralLayout *currentLayout = m_layoutsManager->currentLayout(); if (currentLayout) { viewType = currentLayout->latteViewType(m_contextMenuViewId); } data << QString::number((int)m_layoutsManager->memoryUsage()); data << m_layoutsManager->currentLayoutName(); data << QString::number((int)viewType); for(const auto &layoutName : m_layoutsManager->menuLayouts()) { if (m_layoutsManager->centralLayout(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(); qmlRegisterType(); } } diff --git a/app/layouts/CMakeLists.txt b/app/layouts/CMakeLists.txt index 2c80e302..d31a6d03 100644 --- a/app/layouts/CMakeLists.txt +++ b/app/layouts/CMakeLists.txt @@ -1,7 +1,8 @@ set(lattedock-app_SRCS ${lattedock-app_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/importer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/launcherssignals.cpp ${CMAKE_CURRENT_SOURCE_DIR}/manager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/synchronizer.cpp PARENT_SCOPE ) diff --git a/app/layouts/manager.cpp b/app/layouts/manager.cpp index 25c1cc8d..210df009 100644 --- a/app/layouts/manager.cpp +++ b/app/layouts/manager.cpp @@ -1,1226 +1,550 @@ /* * Copyright 2017 Smith AR * Michail Vourlakos * * 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 "manager.h" // local #include "importer.h" #include "launcherssignals.h" #include "../infoview.h" #include "../screenpool.h" #include "../layout/abstractlayout.h" #include "../layout/centrallayout.h" #include "../layout/genericlayout.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 { namespace Layouts { const int MultipleLayoutsPresetId = 10; Manager::Manager(QObject *parent) : QObject(parent), m_importer(new Importer(this)), m_launchersSignals(new LaunchersSignals(this)), - m_activitiesController(new KActivities::Controller(this)) + m_synchronizer(new Synchronizer(this)) { m_corona = qobject_cast(parent); if (m_corona) { connect(m_corona->universalSettings(), &UniversalSettings::currentLayoutNameChanged, this, &Manager::currentLayoutNameChanged); - connect(m_corona->universalSettings(), &UniversalSettings::showInfoWindowChanged, this, &Manager::showInfoWindowChanged); - m_dynamicSwitchTimer.setSingleShot(true); - showInfoWindowChanged(); - connect(&m_dynamicSwitchTimer, &QTimer::timeout, this, &Manager::confirmDynamicSwitch); + connect(m_synchronizer, &Synchronizer::centralLayoutsChanged, this, &Manager::centralLayoutsChanged); + connect(m_synchronizer, &Synchronizer::currentLayoutNameChanged, this, &Manager::currentLayoutNameChanged); + connect(m_synchronizer, &Synchronizer::currentLayoutIsSwitching, this, &Manager::currentLayoutIsSwitching); + connect(m_synchronizer, &Synchronizer::layoutsChanged, this, &Manager::layoutsChanged); + connect(m_synchronizer, &Synchronizer::menuLayoutsChanged, this, &Manager::menuLayoutsChanged); } } Manager::~Manager() { m_importer->deleteLater(); m_launchersSignals->deleteLater(); - unload(); + //! no needed because Latte:Corona is calling it at better place + // unload(); - m_activitiesController->deleteLater(); + m_synchronizer->deleteLater(); } void Manager::load() { + m_presetsPaths.clear(); + 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, &Manager::currentActivityChanged); - - connect(m_corona->m_activityConsumer, &KActivities::Consumer::runningActivitiesChanged, - this, [&]() { - if (memoryUsage() == Types::MultipleLayouts) { - syncMultipleLayoutsToActivities(); - } - }); + 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")); - loadLayouts(); + m_synchronizer->loadLayouts(); } void Manager::unload() { - //! Unload all CentralLayouts - while (!m_centralLayouts.isEmpty()) { - CentralLayout *layout = m_centralLayouts.at(0); - unloadCentralLayout(layout); - } - - m_multipleModeInitialized = false; + m_synchronizer->unloadLayouts(); //! 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()) + if (file1.exists()) { file1.remove(); - - if (file2.exists()) - file2.remove(); -} - -void Manager::unloadCentralLayout(CentralLayout *layout) -{ - int pos = m_centralLayouts.indexOf(layout); - - if (pos>=0) { - CentralLayout *central = m_centralLayouts.takeAt(0); - - if (m_multipleModeInitialized) { - central->syncToLayoutFile(true); - } - - central->unloadContainments(); - central->unloadLatteViews(); - - if (m_multipleModeInitialized) { - clearUnloadedContainmentsFromLinkedFile(central->unloadedContainmentsIds(), true); - } - - delete central; } -} -void Manager::unloadSharedLayout(SharedLayout *layout) -{ - if (m_sharedLayouts.contains(layout)) { - disconnect(layout, &SharedLayout::layoutDestroyed, this, &Manager::unloadSharedLayout); - int pos = m_sharedLayouts.indexOf(layout); - SharedLayout *shared = m_sharedLayouts.takeAt(pos); - shared->syncToLayoutFile(true); - shared->unloadContainments(); - shared->unloadLatteViews(); - clearUnloadedContainmentsFromLinkedFile(shared->unloadedContainmentsIds(), true); - - delete layout; + if (file2.exists()) { + file2.remove(); } } - Latte::Corona *Manager::corona() { return m_corona; } Importer *Manager::importer() { return m_importer; } -LaunchersSignals *Manager::launchersSignals() +LaunchersSignals *Manager::launchersSignals() const { return m_launchersSignals; } -QString Manager::currentLayoutName() const +Synchronizer *Manager::synchronizer() const { - if (memoryUsage() == Types::SingleLayout) { - return m_corona->universalSettings()->currentLayoutName(); - } else if (memoryUsage() == Types::MultipleLayouts) { - return m_currentLayoutNameInMultiEnvironment; - } + return m_synchronizer; +} - return QString(); +QString Manager::currentLayoutName() const +{ + return m_synchronizer->currentLayoutName(); } QString Manager::defaultLayoutName() const { QByteArray presetNameOrig = QString("preset" + QString::number(1)).toUtf8(); QString presetPath = m_corona->kPackage().filePath(presetNameOrig); QString presetName = CentralLayout::layoutName(presetPath); QByteArray presetNameChars = presetName.toUtf8(); presetName = i18n(presetNameChars); return presetName; } bool Manager::layoutExists(QString layoutName) const { - return m_layouts.contains(layoutName); + return m_synchronizer->layoutExists(layoutName); } QStringList Manager::layouts() const { - return m_layouts; + return m_synchronizer->layouts(); } QStringList Manager::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_centralLayouts) { - if (!fixedMenuLayouts.contains(layout->name())) { - fixedMenuLayouts.prepend(layout->name()); - } - } - } - - return fixedMenuLayouts; + return m_synchronizer->menuLayouts(); } void Manager::setMenuLayouts(QStringList layouts) { - if (m_menuLayouts == layouts) { - return; - } - - m_menuLayouts = layouts; - emit menuLayoutsChanged(); + m_synchronizer->setMenuLayouts(layouts); } QStringList Manager::activities() { - return m_corona->m_activityConsumer->activities(); + return m_synchronizer->activities(); } QStringList Manager::runningActivities() { - return m_corona->m_activityConsumer->runningActivities(); + return m_synchronizer->runningActivities(); } QStringList Manager::orphanedActivities() { - QStringList orphans; - - for (const auto &activity : activities()) { - if (m_assignedLayouts[activity].isEmpty()) { - orphans.append(activity); - } - } - - return orphans; + return m_synchronizer->orphanedActivities(); } QStringList Manager::presetsPaths() const { return m_presetsPaths; } -QString Manager::layoutPath(QString layoutName) -{ - QString path = QDir::homePath() + "/.config/latte/" + layoutName + ".layout.latte"; - - if (!QFile(path).exists()) { - path = ""; - } - - return path; -} - Types::LayoutsMemoryUsage Manager::memoryUsage() const { return m_corona->universalSettings()->layoutsMemoryUsage(); } int Manager::layoutsMemoryUsage() { return (int)m_corona->universalSettings()->layoutsMemoryUsage(); } void Manager::setMemoryUsage(Types::LayoutsMemoryUsage memoryUsage) { m_corona->universalSettings()->setLayoutsMemoryUsage(memoryUsage); } bool Manager::latteViewExists(Latte::View *view) const { - for (const auto layout : m_centralLayouts) { - for (const auto &v : layout->latteViews()) { - if (v == view) { - return true; - } - } - } - - return false; + return m_synchronizer->latteViewExists(view); } QStringList Manager::centralLayoutsNames() { - QStringList names; - - if (memoryUsage() == Types::SingleLayout) { - names << currentLayoutName(); - } else { - for (int i = 0; i < m_centralLayouts.size(); ++i) { - CentralLayout *layout = m_centralLayouts.at(i); - names << layout->name(); - } - } - - return names; - + return m_synchronizer->centralLayoutsNames(); } QStringList Manager::sharedLayoutsNames() { - QStringList names; - - for (int i = 0; i < m_sharedLayouts.size(); ++i) { - SharedLayout *layout = m_sharedLayouts.at(i); - names << layout->name(); - } - - return names; + return m_synchronizer->sharedLayoutsNames(); } QStringList Manager::storedSharedLayouts() const { - return m_sharedLayoutIds; + return m_synchronizer->storedSharedLayouts(); } Layout::GenericLayout *Manager::layout(QString id) const { - Layout::GenericLayout *l = centralLayout(id); - - if (!l) { - l = sharedLayout(id); - } - - return l; + return m_synchronizer->layout(id); } CentralLayout *Manager::centralLayout(QString id) const { - for (int i = 0; i < m_centralLayouts.size(); ++i) { - CentralLayout *layout = m_centralLayouts.at(i); - - if (layout->name() == id) { - - return layout; - } - } - - return nullptr; + return m_synchronizer->centralLayout(id); } int Manager::centralLayoutPos(QString id) const { - for (int i = 0; i < m_centralLayouts.size(); ++i) { - CentralLayout *layout = m_centralLayouts.at(i); - - if (layout->name() == id) { - - return i; - } - } - - return -1; + return m_synchronizer->centralLayoutPos(id); } SharedLayout *Manager::sharedLayout(QString id) const { - for (int i = 0; i < m_sharedLayouts.size(); ++i) { - SharedLayout *layout = m_sharedLayouts.at(i); - - if (layout->name() == id) { - return layout; - } - } - - return nullptr; + return m_synchronizer->sharedLayout(id); } bool Manager::registerAtSharedLayout(CentralLayout *central, QString id) { - if (memoryUsage() == Types::SingleLayout || centralLayout(id)) { - //! if memory is functioning to SINGLE mode OR shared layout has already - //! been loaded as CentralLayout - return false; - } - - for (int i = 0; i < m_sharedLayouts.size(); ++i) { - SharedLayout *layout = m_sharedLayouts.at(i); - - if (layout->name() == id) { - layout->addCentralLayout(central); - return true; - } - } - - //! If SharedLayout was not found, we must create it - SharedLayout *top = new SharedLayout(central, this, Importer::layoutFilePath(id)); - m_sharedLayouts.append(top); - top->importToCorona(); - - connect(top, &SharedLayout::layoutDestroyed, this, &Manager::unloadSharedLayout); - - return true; + return m_synchronizer->registerAtSharedLayout(central, id); } CentralLayout *Manager::currentLayout() const { - if (memoryUsage() == Types::SingleLayout) { - return m_centralLayouts.at(0); - } else { - for (auto layout : m_centralLayouts) { - if (layout->activities().contains(m_corona->m_activityConsumer->currentActivity())) { - return layout; - } - } - - for (auto layout : m_centralLayouts) { - if (layout->activities().isEmpty()) { - return layout; - } - } - } - - return nullptr; -} - -void Manager::updateCurrentLayoutNameInMultiEnvironment() -{ - for (const auto layout : m_centralLayouts) { - if (layout->activities().contains(m_corona->activitiesConsumer()->currentActivity())) { - m_currentLayoutNameInMultiEnvironment = layout->name(); - emit currentLayoutNameChanged(); - return; - } - } - - for (const auto layout : m_centralLayouts) { - if (layout->activities().isEmpty()) { - m_currentLayoutNameInMultiEnvironment = layout->name(); - emit currentLayoutNameChanged(); - return; - } - } + return m_synchronizer->currentLayout(); } -void Manager::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 Manager::showInfoWindowChanged() +bool Manager::switchToLayout(QString layoutName, int previousMemoryUsage) { - if (m_corona->universalSettings()->showInfoWindow()) { - m_dynamicSwitchTimer.setInterval(1800); - } else { - m_dynamicSwitchTimer.setInterval(2300); - } + return m_synchronizer->switchToLayout(layoutName, previousMemoryUsage); } QString Manager::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 Manager::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 Manager::loadLayouts() -{ - m_layouts.clear(); - m_menuLayouts.clear(); - m_presetsPaths.clear(); - m_assignedLayouts.clear(); - m_sharedLayoutIds.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) { - if (layout.contains(Layout::AbstractLayout::MultipleLayoutsName)) { - //! IMPORTANT: DONT ADD MultipleLayouts hidden file in layouts list - continue; - } - - CentralLayout centralLayout(this, layoutDir.absolutePath() + "/" + layout); - - QStringList validActivityIds = validActivities(centralLayout.activities()); - centralLayout.setActivities(validActivityIds); - - for (const auto &activity : validActivityIds) { - m_assignedLayouts[activity] = centralLayout.name(); - } - - m_layouts.append(centralLayout.name()); - - if (centralLayout.showInMenu()) { - m_menuLayouts.append(centralLayout.name()); - } - - QString sharedName = centralLayout.sharedLayoutName(); - - if (!sharedName.isEmpty() && !m_sharedLayoutIds.contains(sharedName)) { - m_sharedLayoutIds << sharedName; - } - } - - //! Shared Layouts should not be used for Activities->Layouts assignments or published lists - clearSharedLayoutsFromCentralLists(); - - 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 Manager::clearSharedLayoutsFromCentralLists() -{ - QStringList unassign; - - for(const QString &name : m_sharedLayoutIds) { - //! remove from ContextMenu - m_menuLayouts.removeAll(name); - - //! remove from layouts assigned to activities - QHashIterator i(m_assignedLayouts); - - while (i.hasNext()) { - i.next(); - - if (i.value() == name) { - unassign << i.key(); - } - } - } - - for(const QString &activity : unassign) { - m_assignedLayouts.remove(activity); - } - - + return m_synchronizer->shouldSwitchToLayout(activityId); } void Manager::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); + m_synchronizer->switchToLayout(layoutName); } void Manager::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); } } void Manager::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 Manager::showAboutDialog() { m_corona->aboutApplication(); } -void Manager::importLatteLayout(QString layoutPath) -{ - //! This might not be needed as it is Layout responsibility -} - void Manager::hideAllViews() { - for (const auto layout : m_centralLayouts) { - emit currentLayoutIsSwitching(layout->name()); - } -} - -void Manager::addLayout(CentralLayout *layout) -{ - if (!m_centralLayouts.contains(layout)) { - m_centralLayouts.append(layout); - layout->initToCorona(m_corona); - } -} - -bool Manager::switchToLayout(QString layoutName, int previousMemoryUsage) -{ - if (m_centralLayouts.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) { - CentralLayout *layout = centralLayout(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_centralLayouts) { - emit currentLayoutIsSwitching(layout->name()); - } - - 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) { - CentralLayout toLayout(this, lPath); - - QStringList toActivities = toLayout.activities(); - - CentralLayout *centralForOrphans{nullptr}; - - for (const auto fromLayout : m_centralLayouts) { - if (fromLayout->activities().isEmpty()) { - centralForOrphans = fromLayout; - break; - } - } - - if (toActivities.isEmpty() && centralForOrphans && (toLayout.name() != centralForOrphans->name())) { - emit currentLayoutIsSwitching(centralForOrphans->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 && !m_multipleModeInitialized) { - initializingMultipleLayouts = true; - } - - if (memoryUsage() == Types::SingleLayout || initializingMultipleLayouts || previousMemoryUsage == Types::MultipleLayouts) { - unload(); - - if (initializingMultipleLayouts) { - fixedLayoutName = QString(Layout::AbstractLayout::MultipleLayoutsName); - fixedLPath = layoutPath(fixedLayoutName); - } - - if (fixedLayoutName != Layout::AbstractLayout::MultipleLayoutsName) { - CentralLayout *newLayout = new CentralLayout(this, fixedLPath, fixedLayoutName); - addLayout(newLayout); - } - - loadLatteLayout(fixedLPath); - - if (initializingMultipleLayouts) { - m_multipleModeInitialized = true; - } - - emit centralLayoutsChanged(); - } - - if (memoryUsage() == Types::MultipleLayouts) { - if (!initializingMultipleLayouts && !centralLayout(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 - CentralLayout 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 Manager::syncMultipleLayoutsToActivities(QString layoutForOrphans) -{ - qDebug() << " ---- --------- ------ syncMultipleLayoutsToActivities ------- "; - qDebug() << " ---- --------- ------ ------------------------------- ------- "; - - QStringList layoutsToUnload; - QStringList layoutsToLoad; - - 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_centralLayouts) { - 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) { - CentralLayout *layout = centralLayout(layoutName); - int posLayout = centralLayoutPos(layoutName); - - if (posLayout >= 0) { - qDebug() << "REMOVING LAYOUT ::::: " << layoutName; - m_centralLayouts.removeAt(posLayout); - - layout->syncToLayoutFile(true); - layout->unloadContainments(); - layout->unloadLatteViews(); - clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds()); - delete layout; - } - } - - //! Add Layout for orphan activities - if (!allRunningActivitiesWillBeReserved) { - if (!centralLayout(layoutForOrphans)) { - CentralLayout *newLayout = new CentralLayout(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 (!centralLayout(layoutName)) { - CentralLayout *newLayout = new CentralLayout(this, QString(layoutPath(layoutName)), layoutName); - - if (newLayout) { - qDebug() << "ACTIVATING LAYOUT ::::: " << layoutName; - addLayout(newLayout); - newLayout->importToCorona(); - - if (m_corona->universalSettings()->showInfoWindow()) { - showInfoWindow(i18n("Activating layout: %0 ...").arg(newLayout->name()), 5000, newLayout->appliedActivities()); - } - } - } - } - - updateCurrentLayoutNameInMultiEnvironment(); - emit centralLayoutsChanged(); + m_synchronizer->hideAllViews(); } void Manager::pauseLayout(QString layoutName) { - if (memoryUsage() == Types::MultipleLayouts) { - CentralLayout *layout = centralLayout(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; - } - } - } + m_synchronizer->pauseLayout(layoutName); } void Manager::syncActiveLayoutsToOriginalFiles() { - if (memoryUsage() == Types::MultipleLayouts) { - for (const auto layout : m_centralLayouts) { - layout->syncToLayoutFile(); - } - - for (const auto layout : m_sharedLayouts) { - layout->syncToLayoutFile(); - } - } + m_synchronizer->syncActiveLayoutsToOriginalFiles(); } void Manager::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 Manager::syncLatteViewsToScreens() { - for (const auto layout : m_sharedLayouts) { - layout->syncLatteViewsToScreens(); - } - - for (const auto layout : m_centralLayouts) { - layout->syncLatteViewsToScreens(); - } + m_synchronizer->syncLatteViewsToScreens(); } QString Manager::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; } void Manager::importDefaultLayout(bool newInstanceIfPresent) { importPreset(1, newInstanceIfPresent); if (newInstanceIfPresent) { - loadLayouts(); + m_synchronizer->loadLayouts(); } } void Manager::importPresets(bool includeDefault) { int start = 1; if (!includeDefault) { start = 2; } for (int i = start; i <= 4; ++i) { importPreset(i, false); } } void Manager::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 = Layout::AbstractLayout::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 Manager::validActivities(QStringList currentList) -{ - QStringList validIds; - - for (const auto &activity : currentList) { - if (activities().contains(activity)) { - validIds.append(activity); - } - } - - return validIds; -} - -bool Manager::layoutIsAssigned(QString layoutName) -{ - QHashIterator i(m_assignedLayouts); - - while (i.hasNext()) { - i.next(); - - if (i.value() == layoutName) { - return true; - } - } - - return false; -} - void Manager::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 Manager::hideLatteSettingsDialog() { if (m_latteSettingsDialog) { m_latteSettingsDialog->deleteLater(); m_latteSettingsDialog = nullptr; } } void Manager::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 Manager::ghostForTranslatedPresets() { QString preset1 = i18n("Default"); QString preset2 = i18n("Plasma"); QString preset3 = i18n("Unity"); QString preset4 = i18n("Extended"); } } } diff --git a/app/layouts/manager.h b/app/layouts/manager.h index 1313b6b8..97bcef4e 100644 --- a/app/layouts/manager.h +++ b/app/layouts/manager.h @@ -1,219 +1,185 @@ /* * 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 LAYOUTSMANAGER_H #define LAYOUTSMANAGER_H // local #include "launcherssignals.h" +#include "synchronizer.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 CentralLayout; class SharedLayout; class View; namespace Layout { class GenericLayout; } namespace Layouts { class Importer; class LaunchersSignals; +class Synchronizer; } } namespace Latte { namespace Layouts { //! This class is responsible to manipulate all layouts. //! add,remove,rename, update configurations etc. class Manager : 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: Manager(QObject *parent = nullptr); ~Manager() override; Latte::Corona *corona(); Importer *importer(); void load(); void loadLayoutOnStartup(QString layoutName); + void showInfoWindow(QString info, int duration, QStringList activities = {"0"}); void unload(); - void unloadCentralLayout(CentralLayout *layout); 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; QStringList storedSharedLayouts() const; Types::LayoutsMemoryUsage memoryUsage() const; void setMemoryUsage(Types::LayoutsMemoryUsage memoryUsage); //! returns an central layout with that #id (name), it returns null if such //! layout cant be found CentralLayout *centralLayout(QString id) const; int centralLayoutPos(QString id) const; SharedLayout *sharedLayout(QString id) const; //! return an central 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 central layout based on activities and user preferences CentralLayout *currentLayout() const; - LaunchersSignals *launchersSignals(); + LaunchersSignals *launchersSignals() const; + Synchronizer *synchronizer() const; 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 registerAtSharedLayout(CentralLayout *central, 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 centralLayoutsNames(); Q_INVOKABLE QStringList sharedLayoutsNames(); signals: void centralLayoutsChanged(); 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()); - void unloadSharedLayout(SharedLayout *layout); - private: - void addLayout(CentralLayout *layout); void cleanupOnStartup(QString path); //!remove deprecated or oldstyle config options - void clearSharedLayoutsFromCentralLists(); void clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds, bool bypassChecks = false); - void confirmDynamicSwitch(); //! it is used just in order to provide translations for the presets void ghostForTranslatedPresets(); - 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); + void setMenuLayouts(QStringList layouts); private: - bool m_multipleModeInitialized{false}; - - QString m_currentLayoutNameInMultiEnvironment; - QString m_shouldSwitchToLayout; - - QStringList m_layouts; - QStringList m_menuLayouts; QStringList m_presetsPaths; - QStringList m_sharedLayoutIds; - - QHash m_assignedLayouts; - - QTimer m_dynamicSwitchTimer; QPointer m_latteSettingsDialog; Latte::Corona *m_corona{nullptr}; Importer *m_importer{nullptr}; LaunchersSignals *m_launchersSignals{nullptr}; - - QList m_centralLayouts; - QList m_sharedLayouts; - - KActivities::Controller *m_activitiesController; + Synchronizer *m_synchronizer{nullptr}; friend class Latte::SettingsDialog; + friend class Synchronizer; }; } } #endif // LAYOUTSMANAGER_H diff --git a/app/layouts/manager.cpp b/app/layouts/synchronizer.cpp similarity index 54% copy from app/layouts/manager.cpp copy to app/layouts/synchronizer.cpp index 25c1cc8d..9ade660b 100644 --- a/app/layouts/manager.cpp +++ b/app/layouts/synchronizer.cpp @@ -1,1226 +1,868 @@ /* -* Copyright 2017 Smith AR -* Michail Vourlakos -* -* 2019 Michail Vourlakos +* 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 "manager.h" +#include "synchronizer.h" -// local +//! local #include "importer.h" -#include "launcherssignals.h" -#include "../infoview.h" -#include "../screenpool.h" -#include "../layout/abstractlayout.h" +#include "manager.h" +#include "../lattecorona.h" #include "../layout/centrallayout.h" #include "../layout/genericlayout.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 { namespace Layouts { -const int MultipleLayoutsPresetId = 10; - -Manager::Manager(QObject *parent) +Synchronizer::Synchronizer(QObject *parent) : QObject(parent), - m_importer(new Importer(this)), - m_launchersSignals(new LaunchersSignals(this)), - m_activitiesController(new KActivities::Controller(this)) + m_activitiesController(new KActivities::Controller) { - m_corona = qobject_cast(parent); + m_manager = qobject_cast(parent); - if (m_corona) { - connect(m_corona->universalSettings(), &UniversalSettings::currentLayoutNameChanged, this, &Manager::currentLayoutNameChanged); - connect(m_corona->universalSettings(), &UniversalSettings::showInfoWindowChanged, this, &Manager::showInfoWindowChanged); + //! Dynamic Switching + m_dynamicSwitchTimer.setSingleShot(true); + updateDynamicSwitchInterval(); + connect(m_manager->corona()->universalSettings(), &UniversalSettings::showInfoWindowChanged, this, &Synchronizer::updateDynamicSwitchInterval); + connect(&m_dynamicSwitchTimer, &QTimer::timeout, this, &Synchronizer::confirmDynamicSwitch); - m_dynamicSwitchTimer.setSingleShot(true); - showInfoWindowChanged(); - connect(&m_dynamicSwitchTimer, &QTimer::timeout, this, &Manager::confirmDynamicSwitch); - } + //! KActivities tracking + connect(m_manager->corona()->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, + this, &Synchronizer::currentActivityChanged); + + connect(m_manager->corona()->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, + this, [&]() { + if (m_manager->memoryUsage() == Types::MultipleLayouts) { + syncMultipleLayoutsToActivities(); + } + }); } -Manager::~Manager() +Synchronizer::~Synchronizer() { - m_importer->deleteLater(); - m_launchersSignals->deleteLater(); - - unload(); - m_activitiesController->deleteLater(); } -void Manager::load() +bool Synchronizer::latteViewExists(Latte::View *view) const { - 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"); + for (const auto layout : m_centralLayouts) { + for (const auto &v : layout->latteViews()) { + if (v == view) { + return true; + } } - - 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); - } + return false; +} - qDebug() << "Latte is loading its layouts..."; +bool Synchronizer::layoutExists(QString layoutName) const +{ + return m_layouts.contains(layoutName); +} - connect(m_corona->m_activityConsumer, &KActivities::Consumer::currentActivityChanged, - this, &Manager::currentActivityChanged); - connect(m_corona->m_activityConsumer, &KActivities::Consumer::runningActivitiesChanged, - this, [&]() { - if (memoryUsage() == Types::MultipleLayouts) { - syncMultipleLayoutsToActivities(); +bool Synchronizer::layoutIsAssigned(QString layoutName) +{ + QHashIterator i(m_assignedLayouts); + + while (i.hasNext()) { + i.next(); + + if (i.value() == layoutName) { + return true; } - }); + } - loadLayouts(); + return false; } -void Manager::unload() +bool Synchronizer::registerAtSharedLayout(CentralLayout *central, QString id) { - //! Unload all CentralLayouts - while (!m_centralLayouts.isEmpty()) { - CentralLayout *layout = m_centralLayouts.at(0); - unloadCentralLayout(layout); + if (m_manager->memoryUsage() == Types::SingleLayout || centralLayout(id)) { + //! if memory is functioning to SINGLE mode OR shared layout has already + //! been loaded as CentralLayout + return false; } - m_multipleModeInitialized = false; + for (int i = 0; i < m_sharedLayouts.size(); ++i) { + SharedLayout *layout = m_sharedLayouts.at(i); - //! Remove no-needed temp files - QString temp1File = QDir::homePath() + "/.config/lattedock.copy1.bak"; - QString temp2File = QDir::homePath() + "/.config/lattedock.copy2.bak"; + if (layout->name() == id) { + layout->addCentralLayout(central); + return true; + } + } - QFile file1(temp1File); - QFile file2(temp2File); + //! If SharedLayout was not found, we must create it + SharedLayout *top = new SharedLayout(central, this, Importer::layoutFilePath(id)); + m_sharedLayouts.append(top); + top->importToCorona(); - if (file1.exists()) - file1.remove(); + connect(top, &SharedLayout::layoutDestroyed, this, &Synchronizer::unloadSharedLayout); - if (file2.exists()) - file2.remove(); + return true; } -void Manager::unloadCentralLayout(CentralLayout *layout) +int Synchronizer::centralLayoutPos(QString id) const { - int pos = m_centralLayouts.indexOf(layout); - - if (pos>=0) { - CentralLayout *central = m_centralLayouts.takeAt(0); - - if (m_multipleModeInitialized) { - central->syncToLayoutFile(true); - } + for (int i = 0; i < m_centralLayouts.size(); ++i) { + CentralLayout *layout = m_centralLayouts.at(i); - central->unloadContainments(); - central->unloadLatteViews(); + if (layout->name() == id) { - if (m_multipleModeInitialized) { - clearUnloadedContainmentsFromLinkedFile(central->unloadedContainmentsIds(), true); + return i; } - - delete central; } + + return -1; } -void Manager::unloadSharedLayout(SharedLayout *layout) +QString Synchronizer::currentLayoutName() const { - if (m_sharedLayouts.contains(layout)) { - disconnect(layout, &SharedLayout::layoutDestroyed, this, &Manager::unloadSharedLayout); - int pos = m_sharedLayouts.indexOf(layout); - SharedLayout *shared = m_sharedLayouts.takeAt(pos); - shared->syncToLayoutFile(true); - shared->unloadContainments(); - shared->unloadLatteViews(); - clearUnloadedContainmentsFromLinkedFile(shared->unloadedContainmentsIds(), true); - - delete layout; + if (m_manager->memoryUsage() == Types::SingleLayout) { + return m_manager->corona()->universalSettings()->currentLayoutName(); + } else if (m_manager->memoryUsage() == Types::MultipleLayouts) { + return currentLayoutNameInMultiEnvironment(); } + + return QString(); } +QString Synchronizer::currentLayoutNameInMultiEnvironment() const +{ + return m_currentLayoutNameInMultiEnvironment; +} -Latte::Corona *Manager::corona() +QString Synchronizer::layoutPath(QString layoutName) { - return m_corona; + QString path = QDir::homePath() + "/.config/latte/" + layoutName + ".layout.latte"; + + if (!QFile(path).exists()) { + path = ""; + } + + return path; } -Importer *Manager::importer() +QStringList Synchronizer::activities() { - return m_importer; + return m_manager->corona()->activitiesConsumer()->activities(); } -LaunchersSignals *Manager::launchersSignals() +QStringList Synchronizer::runningActivities() { - return m_launchersSignals; + return m_manager->corona()->activitiesConsumer()->runningActivities(); } -QString Manager::currentLayoutName() const +QStringList Synchronizer::orphanedActivities() { - if (memoryUsage() == Types::SingleLayout) { - return m_corona->universalSettings()->currentLayoutName(); - } else if (memoryUsage() == Types::MultipleLayouts) { - return m_currentLayoutNameInMultiEnvironment; + QStringList orphans; + + for (const auto &activity : activities()) { + if (m_assignedLayouts[activity].isEmpty()) { + orphans.append(activity); + } } - return QString(); + return orphans; } -QString Manager::defaultLayoutName() const +QStringList Synchronizer::centralLayoutsNames() { - QByteArray presetNameOrig = QString("preset" + QString::number(1)).toUtf8(); - QString presetPath = m_corona->kPackage().filePath(presetNameOrig); - QString presetName = CentralLayout::layoutName(presetPath); - QByteArray presetNameChars = presetName.toUtf8(); - presetName = i18n(presetNameChars); + QStringList names; - return presetName; -} + if (m_manager->memoryUsage() == Types::SingleLayout) { + names << currentLayoutName(); + } else { + for (int i = 0; i < m_centralLayouts.size(); ++i) { + CentralLayout *layout = m_centralLayouts.at(i); + names << layout->name(); + } + } -bool Manager::layoutExists(QString layoutName) const -{ - return m_layouts.contains(layoutName); + return names; } -QStringList Manager::layouts() const +QStringList Synchronizer::layouts() const { return m_layouts; } -QStringList Manager::menuLayouts() const +QStringList Synchronizer::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) { + if (!fixedMenuLayouts.contains(currentLayoutName()) && m_manager->memoryUsage() == Types::SingleLayout) { fixedMenuLayouts.prepend(currentLayoutName()); - } else if (memoryUsage() == Types::MultipleLayouts) { + } else if (m_manager->memoryUsage() == Types::MultipleLayouts) { for (const auto layout : m_centralLayouts) { if (!fixedMenuLayouts.contains(layout->name())) { fixedMenuLayouts.prepend(layout->name()); } } } return fixedMenuLayouts; } -void Manager::setMenuLayouts(QStringList layouts) +void Synchronizer::setMenuLayouts(QStringList layouts) { if (m_menuLayouts == layouts) { return; } m_menuLayouts = layouts; emit menuLayoutsChanged(); } -QStringList Manager::activities() +QString Synchronizer::shouldSwitchToLayout(QString activityId) { - return m_corona->m_activityConsumer->activities(); -} + if (m_assignedLayouts.contains(activityId) && m_assignedLayouts[activityId] != currentLayoutName()) { + return m_assignedLayouts[activityId]; + } else if (!m_assignedLayouts.contains(activityId) && !m_manager->corona()->universalSettings()->lastNonAssignedLayoutName().isEmpty() + && m_manager->corona()->universalSettings()->lastNonAssignedLayoutName() != currentLayoutName()) { + return m_manager->corona()->universalSettings()->lastNonAssignedLayoutName(); + } -QStringList Manager::runningActivities() -{ - return m_corona->m_activityConsumer->runningActivities(); + return QString(); } -QStringList Manager::orphanedActivities() +QStringList Synchronizer::sharedLayoutsNames() { - QStringList orphans; + QStringList names; - for (const auto &activity : activities()) { - if (m_assignedLayouts[activity].isEmpty()) { - orphans.append(activity); - } + for (int i = 0; i < m_sharedLayouts.size(); ++i) { + SharedLayout *layout = m_sharedLayouts.at(i); + names << layout->name(); } - return orphans; + return names; } -QStringList Manager::presetsPaths() const +QStringList Synchronizer::storedSharedLayouts() const { - return m_presetsPaths; + return m_sharedLayoutIds; } -QString Manager::layoutPath(QString layoutName) +QStringList Synchronizer::validActivities(QStringList currentList) { - QString path = QDir::homePath() + "/.config/latte/" + layoutName + ".layout.latte"; + QStringList validIds; - if (!QFile(path).exists()) { - path = ""; + for (const auto &activity : currentList) { + if (activities().contains(activity)) { + validIds.append(activity); + } } - return path; -} - -Types::LayoutsMemoryUsage Manager::memoryUsage() const -{ - return m_corona->universalSettings()->layoutsMemoryUsage(); + return validIds; } -int Manager::layoutsMemoryUsage() +CentralLayout *Synchronizer::centralLayout(QString id) const { - return (int)m_corona->universalSettings()->layoutsMemoryUsage(); -} + for (int i = 0; i < m_centralLayouts.size(); ++i) { + CentralLayout *layout = m_centralLayouts.at(i); -void Manager::setMemoryUsage(Types::LayoutsMemoryUsage memoryUsage) -{ - m_corona->universalSettings()->setLayoutsMemoryUsage(memoryUsage); -} + if (layout->name() == id) { -bool Manager::latteViewExists(Latte::View *view) const -{ - for (const auto layout : m_centralLayouts) { - for (const auto &v : layout->latteViews()) { - if (v == view) { - return true; - } + return layout; } } - return false; + return nullptr; } -QStringList Manager::centralLayoutsNames() +CentralLayout *Synchronizer::currentLayout() const { - QStringList names; - - if (memoryUsage() == Types::SingleLayout) { - names << currentLayoutName(); + if (m_manager->memoryUsage() == Types::SingleLayout) { + return m_centralLayouts.at(0); } else { - for (int i = 0; i < m_centralLayouts.size(); ++i) { - CentralLayout *layout = m_centralLayouts.at(i); - names << layout->name(); + for (auto layout : m_centralLayouts) { + if (layout->activities().contains(m_manager->corona()->activitiesConsumer()->currentActivity())) { + return layout; + } } - } - return names; - -} - -QStringList Manager::sharedLayoutsNames() -{ - QStringList names; - - for (int i = 0; i < m_sharedLayouts.size(); ++i) { - SharedLayout *layout = m_sharedLayouts.at(i); - names << layout->name(); + for (auto layout : m_centralLayouts) { + if (layout->activities().isEmpty()) { + return layout; + } + } } - return names; -} - -QStringList Manager::storedSharedLayouts() const -{ - return m_sharedLayoutIds; + return nullptr; } -Layout::GenericLayout *Manager::layout(QString id) const +Layout::GenericLayout *Synchronizer::layout(QString id) const { Layout::GenericLayout *l = centralLayout(id); if (!l) { l = sharedLayout(id); } return l; } - -CentralLayout *Manager::centralLayout(QString id) const +SharedLayout *Synchronizer::sharedLayout(QString id) const { - for (int i = 0; i < m_centralLayouts.size(); ++i) { - CentralLayout *layout = m_centralLayouts.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; } -int Manager::centralLayoutPos(QString id) const +void Synchronizer::addLayout(CentralLayout *layout) { - for (int i = 0; i < m_centralLayouts.size(); ++i) { - CentralLayout *layout = m_centralLayouts.at(i); - - if (layout->name() == id) { - - return i; - } + if (!m_centralLayouts.contains(layout)) { + m_centralLayouts.append(layout); + layout->initToCorona(m_manager->corona()); } - - return -1; } -SharedLayout *Manager::sharedLayout(QString id) const +void Synchronizer::clearSharedLayoutsFromCentralLists() { - for (int i = 0; i < m_sharedLayouts.size(); ++i) { - SharedLayout *layout = m_sharedLayouts.at(i); + QStringList unassign; - if (layout->name() == id) { - return layout; + for(const QString &name : m_sharedLayoutIds) { + //! remove from ContextMenu + m_menuLayouts.removeAll(name); + + //! remove from layouts assigned to activities + QHashIterator i(m_assignedLayouts); + + while (i.hasNext()) { + i.next(); + + if (i.value() == name) { + unassign << i.key(); + } } } - return nullptr; + for(const QString &activity : unassign) { + m_assignedLayouts.remove(activity); + } } -bool Manager::registerAtSharedLayout(CentralLayout *central, QString id) +void Synchronizer::confirmDynamicSwitch() { - if (memoryUsage() == Types::SingleLayout || centralLayout(id)) { - //! if memory is functioning to SINGLE mode OR shared layout has already - //! been loaded as CentralLayout - return false; - } + QString tempShouldSwitch = shouldSwitchToLayout(m_manager->corona()->activitiesConsumer()->currentActivity()); - for (int i = 0; i < m_sharedLayouts.size(); ++i) { - SharedLayout *layout = m_sharedLayouts.at(i); - - if (layout->name() == id) { - layout->addCentralLayout(central); - return true; - } + if (tempShouldSwitch.isEmpty()) { + return; } - //! If SharedLayout was not found, we must create it - SharedLayout *top = new SharedLayout(central, this, Importer::layoutFilePath(id)); - m_sharedLayouts.append(top); - top->importToCorona(); - - connect(top, &SharedLayout::layoutDestroyed, this, &Manager::unloadSharedLayout); + if (m_shouldSwitchToLayout == tempShouldSwitch && m_shouldSwitchToLayout != currentLayoutName()) { + qDebug() << "dynamic switch to layout :: " << m_shouldSwitchToLayout; - return true; -} + emit currentLayoutIsSwitching(currentLayoutName()); -CentralLayout *Manager::currentLayout() const -{ - if (memoryUsage() == Types::SingleLayout) { - return m_centralLayouts.at(0); - } else { - for (auto layout : m_centralLayouts) { - if (layout->activities().contains(m_corona->m_activityConsumer->currentActivity())) { - return layout; - } + if (m_manager->corona()->universalSettings()->showInfoWindow()) { + m_manager->showInfoWindow(i18n("Switching to layout %0 ...").arg(m_shouldSwitchToLayout), 4000); } - for (auto layout : m_centralLayouts) { - if (layout->activities().isEmpty()) { - return layout; - } - } + QTimer::singleShot(500, [this, tempShouldSwitch]() { + switchToLayout(tempShouldSwitch); + }); + } else { + m_shouldSwitchToLayout = tempShouldSwitch; + m_dynamicSwitchTimer.start(); } - - return nullptr; } -void Manager::updateCurrentLayoutNameInMultiEnvironment() +void Synchronizer::currentActivityChanged(const QString &id) { - for (const auto layout : m_centralLayouts) { - if (layout->activities().contains(m_corona->activitiesConsumer()->currentActivity())) { - m_currentLayoutNameInMultiEnvironment = layout->name(); - emit currentLayoutNameChanged(); - return; - } + if (m_manager->memoryUsage() == Types::SingleLayout) { + qDebug() << "activity changed :: " << id; + + m_shouldSwitchToLayout = shouldSwitchToLayout(id); + + m_dynamicSwitchTimer.start(); + } else if (m_manager->memoryUsage() == Types::MultipleLayouts) { + updateCurrentLayoutNameInMultiEnvironment(); } +} +void Synchronizer::hideAllViews() +{ for (const auto layout : m_centralLayouts) { - if (layout->activities().isEmpty()) { - m_currentLayoutNameInMultiEnvironment = layout->name(); - emit currentLayoutNameChanged(); - return; - } + emit currentLayoutIsSwitching(layout->name()); } } -void Manager::currentActivityChanged(const QString &id) +void Synchronizer::pauseLayout(QString layoutName) { - if (memoryUsage() == Types::SingleLayout) { - qDebug() << "activity changed :: " << id; + if (m_manager->memoryUsage() == Types::MultipleLayouts) { + CentralLayout *layout = centralLayout(layoutName); - m_shouldSwitchToLayout = shouldSwitchToLayout(id); + if (layout && !layout->activities().isEmpty()) { + int i = 0; - m_dynamicSwitchTimer.start(); - } else if (memoryUsage() == Types::MultipleLayouts) { - updateCurrentLayoutNameInMultiEnvironment(); + 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 Manager::showInfoWindowChanged() +void Synchronizer::syncActiveLayoutsToOriginalFiles() { - if (m_corona->universalSettings()->showInfoWindow()) { - m_dynamicSwitchTimer.setInterval(1800); - } else { - m_dynamicSwitchTimer.setInterval(2300); + if (m_manager->memoryUsage() == Types::MultipleLayouts) { + for (const auto layout : m_centralLayouts) { + layout->syncToLayoutFile(); + } + + for (const auto layout : m_sharedLayouts) { + layout->syncToLayoutFile(); + } } } -QString Manager::shouldSwitchToLayout(QString activityId) +void Synchronizer::syncLatteViewsToScreens() { - 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(); + for (const auto layout : m_sharedLayouts) { + layout->syncLatteViewsToScreens(); } - return QString(); + for (const auto layout : m_centralLayouts) { + layout->syncLatteViewsToScreens(); + } } -void Manager::confirmDynamicSwitch() +void Synchronizer::unloadCentralLayout(CentralLayout *layout) { - QString tempShouldSwitch = shouldSwitchToLayout(m_corona->m_activityConsumer->currentActivity()); + int pos = m_centralLayouts.indexOf(layout); - if (tempShouldSwitch.isEmpty()) { - return; - } + if (pos>=0) { + CentralLayout *central = m_centralLayouts.takeAt(0); - if (m_shouldSwitchToLayout == tempShouldSwitch && m_shouldSwitchToLayout != currentLayoutName()) { - qDebug() << "dynamic switch to layout :: " << m_shouldSwitchToLayout; + if (m_multipleModeInitialized) { + central->syncToLayoutFile(true); + } - emit currentLayoutIsSwitching(currentLayoutName()); + central->unloadContainments(); + central->unloadLatteViews(); - if (m_corona->universalSettings()->showInfoWindow()) { - showInfoWindow(i18n("Switching to layout %0 ...").arg(m_shouldSwitchToLayout), 4000); + if (m_multipleModeInitialized) { + m_manager->clearUnloadedContainmentsFromLinkedFile(central->unloadedContainmentsIds(), true); } - QTimer::singleShot(500, [this, tempShouldSwitch]() { - switchToLayout(tempShouldSwitch); - }); - } else { - m_shouldSwitchToLayout = tempShouldSwitch; - m_dynamicSwitchTimer.start(); + delete central; + } +} + +void Synchronizer::unloadSharedLayout(SharedLayout *layout) +{ + if (m_sharedLayouts.contains(layout)) { + disconnect(layout, &SharedLayout::layoutDestroyed, this, &Synchronizer::unloadSharedLayout); + int pos = m_sharedLayouts.indexOf(layout); + SharedLayout *shared = m_sharedLayouts.takeAt(pos); + shared->syncToLayoutFile(true); + shared->unloadContainments(); + shared->unloadLatteViews(); + m_manager->clearUnloadedContainmentsFromLinkedFile(shared->unloadedContainmentsIds(), true); + + delete layout; } } -void Manager::loadLayouts() + +void Synchronizer::loadLayouts() { m_layouts.clear(); m_menuLayouts.clear(); - m_presetsPaths.clear(); m_assignedLayouts.clear(); m_sharedLayoutIds.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) { if (layout.contains(Layout::AbstractLayout::MultipleLayoutsName)) { //! IMPORTANT: DONT ADD MultipleLayouts hidden file in layouts list continue; } CentralLayout centralLayout(this, layoutDir.absolutePath() + "/" + layout); QStringList validActivityIds = validActivities(centralLayout.activities()); centralLayout.setActivities(validActivityIds); for (const auto &activity : validActivityIds) { m_assignedLayouts[activity] = centralLayout.name(); } m_layouts.append(centralLayout.name()); if (centralLayout.showInMenu()) { m_menuLayouts.append(centralLayout.name()); } QString sharedName = centralLayout.sharedLayoutName(); if (!sharedName.isEmpty() && !m_sharedLayoutIds.contains(sharedName)) { m_sharedLayoutIds << sharedName; } } //! Shared Layouts should not be used for Activities->Layouts assignments or published lists clearSharedLayoutsFromCentralLists(); - 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 Manager::clearSharedLayoutsFromCentralLists() -{ - QStringList unassign; - - for(const QString &name : m_sharedLayoutIds) { - //! remove from ContextMenu - m_menuLayouts.removeAll(name); - - //! remove from layouts assigned to activities - QHashIterator i(m_assignedLayouts); - - while (i.hasNext()) { - i.next(); - - if (i.value() == name) { - unassign << i.key(); - } - } - } - - for(const QString &activity : unassign) { - m_assignedLayouts.remove(activity); - } - - -} - -void Manager::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 Manager::loadLatteLayout(QString layoutPath) +void Synchronizer::unloadLayouts() { - qDebug() << " -------------------------------------------------------------------- "; - qDebug() << " -------------------------------------------------------------------- "; - - if (m_corona->containments().size() > 0) { - qDebug() << "LOAD LATTE LAYOUT ::: There are still containments present !!!! :: " << m_corona->containments().size(); + //! Unload all CentralLayouts + while (!m_centralLayouts.isEmpty()) { + CentralLayout *layout = m_centralLayouts.at(0); + unloadCentralLayout(layout); } - if (!layoutPath.isEmpty() && m_corona->containments().size() == 0) { - cleanupOnStartup(layoutPath); - qDebug() << "LOADING CORONA LAYOUT:" << layoutPath; - m_corona->loadLayout(layoutPath); - } + m_multipleModeInitialized = false; } -void Manager::cleanupOnStartup(QString path) +void Synchronizer::updateCurrentLayoutNameInMultiEnvironment() { - 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 layout : m_centralLayouts) { + if (layout->activities().contains(m_manager->corona()->activitiesConsumer()->currentActivity())) { + m_currentLayoutNameInMultiEnvironment = layout->name(); + emit currentLayoutNameChanged(); + return; } } - for (const auto &cId : removeContaimentsList) { - containmentGroups.group(cId).deleteGroup(); - } - - - actionGroups.sync(); - containmentGroups.sync(); -} - - -void Manager::showAboutDialog() -{ - m_corona->aboutApplication(); -} - -void Manager::importLatteLayout(QString layoutPath) -{ - //! This might not be needed as it is Layout responsibility -} - -void Manager::hideAllViews() -{ for (const auto layout : m_centralLayouts) { - emit currentLayoutIsSwitching(layout->name()); + if (layout->activities().isEmpty()) { + m_currentLayoutNameInMultiEnvironment = layout->name(); + emit currentLayoutNameChanged(); + return; + } } } -void Manager::addLayout(CentralLayout *layout) +void Synchronizer::updateDynamicSwitchInterval() { - if (!m_centralLayouts.contains(layout)) { - m_centralLayouts.append(layout); - layout->initToCorona(m_corona); + if (m_manager->corona()->universalSettings()->showInfoWindow()) { + m_dynamicSwitchTimer.setInterval(1800); + } else { + m_dynamicSwitchTimer.setInterval(2300); } } -bool Manager::switchToLayout(QString layoutName, int previousMemoryUsage) +bool Synchronizer::switchToLayout(QString layoutName, int previousMemoryUsage) { if (m_centralLayouts.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) { + if (m_manager->memoryUsage() == Types::MultipleLayouts && previousMemoryUsage == -1) { CentralLayout *layout = centralLayout(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())) { + if (!appliedActivities.contains(m_manager->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_centralLayouts) { emit currentLayoutIsSwitching(layout->name()); } 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")); + lPath = m_manager->newLayout(i18n("Alternative"), i18n("Default")); } if (!lPath.isEmpty()) { - if (memoryUsage() == Types::SingleLayout) { + if (m_manager->memoryUsage() == Types::SingleLayout) { // emit currentLayoutIsSwitching(currentLayoutName()); - } else if (memoryUsage() == Types::MultipleLayouts && layoutName != Layout::AbstractLayout::MultipleLayoutsName) { + } else if (m_manager->memoryUsage() == Types::MultipleLayouts && layoutName != Layout::AbstractLayout::MultipleLayoutsName) { CentralLayout toLayout(this, lPath); QStringList toActivities = toLayout.activities(); CentralLayout *centralForOrphans{nullptr}; for (const auto fromLayout : m_centralLayouts) { if (fromLayout->activities().isEmpty()) { centralForOrphans = fromLayout; break; } } if (toActivities.isEmpty() && centralForOrphans && (toLayout.name() != centralForOrphans->name())) { emit currentLayoutIsSwitching(centralForOrphans->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 && !m_multipleModeInitialized) { + if (m_manager->memoryUsage() == Types::MultipleLayouts && !m_multipleModeInitialized) { initializingMultipleLayouts = true; } - if (memoryUsage() == Types::SingleLayout || initializingMultipleLayouts || previousMemoryUsage == Types::MultipleLayouts) { - unload(); + if (m_manager->memoryUsage() == Types::SingleLayout || initializingMultipleLayouts || previousMemoryUsage == Types::MultipleLayouts) { + unloadLayouts(); if (initializingMultipleLayouts) { fixedLayoutName = QString(Layout::AbstractLayout::MultipleLayoutsName); fixedLPath = layoutPath(fixedLayoutName); } if (fixedLayoutName != Layout::AbstractLayout::MultipleLayoutsName) { CentralLayout *newLayout = new CentralLayout(this, fixedLPath, fixedLayoutName); addLayout(newLayout); } - loadLatteLayout(fixedLPath); + m_manager->loadLatteLayout(fixedLPath); if (initializingMultipleLayouts) { m_multipleModeInitialized = true; } emit centralLayoutsChanged(); } - if (memoryUsage() == Types::MultipleLayouts) { + if (m_manager->memoryUsage() == Types::MultipleLayouts) { if (!initializingMultipleLayouts && !centralLayout(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 CentralLayout 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())) { + || !assignedActivities.contains(m_manager->corona()->activitiesConsumer()->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); + m_manager->corona()->universalSettings()->setCurrentLayoutName(layoutName); if (!layoutIsAssigned(layoutName)) { - m_corona->universalSettings()->setLastNonAssignedLayoutName(layoutName); + m_manager->corona()->universalSettings()->setLastNonAssignedLayoutName(layoutName); } }); } else { qDebug() << "Layout : " << layoutName << " was not found..."; } return true; } -void Manager::syncMultipleLayoutsToActivities(QString layoutForOrphans) +void Synchronizer::syncMultipleLayoutsToActivities(QString layoutForOrphans) { qDebug() << " ---- --------- ------ syncMultipleLayoutsToActivities ------- "; qDebug() << " ---- --------- ------ ------------------------------- ------- "; QStringList layoutsToUnload; QStringList layoutsToLoad; bool allRunningActivitiesWillBeReserved{true}; if (layoutForOrphans.isEmpty() || m_assignedLayouts.values().contains(layoutForOrphans)) { - layoutForOrphans = m_corona->universalSettings()->lastNonAssignedLayoutName(); + layoutForOrphans = m_manager->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_centralLayouts) { 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) { CentralLayout *layout = centralLayout(layoutName); int posLayout = centralLayoutPos(layoutName); if (posLayout >= 0) { qDebug() << "REMOVING LAYOUT ::::: " << layoutName; m_centralLayouts.removeAt(posLayout); layout->syncToLayoutFile(true); layout->unloadContainments(); layout->unloadLatteViews(); - clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds()); + m_manager->clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds()); delete layout; } } //! Add Layout for orphan activities if (!allRunningActivitiesWillBeReserved) { if (!centralLayout(layoutForOrphans)) { CentralLayout *newLayout = new CentralLayout(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 (!centralLayout(layoutName)) { CentralLayout *newLayout = new CentralLayout(this, QString(layoutPath(layoutName)), layoutName); if (newLayout) { qDebug() << "ACTIVATING LAYOUT ::::: " << layoutName; addLayout(newLayout); newLayout->importToCorona(); - if (m_corona->universalSettings()->showInfoWindow()) { - showInfoWindow(i18n("Activating layout: %0 ...").arg(newLayout->name()), 5000, newLayout->appliedActivities()); + if (m_manager->corona()->universalSettings()->showInfoWindow()) { + m_manager->showInfoWindow(i18n("Activating layout: %0 ...").arg(newLayout->name()), 5000, newLayout->appliedActivities()); } } } } updateCurrentLayoutNameInMultiEnvironment(); emit centralLayoutsChanged(); } -void Manager::pauseLayout(QString layoutName) -{ - if (memoryUsage() == Types::MultipleLayouts) { - CentralLayout *layout = centralLayout(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 Manager::syncActiveLayoutsToOriginalFiles() -{ - if (memoryUsage() == Types::MultipleLayouts) { - for (const auto layout : m_centralLayouts) { - layout->syncToLayoutFile(); - } - - for (const auto layout : m_sharedLayouts) { - layout->syncToLayoutFile(); - } - } -} - -void Manager::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 Manager::syncLatteViewsToScreens() -{ - for (const auto layout : m_sharedLayouts) { - layout->syncLatteViewsToScreens(); - } - - for (const auto layout : m_centralLayouts) { - layout->syncLatteViewsToScreens(); - } -} - -QString Manager::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; -} - -void Manager::importDefaultLayout(bool newInstanceIfPresent) -{ - importPreset(1, newInstanceIfPresent); - - if (newInstanceIfPresent) { - loadLayouts(); - } -} - -void Manager::importPresets(bool includeDefault) -{ - int start = 1; - - if (!includeDefault) { - start = 2; - } - - for (int i = start; i <= 4; ++i) { - importPreset(i, false); - } -} - -void Manager::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 = Layout::AbstractLayout::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 Manager::validActivities(QStringList currentList) -{ - QStringList validIds; - - for (const auto &activity : currentList) { - if (activities().contains(activity)) { - validIds.append(activity); - } - } - - return validIds; -} - -bool Manager::layoutIsAssigned(QString layoutName) -{ - QHashIterator i(m_assignedLayouts); - - while (i.hasNext()) { - i.next(); - - if (i.value() == layoutName) { - return true; - } - } - - return false; -} - -void Manager::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 Manager::hideLatteSettingsDialog() -{ - if (m_latteSettingsDialog) { - m_latteSettingsDialog->deleteLater(); - m_latteSettingsDialog = nullptr; - } -} - -void Manager::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 Manager::ghostForTranslatedPresets() -{ - QString preset1 = i18n("Default"); - QString preset2 = i18n("Plasma"); - QString preset3 = i18n("Unity"); - QString preset4 = i18n("Extended"); -} - -} } +} // end of namespace diff --git a/app/layouts/manager.h b/app/layouts/synchronizer.h similarity index 50% copy from app/layouts/manager.h copy to app/layouts/synchronizer.h index 1313b6b8..c2055574 100644 --- a/app/layouts/manager.h +++ b/app/layouts/synchronizer.h @@ -1,219 +1,146 @@ /* -* Copyright 2017 Smith AR -* Michail Vourlakos +* 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 LAYOUTSMANAGER_H -#define LAYOUTSMANAGER_H - -// local -#include "launcherssignals.h" -#include "settings/settingsdialog.h" +#ifndef LAYOUTSSYNCHRONIZER_H +#define LAYOUTSSYNCHRONIZER_H // Qt -#include #include -#include - -// KDE -#include +#include +#include -namespace Plasma { -class Containment; -class Types; -} - -namespace KActivities { -class Controller; -} namespace Latte { -class Corona; class CentralLayout; class SharedLayout; class View; -namespace Layout { +namespace Layout{ class GenericLayout; } namespace Layouts { -class Importer; -class LaunchersSignals; +class Manager; } } +namespace KActivities { +class Controller; +} + namespace Latte { namespace Layouts { -//! This class is responsible to manipulate all layouts. -//! add,remove,rename, update configurations etc. -class Manager : public QObject -{ +class Synchronizer : 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: - Manager(QObject *parent = nullptr); - ~Manager() override; - - Latte::Corona *corona(); - Importer *importer(); + Synchronizer(QObject *parent); + ~Synchronizer() override; - void load(); - void loadLayoutOnStartup(QString layoutName); - void unload(); - void unloadCentralLayout(CentralLayout *layout); + void loadLayouts(); + void unloadLayouts(); void hideAllViews(); void pauseLayout(QString layoutName); - void syncLatteViewsToScreens(); void syncActiveLayoutsToOriginalFiles(); + void syncLatteViewsToScreens(); + void syncMultipleLayoutsToActivities(QString layoutForOrphans = QString()); bool latteViewExists(Latte::View *view) const; bool layoutExists(QString layoutName) const; + bool registerAtSharedLayout(CentralLayout *central, QString id); + //! switch to specified layout, default previousMemoryUsage means that it didn't change + bool switchToLayout(QString layoutName, int previousMemoryUsage = -1); - QString shouldSwitchToLayout(QString activityId); + int centralLayoutPos(QString id) const; QString currentLayoutName() const; - QString defaultLayoutName() const; + QString currentLayoutNameInMultiEnvironment() const; + QString shouldSwitchToLayout(QString activityId); + QStringList centralLayoutsNames(); QStringList layouts() const; QStringList menuLayouts() const; - QStringList presetsPaths() const; + void setMenuLayouts(QStringList layouts); + QStringList sharedLayoutsNames(); QStringList storedSharedLayouts() const; - Types::LayoutsMemoryUsage memoryUsage() const; - void setMemoryUsage(Types::LayoutsMemoryUsage memoryUsage); - - //! returns an central layout with that #id (name), it returns null if such - //! layout cant be found - CentralLayout *centralLayout(QString id) const; - int centralLayoutPos(QString id) const; - SharedLayout *sharedLayout(QString id) const; - //! return an central 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 central layout based on activities and user preferences - CentralLayout *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 registerAtSharedLayout(CentralLayout *central, 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 centralLayoutsNames(); - Q_INVOKABLE QStringList sharedLayoutsNames(); + CentralLayout *currentLayout() const; + CentralLayout *centralLayout(QString id) const; + SharedLayout *sharedLayout(QString id) const; + Layout::GenericLayout *layout(QString id) const; signals: void centralLayoutsChanged(); - void currentLayoutChanged(); void currentLayoutNameChanged(); - void launchersSignalsChanged(); void layoutsChanged(); void menuLayoutsChanged(); + void runningActicitiesChanged(); void currentLayoutIsSwitching(QString layoutName); private slots: + void confirmDynamicSwitch(); + void updateDynamicSwitchInterval(); + void updateCurrentLayoutNameInMultiEnvironment(); + void currentActivityChanged(const QString &id); - void showInfoWindowChanged(); - void syncMultipleLayoutsToActivities(QString layoutForOrphans = QString()); - void unloadSharedLayout(SharedLayout *layout); private: - void addLayout(CentralLayout *layout); - void cleanupOnStartup(QString path); //!remove deprecated or oldstyle config options void clearSharedLayoutsFromCentralLists(); - void clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds, bool bypassChecks = false); - void confirmDynamicSwitch(); - - //! it is used just in order to provide translations for the presets - void ghostForTranslatedPresets(); - 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(); + void addLayout(CentralLayout *layout); + void unloadCentralLayout(CentralLayout *layout); + void unloadSharedLayout(SharedLayout *layout); bool layoutIsAssigned(QString layoutName); QString layoutPath(QString layoutName); QStringList validActivities(QStringList currentList); private: bool m_multipleModeInitialized{false}; QString m_currentLayoutNameInMultiEnvironment; QString m_shouldSwitchToLayout; QStringList m_layouts; QStringList m_menuLayouts; - QStringList m_presetsPaths; QStringList m_sharedLayoutIds; QHash m_assignedLayouts; QTimer m_dynamicSwitchTimer; - QPointer m_latteSettingsDialog; - - Latte::Corona *m_corona{nullptr}; - Importer *m_importer{nullptr}; - LaunchersSignals *m_launchersSignals{nullptr}; - QList m_centralLayouts; QList m_sharedLayouts; + Layouts::Manager *m_manager; KActivities::Controller *m_activitiesController; - - friend class Latte::SettingsDialog; }; } } -#endif // LAYOUTSMANAGER_H + +#endif diff --git a/app/settings/settingsdialog.cpp b/app/settings/settingsdialog.cpp index fc39bb8d..c4ff7633 100644 --- a/app/settings/settingsdialog.cpp +++ b/app/settings/settingsdialog.cpp @@ -1,2013 +1,2015 @@ /* * 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 "settingsdialog.h" // local #include "universalsettings.h" #include "ui_settingsdialog.h" #include "../lattecorona.h" #include "../layout/genericlayout.h" #include "../layout/centrallayout.h" #include "../layout/sharedlayout.h" #include "../layouts/importer.h" #include "../layouts/manager.h" +#include "../layouts/synchronizer.h" #include "../liblatte2/types.h" #include "../plasma/extended/theme.h" #include "delegates/activitiesdelegate.h" #include "delegates/checkboxdelegate.h" #include "delegates/colorcmbboxdelegate.h" #include "delegates/layoutnamedelegate.h" #include "delegates/shareddelegate.h" // Qt #include #include #include #include #include #include #include #include #include #include #include #include // KDE #include #include #include #include #include #include #include #include namespace Latte { const int IDCOLUMN = 0; const int HIDDENTEXTCOLUMN = 1; const int COLORCOLUMN = 2; const int NAMECOLUMN = 3; const int MENUCOLUMN = 4; const int BORDERSCOLUMN = 5; const int ACTIVITYCOLUMN = 6; const int SHAREDCOLUMN = 7; const int SCREENTRACKERDEFAULTVALUE = 2500; const int OUTLINEDEFAULTWIDTH = 1; const QChar CheckMark{0x2714}; SettingsDialog::SettingsDialog(QWidget *parent, Latte::Corona *corona) : QDialog(parent), ui(new Ui::SettingsDialog), m_corona(corona) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose, true); setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); resize(m_corona->universalSettings()->layoutsWindowSize()); connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked , this, &SettingsDialog::apply); connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked , this, &SettingsDialog::restoreDefaults); m_model = new QStandardItemModel(m_corona->layoutsManager()->layouts().count(), 6, this); ui->layoutsView->setModel(m_model); ui->layoutsView->horizontalHeader()->setStretchLastSection(true); ui->layoutsView->verticalHeader()->setVisible(false); connect(m_corona->layoutsManager(), &Layouts::Manager::currentLayoutNameChanged, this, &SettingsDialog::layoutsChanged); connect(m_corona->layoutsManager(), &Layouts::Manager::centralLayoutsChanged, this, &SettingsDialog::layoutsChanged); QString iconsPath(m_corona->kPackage().path() + "../../plasmoids/org.kde.latte.containment/contents/icons/"); //!find the available colors QDir layoutDir(iconsPath); QStringList filter; filter.append(QString("*print.jpg")); QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); QStringList colors; for (auto &file : files) { int colorEnd = file.lastIndexOf("print.jpg"); QString color = file.remove(colorEnd, 9); colors.append(color); } ui->layoutsView->setItemDelegateForColumn(NAMECOLUMN, new LayoutNameDelegate(this)); ui->layoutsView->setItemDelegateForColumn(COLORCOLUMN, new ColorCmbBoxDelegate(this, iconsPath, colors)); ui->layoutsView->setItemDelegateForColumn(MENUCOLUMN, new CheckBoxDelegate(this)); ui->layoutsView->setItemDelegateForColumn(BORDERSCOLUMN, new CheckBoxDelegate(this)); ui->layoutsView->setItemDelegateForColumn(ACTIVITYCOLUMN, new ActivitiesDelegate(this)); ui->layoutsView->setItemDelegateForColumn(SHAREDCOLUMN, new SharedDelegate(this)); m_inMemoryButtons = new QButtonGroup(this); m_inMemoryButtons->addButton(ui->singleToolBtn, Latte::Types::SingleLayout); m_inMemoryButtons->addButton(ui->multipleToolBtn, Latte::Types::MultipleLayouts); m_inMemoryButtons->setExclusive(true); if (KWindowSystem::isPlatformWayland()) { m_inMemoryButtons->button(Latte::Types::MultipleLayouts)->setEnabled(false); } m_mouseSensitivityButtons = new QButtonGroup(this); m_mouseSensitivityButtons->addButton(ui->lowSensitivityBtn, Latte::Types::LowSensitivity); m_mouseSensitivityButtons->addButton(ui->mediumSensitivityBtn, Latte::Types::MediumSensitivity); m_mouseSensitivityButtons->addButton(ui->highSensitivityBtn, Latte::Types::HighSensitivity); m_mouseSensitivityButtons->setExclusive(true); ui->screenTrackerSpinBox->setValue(m_corona->universalSettings()->screenTrackerInterval()); ui->outlineSpinBox->setValue(m_corona->themeExtended()->outlineWidth()); //! About Menu QMenuBar *menuBar = new QMenuBar(this); // QMenuBar *rightAlignedMenuBar = new QMenuBar(menuBar); layout()->setMenuBar(menuBar); //menuBar->setCornerWidget(rightAlignedMenuBar); QMenu *fileMenu = new QMenu(i18n("File"), menuBar); menuBar->addMenu(fileMenu); QMenu *helpMenu = new QMenu(i18n("Help"), menuBar); //rightAlignedMenuBar->addMenu(helpMenu); menuBar->addMenu(helpMenu); QAction *quitAction = fileMenu->addAction(i18n("Quit Latte")); quitAction->setIcon(QIcon::fromTheme("application-exit")); quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); QAction *aboutAction = helpMenu->addAction(i18n("About Latte")); aboutAction->setIcon(QIcon::fromTheme("latte-dock")); //! RTL support for labels in preferences if (qApp->layoutDirection() == Qt::RightToLeft) { ui->behaviorLbl->setAlignment(Qt::AlignRight | Qt::AlignTop); ui->mouseSensetivityLbl->setAlignment(Qt::AlignRight | Qt::AlignTop); ui->delayLbl->setAlignment(Qt::AlignRight | Qt::AlignTop); } loadSettings(); //! SIGNALS connect(m_model, &QStandardItemModel::itemChanged, this, &SettingsDialog::itemChanged); connect(ui->layoutsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, [&]() { updatePerLayoutButtonsState(); updateApplyButtonsState(); }); connect(m_inMemoryButtons, static_cast(&QButtonGroup::buttonToggled), [ = ](int id, bool checked) { updateApplyButtonsState(); updateSharedLayoutsStates(); updateSharedLayoutsUiElements(); }); connect(m_mouseSensitivityButtons, static_cast(&QButtonGroup::buttonToggled), [ = ](int id, bool checked) { updateApplyButtonsState(); }); connect(ui->screenTrackerSpinBox, QOverload::of(&QSpinBox::valueChanged), [ = ](int i) { updateApplyButtonsState(); }); connect(ui->outlineSpinBox, QOverload::of(&QSpinBox::valueChanged), [ = ](int i) { updateApplyButtonsState(); }); connect(ui->autostartChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState); connect(ui->metaPressChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState); connect(ui->metaPressHoldChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState); connect(ui->infoWindowChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &SettingsDialog::updateApplyButtonsState); connect(ui->noBordersForMaximizedChkBox, &QCheckBox::stateChanged, this, [&]() { bool noBordersForMaximized = ui->noBordersForMaximizedChkBox->isChecked(); if (noBordersForMaximized) { ui->layoutsView->setColumnHidden(BORDERSCOLUMN, false); } else { ui->layoutsView->setColumnHidden(BORDERSCOLUMN, true); } updateApplyButtonsState(); }); connect(aboutAction, &QAction::triggered, m_corona, &Latte::Corona::aboutApplication); connect(quitAction, &QAction::triggered, m_corona, &Latte::Corona::closeApplication); //! update all layouts view when runningActivities changed. This way we update immediately //! the running Activities in Activities checkboxes which are shown as bold connect(m_corona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() { ui->layoutsView->update(); }); blockDeleteOnActivityStopped(); } SettingsDialog::~SettingsDialog() { qDebug() << Q_FUNC_INFO; qDeleteAll(m_layouts); if (m_model) { delete m_model; } if (m_corona && m_corona->universalSettings()) { m_corona->universalSettings()->setLayoutsWindowSize(size()); QStringList columnWidths; columnWidths << QString::number(ui->layoutsView->columnWidth(COLORCOLUMN)); columnWidths << QString::number(ui->layoutsView->columnWidth(NAMECOLUMN)); columnWidths << QString::number(ui->layoutsView->columnWidth(MENUCOLUMN)); columnWidths << QString::number(ui->layoutsView->columnWidth(BORDERSCOLUMN)); Latte::Types::LayoutsMemoryUsage inMemoryOption = static_cast(m_inMemoryButtons->checkedId()); if (inMemoryOption == Latte::Types::MultipleLayouts) { columnWidths << QString::number(ui->layoutsView->columnWidth(ACTIVITYCOLUMN)); } else { //! In Single Mode, keed recorded value for ACTIVITYCOLUMN QStringList currentWidths = m_corona->universalSettings()->layoutsColumnWidths(); if (currentWidths.count()>=5) { columnWidths << currentWidths[4]; } } m_corona->universalSettings()->setLayoutsColumnWidths(columnWidths); } m_inMemoryButtons->deleteLater(); m_mouseSensitivityButtons->deleteLater(); for (const auto &tempDir : m_tempDirectories) { QDir tDir(tempDir); if (tDir.exists() && tempDir.startsWith("/tmp/")) { tDir.removeRecursively(); } } } void SettingsDialog::blockDeleteOnActivityStopped() { connect(m_corona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() { m_blockDeleteOnReject = true; m_activityClosedTimer.start(); }); m_activityClosedTimer.setSingleShot(true); m_activityClosedTimer.setInterval(500); connect(&m_activityClosedTimer, &QTimer::timeout, this, [&]() { m_blockDeleteOnReject = false; }); } QStringList SettingsDialog::activities() { return m_corona->layoutsManager()->activities(); } QStringList SettingsDialog::availableActivities() { return m_availableActivities; } QStringList SettingsDialog::availableSharesFor(int row) { QStringList availables; QStringList regs; for (int i = 0; i < m_model->rowCount(); ++i) { QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString(); QStringList shares = m_model->data(m_model->index(i, SHAREDCOLUMN), Qt::UserRole).toStringList(); if (i != row) { if (shares.isEmpty()) { availables << id; } else { regs << shares; } } } for (const auto r : regs) { availables.removeAll(r); } return availables; } void SettingsDialog::setCurrentPage(Types::LatteConfigPage page) { if (page == Types::LayoutPage) { ui->tabWidget->setCurrentIndex(0); } else if (page == Types::PreferencesPage) { ui->tabWidget->setCurrentIndex(1); } } void SettingsDialog::on_newButton_clicked() { qDebug() << Q_FUNC_INFO; //! find Default preset path for (const auto &preset : m_corona->layoutsManager()->presetsPaths()) { QString presetName = CentralLayout::layoutName(preset); if (presetName == "Default") { QByteArray presetNameChars = presetName.toUtf8(); const char *prset_str = presetNameChars.data(); presetName = uniqueLayoutName(i18n(prset_str)); addLayoutForFile(preset, presetName, true, false); break; } } } void SettingsDialog::on_copyButton_clicked() { qDebug() << Q_FUNC_INFO; int row = ui->layoutsView->currentIndex().row(); if (row < 0) { return; } //! Update original layout before copying if this layout is active if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { QString lName = (m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole)).toString(); Layout::GenericLayout *generic = m_corona->layoutsManager()->layout(lName); if (generic) { generic->syncToLayoutFile(); } } QString tempDir = uniqueTempDirectory(); QString id = m_model->data(m_model->index(row, IDCOLUMN), Qt::DisplayRole).toString(); QString color = m_model->data(m_model->index(row, COLORCOLUMN), Qt::BackgroundRole).toString(); QString textColor = m_model->data(m_model->index(row, COLORCOLUMN), Qt::UserRole).toString(); QString layoutName = uniqueLayoutName(m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole).toString()); bool menu = m_model->data(m_model->index(row, MENUCOLUMN), Qt::DisplayRole).toString() == CheckMark; bool disabledBorders = m_model->data(m_model->index(row, BORDERSCOLUMN), Qt::DisplayRole).toString() == CheckMark; QString copiedId = tempDir + "/" + layoutName + ".layout.latte"; QFile(id).copy(copiedId); QFileInfo newFileInfo(copiedId); if (newFileInfo.exists() && !newFileInfo.isWritable()) { QFile(copiedId).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } CentralLayout *settings = new CentralLayout(this, copiedId); m_layouts[copiedId] = settings; insertLayoutInfoAtRow(row + 1, copiedId, color, textColor, layoutName, menu, disabledBorders, QStringList(), false); ui->layoutsView->selectRow(row + 1); } void SettingsDialog::on_downloadButton_clicked() { qDebug() << Q_FUNC_INFO; KNS3::DownloadDialog dialog(QStringLiteral("latte-layouts.knsrc"), this); dialog.resize(m_corona->universalSettings()->downloadWindowSize()); dialog.exec(); bool layoutAdded{false}; if (!dialog.changedEntries().isEmpty() || !dialog.installedEntries().isEmpty()) { for (const auto &entry : dialog.installedEntries()) { for (const auto &entryFile : entry.installedFiles()) { Layouts::Importer::LatteFileVersion version = Layouts::Importer::fileVersion(entryFile); if (version == Layouts::Importer::LayoutVersion2) { layoutAdded = true; addLayoutForFile(entryFile); break; } } } } m_corona->universalSettings()->setDownloadWindowSize(dialog.size()); if (layoutAdded) { apply(); } } void SettingsDialog::on_removeButton_clicked() { qDebug() << Q_FUNC_INFO; int row = ui->layoutsView->currentIndex().row(); if (row < 0) { return; } QString layoutName = m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole).toString(); if (m_corona->layoutsManager()->centralLayout(layoutName)) { return; } m_model->removeRow(row); updateApplyButtonsState(); row = qMax(row - 1, 0); ui->layoutsView->selectRow(row); } void SettingsDialog::on_lockedButton_clicked() { qDebug() << Q_FUNC_INFO; int row = ui->layoutsView->currentIndex().row(); if (row < 0) { return; } bool lockedModel = m_model->data(m_model->index(row, NAMECOLUMN), Qt::UserRole).toBool(); m_model->setData(m_model->index(row, NAMECOLUMN), QVariant(!lockedModel), Qt::UserRole); updatePerLayoutButtonsState(); updateApplyButtonsState(); } void SettingsDialog::on_sharedButton_clicked() { qDebug() << Q_FUNC_INFO; int row = ui->layoutsView->currentIndex().row(); if (row < 0) { return; } if (isShared(row)) { m_model->setData(m_model->index(row, SHAREDCOLUMN), QStringList(), Qt::UserRole); } else { bool assigned{false}; QStringList assignedList; QStringList availableShares = availableSharesFor(row); for (const auto &id : availableShares) { QString name = nameForId(id); if (m_corona->layoutsManager()->layout(name)) { assignedList << id; m_model->setData(m_model->index(row, SHAREDCOLUMN), assignedList, Qt::UserRole); assigned = true; break; } } if (!assigned && availableShares.count()>0) { assignedList << availableShares[0]; m_model->setData(m_model->index(row, SHAREDCOLUMN), assignedList, Qt::UserRole); assigned = true; } } updatePerLayoutButtonsState(); updateApplyButtonsState(); } void SettingsDialog::on_importButton_clicked() { qDebug() << Q_FUNC_INFO; QFileDialog *fileDialog = new QFileDialog(this, i18nc("import layout/configuration", "Import Layout/Configuration") , QDir::homePath() , QStringLiteral("layout.latte")); fileDialog->setFileMode(QFileDialog::AnyFile); fileDialog->setAcceptMode(QFileDialog::AcceptOpen); fileDialog->setDefaultSuffix("layout.latte"); QStringList filters; filters << QString(i18nc("import latte layout", "Latte Dock Layout file v0.2") + "(*.layout.latte)") << QString(i18nc("import latte layouts/configuration", "Latte Dock Full Configuration file (v0.1, v0.2)") + "(*.latterc)"); fileDialog->setNameFilters(filters); connect(fileDialog, &QFileDialog::finished , fileDialog, &QFileDialog::deleteLater); connect(fileDialog, &QFileDialog::fileSelected , this, [&](const QString & file) { Layouts::Importer::LatteFileVersion version = Layouts::Importer::fileVersion(file); qDebug() << "VERSION :::: " << version; if (version == Layouts::Importer::LayoutVersion2) { addLayoutForFile(file); } else if (version == Layouts::Importer::ConfigVersion1) { auto msg = new QMessageBox(this); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Import: Configuration file version v0.1")); msg->setText( i18n("You are going to import an old version v0.1 configuration file.
Be careful, importing the entire configuration will erase all your current configuration!!!

Alternative, you can import safely from this file
only the contained layouts...
")); msg->setStandardButtons(QMessageBox::Cancel); QPushButton *fullBtn = new QPushButton(msg); QPushButton *layoutsBtn = new QPushButton(msg); fullBtn->setText(i18nc("import full configuration", "Full Configuration")); fullBtn->setIcon(QIcon::fromTheme("settings")); layoutsBtn->setText(i18nc("import only the layouts", "Only Layouts")); layoutsBtn->setIcon(QIcon::fromTheme("user-identity")); msg->addButton(fullBtn, QMessageBox::AcceptRole); msg->addButton(layoutsBtn, QMessageBox::AcceptRole); msg->setDefaultButton(layoutsBtn); connect(msg, &QMessageBox::finished, msg, &QMessageBox::deleteLater); msg->open(); connect(layoutsBtn, &QPushButton::clicked , this, [ &, file](bool check) { importLayoutsFromV1ConfigFile(file); }); connect(fullBtn, &QPushButton::clicked , this, [ &, file](bool check) { //!NOTE: Restart latte for import the new configuration QProcess::startDetached(qGuiApp->applicationFilePath() + " --import-full \"" + file + "\""); qGuiApp->exit(); }); } else if (version == Layouts::Importer::ConfigVersion2) { auto msg = new QMessageBox(this); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Import: Configuration file version v0.2")); msg->setText( i18n("You are going to import a v0.2 configuration file.
Be careful, importing will erase all your current configuration!!!

Would you like to proceed?")); msg->setStandardButtons(QMessageBox::Yes | QMessageBox::No); msg->setDefaultButton(QMessageBox::No); connect(msg, &QMessageBox::finished, this, [ &, msg, file](int result) { if (result == QMessageBox::Yes) { //!NOTE: Restart latte for import the new configuration msg->deleteLater(); QProcess::startDetached(qGuiApp->applicationFilePath() + " --import-full \"" + file + "\""); qGuiApp->exit(); } }); msg->open(); } }); fileDialog->open(); } bool SettingsDialog::importLayoutsFromV1ConfigFile(QString file) { KTar archive(file, QStringLiteral("application/x-tar")); archive.open(QIODevice::ReadOnly); //! if the file isnt a tar archive if (archive.isOpen()) { QDir tempDir{uniqueTempDirectory()}; const auto archiveRootDir = archive.directory(); for (const auto &name : archiveRootDir->entries()) { auto fileEntry = archiveRootDir->file(name); fileEntry->copyTo(tempDir.absolutePath()); } QString name = Layouts::Importer::nameOfConfigFile(file); QString applets(tempDir.absolutePath() + "/" + "lattedock-appletsrc"); if (QFile(applets).exists()) { if (m_corona->layoutsManager()->importer()->importOldLayout(applets, name, false, tempDir.absolutePath())) { addLayoutForFile(tempDir.absolutePath() + "/" + name + ".layout.latte", name, false); } QString alternativeName = name + "-" + i18nc("layout", "Alternative"); if (m_corona->layoutsManager()->importer()->importOldLayout(applets, alternativeName, false, tempDir.absolutePath())) { addLayoutForFile(tempDir.absolutePath() + "/" + alternativeName + ".layout.latte", alternativeName, false); } } return true; } return false; } void SettingsDialog::on_exportButton_clicked() { int row = ui->layoutsView->currentIndex().row(); if (row < 0) { return; } QString layoutExported = m_model->data(m_model->index(row, IDCOLUMN), Qt::DisplayRole).toString(); //! Update ALL active original layouts before exporting, //! this is needed because the export method can export also the full configuration qDebug() << Q_FUNC_INFO; m_corona->layoutsManager()->syncActiveLayoutsToOriginalFiles(); QFileDialog *fileDialog = new QFileDialog(this, i18nc("export layout/configuration", "Export Layout/Configuration") , QDir::homePath(), QStringLiteral("layout.latte")); fileDialog->setFileMode(QFileDialog::AnyFile); fileDialog->setAcceptMode(QFileDialog::AcceptSave); fileDialog->setDefaultSuffix("layout.latte"); QStringList filters; QString filter1(i18nc("export layout", "Latte Dock Layout file v0.2") + "(*.layout.latte)"); QString filter2(i18nc("export full configuration", "Latte Dock Full Configuration file v0.2") + "(*.latterc)"); filters << filter1 << filter2; fileDialog->setNameFilters(filters); connect(fileDialog, &QFileDialog::finished , fileDialog, &QFileDialog::deleteLater); connect(fileDialog, &QFileDialog::fileSelected , this, [ &, layoutExported](const QString & file) { auto showNotificationError = []() { auto notification = new KNotification("export-fail", KNotification::CloseOnTimeout); notification->setText(i18nc("export layout", "Failed to export layout")); notification->sendEvent(); }; if (QFile::exists(file) && !QFile::remove(file)) { showNotificationError(); return; } if (file.endsWith(".layout.latte")) { if (!QFile(layoutExported).copy(file)) { showNotificationError(); return; } QFileInfo newFileInfo(file); if (newFileInfo.exists() && !newFileInfo.isWritable()) { QFile(file).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } CentralLayout layoutS(this, file); layoutS.setActivities(QStringList()); layoutS.clearLastUsedActivity(); //NOTE: The pointer is automatically deleted when the event is closed auto notification = new KNotification("export-done", KNotification::CloseOnTimeout); notification->setActions({i18nc("export layout", "Open location")}); notification->setText(i18nc("export layout", "Layout exported successfully")); connect(notification, &KNotification::action1Activated , this, [file]() { QDesktopServices::openUrl({QFileInfo(file).canonicalPath()}); }); notification->sendEvent(); } else if (file.endsWith(".latterc")) { auto showNotificationError = []() { auto notification = new KNotification("export-fail", KNotification::CloseOnTimeout); notification->setText(i18nc("import/export config", "Failed to export configuration")); notification->sendEvent(); }; if (m_corona->layoutsManager()->importer()->exportFullConfiguration(file)) { auto notification = new KNotification("export-done", KNotification::CloseOnTimeout); notification->setActions({i18nc("import/export config", "Open location")}); notification->setText(i18nc("import/export config", "Full Configuration exported successfully")); connect(notification, &KNotification::action1Activated , this, [file]() { QDesktopServices::openUrl({QFileInfo(file).canonicalPath()}); }); notification->sendEvent(); } else { showNotificationError(); } } }); fileDialog->open(); } void SettingsDialog::requestImagesDialog(int row) { QStringList mimeTypeFilters; mimeTypeFilters << "image/jpeg" // will show "JPEG image (*.jpeg *.jpg) << "image/png"; // will show "PNG image (*.png)" QFileDialog dialog(this); dialog.setMimeTypeFilters(mimeTypeFilters); QString background = m_model->data(m_model->index(row, COLORCOLUMN), Qt::BackgroundRole).toString(); if (background.startsWith("/") && QFileInfo(background).exists()) { dialog.setDirectory(QFileInfo(background).absolutePath()); dialog.selectFile(background); } if (dialog.exec()) { QStringList files = dialog.selectedFiles(); if (files.count() > 0) { m_model->setData(m_model->index(row, COLORCOLUMN), files[0], Qt::BackgroundRole); } } } void SettingsDialog::requestColorsDialog(int row) { QColorDialog dialog(this); QString textColor = m_model->data(m_model->index(row, COLORCOLUMN), Qt::UserRole).toString(); dialog.setCurrentColor(QColor(textColor)); if (dialog.exec()) { qDebug() << dialog.selectedColor().name(); m_model->setData(m_model->index(row, COLORCOLUMN), dialog.selectedColor().name(), Qt::UserRole); } } void SettingsDialog::accept() { qDebug() << Q_FUNC_INFO; if (saveAllChanges()) { deleteLater(); } } void SettingsDialog::reject() { qDebug() << Q_FUNC_INFO; if (!m_blockDeleteOnReject) { deleteLater(); } } void SettingsDialog::apply() { qDebug() << Q_FUNC_INFO; saveAllChanges(); o_settings = currentSettings(); o_settingsLayouts = currentLayoutsSettings(); updateApplyButtonsState(); updatePerLayoutButtonsState(); } void SettingsDialog::restoreDefaults() { qDebug() << Q_FUNC_INFO; if (ui->tabWidget->currentIndex() == 0) { //! Default layouts missing from layouts list for (const auto &preset : m_corona->layoutsManager()->presetsPaths()) { QString presetName = CentralLayout::layoutName(preset); QByteArray presetNameChars = presetName.toUtf8(); const char *prset_str = presetNameChars.data(); presetName = i18n(prset_str); if (!nameExistsInModel(presetName)) { addLayoutForFile(preset, presetName); } } } else if (ui->tabWidget->currentIndex() == 1) { //! Defaults for general Latte settings ui->autostartChkBox->setChecked(true); ui->infoWindowChkBox->setChecked(true); ui->metaPressChkBox->setChecked(false); ui->metaPressHoldChkBox->setChecked(true); ui->noBordersForMaximizedChkBox->setChecked(false); ui->highSensitivityBtn->setChecked(true); ui->screenTrackerSpinBox->setValue(SCREENTRACKERDEFAULTVALUE); ui->outlineSpinBox->setValue(OUTLINEDEFAULTWIDTH); } } void SettingsDialog::addLayoutForFile(QString file, QString layoutName, bool newTempDirectory, bool showNotification) { if (layoutName.isEmpty()) { layoutName = CentralLayout::layoutName(file); } QString copiedId; if (newTempDirectory) { QString tempDir = uniqueTempDirectory(); copiedId = tempDir + "/" + layoutName + ".layout.latte"; QFile(file).copy(copiedId); } else { copiedId = file; } QFileInfo newFileInfo(copiedId); if (newFileInfo.exists() && !newFileInfo.isWritable()) { QFile(copiedId).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } if (m_layouts.contains(copiedId)) { CentralLayout *oldSettings = m_layouts.take(copiedId); delete oldSettings; } CentralLayout *settings = new CentralLayout(this, copiedId); m_layouts[copiedId] = settings; QString id = copiedId; QString color = settings->color(); QString textColor = settings->textColor(); QString background = settings->background(); bool menu = settings->showInMenu(); bool disabledBorders = settings->disableBordersForMaximizedWindows(); bool locked = !settings->isWritable(); layoutName = uniqueLayoutName(layoutName); int row = ascendingRowFor(layoutName); if (background.isEmpty()) { insertLayoutInfoAtRow(row, copiedId, color, QString(), layoutName, menu, disabledBorders, QStringList(), locked); } else { insertLayoutInfoAtRow(row, copiedId, background, textColor, layoutName, menu, disabledBorders, QStringList(), locked); } ui->layoutsView->selectRow(row); if (showNotification) { //NOTE: The pointer is automatically deleted when the event is closed auto notification = new KNotification("import-done", KNotification::CloseOnTimeout); notification->setText(i18nc("import-done", "Layout: %0 imported successfully
").arg(layoutName)); notification->sendEvent(); } } void SettingsDialog::loadSettings() { m_initLayoutPaths.clear(); m_model->clear(); m_sharesMap.clear(); int i = 0; QStringList brokenLayouts; if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { m_corona->layoutsManager()->syncActiveLayoutsToOriginalFiles(); } for (const auto layout : m_corona->layoutsManager()->layouts()) { QString layoutPath = QDir::homePath() + "/.config/latte/" + layout + ".layout.latte"; m_initLayoutPaths.append(layoutPath); CentralLayout *central = new CentralLayout(this, layoutPath); m_layouts[layoutPath] = central; QString background = central->background(); //! add central layout properties if (background.isEmpty()) { insertLayoutInfoAtRow(i, layoutPath, central->color(), QString(), central->name(), central->showInMenu(), central->disableBordersForMaximizedWindows(), central->activities(), !central->isWritable()); } else { insertLayoutInfoAtRow(i, layoutPath, background, central->textColor(), central->name(), central->showInMenu(), central->disableBordersForMaximizedWindows(), central->activities(), !central->isWritable()); } //! create initial SHARES maps QString shared = central->sharedLayoutName(); if (!shared.isEmpty()) { m_sharesMap[shared].append(layoutPath); } qDebug() << "counter:" << i << " total:" << m_model->rowCount(); i++; if (central->name() == m_corona->layoutsManager()->currentLayoutName()) { ui->layoutsView->selectRow(i - 1); } Layout::GenericLayout *generic = m_corona->layoutsManager()->layout(central->name()); if ((generic && generic->layoutIsBroken()) || (!generic && central->layoutIsBroken())) { brokenLayouts.append(central->name()); } } //! update SHARES map keys in order to use the #settingsid(s) QStringList forremoval; //! remove these records after updating for (QHash::iterator i=m_sharesMap.begin(); i!=m_sharesMap.end(); ++i) { forremoval << i.key(); } //! update keys for (QHash::iterator i=m_sharesMap.begin(); i!=m_sharesMap.end(); ++i) { QString shareid = idForRow(rowForName(i.key())); m_sharesMap[shareid] = i.value(); } //! remove deprecated keys for (const auto &key : forremoval) { m_sharesMap.remove(key); } qDebug() << "SHARES MAP ::: " << m_sharesMap; for (QHash::iterator i=m_sharesMap.begin(); i!=m_sharesMap.end(); ++i) { int sharedPos = rowForId(i.key()); if (sharedPos >= 0) { m_model->setData(m_model->index(sharedPos, SHAREDCOLUMN), i.value(), Qt::UserRole); } } recalculateAvailableActivities(); m_model->setHorizontalHeaderItem(IDCOLUMN, new QStandardItem(QString("#path"))); m_model->setHorizontalHeaderItem(COLORCOLUMN, new QStandardItem(QIcon::fromTheme("games-config-background"), QString(i18nc("column for layout background", "Background")))); m_model->setHorizontalHeaderItem(NAMECOLUMN, new QStandardItem(QString(i18nc("column for layout name", "Name")))); m_model->setHorizontalHeaderItem(MENUCOLUMN, new QStandardItem(QString(i18nc("column for layout to show in menu", "In Menu")))); m_model->setHorizontalHeaderItem(BORDERSCOLUMN, new QStandardItem(QString(i18nc("column for layout to hide borders for maximized windows", "Borderless")))); m_model->setHorizontalHeaderItem(ACTIVITYCOLUMN, new QStandardItem(QIcon::fromTheme("preferences-activities"), QString(i18nc("column for layout to show which activities is assigned to", "Activities")))); m_model->setHorizontalHeaderItem(SHAREDCOLUMN, new QStandardItem(QIcon::fromTheme("document-share"), QString(i18nc("column for shared layout to show which layouts is assigned to", "Shared To")))); //! this line should be commented for debugging layouts window functionality ui->layoutsView->setColumnHidden(IDCOLUMN, true); ui->layoutsView->setColumnHidden(HIDDENTEXTCOLUMN, true); if (m_corona->universalSettings()->canDisableBorders()) { ui->layoutsView->setColumnHidden(BORDERSCOLUMN, false); } else { ui->layoutsView->setColumnHidden(BORDERSCOLUMN, true); } ui->layoutsView->resizeColumnsToContents(); QStringList columnWidths = m_corona->universalSettings()->layoutsColumnWidths(); if (!columnWidths.isEmpty()) { for (int i=0; ilayoutsView->setColumnWidth(COLORCOLUMN+i, columnWidths[i].toInt()); } } if (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout) { ui->singleToolBtn->setChecked(true); } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { ui->multipleToolBtn->setChecked(true); } updatePerLayoutButtonsState(); updateSharedLayoutsStates(); ui->autostartChkBox->setChecked(m_corona->universalSettings()->autostart()); ui->infoWindowChkBox->setChecked(m_corona->universalSettings()->showInfoWindow()); ui->metaPressChkBox->setChecked(m_corona->universalSettings()->metaForwardedToLatte()); ui->metaPressHoldChkBox->setChecked(m_corona->universalSettings()->metaPressAndHoldEnabled()); ui->noBordersForMaximizedChkBox->setChecked(m_corona->universalSettings()->canDisableBorders()); if (m_corona->universalSettings()->mouseSensitivity() == Types::LowSensitivity) { ui->lowSensitivityBtn->setChecked(true); } else if (m_corona->universalSettings()->mouseSensitivity() == Types::MediumSensitivity) { ui->mediumSensitivityBtn->setChecked(true); } else if (m_corona->universalSettings()->mouseSensitivity() == Types::HighSensitivity) { ui->highSensitivityBtn->setChecked(true); } o_settings = currentSettings(); o_settingsLayouts = currentLayoutsSettings(); updateApplyButtonsState(); updateSharedLayoutsUiElements(); //! there are broken layouts and the user must be informed! if (brokenLayouts.count() > 0) { auto msg = new QMessageBox(this); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Layout Warning")); msg->setText(i18n("The layout(s) %0 have broken configuration!!! Please remove them to improve the system stability...").arg(brokenLayouts.join(","))); msg->setStandardButtons(QMessageBox::Ok); msg->open(); } } QList SettingsDialog::currentSettings() { QList settings; settings << m_inMemoryButtons->checkedId(); settings << (int)ui->autostartChkBox->isChecked(); settings << (int)ui->infoWindowChkBox->isChecked(); settings << (int)ui->metaPressChkBox->isChecked(); settings << (int)ui->metaPressHoldChkBox->isChecked(); settings << (int)ui->noBordersForMaximizedChkBox->isChecked(); settings << m_mouseSensitivityButtons->checkedId(); settings << ui->screenTrackerSpinBox->value(); settings << ui->outlineSpinBox->value(); settings << m_model->rowCount(); return settings; } QStringList SettingsDialog::currentLayoutsSettings() { QStringList layoutSettings; for (int i = 0; i < m_model->rowCount(); ++i) { QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString(); QString color = m_model->data(m_model->index(i, COLORCOLUMN), Qt::BackgroundRole).toString(); QString textColor = m_model->data(m_model->index(i, COLORCOLUMN), Qt::UserRole).toString(); QString name = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); bool locked = m_model->data(m_model->index(i, NAMECOLUMN), Qt::UserRole).toBool(); bool menu = m_model->data(m_model->index(i, MENUCOLUMN), Qt::DisplayRole).toString() == CheckMark; bool borders = m_model->data(m_model->index(i, BORDERSCOLUMN), Qt::DisplayRole).toString() == CheckMark; QStringList lActivities = m_model->data(m_model->index(i, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); QStringList shares = m_model->data(m_model->index(i, SHAREDCOLUMN), Qt::UserRole).toStringList(); layoutSettings << id; layoutSettings << color; layoutSettings << textColor; layoutSettings << name; layoutSettings << QString::number((int)locked); layoutSettings << QString::number((int)menu); layoutSettings << QString::number((int)borders); layoutSettings << lActivities; layoutSettings << shares; } return layoutSettings; } void SettingsDialog::insertLayoutInfoAtRow(int row, QString path, QString color, QString textColor, QString name, bool menu, bool disabledBorders, QStringList activities, bool locked) { QStandardItem *pathItem = new QStandardItem(path); QStandardItem *hiddenTextItem = new QStandardItem(); QStandardItem *colorItem = new QStandardItem(); colorItem->setSelectable(false); QStandardItem *nameItem = new QStandardItem(name); nameItem->setTextAlignment(Qt::AlignCenter); QStandardItem *menuItem = new QStandardItem(); menuItem->setEditable(false); menuItem->setSelectable(true); menuItem->setText(menu ? CheckMark : QString()); menuItem->setTextAlignment(Qt::AlignCenter); QStandardItem *bordersItem = new QStandardItem(); bordersItem->setEditable(false); bordersItem->setSelectable(true); bordersItem->setText(disabledBorders ? CheckMark : QString()); bordersItem->setTextAlignment(Qt::AlignCenter); QStandardItem *activitiesItem = new QStandardItem(activities.join(",")); QStandardItem *sharesItem = new QStandardItem(); QList items; items.append(pathItem); items.append(hiddenTextItem); items.append(colorItem); items.append(nameItem); items.append(menuItem); items.append(bordersItem); items.append(activitiesItem); items.append(sharesItem); if (row > m_model->rowCount() - 1) { m_model->appendRow(items); row = m_model->rowCount() - 1; qDebug() << "append row at:" << row << " rows:" << m_model->rowCount(); } else { m_model->insertRow(row, items); qDebug() << "insert row at:" << row << " rows:" << m_model->rowCount(); } m_model->setData(m_model->index(row, IDCOLUMN), path, Qt::DisplayRole); m_model->setData(m_model->index(row, COLORCOLUMN), color, Qt::BackgroundRole); m_model->setData(m_model->index(row, COLORCOLUMN), textColor, Qt::UserRole); QFont font; if (m_corona->layoutsManager()->layout(name)) { font.setBold(true); } else { font.setBold(false); } if (path.startsWith("/tmp/")) { font.setItalic(true); } else { font.setItalic(false); } m_model->setData(m_model->index(row, NAMECOLUMN), QVariant(name), Qt::DisplayRole); m_model->setData(m_model->index(row, NAMECOLUMN), font, Qt::FontRole); m_model->setData(m_model->index(row, NAMECOLUMN), QVariant(locked), Qt::UserRole); m_model->setData(m_model->index(row, ACTIVITYCOLUMN), activities, Qt::UserRole); } void SettingsDialog::on_switchButton_clicked() { if (ui->buttonBox->button(QDialogButtonBox::Apply)->isEnabled()) { //! thus there are changes in the settings QString lName; QStringList lActivities; if (m_inMemoryButtons->checkedId() == Latte::Types::MultipleLayouts) { lName = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), NAMECOLUMN), Qt::DisplayRole).toString(); lActivities = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), ACTIVITYCOLUMN), Qt::UserRole).toStringList(); } apply(); if (!lName.isEmpty() && !lActivities.isEmpty()) { //! an activities-assigned layout is chosen and at the same time we are moving //! to multiple layouts state m_corona->layoutsManager()->switchToLayout(lName); } } else { QVariant value = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), NAMECOLUMN), Qt::DisplayRole); if (value.isValid()) { m_corona->layoutsManager()->switchToLayout(value.toString()); } else { qDebug() << "not valid layout"; } } updatePerLayoutButtonsState(); } void SettingsDialog::on_pauseButton_clicked() { ui->pauseButton->setEnabled(false); QString id = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), IDCOLUMN), Qt::DisplayRole).toString(); CentralLayout *layout = m_layouts[id]; if (layout) { m_corona->layoutsManager()->pauseLayout(layout->name()); } } void SettingsDialog::layoutsChanged() { for (int i = 0; i < m_model->rowCount(); ++i) { QModelIndex nameIndex = m_model->index(i, NAMECOLUMN); QVariant value = m_model->data(nameIndex); if (value.isValid()) { QString name = value.toString(); QFont font; if (m_corona->layoutsManager()->currentLayoutName() == name) { font.setBold(true); // ui->layoutsView->selectRow(i); } else { Layout::GenericLayout *layout = m_corona->layoutsManager()->layout(name); if (layout && (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts)) { font.setBold(true); } else { font.setBold(false); } } m_model->setData(nameIndex, font, Qt::FontRole); } } } void SettingsDialog::itemChanged(QStandardItem *item) { updatePerLayoutButtonsState(); if (item->column() == ACTIVITYCOLUMN) { //! recalculate the available activities recalculateAvailableActivities(); } else if (item->column() == NAMECOLUMN) { int currentRow = ui->layoutsView->currentIndex().row(); QString id = m_model->data(m_model->index(currentRow, IDCOLUMN), Qt::DisplayRole).toString(); QString name = m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::DisplayRole).toString(); QFont font = qvariant_cast(m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::FontRole)); if (m_corona->layoutsManager()->layout(m_layouts[id]->name())) { font.setBold(true); } else { font.setBold(false); } if (m_layouts[id]->name() != name) { font.setItalic(true); } else { font.setItalic(false); } m_model->setData(m_model->index(currentRow, NAMECOLUMN), font, Qt::FontRole); } else if (item->column() == SHAREDCOLUMN) { updateSharedLayoutsStates(); } updateApplyButtonsState(); } void SettingsDialog::updateApplyButtonsState() { bool changed{false}; //! Ok, Apply Buttons if ((o_settings != currentSettings()) || (o_settingsLayouts != currentLayoutsSettings())) { changed = true; } if (changed) { ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); } else { //ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); } //! RestoreDefaults Button if (ui->tabWidget->currentIndex() == 0) { //! Check Default layouts missing from layouts list bool layoutMissing{false}; for (const auto &preset : m_corona->layoutsManager()->presetsPaths()) { QString presetName = CentralLayout::layoutName(preset); QByteArray presetNameChars = presetName.toUtf8(); const char *prset_str = presetNameChars.data(); presetName = i18n(prset_str); if (!nameExistsInModel(presetName)) { layoutMissing = true; break; } } if (layoutMissing) { ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true); } else { ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false); } } else if (ui->tabWidget->currentIndex() == 1) { //! Defaults for general Latte settings if (!ui->autostartChkBox->isChecked() || ui->metaPressChkBox->isChecked() || !ui->metaPressHoldChkBox->isChecked() || !ui->infoWindowChkBox->isChecked() || ui->noBordersForMaximizedChkBox->isChecked() || !ui->highSensitivityBtn->isChecked() || ui->screenTrackerSpinBox->value() != SCREENTRACKERDEFAULTVALUE || ui->outlineSpinBox->value() != OUTLINEDEFAULTWIDTH ) { ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true); } else { ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false); } } } void SettingsDialog::updatePerLayoutButtonsState() { int currentRow = ui->layoutsView->currentIndex().row(); QString id = m_model->data(m_model->index(currentRow, IDCOLUMN), Qt::DisplayRole).toString(); QString nameInModel = m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::DisplayRole).toString(); QString originalName = m_layouts.contains(id) ? m_layouts[id]->name() : ""; bool lockedInModel = m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::UserRole).toBool(); bool sharedInModel = !m_model->data(m_model->index(currentRow, SHAREDCOLUMN), Qt::UserRole).toStringList().isEmpty(); //! Switch Button if (id.startsWith("/tmp/") || originalName != nameInModel) { ui->switchButton->setEnabled(false); } else { ui->switchButton->setEnabled(true); } //! Pause Button if (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout) { ui->pauseButton->setVisible(false); } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { ui->pauseButton->setVisible(true); QStringList lActivities = m_model->data(m_model->index(currentRow, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); Latte::CentralLayout *layout = m_layouts[id]; if (!lActivities.isEmpty() && layout && m_corona->layoutsManager()->centralLayout(layout->name())) { ui->pauseButton->setEnabled(true); } else { ui->pauseButton->setEnabled(false); } } //! Remove Layout Button if (originalName != nameInModel || (originalName == m_corona->layoutsManager()->currentLayoutName()) || (m_corona->layoutsManager()->centralLayout(originalName)) || lockedInModel) { ui->removeButton->setEnabled(false); } else { ui->removeButton->setEnabled(true); } //! Layout Locked Button if (lockedInModel) { ui->lockedButton->setChecked(true); } else { ui->lockedButton->setChecked(false); } //! Layout Shared Button if (sharedInModel) { ui->sharedButton->setChecked(true); } else { ui->sharedButton->setChecked(false); } } void SettingsDialog::updateSharedLayoutsStates() { bool inMultiple{inMultipleLayoutsLook()}; for (int i = 0; i < m_model->rowCount(); ++i) { QStringList shares = m_model->data(m_model->index(i, SHAREDCOLUMN), Qt::UserRole).toStringList(); if (shares.isEmpty() || !inMultiple) { QStandardItem *item = m_model->item(i, MENUCOLUMN); item->setEnabled(true); item = m_model->item(i, BORDERSCOLUMN); item->setEnabled(true); item = m_model->item(i, ACTIVITYCOLUMN); item->setEnabled(true); } else { QStandardItem *item = m_model->item(i, MENUCOLUMN); item->setEnabled(false); item = m_model->item(i, BORDERSCOLUMN); item->setEnabled(false); item = m_model->item(i, ACTIVITYCOLUMN); item->setEnabled(false); } //! refresh LayoutName QStandardItem *nameItem = m_model->item(i, NAMECOLUMN); nameItem->setEnabled(false); nameItem->setEnabled(true); } } void SettingsDialog::updateSharedLayoutsUiElements() { //! UI Elements that need to be enabled/disabled Latte::Types::LayoutsMemoryUsage inMemoryOption = static_cast(m_inMemoryButtons->checkedId()); if (inMemoryOption == Latte::Types::MultipleLayouts) { ui->layoutsView->setColumnHidden(SHAREDCOLUMN, false); ui->sharedButton->setVisible(true); //! column widths QStringList cWidths = m_corona->universalSettings()->layoutsColumnWidths(); if (cWidths.count()>=5) { ui->layoutsView->setColumnWidth(ACTIVITYCOLUMN, cWidths[4].toInt()); } } else { ui->layoutsView->setColumnHidden(SHAREDCOLUMN, true); ui->sharedButton->setVisible(false); } } void SettingsDialog::recalculateAvailableActivities() { QStringList tempActivities = m_corona->layoutsManager()->activities(); for (int i = 0; i < m_model->rowCount(); ++i) { QStringList assigned = m_model->data(m_model->index(i, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); for (const auto &activity : assigned) { if (tempActivities.contains(activity)) { tempActivities.removeAll(activity); } } } m_availableActivities = tempActivities; } bool SettingsDialog::dataAreAccepted() { for (int i = 0; i < m_model->rowCount(); ++i) { QString layout1 = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); for (int j = i + 1; j < m_model->rowCount(); ++j) { QString temp = m_model->data(m_model->index(j, NAMECOLUMN), Qt::DisplayRole).toString(); //!same layout name exists again if (layout1 == temp) { auto msg = new QMessageBox(this); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Layout Warning")); msg->setText(i18n("There are layouts with the same name, that is not permitted!!! Please update these names to re-apply the changes...")); msg->setStandardButtons(QMessageBox::Ok); connect(msg, &QMessageBox::finished, this, [ &, i, j](int result) { QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::ClearAndSelect; QModelIndex indexBase = m_model->index(i, NAMECOLUMN); ui->layoutsView->selectionModel()->select(indexBase, flags); QModelIndex indexOccurence = m_model->index(j, NAMECOLUMN); ui->layoutsView->edit(indexOccurence); }); msg->open(); return false; } } } return true; } bool SettingsDialog::saveAllChanges() { if (!dataAreAccepted()) { return false; } //! Update universal settings Latte::Types::MouseSensitivity sensitivity = static_cast(m_mouseSensitivityButtons->checkedId()); bool autostart = ui->autostartChkBox->isChecked(); bool forwardMetaPress = ui->metaPressChkBox->isChecked(); bool metaPressAndHold = ui->metaPressHoldChkBox->isChecked(); bool showInfoWindow = ui->infoWindowChkBox->isChecked(); bool noBordersForMaximized = ui->noBordersForMaximizedChkBox->isChecked(); m_corona->universalSettings()->setMouseSensitivity(sensitivity); m_corona->universalSettings()->setAutostart(autostart); m_corona->universalSettings()->forwardMetaToLatte(forwardMetaPress); m_corona->universalSettings()->setMetaPressAndHoldEnabled(metaPressAndHold); m_corona->universalSettings()->setShowInfoWindow(showInfoWindow); m_corona->universalSettings()->setCanDisableBorders(noBordersForMaximized); m_corona->universalSettings()->setScreenTrackerInterval(ui->screenTrackerSpinBox->value()); m_corona->themeExtended()->setOutlineWidth(ui->outlineSpinBox->value()); //! Update Layouts QStringList knownActivities = activities(); QTemporaryDir layoutTempDir; qDebug() << "Temporary Directory ::: " << layoutTempDir.path(); QStringList fromRenamePaths; QStringList toRenamePaths; QStringList toRenameNames; QString switchToLayout; QHash activeLayoutsToRename; //! remove layouts that have been removed from the user for (const auto &initLayout : m_initLayoutPaths) { if (!idExistsInModel(initLayout)) { QFile(initLayout).remove(); if (m_layouts.contains(initLayout)) { CentralLayout *removedLayout = m_layouts.take(initLayout); delete removedLayout; } } } for (int i = 0; i < m_model->rowCount(); ++i) { QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString(); QString color = m_model->data(m_model->index(i, COLORCOLUMN), Qt::BackgroundRole).toString(); QString textColor = m_model->data(m_model->index(i, COLORCOLUMN), Qt::UserRole).toString(); QString name = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); bool locked = m_model->data(m_model->index(i, NAMECOLUMN), Qt::UserRole).toBool(); bool menu = m_model->data(m_model->index(i, MENUCOLUMN), Qt::DisplayRole).toString() == CheckMark; bool disabledBorders = m_model->data(m_model->index(i, BORDERSCOLUMN), Qt::DisplayRole).toString() == CheckMark; QStringList lActivities = m_model->data(m_model->index(i, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); QStringList cleanedActivities; //!update only activities that are valid for (const auto &activity : lActivities) { if (knownActivities.contains(activity)) { cleanedActivities.append(activity); } } //qDebug() << i << ". " << id << " - " << color << " - " << name << " - " << menu << " - " << lActivities; //! update the generic parts of the layouts Layout::GenericLayout *genericActive= m_corona->layoutsManager()->layout(m_layouts[id]->name()); Layout::GenericLayout *generic = genericActive ? genericActive : m_layouts[id]; //! unlock read-only layout if (!generic->isWritable()) { generic->unlock(); } if (color.startsWith("/")) { //it is image file in such case if (color != generic->background()) { generic->setBackground(color); } if (generic->textColor() != textColor) { generic->setTextColor(textColor); } } else { if (color != generic->color()) { generic->setColor(color); generic->setBackground(QString()); generic->setTextColor(QString()); } } //! update only the Central-specific layout parts CentralLayout *centralActive= m_corona->layoutsManager()->centralLayout(m_layouts[id]->name()); CentralLayout *central = centralActive ? centralActive : m_layouts[id]; if (central->showInMenu() != menu) { central->setShowInMenu(menu); } if (central->disableBordersForMaximizedWindows() != disabledBorders) { central->setDisableBordersForMaximizedWindows(disabledBorders); } if (central->activities() != cleanedActivities) { central->setActivities(cleanedActivities); } //! If the layout name changed OR the layout path is a temporary one if (generic->name() != name || (id.startsWith("/tmp/"))) { //! If the layout is Active in MultipleLayouts if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts && generic->isActive()) { qDebug() << " Active Layout Should Be Renamed From : " << generic->name() << " TO :: " << name; activeLayoutsToRename[name] = generic; } QString tempFile = layoutTempDir.path() + "/" + QString(generic->name() + ".layout.latte"); qDebug() << "new temp file ::: " << tempFile; if ((m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout) && (generic->name() == m_corona->layoutsManager()->currentLayoutName())) { switchToLayout = name; } generic = m_layouts.take(id); delete generic; QFile(id).rename(tempFile); fromRenamePaths.append(id); toRenamePaths.append(tempFile); toRenameNames.append(name); } } //! this is necessary in case two layouts have to swap names //! so we copy first the layouts in a temp directory and afterwards all //! together we move them in the official layout directory for (int i = 0; i < toRenamePaths.count(); ++i) { QString newFile = QDir::homePath() + "/.config/latte/" + toRenameNames[i] + ".layout.latte"; QFile(toRenamePaths[i]).rename(newFile); CentralLayout *nLayout = new CentralLayout(this, newFile); m_layouts[newFile] = nLayout; //! updating the #SETTINGSID in the model for the layout that was renamed for (int j = 0; j < m_model->rowCount(); ++j) { QString tId = m_model->data(m_model->index(j, IDCOLUMN), Qt::DisplayRole).toString(); if (tId == fromRenamePaths[i]) { m_model->setData(m_model->index(j, IDCOLUMN), newFile, Qt::DisplayRole); m_initLayoutPaths.append(newFile); QFont font = qvariant_cast(m_model->data(m_model->index(j, NAMECOLUMN), Qt::FontRole)); font.setItalic(false); m_model->setData(m_model->index(j, NAMECOLUMN), font, Qt::FontRole); } } } QString orphanedLayout; if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { for (const auto &newLayoutName : activeLayoutsToRename.keys()) { Layout::GenericLayout *layout = activeLayoutsToRename[newLayoutName]; qDebug() << " Active Layout of Type: " << layout->type() << " Is Renamed From : " << activeLayoutsToRename[newLayoutName]->name() << " TO :: " << newLayoutName; layout->renameLayout(newLayoutName); if (layout->type() == Layout::Type::Central) { CentralLayout *central = qobject_cast(layout); if (central->activities().isEmpty()) { //! that means it is an active layout for orphaned Activities orphanedLayout = newLayoutName; } } //! broadcast the name change int row = rowForName(newLayoutName); QStandardItem *item = m_model->item(row, NAMECOLUMN); if (item) { emit itemChanged(item); } } } //! lock layouts in the end when the user has chosen it for (int i = 0; i < m_model->rowCount(); ++i) { QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString(); QString name = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); bool locked = m_model->data(m_model->index(i, NAMECOLUMN), Qt::UserRole).toBool(); Layout::GenericLayout *generic = m_corona->layoutsManager()->layout(m_layouts[id]->name()); Layout::GenericLayout *layout = generic ? generic : m_layouts[id]; if (layout && locked && layout->isWritable()) { layout->lock(); } } //! update SharedLayouts that are Active if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { updateActiveShares(); } //! reload layouts in layoutsmanager - m_corona->layoutsManager()->loadLayouts(); + m_corona->layoutsManager()->synchronizer()->loadLayouts(); //! send to layout manager in which layout to switch Latte::Types::LayoutsMemoryUsage inMemoryOption = static_cast(m_inMemoryButtons->checkedId()); if (m_corona->layoutsManager()->memoryUsage() != inMemoryOption) { Types::LayoutsMemoryUsage previousMemoryUsage = m_corona->layoutsManager()->memoryUsage(); m_corona->layoutsManager()->setMemoryUsage(inMemoryOption); QVariant value = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), NAMECOLUMN), Qt::DisplayRole); QString layoutName = value.toString(); m_corona->layoutsManager()->switchToLayout(layoutName, previousMemoryUsage); } else { if (!switchToLayout.isEmpty()) { m_corona->layoutsManager()->switchToLayout(switchToLayout); } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { - m_corona->layoutsManager()->syncMultipleLayoutsToActivities(orphanedLayout); + m_corona->layoutsManager()->synchronizer()->syncMultipleLayoutsToActivities(orphanedLayout); } } return true; } void SettingsDialog::updateActiveShares() { QHash currentSharesMap; for (int i = 0; i < m_model->rowCount(); ++i) { if (isShared(i)) { QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString(); QStringList shares = m_model->data(m_model->index(i, SHAREDCOLUMN), Qt::UserRole).toStringList(); currentSharesMap[id] = shares; } } qDebug() << " CURRENT SHARES MAP :: " << currentSharesMap; QHash unassign; //! CENTRAL (active) layouts that will become SHARED must be unloaded first for (QHash::iterator i=currentSharesMap.begin(); i!=currentSharesMap.end(); ++i) { CentralLayout *central = m_corona->layoutsManager()->centralLayout(nameForId(i.key())); if (central) { - m_corona->layoutsManager()->unloadCentralLayout(central); + //IMPORTANT !!!!! REENABLE WHEN THE CODE IS MOVED!!!! + //m_corona->layoutsManager()->synchronizer()->unloadCentralLayout(central); } } //! CENTRAL (active) layouts that update their (active) SHARED layouts //! AND load SHARED layouts that are NOT ACTIVE for (QHash::iterator i=currentSharesMap.begin(); i!=currentSharesMap.end(); ++i) { SharedLayout *shared = m_corona->layoutsManager()->sharedLayout(nameForId(i.key())); qDebug() << " SHARED :: " << nameForId(i.key()); for (const auto ¢ralId : i.value()) { CentralLayout *central = m_corona->layoutsManager()->centralLayout(nameForId(centralId)); qDebug() << " CENTRAL NAME :: " << nameForId(centralId); if (central) { //! Assign this Central Layout at a different Shared Layout SharedLayout *oldShared = central->sharedLayout(); if (!shared) { //Shared not loaded and it must be loaded before proceed m_corona->layoutsManager()->registerAtSharedLayout(central, nameForId(i.key())); shared = m_corona->layoutsManager()->sharedLayout(nameForId(i.key())); } if (shared != oldShared) { shared->addCentralLayout(central); central->setSharedLayout(shared); if (oldShared) { //! CENTRAL layout that changed from one ACTIVESHARED layout to another unassign[central] = shared; } } } } } //! CENTRAL Layouts that wont have any SHARED Layout any more for (QHash::iterator i=m_sharesMap.begin(); i!=m_sharesMap.end(); ++i) { for (const auto ¢ralId : i.value()) { if (!mapHasRecord(centralId, currentSharesMap)) { CentralLayout *central = m_corona->layoutsManager()->centralLayout(nameForId(centralId)); if (central && central->sharedLayout()) { central->sharedLayout()->removeCentralLayout(central); central->setSharedLayoutName(QString()); central->setSharedLayout(nullptr); } } } } //! Unassing from Shared Layouts Central ones that are not assigned any more //! IMPORTANT: This must be done after all the ASSIGNMENTS in order to avoid //! to unload a SharedLayout that it should not for (QHash::iterator i=unassign.begin(); i!=unassign.end(); ++i) { i.value()->removeCentralLayout(i.key()); } //! TODO : (active) SharedLayouts that become Active should be unloaded first m_sharesMap.clear(); m_sharesMap = currentSharesMap; } void SettingsDialog::addActivityInCurrent(const QString &activityId) { int currentRow = ui->layoutsView->currentIndex().row(); QStringList activities = m_model->data(m_model->index(currentRow, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); if (!activities.contains(activityId)) { activities << activityId; m_model->setData(m_model->index(currentRow, ACTIVITYCOLUMN), activities, Qt::UserRole); } } void SettingsDialog::removeActivityFromCurrent(const QString &activityId) { int currentRow = ui->layoutsView->currentIndex().row(); QStringList activities = m_model->data(m_model->index(currentRow, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); if (activities.contains(activityId)) { activities.removeAll(activityId); m_model->setData(m_model->index(currentRow, ACTIVITYCOLUMN), activities, Qt::UserRole); } } void SettingsDialog::addShareInCurrent(const QString &layoutId) { int currentRow = ui->layoutsView->currentIndex().row(); QStringList shares = m_model->data(m_model->index(currentRow, SHAREDCOLUMN), Qt::UserRole).toStringList(); if (!shares.contains(layoutId)) { shares << layoutId; m_model->setData(m_model->index(currentRow, SHAREDCOLUMN), shares, Qt::UserRole); } } void SettingsDialog::removeShareFromCurrent(const QString &layoutId) { int currentRow = ui->layoutsView->currentIndex().row(); QStringList shares = m_model->data(m_model->index(currentRow, SHAREDCOLUMN), Qt::UserRole).toStringList(); if (shares.contains(layoutId)) { shares.removeAll(layoutId); m_model->setData(m_model->index(currentRow, SHAREDCOLUMN), shares, Qt::UserRole); } } bool SettingsDialog::idExistsInModel(QString id) { for (int i = 0; i < m_model->rowCount(); ++i) { QString rowId = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString(); if (rowId == id) { return true; } } return false; } bool SettingsDialog::mapHasRecord(const QString &record, QHash &map) { for (QHash::iterator i=map.begin(); i!=map.end(); ++i) { if (i.value().contains(record)) { return true; } } return false; } bool SettingsDialog::nameExistsInModel(QString name) { for (int i = 0; i < m_model->rowCount(); ++i) { QString rowName = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); if (rowName == name) { return true; } } return false; } bool SettingsDialog::inMultipleLayoutsLook() const { Latte::Types::LayoutsMemoryUsage inMemoryOption = static_cast(m_inMemoryButtons->checkedId()); return inMemoryOption == Latte::Types::MultipleLayouts; } bool SettingsDialog::isActive(QString layoutName) const { return (m_corona->layoutsManager()->layout(layoutName) != nullptr); } bool SettingsDialog::isMenuCell(int column) const { return column == MENUCOLUMN; } bool SettingsDialog::isShared(int row) const { if (row >=0 ) { QStringList shares = m_model->data(m_model->index(row, SHAREDCOLUMN), Qt::UserRole).toStringList(); if (!shares.isEmpty()) { return true; } } return false; } int SettingsDialog::ascendingRowFor(QString name) { for (int i = 0; i < m_model->rowCount(); ++i) { QString rowName = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); if (rowName.toUpper() > name.toUpper()) { return i; } } return m_model->rowCount(); } int SettingsDialog::rowForId(QString id) const { for (int i = 0; i < m_model->rowCount(); ++i) { QString rowId = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString(); if (rowId == id) { return i; } } return -1; } int SettingsDialog::rowForName(QString layoutName) const { for (int i = 0; i < m_model->rowCount(); ++i) { QString rowName = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); if (rowName == layoutName) { return i; } } return -1; } QString SettingsDialog::idForRow(int row) const { return m_model->data(m_model->index(row, IDCOLUMN), Qt::DisplayRole).toString(); } QString SettingsDialog::nameForId(QString id) const { int row = rowForId(id); return m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole).toString(); } QString SettingsDialog::uniqueTempDirectory() { QTemporaryDir tempDir; tempDir.setAutoRemove(false); m_tempDirectories.append(tempDir.path()); return tempDir.path(); } QString SettingsDialog::uniqueLayoutName(QString name) { int pos_ = name.lastIndexOf(QRegExp(QString("[-][0-9]+"))); if (nameExistsInModel(name) && pos_ > 0) { name = name.left(pos_); } int i = 2; QString namePart = name; while (nameExistsInModel(name)) { name = namePart + "-" + QString::number(i); i++; } return name; } }//end of namespace