diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 89053d6b..52889cc7 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,76 +1,75 @@ set(lattedock-app_SRCS ../liblatte2/commontools.cpp ../liblatte2/types.cpp alternativeshelper.cpp - importer.cpp infoview.cpp lattecorona.cpp schemecolors.cpp screenpool.cpp main.cpp ) add_subdirectory(indicator) add_subdirectory(layout) add_subdirectory(layouts) add_subdirectory(package) add_subdirectory(plasma/extended) add_subdirectory(settings) add_subdirectory(settings/delegates) add_subdirectory(settings/tools) add_subdirectory(shortcuts) add_subdirectory(view) add_subdirectory(view/settings) add_subdirectory(wm) set(latte_dbusXML dbus/org.kde.LatteDock.xml) qt5_add_dbus_adaptor(lattedock-app_SRCS ${latte_dbusXML} lattecorona.h Latte::Corona lattedockadaptor) ki18n_wrap_ui(lattedock-app_SRCS settings/settingsdialog.ui) add_executable(latte-dock ${lattedock-app_SRCS}) include(FakeTarget.cmake) target_link_libraries(latte-dock Qt5::DBus Qt5::Quick Qt5::Qml KF5::I18n KF5::DBusAddons KF5::Declarative KF5::CoreAddons KF5::GuiAddons KF5::GlobalAccel KF5::Archive KF5::Crash KF5::XmlGui KF5::Plasma KF5::PlasmaQuick KF5::Activities KF5::Notifications KF5::NewStuff KF5::QuickAddons KF5::WaylandClient ) if(HAVE_X11) target_link_libraries(latte-dock Qt5::X11Extras KF5::WindowSystem ${X11_LIBRARIES} ${XCB_LIBRARIES} ) endif() configure_file(org.kde.latte-dock.desktop.cmake org.kde.latte-dock.desktop) configure_file(org.kde.latte-dock.appdata.xml.cmake org.kde.latte-dock.appdata.xml) install(TARGETS latte-dock ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.latte-dock.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.latte-dock.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install(FILES dbus/org.kde.LatteDock.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR}) install(FILES lattedock.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) install(FILES latte-layouts.knsrc DESTINATION ${CONFIG_INSTALL_DIR}) install(FILES latte-indicators.knsrc DESTINATION ${CONFIG_INSTALL_DIR}) add_subdirectory(packageplugins) diff --git a/app/indicator/factory.cpp b/app/indicator/factory.cpp index 18ccc725..40e81e36 100644 --- a/app/indicator/factory.cpp +++ b/app/indicator/factory.cpp @@ -1,308 +1,308 @@ /* * 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 "factory.h" // local -#include "../importer.h" +#include "../layouts/importer.h" // Qt #include #include #include #include #include #include // KDE #include #include #include #include #include #include #include #include #include namespace Latte { namespace Indicator { Factory::Factory(QObject *parent) : QObject(parent) { m_parentWidget = new QWidget(); - m_watchedPaths = Latte::Importer::standardPaths(); + m_watchedPaths = Latte::Layouts::Importer::standardPaths(); for(int i=0; iaddDir(dir); } connect(KDirWatch::self(), &KDirWatch::dirty, this, [ & ](const QString & path) { if (m_watchedPaths.contains(path)) { reload(); } }); qDebug() << m_plugins["org.kde.latte.default"].name(); } Factory::~Factory() { m_parentWidget->deleteLater(); } bool Factory::pluginExists(QString id) const { return m_plugins.contains(id); } int Factory::customPluginsCount() { return m_customPluginIds.count(); } QStringList Factory::customPluginIds() { return m_customPluginIds; } QStringList Factory::customPluginNames() { return m_customPluginNames; } QStringList Factory::customLocalPluginIds() { return m_customLocalPluginIds; } KPluginMetaData Factory::metadata(QString pluginId) { if (m_plugins.contains(pluginId)) { return m_plugins[pluginId]; } return KPluginMetaData(); } void Factory::reload() { m_plugins.clear(); m_customPluginIds.clear(); m_customPluginNames.clear(); m_customLocalPluginIds.clear(); for(const auto &path : m_watchedPaths) { QDir standard(path); if (standard.exists()) { QStringList pluginDirs = standard.entryList(QStringList(),QDir::AllDirs | QDir::NoSymLinks); for (const auto &pluginDir : pluginDirs) { if (pluginDir != "." && pluginDir != "..") { QString metadataFile = standard.absolutePath() + "/" + pluginDir + "/metadata.desktop"; KPluginMetaData metadata = KPluginMetaData::fromDesktopFile(metadataFile); if (metadataAreValid(metadata)) { QString uiFile = standard.absolutePath() + "/" + pluginDir + "/package/" + metadata.value("X-Latte-MainScript"); if (QFileInfo(uiFile).exists() && !m_plugins.contains(metadata.pluginId())) { m_plugins[metadata.pluginId()] = metadata; if ((metadata.pluginId() != "org.kde.latte.default") && (metadata.pluginId() != "org.kde.latte.plasma")) { m_customPluginIds << metadata.pluginId(); m_customPluginNames << metadata.name(); } if (standard.absolutePath().startsWith(QDir::homePath())) { m_customLocalPluginIds << metadata.pluginId(); } QString pluginPath = metadata.fileName().remove("metadata.desktop"); qDebug() << " Indicator Package Loaded ::: " << metadata.name() << " [" << metadata.pluginId() << "]" << " - [" <setText(i18n("Failed to import indicator")); notification->sendEvent(); }; auto showNotificationSucceed = [](QString name, bool updated) { auto notification = new KNotification("import-done", KNotification::CloseOnTimeout); notification->setText(updated ? i18nc("indicator_name, imported updated","%0 indicator updated successfully").arg(name) : i18nc("indicator_name, imported success","%0 indicator installed successfully").arg(name)); notification->sendEvent(); }; KArchive *archive; KZip *zipArchive = new KZip(compressedFile); zipArchive->open(QIODevice::ReadOnly); //! if the file isnt a zip archive if (!zipArchive->isOpen()) { delete zipArchive; KTar *tarArchive = new KTar(compressedFile, QStringLiteral("application/x-tar")); tarArchive->open(QIODevice::ReadOnly); if (!tarArchive->isOpen()) { delete tarArchive; showNotificationError(); return Latte::Types::Failed; } else { archive = tarArchive; } } else { archive = zipArchive; } QTemporaryDir archiveTempDir; archive->directory()->copyTo(archiveTempDir.path()); //metadata file QString packagePath = archiveTempDir.path(); QString metadataFile = archiveTempDir.path() + "/metadata.desktop"; if (!QFileInfo(metadataFile).exists()){ QDirIterator iter(archiveTempDir.path(), QDir::Dirs | QDir::NoDotAndDotDot); while(iter.hasNext() ) { QString currentPath = iter.next(); QString tempMetadata = currentPath + "/metadata.desktop"; if (QFileInfo(tempMetadata).exists()) { metadataFile = tempMetadata; packagePath = currentPath; } } } KPluginMetaData metadata = KPluginMetaData::fromDesktopFile(metadataFile); if (metadataAreValid(metadata)) { - QStringList standardPaths = Latte::Importer::standardPaths(); + QStringList standardPaths = Latte::Layouts::Importer::standardPaths(); QString installPath = standardPaths[0] + "/latte/indicators/" + metadata.pluginId(); bool updated{QDir(installPath).exists()}; if (QDir(installPath).exists()) { QDir(installPath).removeRecursively(); } QProcess process; process.start(QString("mv " +packagePath + " " + installPath)); process.waitForFinished(); QString output(process.readAllStandardOutput()); showNotificationSucceed(metadata.name(), updated); return Latte::Types::Installed; } showNotificationError(); return Latte::Types::Failed; } void Factory::removeIndicator(QString id) { if (m_plugins.contains(id)) { QString pluginName = m_plugins[id].name(); auto msg = new QMessageBox(m_parentWidget); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Remove Indicator")); msg->setText( i18n("Do you want to remove %0 indicator from your system?").arg(pluginName)); msg->setStandardButtons(QMessageBox::Yes | QMessageBox::No); msg->setDefaultButton(QMessageBox::No); connect(msg, &QMessageBox::finished, this, [ &, msg, id, pluginName](int result) { auto showRemovedSucceed = [](QString name) { auto notification = new KNotification("remove-done", KNotification::CloseOnTimeout); notification->setText(i18nc("indicator_name, removed success","%0 indicator removed successfully").arg(name)); notification->sendEvent(); }; if (result == QMessageBox::Yes) { qDebug() << "Trying to remove indicator :: " << id; QProcess process; process.start(QString("kpackagetool5 -r " +id + " -t Latte/Indicator")); process.waitForFinished(); showRemovedSucceed(pluginName); } }); msg->open(); } } void Factory::downloadIndicator() { KNS3::DownloadDialog dialog(QStringLiteral("latte-indicators.knsrc"), m_parentWidget); dialog.exec(); } } } diff --git a/app/lattecorona.cpp b/app/lattecorona.cpp index 61e85318..392764ff 100644 --- a/app/lattecorona.cpp +++ b/app/lattecorona.cpp @@ -1,1060 +1,1060 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "lattecorona.h" // local #include "alternativeshelper.h" -#include "importer.h" #include "lattedockadaptor.h" #include "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)) { //! 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) { disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); m_layoutsManager->load(); m_activitiesStarting = false; 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/layout/centrallayout.cpp b/app/layout/centrallayout.cpp index d9b3f405..225b2f56 100644 --- a/app/layout/centrallayout.cpp +++ b/app/layout/centrallayout.cpp @@ -1,479 +1,479 @@ /* * 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 "centrallayout.h" // local #include "sharedlayout.h" -#include "importer.h" #include "../lattecorona.h" #include "../screenpool.h" +#include "../layouts/importer.h" #include "../layouts/manager.h" #include "../settings/universalsettings.h" #include "../view/view.h" #include "../../liblatte2/types.h" // Qt #include // KDE #include #include namespace Latte { CentralLayout::CentralLayout(QObject *parent, QString layoutFile, QString assignedName) : Layout::GenericLayout(parent, layoutFile, assignedName) { if (m_loadedCorrectly) { loadConfig(); init(); } } CentralLayout::~CentralLayout() { if (!m_layoutFile.isEmpty()) { m_layoutGroup.sync(); } } void CentralLayout::unloadContainments() { Layout::GenericLayout::unloadContainments(); if (m_sharedLayout) { disconnectSharedConnections(); m_sharedLayout->removeCentralLayout(this); } } void CentralLayout::init() { connect(this, &CentralLayout::activitiesChanged, this, &CentralLayout::saveConfig); connect(this, &CentralLayout::disableBordersForMaximizedWindowsChanged, this, &CentralLayout::saveConfig); connect(this, &CentralLayout::sharedLayoutNameChanged, this, &CentralLayout::saveConfig); connect(this, &CentralLayout::showInMenuChanged, this, &CentralLayout::saveConfig); } void CentralLayout::initToCorona(Latte::Corona *corona) { if (GenericLayout::initToCorona(corona)) { connect(m_corona->universalSettings(), &UniversalSettings::canDisableBordersChanged, this, [&]() { if (m_corona->universalSettings()->canDisableBorders()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } else { kwin_setDisabledMaximizedBorders(false); } }); if (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout && m_corona->universalSettings()->canDisableBorders()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { connect(m_corona->layoutsManager(), &Layouts::Manager::currentLayoutNameChanged, this, [&]() { if (m_corona->universalSettings()->canDisableBorders() && m_corona->layoutsManager()->currentLayoutName() == name()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } }); } //! Request the SharedLayout in case there is one and Latte is functioning in MultipleLayouts mode if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts && !m_sharedLayoutName.isEmpty()) { if (m_corona->layoutsManager()->registerAtSharedLayout(this, m_sharedLayoutName)) { setSharedLayout(m_corona->layoutsManager()->sharedLayout(m_sharedLayoutName)); } } } } bool CentralLayout::disableBordersForMaximizedWindows() const { return m_disableBordersForMaximizedWindows; } void CentralLayout::setDisableBordersForMaximizedWindows(bool disable) { if (m_disableBordersForMaximizedWindows == disable) { return; } m_disableBordersForMaximizedWindows = disable; kwin_setDisabledMaximizedBorders(disable); emit disableBordersForMaximizedWindowsChanged(); } bool CentralLayout::kwin_disabledMaximizedBorders() const { //! Identify Plasma Desktop version QProcess process; process.start("kreadconfig5 --file kwinrc --group Windows --key BorderlessMaximizedWindows"); process.waitForFinished(); QString output(process.readAllStandardOutput()); output = output.remove("\n"); return (output == "true"); } void CentralLayout::kwin_setDisabledMaximizedBorders(bool disable) { if (kwin_disabledMaximizedBorders() == disable) { return; } QString disableText = disable ? "true" : "false"; QProcess process; QString commandStr = "kwriteconfig5 --file kwinrc --group Windows --key BorderlessMaximizedWindows --type bool " + disableText; process.start(commandStr); process.waitForFinished(); QDBusInterface iface("org.kde.KWin", "/KWin", "", QDBusConnection::sessionBus()); if (iface.isValid()) { iface.call("reconfigure"); } } bool CentralLayout::showInMenu() const { return m_showInMenu; } void CentralLayout::setShowInMenu(bool show) { if (m_showInMenu == show) { return; } m_showInMenu = show; emit showInMenuChanged(); } Layout::Type CentralLayout::type() const { return Layout::Type::Central; } QStringList CentralLayout::activities() const { return m_activities; } void CentralLayout::setActivities(QStringList activities) { if (m_activities == activities) { return; } m_activities = activities; emit activitiesChanged(); } QString CentralLayout::sharedLayoutName() const { return m_sharedLayoutName; } void CentralLayout::setSharedLayoutName(QString name) { - if (m_sharedLayoutName == name || (!Importer::layoutExists(name) && !name.isEmpty())) { + if (m_sharedLayoutName == name || (!Layouts::Importer::layoutExists(name) && !name.isEmpty())) { return; } m_sharedLayoutName = name; emit sharedLayoutNameChanged(); } SharedLayout *CentralLayout::sharedLayout() const { return m_sharedLayout; } void CentralLayout::setSharedLayout(SharedLayout *layout) { if (m_sharedLayout == layout) { return; } disconnectSharedConnections(); m_sharedLayout = layout; if (layout) { setSharedLayoutName(m_sharedLayout->name()); //! attach new signals m_sharedConnections << connect(m_sharedLayout, &Layout::GenericLayout::viewsCountChanged, this, &Layout::GenericLayout::viewsCountChanged); m_sharedConnections << connect(m_sharedLayout, &Layout::AbstractLayout::nameChanged, this, [this]() { setSharedLayoutName(m_sharedLayout->name()); }); } else { setSharedLayoutName(QString()); } emit viewsCountChanged(); } void CentralLayout::disconnectSharedConnections() { //! drop old signals for (const auto &sc : m_sharedConnections) { QObject::disconnect(sc); } m_sharedConnections.clear(); } void CentralLayout::loadConfig() { m_disableBordersForMaximizedWindows = m_layoutGroup.readEntry("disableBordersForMaximizedWindows", false); m_showInMenu = m_layoutGroup.readEntry("showInMenu", false); m_activities = m_layoutGroup.readEntry("activities", QStringList()); QString sharedLayoutName = m_layoutGroup.readEntry("sharedLayout", QString()); - if (Importer::layoutExists(sharedLayoutName)) { + if (Layouts::Importer::layoutExists(sharedLayoutName)) { m_sharedLayoutName = sharedLayoutName; } emit activitiesChanged(); } void CentralLayout::saveConfig() { qDebug() << "CENTRAL layout is saving... for layout:" << m_layoutName; m_layoutGroup.writeEntry("showInMenu", m_showInMenu); m_layoutGroup.writeEntry("disableBordersForMaximizedWindows", m_disableBordersForMaximizedWindows); m_layoutGroup.writeEntry("sharedLayout", m_sharedLayoutName); m_layoutGroup.writeEntry("activities", m_activities); m_layoutGroup.sync(); } //! OVERRIDES void CentralLayout::addView(Plasma::Containment *containment, bool forceOnPrimary, int explicitScreen, Layout::ViewsMap *occupied) { if (m_sharedLayout) { //! consider already occupied edges from SharedLayout Layout::ViewsMap ocMap = m_sharedLayout->validViewsMap(); Layout::GenericLayout::addView(containment, forceOnPrimary, explicitScreen, &ocMap); } else { Layout::GenericLayout::addView(containment, forceOnPrimary, explicitScreen, occupied); } } const QStringList CentralLayout::appliedActivities() { if (!m_corona) { return {}; } if (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout) { return {"0"}; } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { if (m_activities.isEmpty()) { return m_corona->layoutsManager()->orphanedActivities(); } else { return m_activities; } } else { return {"0"}; } } QList CentralLayout::latteViews() { if (m_sharedLayout) { QList views = Layout::GenericLayout::latteViews(); views << m_sharedLayout->latteViews(); return views; } return Layout::GenericLayout::latteViews(); } int CentralLayout::viewsCount(int screen) const { if (!m_corona) { return 0; } int views = Layout::GenericLayout::viewsCount(screen); if (m_sharedLayout) { QScreen *scr = m_corona->screenPool()->screenForId(screen); for (const auto view : m_sharedLayout->latteViews()) { if (view && view->screen() == scr && !view->containment()->destroyed()) { ++views; } } } return views; } int CentralLayout::viewsCount(QScreen *screen) const { if (!m_corona) { return 0; } int views = Layout::GenericLayout::viewsCount(screen); if (m_sharedLayout) { for (const auto view : m_sharedLayout->latteViews()) { if (view && view->screen() == screen && !view->containment()->destroyed()) { ++views; } } } return views; } int CentralLayout::viewsCount() const { if (!m_corona) { return 0; } int views = Layout::GenericLayout::viewsCount(); if (m_sharedLayout) { for (const auto view : m_sharedLayout->latteViews()) { if (view && view->containment() && !view->containment()->destroyed()) { ++views; } } } return views; } QList CentralLayout::availableEdgesForView(QScreen *scr, Latte::View *forView) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } edges = Layout::GenericLayout::availableEdgesForView(scr, forView); if (m_sharedLayout) { for (const auto view : m_sharedLayout->latteViews()) { //! make sure that availabe edges takes into account only views that should be excluded, //! this is why the forView should not be excluded if (view && view != forView && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } } return edges; } QList CentralLayout::freeEdges(QScreen *scr) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } edges = Layout::GenericLayout::freeEdges(scr); if (m_sharedLayout) { for (const auto view : m_sharedLayout->latteViews()) { if (view && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } } return edges; } QList CentralLayout::freeEdges(int screen) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } edges = Layout::GenericLayout::freeEdges(screen); QScreen *scr = m_corona->screenPool()->screenForId(screen); if (m_sharedLayout) { for (const auto view : m_sharedLayout->latteViews()) { if (view && scr && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } } return edges; } QList CentralLayout::sortedLatteViews(QList views) { QList vws = latteViews(); return Layout::GenericLayout::sortedLatteViews(vws); } QList CentralLayout::viewsWithPlasmaShortcuts() { QList combined = Layout::GenericLayout::viewsWithPlasmaShortcuts(); if (m_sharedLayout) { combined << m_sharedLayout->viewsWithPlasmaShortcuts(); } return combined; } void CentralLayout::syncLatteViewsToScreens(Layout::ViewsMap *occupiedMap) { if (m_sharedLayout) { Layout::ViewsMap map = m_sharedLayout->validViewsMap(); Layout::GenericLayout::syncLatteViewsToScreens(&map); } else { Layout::GenericLayout::syncLatteViewsToScreens(); } } } diff --git a/app/layout/genericlayout.cpp b/app/layout/genericlayout.cpp index 2d6ebbb7..faa43afe 100644 --- a/app/layout/genericlayout.cpp +++ b/app/layout/genericlayout.cpp @@ -1,1211 +1,1211 @@ /* * 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 "genericlayout.h" // local #include "abstractlayout.h" #include "storage.h" -#include "../importer.h" #include "../lattecorona.h" #include "../screenpool.h" +#include "../layouts/importer.h" #include "../layouts/manager.h" #include "../shortcuts/shortcutstracker.h" #include "../view/view.h" #include "../view/positioner.h" // Qt #include #include // Plasma #include #include #include // KDE #include namespace Latte { namespace Layout { GenericLayout::GenericLayout(QObject *parent, QString layoutFile, QString assignedName) : AbstractLayout (parent, layoutFile, assignedName), m_storage(new Storage(this)) { } GenericLayout::~GenericLayout() { } Type GenericLayout::type() const { return Type::Generic; } void GenericLayout::unloadContainments() { if (!m_corona) { return; } //!disconnect signals in order to avoid crashes when the layout is unloading disconnect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged); disconnect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged); disconnect(m_corona->activityConsumer(), &KActivities::Consumer::currentActivityChanged, this, &GenericLayout::updateLastUsedActivity); qDebug() << "Layout - " + name() + " unload: containments ... size ::: " << m_containments.size() << " ,latteViews in memory ::: " << m_latteViews.size() << " ,hidden latteViews in memory ::: " << m_waitingLatteViews.size(); for (const auto view : m_latteViews) { view->disconnectSensitiveSignals(); } for (const auto view : m_waitingLatteViews) { view->disconnectSensitiveSignals(); } m_unloadedContainmentsIds.clear(); QList systrays; //!identify systrays and unload them first for (const auto containment : m_containments) { if (Plasma::Applet *parentApplet = qobject_cast(containment->parent())) { systrays.append(containment); } } while (!systrays.isEmpty()) { Plasma::Containment *systray = systrays.at(0); m_unloadedContainmentsIds << QString::number(systray->id()); systrays.removeFirst(); m_containments.removeAll(systray); delete systray; } while (!m_containments.isEmpty()) { Plasma::Containment *containment = m_containments.at(0); m_unloadedContainmentsIds << QString::number(containment->id()); m_containments.removeFirst(); delete containment; } } void GenericLayout::unloadLatteViews() { if (!m_corona) { return; } qDebug() << "Layout - " + name() + " unload: latteViews ... size: " << m_latteViews.size(); qDeleteAll(m_latteViews); qDeleteAll(m_waitingLatteViews); m_latteViews.clear(); m_waitingLatteViews.clear(); } bool GenericLayout::blockAutomaticLatteViewCreation() const { return m_blockAutomaticLatteViewCreation; } void GenericLayout::setBlockAutomaticLatteViewCreation(bool block) { if (m_blockAutomaticLatteViewCreation == block) { return; } m_blockAutomaticLatteViewCreation = block; } bool GenericLayout::isActive() const { if (!m_corona) { return false; } GenericLayout *generic = m_corona->layoutsManager()->layout(m_layoutName); if (generic) { return true; } else { return false; } } bool GenericLayout::isCurrent() const { if (!m_corona) { return false; } return name() == m_corona->layoutsManager()->currentLayoutName(); } int GenericLayout::viewsCount(int screen) const { if (!m_corona) { return 0; } QScreen *scr = m_corona->screenPool()->screenForId(screen); int views{0}; for (const auto view : m_latteViews) { if (view && view->screen() == scr && !view->containment()->destroyed()) { ++views; } } return views; } int GenericLayout::viewsCount(QScreen *screen) const { if (!m_corona) { return 0; } int views{0}; for (const auto view : m_latteViews) { if (view && view->screen() == screen && !view->containment()->destroyed()) { ++views; } } return views; } int GenericLayout::viewsCount() const { if (!m_corona) { return 0; } int views{0}; for (const auto view : m_latteViews) { if (view && view->containment() && !view->containment()->destroyed()) { ++views; } } return views; } QList GenericLayout::qmlFreeEdges(int screen) const { if (!m_corona) { const QList emptyEdges; return emptyEdges; } const auto edges = freeEdges(screen); QList edgesInt; for (const Plasma::Types::Location &edge : edges) { edgesInt.append(static_cast(edge)); } return edgesInt; } QList GenericLayout::freeEdges(QScreen *scr) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } for (const auto view : m_latteViews) { if (view && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } QList GenericLayout::freeEdges(int screen) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } QScreen *scr = m_corona->screenPool()->screenForId(screen); for (const auto view : m_latteViews) { if (view && scr && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } int GenericLayout::viewsWithTasks() const { if (!m_corona) { return 0; } int result = 0; for (const auto view : m_latteViews) { if (view->tasksPresent()) { result++; } } return result; } QStringList GenericLayout::unloadedContainmentsIds() { return m_unloadedContainmentsIds; } Latte::Corona *GenericLayout::corona() { return m_corona; } Types::ViewType GenericLayout::latteViewType(int containmentId) const { for (const auto view : m_latteViews) { if (view->containment() && view->containment()->id() == containmentId) { return view->type(); } } return Types::DockView; } Latte::View *GenericLayout::highestPriorityView() { QList views = sortedLatteViews(); return (views.count() > 0 ? views[0] : nullptr); } Latte::View *GenericLayout::viewForContainment(const Plasma::Containment *containment) { if (m_latteViews.contains(containment)) { return m_latteViews[containment]; } return nullptr; } QList GenericLayout::latteViews() { return m_latteViews.values(); } QList GenericLayout::sortedLatteViews(QList views) { QList sortedViews = views.isEmpty() ? latteViews() : views; qDebug() << " -------- "; for (int i = 0; i < sortedViews.count(); ++i) { qDebug() << i << ". " << sortedViews[i]->screen()->name() << " - " << sortedViews[i]->location(); } //! sort the views based on screens and edges priorities //! views on primary screen have higher priority and //! for views in the same screen the priority goes to //! Bottom,Left,Top,Right for (int i = 0; i < sortedViews.size(); ++i) { for (int j = 0; j < sortedViews.size() - i - 1; ++j) { if (viewAtLowerScreenPriority(sortedViews[j], sortedViews[j + 1]) || (sortedViews[j]->screen() == sortedViews[j + 1]->screen() && viewAtLowerEdgePriority(sortedViews[j], sortedViews[j + 1]))) { Latte::View *temp = sortedViews[j + 1]; sortedViews[j + 1] = sortedViews[j]; sortedViews[j] = temp; } } } Latte::View *highestPriorityView{nullptr}; for (int i = 0; i < sortedViews.size(); ++i) { if (sortedViews[i]->isPreferredForShortcuts()) { highestPriorityView = sortedViews[i]; sortedViews.removeAt(i); break; } } if (highestPriorityView) { sortedViews.prepend(highestPriorityView); } qDebug() << " -------- sorted -----"; for (int i = 0; i < sortedViews.count(); ++i) { qDebug() << i << ". " << sortedViews[i]->isPreferredForShortcuts() << " - " << sortedViews[i]->screen()->name() << " - " << sortedViews[i]->location(); } return sortedViews; } bool GenericLayout::viewAtLowerScreenPriority(Latte::View *test, Latte::View *base) { if (!base || ! test) { return true; } if (base->screen() == test->screen()) { return false; } else if (base->screen() != qGuiApp->primaryScreen() && test->screen() == qGuiApp->primaryScreen()) { return false; } else if (base->screen() == qGuiApp->primaryScreen() && test->screen() != qGuiApp->primaryScreen()) { return true; } else { int basePriority = -1; int testPriority = -1; for (int i = 0; i < qGuiApp->screens().count(); ++i) { if (base->screen() == qGuiApp->screens()[i]) { basePriority = i; } if (test->screen() == qGuiApp->screens()[i]) { testPriority = i; } } if (testPriority <= basePriority) { return true; } else { return false; } } qDebug() << "viewAtLowerScreenPriority : shouldn't had reached here..."; return false; } bool GenericLayout::viewAtLowerEdgePriority(Latte::View *test, Latte::View *base) { if (!base || ! test) { return true; } QList edges{Plasma::Types::RightEdge, Plasma::Types::TopEdge, Plasma::Types::LeftEdge, Plasma::Types::BottomEdge}; int testPriority = -1; int basePriority = -1; for (int i = 0; i < edges.count(); ++i) { if (edges[i] == base->location()) { basePriority = i; } if (edges[i] == test->location()) { testPriority = i; } } if (testPriority < basePriority) return true; else return false; } const QList *GenericLayout::containments() { return &m_containments; } QList GenericLayout::viewsWithPlasmaShortcuts() { QList views; if (!m_corona) { return views; } QList appletsWithShortcuts = m_corona->globalShortcuts()->shortcutsTracker()->appletsWithPlasmaShortcuts(); for (const auto &appletId : appletsWithShortcuts) { for (const auto view : m_latteViews) { bool found{false}; for (const auto applet : view->containment()->applets()) { if (appletId == applet->id()) { if (!views.contains(view)) { views.append(view); found = true; break; } } } if (found) { break; } } } return views; } //! Containments Actions void GenericLayout::addContainment(Plasma::Containment *containment) { if (!containment || m_containments.contains(containment)) { return; } bool containmentInLayout{false}; if (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout) { m_containments.append(containment); containmentInLayout = true; } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty() && (layoutId == m_layoutName)) { m_containments.append(containment); containmentInLayout = true; } } if (containmentInLayout) { if (!blockAutomaticLatteViewCreation()) { addView(containment); } else { qDebug() << "delaying LatteView creation for containment :: " << containment->id(); } connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed); } } void GenericLayout::appletCreated(Plasma::Applet *applet) { //! In Multiple Layout the orphaned systrays must be assigned to layouts //! when the user adds them KConfigGroup appletSettings = applet->containment()->config().group("Applets").group(QString::number(applet->id())).group("Configuration"); int systrayId = appletSettings.readEntry("SystrayContainmentId", -1); if (systrayId != -1) { uint sId = (uint)systrayId; for (const auto containment : m_corona->containments()) { if (containment->id() == sId) { containment->config().writeEntry("layoutId", m_layoutName); } addContainment(containment); } } } void GenericLayout::containmentDestroyed(QObject *cont) { if (!m_corona) { return; } Plasma::Containment *containment = static_cast(cont); if (containment) { int containmentIndex = m_containments.indexOf(containment); if (containmentIndex >= 0) { m_containments.removeAt(containmentIndex); } qDebug() << "Layout " << name() << " :: containment destroyed!!!!"; auto view = m_latteViews.take(containment); if (!view) { view = m_waitingLatteViews.take(containment); } if (view) { view->disconnectSensitiveSignals(); view->deleteLater(); emit viewsCountChanged(); } } } void GenericLayout::destroyedChanged(bool destroyed) { if (!m_corona) { return; } qDebug() << "dock containment destroyed changed!!!!"; Plasma::Containment *sender = qobject_cast(QObject::sender()); if (!sender) { return; } if (destroyed) { m_waitingLatteViews[sender] = m_latteViews.take(static_cast(sender)); } else { m_latteViews[sender] = m_waitingLatteViews.take(static_cast(sender)); } emit viewsCountChanged(); } void GenericLayout::renameLayout(QString newName) { if (!m_corona || m_corona->layoutsManager()->memoryUsage() != Types::MultipleLayouts) { return; } - if (m_layoutFile != Importer::layoutFilePath(newName)) { - setFile(Importer::layoutFilePath(newName)); + if (m_layoutFile != Layouts::Importer::layoutFilePath(newName)) { + setFile(Layouts::Importer::layoutFilePath(newName)); } setName(newName); for (const auto containment : m_containments) { qDebug() << "Cont ID :: " << containment->id(); containment->config().writeEntry("layoutId", m_layoutName); } } void GenericLayout::addNewView() { if (!m_corona) { return; } m_corona->addViewForLayout(name()); } void GenericLayout::addView(Plasma::Containment *containment, bool forceOnPrimary, int explicitScreen, Layout::ViewsMap *occupied) { qDebug() << "Layout :::: " << m_layoutName << " ::: addView was called... m_containments :: " << m_containments.size(); if (!containment || !m_corona || !containment->kPackage().isValid()) { qWarning() << "the requested containment plugin can not be located or loaded"; return; } qDebug() << "step 1..."; if (!m_storage->isLatteContainment(containment)) return; qDebug() << "step 2..."; for (auto *dock : m_latteViews) { if (dock->containment() == containment) return; } qDebug() << "step 3..."; QScreen *nextScreen{qGuiApp->primaryScreen()}; bool onPrimary = containment->config().readEntry("onPrimary", true); int id = containment->screen(); if (id == -1 && explicitScreen == -1) { id = containment->lastScreen(); } if (explicitScreen > -1) { id = explicitScreen; } Plasma::Types::Location edge = containment->location(); QString connector = m_corona->screenPool()->hasId(id) ? m_corona->screenPool()->connector(id) : ""; qDebug() << "Adding view - containment id:" << containment->id() << " ,screen :" << id << " - " << connector << " ,onprimary:" << onPrimary << " - " << " edge:" << edge << " ,screenName:" << qGuiApp->primaryScreen()->name() << " ,forceOnPrimary:" << forceOnPrimary; if (occupied && m_corona->screenPool()->hasId(id) && (*occupied).contains(connector) && (*occupied)[connector].contains(edge)) { qDebug() << "Rejected : adding view because the edge is already occupied by a higher priority view ! : " << (*occupied)[connector][edge]; return; } if (id >= 0 && !onPrimary && !forceOnPrimary) { qDebug() << "Add view - connector : " << connector; bool found{false}; if (m_corona->screenPool()->hasId(id)) { for (const auto scr : qGuiApp->screens()) { if (scr && scr->name() == connector) { found = true; nextScreen = scr; break; } } } if (!found) { qDebug() << "Rejected : adding explicit view, screen not available ! : " << connector; return; } //! explicit dock can not be added at explicit screen when that screen is the same with //! primary screen and that edge is already occupied by a primary dock if (nextScreen == qGuiApp->primaryScreen() && primaryDockOccupyEdge(containment->location())) { qDebug() << "Rejected : adding explicit view, primary dock occupies edge at screen ! : " << connector; return; } } if (id >= 0 && onPrimary) { qDebug() << "add dock - connector : " << connector; for (const auto view : m_latteViews) { auto testContainment = view->containment(); int testScreenId = testContainment->screen(); if (testScreenId == -1) { testScreenId = testContainment->lastScreen(); } bool testOnPrimary = testContainment->config().readEntry("onPrimary", true); Plasma::Types::Location testLocation = static_cast((int)testContainment->config().readEntry("location", (int)Plasma::Types::BottomEdge)); if (!testOnPrimary && m_corona->screenPool()->primaryScreenId() == testScreenId && testLocation == containment->location()) { qDebug() << "Rejected explicit latteView and removing it in order add an onPrimary with higher priority at screen: " << connector; auto viewToDelete = m_latteViews.take(testContainment); viewToDelete->disconnectSensitiveSignals(); viewToDelete->deleteLater(); } } } qDebug() << "Adding view passed ALL checks" << " ,onPrimary:" << onPrimary << " ,screen:" << nextScreen->name() << " !!!"; //! it is used to set the correct flag during the creation //! of the window... This of course is also used during //! recreations of the window between different visibility modes auto mode = static_cast(containment->config().readEntry("visibility", static_cast(Types::DodgeActive))); bool byPassWM{false}; if (mode == Types::AlwaysVisible || mode == Types::WindowsGoBelow) { byPassWM = false; } else { byPassWM = containment->config().readEntry("byPassWM", false); } auto latteView = new Latte::View(m_corona, nextScreen, byPassWM); latteView->init(); latteView->setContainment(containment); latteView->setLayout(this); //! force this special dock case to become primary //! even though it isnt if (forceOnPrimary) { qDebug() << "Enforcing onPrimary:true as requested for LatteView..."; latteView->setOnPrimary(true); } // connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed); connect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged); connect(containment, &Plasma::Applet::locationChanged, m_corona, &Latte::Corona::viewLocationChanged); connect(containment, &Plasma::Containment::appletAlternativesRequested , m_corona, &Latte::Corona::showAlternativesForApplet, Qt::QueuedConnection); if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { connect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated); } //! Qt 5.9 creates a crash for this in wayland, that is why the check is used //! but on the other hand we need this for copy to work correctly and show //! the copied dock under X11 //if (!KWindowSystem::isPlatformWayland()) { latteView->show(); //} m_latteViews[containment] = latteView; emit viewsCountChanged(); } bool GenericLayout::initToCorona(Latte::Corona *corona) { if (m_corona) { return false; } m_corona = corona; for (const auto containment : m_corona->containments()) { if (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout) { addContainment(containment); } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty() && (layoutId == m_layoutName)) { addContainment(containment); } } } qDebug() << "Layout ::::: " << name() << " added containments ::: " << m_containments.size(); updateLastUsedActivity(); //! signals connect(m_corona->activityConsumer(), &KActivities::Consumer::currentActivityChanged, this, &GenericLayout::updateLastUsedActivity); connect(m_corona, &Plasma::Corona::containmentAdded, this, &GenericLayout::addContainment); //!connect signals after adding the containment connect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged); connect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged); emit viewsCountChanged(); return true; } void GenericLayout::updateLastUsedActivity() { if (!m_corona) { return; } if (!m_lastUsedActivity.isEmpty() && !m_corona->layoutsManager()->activities().contains(m_lastUsedActivity)) { clearLastUsedActivity(); } QString currentId = m_corona->activitiesConsumer()->currentActivity(); QStringList appliedActivitiesIds = appliedActivities(); if (m_lastUsedActivity != currentId && (appliedActivitiesIds.contains(currentId) || m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout)) { m_lastUsedActivity = currentId; emit lastUsedActivityChanged(); } } void GenericLayout::assignToLayout(Latte::View *latteView, QList containments) { if (!m_corona) { return; } if (latteView) { m_latteViews[latteView->containment()] = latteView; m_containments << containments; for (const auto containment : containments) { containment->config().writeEntry("layoutId", name()); connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed); connect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged); connect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated); } latteView->setLayout(this); emit viewsCountChanged(); } //! sync the original layout file for integrity if (m_corona && m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { m_storage->syncToLayoutFile(false); } } QList GenericLayout::unassignFromLayout(Latte::View *latteView) { QList containments; if (!m_corona) { return containments; } containments << latteView->containment(); for (const auto containment : m_containments) { Plasma::Applet *parentApplet = qobject_cast(containment->parent()); //! add systrays from that latteView if (parentApplet && parentApplet->containment() && parentApplet->containment() == latteView->containment()) { containments << containment; disconnect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed); disconnect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged); disconnect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated); } } for (const auto containment : containments) { m_containments.removeAll(containment); } if (containments.size() > 0) { m_latteViews.remove(latteView->containment()); } //! sync the original layout file for integrity if (m_corona && m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { m_storage->syncToLayoutFile(false); } return containments; } void GenericLayout::recreateView(Plasma::Containment *containment) { if (!m_corona) { return; } //! give the time to config window to close itself first and then recreate the dock //! step:1 remove the latteview QTimer::singleShot(350, [this, containment]() { auto view = m_latteViews.take(containment); if (view) { qDebug() << "recreate - step 1: removing dock for containment:" << containment->id(); //! step:2 add the new latteview connect(view, &QObject::destroyed, this, [this, containment]() { QTimer::singleShot(250, this, [this, containment]() { if (!m_latteViews.contains(containment)) { qDebug() << "recreate - step 2: adding dock for containment:" << containment->id(); addView(containment); } }); }); view->deleteLater(); } }); } bool GenericLayout::latteViewExists(Plasma::Containment *containment) { if (!m_corona) { return false; } return m_latteViews.keys().contains(containment); } QList GenericLayout::availableEdgesForView(QScreen *scr, Latte::View *forView) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } for (const auto view : m_latteViews) { //! make sure that availabe edges takes into account only views that should be excluded, //! this is why the forView should not be excluded if (view && view != forView && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } bool GenericLayout::explicitDockOccupyEdge(int screen, Plasma::Types::Location location) const { if (!m_corona) { return false; } for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { bool onPrimary = containment->config().readEntry("onPrimary", true); int id = containment->lastScreen(); Plasma::Types::Location contLocation = containment->location(); if (!onPrimary && id == screen && contLocation == location) { return true; } } } return false; } bool GenericLayout::primaryDockOccupyEdge(Plasma::Types::Location location) const { if (!m_corona) { return false; } for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { bool onPrimary = containment->config().readEntry("onPrimary", true); Plasma::Types::Location contLocation = containment->location(); if (onPrimary && contLocation == location) { return true; } } } return false; } bool GenericLayout::mapContainsId(const Layout::ViewsMap *map, uint viewId) const { for(const auto &scr : map->keys()) { for(const auto &edge : (*map)[scr].keys()) { if ((*map)[scr][edge] == viewId) { return true; } } } return false; } //! screen name, location, containmentId Layout::ViewsMap GenericLayout::validViewsMap(Layout::ViewsMap *occupiedMap) { Layout::ViewsMap map; if (!m_corona) { return map; } if (occupiedMap != nullptr) { map = (*occupiedMap); } QString prmScreenName = qGuiApp->primaryScreen()->name(); //! first step: primary docks must be placed in primary screen free edges for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { int screenId = 0; //! valid screen id if (latteViewExists(containment)) { screenId = m_latteViews[containment]->positioner()->currentScreenId(); } else { screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } } bool onPrimary{true}; //! valid onPrimary flag if (latteViewExists(containment)) { onPrimary = m_latteViews[containment]->onPrimary(); } else { onPrimary = containment->config().readEntry("onPrimary", true); } //! valid location Plasma::Types::Location location = containment->location(); if (onPrimary && !map[prmScreenName].contains(location)) { map[prmScreenName][location] = containment->id(); } } } //! second step: explicit docks must be placed in their screens if the screen edge is free for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { int screenId = 0; //! valid screen id if (latteViewExists(containment)) { screenId = m_latteViews[containment]->positioner()->currentScreenId(); } else { screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } } bool onPrimary{true}; //! valid onPrimary flag if (latteViewExists(containment)) { onPrimary = m_latteViews[containment]->onPrimary(); } else { onPrimary = containment->config().readEntry("onPrimary", true); } //! valid location Plasma::Types::Location location = containment->location(); if (!onPrimary) { QString expScreenName = m_corona->screenPool()->connector(screenId); if (m_corona->screenPool()->screenExists(screenId) && !map[expScreenName].contains(location)) { map[expScreenName][location] = containment->id(); } } } } return map; } //! the central functions that updates loading/unloading latteviews //! concerning screen changed (for multi-screen setups mainly) void GenericLayout::syncLatteViewsToScreens(Layout::ViewsMap *occupiedMap) { if (!m_corona) { return; } qDebug() << "START of SyncLatteViewsToScreens ...."; qDebug() << "LAYOUT ::: " << name(); qDebug() << "screen count changed -+-+ " << qGuiApp->screens().size(); Layout::ViewsMap viewsMap = validViewsMap(occupiedMap); if (occupiedMap != nullptr) { qDebug() << "Occupied map used :: " << *occupiedMap; } QString prmScreenName = qGuiApp->primaryScreen()->name(); qDebug() << "PRIMARY SCREEN :: " << prmScreenName; qDebug() << "LATTEVIEWS MAP :: " << viewsMap; //! add views for (const auto containment : m_containments) { int screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } if (!latteViewExists(containment) && mapContainsId(&viewsMap, containment->id())) { qDebug() << "syncLatteViewsToScreens: view must be added... for containment:" << containment->id() << " at screen:" << m_corona->screenPool()->connector(screenId); addView(containment); } } //! remove views QList viewsToDelete; for (auto view : m_latteViews) { auto containment = view->containment(); if (containment && !mapContainsId(&viewsMap, containment->id())) { viewsToDelete << containment; } } while(!viewsToDelete.isEmpty()) { auto containment = viewsToDelete.takeFirst(); auto view = m_latteViews.take(containment); qDebug() << "syncLatteViewsToScreens: view must be deleted... for containment:" << containment->id() << " at screen:" << view->positioner()->currentScreenName(); view->disconnectSensitiveSignals(); view->deleteLater(); } //! reconsider views for (const auto view : m_latteViews) { if (view->containment() && mapContainsId(&viewsMap, view->containment()->id())) { //! if the dock will not be deleted its a very good point to reconsider //! if the screen in which is running is the correct one qDebug() << "syncLatteViewsToScreens: view must consider its screen... for containment:" << view->containment()->id() << " at screen:" << view->positioner()->currentScreenName(); view->reconsiderScreen(); } } qDebug() << "end of, syncLatteViewsToScreens ...."; } //! STORAGE bool GenericLayout::isWritable() const { return m_storage->isWritable(); } void GenericLayout::lock() { m_storage->lock(); } void GenericLayout::unlock() { m_storage->unlock(); } void GenericLayout::syncToLayoutFile(bool removeLayoutId) { m_storage->syncToLayoutFile(removeLayoutId); } void GenericLayout::copyView(Plasma::Containment *containment) { m_storage->copyView(containment); } void GenericLayout::importToCorona() { m_storage->importToCorona(); } bool GenericLayout::layoutIsBroken() const { return m_storage->layoutIsBroken(); } } } diff --git a/app/layout/storage.cpp b/app/layout/storage.cpp index 506eecf3..66bcb247 100644 --- a/app/layout/storage.cpp +++ b/app/layout/storage.cpp @@ -1,671 +1,671 @@ /* * 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 "storage.h" // local -#include "../importer.h" #include "../lattecorona.h" #include "../screenpool.h" #include "../layouts/manager.h" +#include "../layouts/importer.h" #include "../view/view.h" // Qt #include #include #include // KDE #include #include // Plasma #include #include #include namespace Latte { namespace Layout { Storage::Storage(GenericLayout *parent) : QObject(parent), m_layout(parent) { } Storage::~Storage() { } bool Storage::isWritable() const { QFileInfo layoutFileInfo(m_layout->file()); if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) { return false; } else { return true; } } bool Storage::isLatteContainment(Plasma::Containment *containment) const { if (!containment) { return false; } if (containment->pluginMetaData().pluginId() == "org.kde.latte.containment") { return true; } return false; } void Storage::lock() { QFileInfo layoutFileInfo(m_layout->file()); if (layoutFileInfo.exists() && layoutFileInfo.isWritable()) { QFile(m_layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } } void Storage::unlock() { QFileInfo layoutFileInfo(m_layout->file()); if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) { QFile(m_layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } } void Storage::importToCorona() { if (!m_layout->corona()) { return; } //! Setting mutable for create a containment m_layout->corona()->setImmutability(Plasma::Types::Mutable); QString temp1FilePath = QDir::homePath() + "/.config/lattedock.copy1.bak"; //! we need to copy first the layout file because the kde cache //! may not have yet been updated (KSharedConfigPtr) //! this way we make sure at the latest changes stored in the layout file //! will be also available when changing to Multiple Layouts QString tempLayoutFilePath = QDir::homePath() + "/.config/lattedock.layout.bak"; //! WE NEED A WAY TO COPY A CONTAINMENT!!!! QFile tempLayoutFile(tempLayoutFilePath); QFile copyFile(temp1FilePath); QFile layoutOriginalFile(m_layout->file()); if (tempLayoutFile.exists()) { tempLayoutFile.remove(); } if (copyFile.exists()) copyFile.remove(); layoutOriginalFile.copy(tempLayoutFilePath); KSharedConfigPtr filePtr = KSharedConfig::openConfig(tempLayoutFilePath); KSharedConfigPtr newFile = KSharedConfig::openConfig(temp1FilePath); KConfigGroup copyGroup = KConfigGroup(newFile, "Containments"); KConfigGroup current_containments = KConfigGroup(filePtr, "Containments"); current_containments.copyTo(©Group); copyGroup.sync(); //! update ids to unique ones QString temp2File = newUniqueIdsLayoutFromFile(temp1FilePath); //! Finally import the configuration importLayoutFile(temp2File); } void Storage::syncToLayoutFile(bool removeLayoutId) { if (!m_layout->corona() || !isWritable()) { return; } KSharedConfigPtr filePtr = KSharedConfig::openConfig(m_layout->file()); KConfigGroup oldContainments = KConfigGroup(filePtr, "Containments"); oldContainments.deleteGroup(); oldContainments.sync(); qDebug() << " LAYOUT :: " << m_layout->name() << " is syncing its original file."; for (const auto containment : *m_layout->containments()) { if (removeLayoutId) { containment->config().writeEntry("layoutId", ""); } KConfigGroup newGroup = oldContainments.group(QString::number(containment->id())); containment->config().copyTo(&newGroup); if (!removeLayoutId) { newGroup.writeEntry("layoutId", ""); newGroup.sync(); } } oldContainments.sync(); } void Storage::copyView(Plasma::Containment *containment) { if (!containment || !m_layout->corona()) return; qDebug() << "copying containment layout"; //! Setting mutable for create a containment m_layout->corona()->setImmutability(Plasma::Types::Mutable); QString temp1File = QDir::homePath() + "/.config/lattedock.copy1.bak"; //! WE NEED A WAY TO COPY A CONTAINMENT!!!! QFile copyFile(temp1File); if (copyFile.exists()) copyFile.remove(); KSharedConfigPtr newFile = KSharedConfig::openConfig(temp1File); KConfigGroup copied_conts = KConfigGroup(newFile, "Containments"); KConfigGroup copied_c1 = KConfigGroup(&copied_conts, QString::number(containment->id())); KConfigGroup copied_systray; // toCopyContainmentIds << QString::number(containment->id()); // toCopyAppletIds << containment->config().group("Applets").groupList(); containment->config().copyTo(&copied_c1); //!investigate if there is a systray in the containment to copy also int systrayId = -1; QString systrayAppletId; auto applets = containment->config().group("Applets"); for (const auto &applet : applets.groupList()) { KConfigGroup appletSettings = applets.group(applet).group("Configuration"); int tSysId = appletSettings.readEntry("SystrayContainmentId", -1); if (tSysId != -1) { systrayId = tSysId; systrayAppletId = applet; qDebug() << "systray was found in the containment... ::: " << tSysId; break; } } if (systrayId != -1) { Plasma::Containment *systray{nullptr}; for (const auto containment : m_layout->corona()->containments()) { if (containment->id() == systrayId) { systray = containment; break; } } if (systray) { copied_systray = KConfigGroup(&copied_conts, QString::number(systray->id())); // toCopyContainmentIds << QString::number(systray->id()); // toCopyAppletIds << systray->config().group("Applets").groupList(); systray->config().copyTo(&copied_systray); } } //! end of systray specific code //! update ids to unique ones QString temp2File = newUniqueIdsLayoutFromFile(temp1File); //! Don't create LatteView when the containment is created because we must update //! its screen settings first m_layout->setBlockAutomaticLatteViewCreation(true); //! Finally import the configuration QList importedDocks = importLayoutFile(temp2File); Plasma::Containment *newContainment{nullptr}; if (importedDocks.size() == 1) { newContainment = importedDocks[0]; } if (!newContainment || !newContainment->kPackage().isValid()) { qWarning() << "the requested containment plugin can not be located or loaded"; return; } auto config = newContainment->config(); //in multi-screen environment the copied dock is moved to alternative screens first const auto screens = qGuiApp->screens(); auto dock = m_layout->viewForContainment(containment); bool setOnExplicitScreen = false; int dockScrId = -1; int copyScrId = -1; if (dock) { dockScrId = dock->positioner()->currentScreenId(); qDebug() << "COPY DOCK SCREEN ::: " << dockScrId; if (dockScrId != -1 && screens.count() > 1) { for (const auto scr : screens) { copyScrId = m_layout->corona()->screenPool()->id(scr->name()); //the screen must exist and not be the same with the original dock if (copyScrId > -1 && copyScrId != dockScrId) { QList fEdges = m_layout->freeEdges(copyScrId); if (fEdges.contains((Plasma::Types::Location)containment->location())) { ///set this containment to an explicit screen config.writeEntry("onPrimary", false); config.writeEntry("lastScreen", copyScrId); newContainment->setLocation(containment->location()); qDebug() << "COPY DOCK SCREEN NEW SCREEN ::: " << copyScrId; setOnExplicitScreen = true; break; } } } } } if (!setOnExplicitScreen) { QList edges = m_layout->freeEdges(newContainment->screen()); if (edges.count() > 0) { newContainment->setLocation(edges.at(0)); } else { newContainment->setLocation(Plasma::Types::BottomEdge); } config.writeEntry("onPrimary", false); config.writeEntry("lastScreen", dockScrId); } newContainment->config().sync(); if (setOnExplicitScreen && copyScrId > -1) { qDebug() << "Copy Dock in explicit screen ::: " << copyScrId; m_layout->addView(newContainment, false, copyScrId); newContainment->reactToScreenChange(); } else { qDebug() << "Copy Dock in current screen..."; m_layout->addView(newContainment, false, dockScrId); } m_layout->setBlockAutomaticLatteViewCreation(false); } QList Storage::importLayoutFile(QString file) { KSharedConfigPtr filePtr = KSharedConfig::openConfig(file); auto newContainments = m_layout->corona()->importLayout(KConfigGroup(filePtr, "")); ///Find latte and systray containments qDebug() << " imported containments ::: " << newContainments.length(); QList importedDocks; //QList systrays; for (const auto containment : newContainments) { if (isLatteContainment(containment)) { qDebug() << "new latte containment id: " << containment->id(); importedDocks << containment; } } return importedDocks; } QString Storage::availableId(QStringList all, QStringList assigned, int base) { bool found = false; int i = base; while (!found && i < 32000) { QString iStr = QString::number(i); if (!all.contains(iStr) && !assigned.contains(iStr)) { return iStr; } i++; } return QString(""); } QString Storage::newUniqueIdsLayoutFromFile(QString file) { if (!m_layout->corona()) { return QString(); } QString tempFile = QDir::homePath() + "/.config/lattedock.copy2.bak"; QFile copyFile(tempFile); if (copyFile.exists()) copyFile.remove(); //! BEGIN updating the ids in the temp file QStringList allIds; allIds << m_layout->corona()->containmentsIds(); allIds << m_layout->corona()->appletsIds(); QStringList toInvestigateContainmentIds; QStringList toInvestigateAppletIds; QStringList toInvestigateSystrayContIds; //! first is the systray containment id QHash systrayParentContainmentIds; QHash systrayAppletIds; //qDebug() << "Ids:" << allIds; //qDebug() << "to copy containments: " << toCopyContainmentIds; //qDebug() << "to copy applets: " << toCopyAppletIds; QStringList assignedIds; QHash assigned; KSharedConfigPtr filePtr = KSharedConfig::openConfig(file); KConfigGroup investigate_conts = KConfigGroup(filePtr, "Containments"); //KConfigGroup copied_c1 = KConfigGroup(&copied_conts, QString::number(containment->id())); //! Record the containment and applet ids for (const auto &cId : investigate_conts.groupList()) { toInvestigateContainmentIds << cId; auto appletsEntries = investigate_conts.group(cId).group("Applets"); toInvestigateAppletIds << appletsEntries.groupList(); //! investigate for systrays for (const auto &appletId : appletsEntries.groupList()) { KConfigGroup appletSettings = appletsEntries.group(appletId).group("Configuration"); int tSysId = appletSettings.readEntry("SystrayContainmentId", -1); //! It is a systray !!! if (tSysId != -1) { QString tSysIdStr = QString::number(tSysId); toInvestigateSystrayContIds << tSysIdStr; systrayParentContainmentIds[tSysIdStr] = cId; systrayAppletIds[tSysIdStr] = appletId; qDebug() << "systray was found in the containment..."; } } } //! Reassign containment and applet ids to unique ones for (const auto &contId : toInvestigateContainmentIds) { QString newId = availableId(allIds, assignedIds, 12); assignedIds << newId; assigned[contId] = newId; } for (const auto &appId : toInvestigateAppletIds) { QString newId = availableId(allIds, assignedIds, 40); assignedIds << newId; assigned[appId] = newId; } qDebug() << "ALL CORONA IDS ::: " << allIds; qDebug() << "FULL ASSIGNMENTS ::: " << assigned; for (const auto &cId : toInvestigateContainmentIds) { QString value = assigned[cId]; if (assigned.contains(value)) { QString value2 = assigned[value]; if (cId != assigned[cId] && !value2.isEmpty() && cId == value2) { qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << cId << " .. fixed .."; assigned[cId] = cId; assigned[value] = value; } } } for (const auto &aId : toInvestigateAppletIds) { QString value = assigned[aId]; if (assigned.contains(value)) { QString value2 = assigned[value]; if (aId != assigned[aId] && !value2.isEmpty() && aId == value2) { qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << aId << " .. fixed .."; assigned[aId] = aId; assigned[value] = value; } } } qDebug() << "FIXED FULL ASSIGNMENTS ::: " << assigned; //! update applet ids in their containment order and in MultipleLayouts update also the layoutId for (const auto &cId : investigate_conts.groupList()) { //! Update options that contain applet ids //! (appletOrder) and (lockedZoomApplets) and (userBlocksColorizingApplets) QStringList options; options << "appletOrder" << "lockedZoomApplets" << "userBlocksColorizingApplets"; for (const auto &settingStr : options) { QString order1 = investigate_conts.group(cId).group("General").readEntry(settingStr, QString()); if (!order1.isEmpty()) { QStringList order1Ids = order1.split(";"); QStringList fixedOrder1Ids; for (int i = 0; i < order1Ids.count(); ++i) { fixedOrder1Ids.append(assigned[order1Ids[i]]); } QString fixedOrder1 = fixedOrder1Ids.join(";"); investigate_conts.group(cId).group("General").writeEntry(settingStr, fixedOrder1); } } if (m_layout->corona()->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { investigate_conts.group(cId).writeEntry("layoutId", m_layout->name()); } } //! must update also the systray id in its applet for (const auto &systrayId : toInvestigateSystrayContIds) { KConfigGroup systrayParentContainment = investigate_conts.group(systrayParentContainmentIds[systrayId]); systrayParentContainment.group("Applets").group(systrayAppletIds[systrayId]).group("Configuration").writeEntry("SystrayContainmentId", assigned[systrayId]); systrayParentContainment.sync(); } investigate_conts.sync(); //! Copy To Temp 2 File And Update Correctly The Ids KSharedConfigPtr file2Ptr = KSharedConfig::openConfig(tempFile); KConfigGroup fixedNewContainmets = KConfigGroup(file2Ptr, "Containments"); for (const auto &contId : investigate_conts.groupList()) { QString pluginId = investigate_conts.group(contId).readEntry("plugin", ""); if (pluginId != "org.kde.desktopcontainment") { //!don't add ghost containments KConfigGroup newContainmentGroup = fixedNewContainmets.group(assigned[contId]); investigate_conts.group(contId).copyTo(&newContainmentGroup); newContainmentGroup.group("Applets").deleteGroup(); for (const auto &appId : investigate_conts.group(contId).group("Applets").groupList()) { KConfigGroup appletGroup = investigate_conts.group(contId).group("Applets").group(appId); KConfigGroup newAppletGroup = fixedNewContainmets.group(assigned[contId]).group("Applets").group(assigned[appId]); appletGroup.copyTo(&newAppletGroup); } } } fixedNewContainmets.sync(); return tempFile; } bool Storage::appletGroupIsValid(KConfigGroup appletGroup) { return !( appletGroup.keyList().count() == 0 && appletGroup.groupList().count() == 1 && appletGroup.groupList().at(0) == "Configuration" && appletGroup.group("Configuration").keyList().count() == 1 && appletGroup.group("Configuration").hasKey("PreloadWeight") ); } bool Storage::layoutIsBroken() const { if (m_layout->file().isEmpty() || !QFile(m_layout->file()).exists()) { return false; } QStringList ids; QStringList conts; QStringList applets; KSharedConfigPtr lFile = KSharedConfig::openConfig(m_layout->file()); if (!m_layout->corona()) { KConfigGroup containmentsEntries = KConfigGroup(lFile, "Containments"); ids << containmentsEntries.groupList(); conts << ids; for (const auto &cId : containmentsEntries.groupList()) { auto appletsEntries = containmentsEntries.group(cId).group("Applets"); QStringList validAppletIds; bool updated{false}; for (const auto &appletId : appletsEntries.groupList()) { KConfigGroup appletGroup = appletsEntries.group(appletId); if (appletGroupIsValid(appletGroup)) { validAppletIds << appletId; } else { updated = true; //! heal layout file by removing applet config records that are not used any more qDebug() << "Layout: " << m_layout->name() << " removing deprecated applet : " << appletId; appletsEntries.deleteGroup(appletId); } } if (updated) { appletsEntries.sync(); } ids << validAppletIds; applets << validAppletIds; } } else { for (const auto containment : *m_layout->containments()) { ids << QString::number(containment->id()); conts << QString::number(containment->id()); for (const auto applet : containment->applets()) { ids << QString::number(applet->id()); applets << QString::number(applet->id()); } } } QSet idsSet = QSet::fromList(ids); /* a different way to count duplicates QMap countOfStrings; for (int i = 0; i < ids.count(); i++) { countOfStrings[ids[i]]++; }*/ if (idsSet.count() != ids.count()) { qDebug() << " ---- ERROR - BROKEN LAYOUT :: " << m_layout->name() << " ----"; if (!m_layout->corona()) { qDebug() << " --- storaged file : " << m_layout->file(); } else { if (m_layout->corona()->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { - qDebug() << " --- in multiple layouts hidden file : " << Importer::layoutFilePath(AbstractLayout::MultipleLayoutsName); + qDebug() << " --- in multiple layouts hidden file : " << Layouts::Importer::layoutFilePath(AbstractLayout::MultipleLayoutsName); } else { qDebug() << " --- in active layout file : " << m_layout->file(); } } qDebug() << "Containments :: " << conts; qDebug() << "Applets :: " << applets; for (const QString &c : conts) { if (applets.contains(c)) { qDebug() << "Error: Same applet and containment id found ::: " << c; } } for (int i = 0; i < ids.count(); ++i) { for (int j = i + 1; j < ids.count(); ++j) { if (ids[i] == ids[j]) { qDebug() << "Error: Applets with same id ::: " << ids[i]; } } } qDebug() << " -- - -- - -- - -- - - -- - - - - -- - - - - "; if (!m_layout->corona()) { KConfigGroup containmentsEntries = KConfigGroup(lFile, "Containments"); for (const auto &cId : containmentsEntries.groupList()) { auto appletsEntries = containmentsEntries.group(cId).group("Applets"); qDebug() << " CONTAINMENT : " << cId << " APPLETS : " << appletsEntries.groupList(); } } else { for (const auto containment : *m_layout->containments()) { QStringList appletsIds; for (const auto applet : containment->applets()) { appletsIds << QString::number(applet->id()); } qDebug() << " CONTAINMENT : " << containment->id() << " APPLETS : " << appletsIds.join(","); } } return true; } return false; } } } diff --git a/app/layouts/CMakeLists.txt b/app/layouts/CMakeLists.txt index 944e11b1..2c80e302 100644 --- a/app/layouts/CMakeLists.txt +++ b/app/layouts/CMakeLists.txt @@ -1,6 +1,7 @@ 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 PARENT_SCOPE ) diff --git a/app/importer.cpp b/app/layouts/importer.cpp similarity index 99% rename from app/importer.cpp rename to app/layouts/importer.cpp index 127b66f7..babafe41 100644 --- a/app/importer.cpp +++ b/app/layouts/importer.cpp @@ -1,643 +1,645 @@ /* * 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 "importer.h" // local -#include "lattecorona.h" -#include "screenpool.h" -#include "layout/abstractlayout.h" -#include "layouts/manager.h" -#include "settings/universalsettings.h" +#include "manager.h" +#include "../lattecorona.h" +#include "../screenpool.h" +#include "../layout/abstractlayout.h" +#include "../settings/universalsettings.h" #include "../liblatte2/types.h" // Qt #include #include // KDE #include #include #include #include #include #include namespace Latte { +namespace Layouts { Importer::Importer(QObject *parent) : QObject(parent) { m_manager = qobject_cast(parent); } Importer::~Importer() { } bool Importer::updateOldConfiguration() { QFile oldAppletsFile(QDir::homePath() + "/.config/lattedock-appletsrc"); if (!oldAppletsFile.exists()) { return false; } //! import standard old configuration and create the relevant layouts importOldLayout(QDir::homePath() + "/.config/lattedock-appletsrc", i18n("My Layout")); importOldLayout(QDir::homePath() + "/.config/lattedock-appletsrc", i18n("Alternative"), true); QFile extFile(QDir::homePath() + "/.config/lattedockextrc"); //! import also the old user layouts into the new architecture if (extFile.exists()) { KSharedConfigPtr extFileConfig = KSharedConfig::openConfig(extFile.fileName()); KConfigGroup externalSettings = KConfigGroup(extFileConfig, "External"); QStringList userLayouts = externalSettings.readEntry("userLayouts", QStringList()); for(const auto &userConfig : userLayouts) { qDebug() << "user layout : " << userConfig; importOldConfiguration(userConfig); } } m_manager->corona()->universalSettings()->setCurrentLayoutName(i18n("My Layout")); m_manager->corona()->universalSettings()->setVersion(2); return true; } bool Importer::importOldLayout(QString oldAppletsPath, QString newName, bool alternative, QString exportDirectory) { QString newLayoutPath = layoutCanBeImported(oldAppletsPath, newName, exportDirectory); qDebug() << "New Layout Should be created: " << newLayoutPath; KSharedConfigPtr oldFile = KSharedConfig::openConfig(oldAppletsPath); KSharedConfigPtr newFile = KSharedConfig::openConfig(newLayoutPath); KConfigGroup containments = KConfigGroup(oldFile, "Containments"); KConfigGroup copiedContainments = KConfigGroup(newFile, "Containments"); QList systrays; bool atLeastOneContainmentWasFound{false}; //! first copy the latte containments that correspond to the correct session //! and find also the systrays that should be copied also for(const auto &containmentId : containments.groupList()) { KConfigGroup containmentGroup = containments.group(containmentId); QString plugin = containmentGroup.readEntry("plugin", QString()); Types::SessionType session = (Types::SessionType)containmentGroup.readEntry("session", (int)Types::DefaultSession); bool shouldImport = false; if (plugin == "org.kde.latte.containment" && session == Types::DefaultSession && !alternative) { qDebug() << containmentId << " - " << plugin << " - " << session; shouldImport = true; } else if (plugin == "org.kde.latte.containment" && session == Types::AlternativeSession && alternative) { qDebug() << containmentId << " - " << plugin << " - " << session; shouldImport = true; } // this latte containment should be imported if (shouldImport) { auto applets = containments.group(containmentId).group("Applets"); for(const auto &applet : applets.groupList()) { KConfigGroup appletSettings = applets.group(applet).group("Configuration"); int systrayId = appletSettings.readEntry("SystrayContainmentId", "-1").toInt(); if (systrayId != -1) { systrays.append(systrayId); qDebug() << "systray was found in the containment..."; break; } } KConfigGroup newContainment = copiedContainments.group(containmentId); containmentGroup.copyTo(&newContainment); atLeastOneContainmentWasFound = true; } } //! not even one latte containment was found for that layout so we must break //! the code here if (!atLeastOneContainmentWasFound) { return false; } //! copy also the systrays that were discovered for(const auto &containmentId : containments.groupList()) { int cId = containmentId.toInt(); if (systrays.contains(cId)) { KConfigGroup containmentGroup = containments.group(containmentId); KConfigGroup newContainment = copiedContainments.group(containmentId); containmentGroup.copyTo(&newContainment); } } copiedContainments.sync(); KConfigGroup oldGeneralSettings = KConfigGroup(oldFile, "General"); QStringList layoutLaunchers; if (!alternative) { layoutLaunchers = oldGeneralSettings.readEntry("globalLaunchers_default", QStringList()); } else { layoutLaunchers = oldGeneralSettings.readEntry("globalLaunchers_alternative", QStringList()); } //! update also the layout settings correctly Layout::AbstractLayout newLayout(this, newLayoutPath, newName); newLayout.setVersion(2); newLayout.setLaunchers(layoutLaunchers); //newLayout.setShowInMenu(true); if (alternative) { newLayout.setColor("purple"); } else { newLayout.setColor("blue"); } return true; } QStringList Importer::standardPaths(bool localfirst) { QStringList paths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); if (localfirst) { return paths; } else { QStringList reversed; for (int i=paths.count()-1; i>=0; i--) { reversed << paths[i]; } return reversed; } } QString Importer::standardPath(QString subPath, bool localfirst) { QStringList paths = standardPaths(localfirst); if (localfirst) { for(const auto &pt : paths) { QString ptF = pt + "/" +subPath; if (QFileInfo(ptF).exists()) { return ptF; } } } else { for (int i=paths.count()-1; i>=0; i--) { QString ptF = paths[i] + "/" +subPath; if (QFileInfo(ptF).exists()) { return ptF; } } } //! in any case that above fails if (QFileInfo("/usr/share/"+subPath).exists()) { return "/usr/share/"+subPath; } return ""; } QString Importer::layoutCanBeImported(QString oldAppletsPath, QString newName, QString exportDirectory) { QFile oldAppletsrc(oldAppletsPath); //! old file doesn't exist if (!oldAppletsrc.exists()) { return QString(); } KSharedConfigPtr lConfig = KSharedConfig::openConfig(oldAppletsPath); KConfigGroup m_layoutGroup = KConfigGroup(lConfig, "LayoutSettings"); int layoutVersion = m_layoutGroup.readEntry("version", 1); //! old file layout appears to not be old as its version is >=2 if (layoutVersion >= 2) { return QString(); } QDir layoutDir(exportDirectory.isNull() ? QDir::homePath() + "/.config/latte" : exportDirectory); if (!layoutDir.exists() && exportDirectory.isNull()) { QDir(QDir::homePath() + "/.config").mkdir("latte"); } //! set up the new layout name if (newName.isEmpty()) { int extension = oldAppletsrc.fileName().lastIndexOf(".latterc"); if (extension > 0) { //! remove the last 8 characters that contain the extension newName = oldAppletsrc.fileName().remove(extension, 8); } else { newName = oldAppletsrc.fileName(); } } QString newLayoutPath = layoutDir.absolutePath() + "/" + newName + ".layout.latte"; QFile newLayoutFile(newLayoutPath); QStringList filter; filter.append(QString(newName + "*.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; newLayoutPath = layoutDir.absolutePath() + "/" + newName + "-" + QString::number(newCounter) + ".layout.latte"; } return newLayoutPath; } bool Importer::importOldConfiguration(QString oldConfigPath, QString newName) { QFile oldConfigFile(oldConfigPath); if (!oldConfigFile.exists()) { return false; } KTar archive(oldConfigPath, QStringLiteral("application/x-tar")); archive.open(QIODevice::ReadOnly); if (!archive.isOpen()) { return false; } auto rootDir = archive.directory(); QTemporaryDir uniqueTempDir; QDir tempDir{uniqueTempDir.path()}; qDebug() << "temp layout directory : " << tempDir.absolutePath(); if (rootDir) { if (!tempDir.exists()) tempDir.mkpath(tempDir.absolutePath()); for(const auto &name : rootDir->entries()) { auto fileEntry = rootDir->file(name); if (fileEntry && (fileEntry->name() == "lattedockrc" || fileEntry->name() == "lattedock-appletsrc")) { if (!fileEntry->copyTo(tempDir.absolutePath())) { qInfo() << i18nc("import/export config", "The extracted file could not be copied!!!"); archive.close(); return false; } } else { qInfo() << i18nc("import/export config", "The file has a wrong format!!!"); archive.close(); return false; } } } else { qInfo() << i18nc("import/export config", "The temp directory could not be created!!!"); archive.close(); return false; } //! only if the above has passed we must process the files QString appletsPath(tempDir.absolutePath() + "/lattedock-appletsrc"); QString screensPath(tempDir.absolutePath() + "/lattedockrc"); if (!QFile(appletsPath).exists() || !QFile(screensPath).exists()) { return false; } if (newName.isEmpty()) { int lastSlash = oldConfigPath.lastIndexOf("/"); newName = oldConfigPath.remove(0, lastSlash + 1); int ext = newName.lastIndexOf(".latterc"); newName = newName.remove(ext, 8); } if (!importOldLayout(appletsPath, newName)) { return false; } //! the old configuration contains also screen values, these must be updated also KSharedConfigPtr oldScreensConfig = KSharedConfig::openConfig(screensPath); KConfigGroup m_screensGroup = KConfigGroup(oldScreensConfig, "ScreenConnectors"); //restore the known ids to connector mappings for(const QString &key : m_screensGroup.keyList()) { QString connector = m_screensGroup.readEntry(key, QString()); int id = key.toInt(); if (id >= 10 && !m_manager->corona()->screenPool()->knownIds().contains(id)) { m_manager->corona()->screenPool()->insertScreenMapping(id, connector); } } return true; } bool Importer::exportFullConfiguration(QString file) { if (QFile::exists(file) && !QFile::remove(file)) { return false; } KTar archive(file, QStringLiteral("application/x-tar")); if (!archive.open(QIODevice::WriteOnly)) { return false; } archive.addLocalFile(QString(QDir::homePath() + "/.config/lattedockrc"), QStringLiteral("lattedockrc")); for(const auto &layoutName : availableLayouts()) { archive.addLocalFile(layoutFilePath(layoutName), QString("latte/" + layoutName + ".layout.latte")); } //archive.addLocalDirectory(QString(QDir::homePath() + "/.config/latte"), QStringLiteral("latte")); archive.close(); return true; } Importer::LatteFileVersion Importer::fileVersion(QString file) { if (!QFile::exists(file)) return UnknownFileType; if (file.endsWith(".layout.latte")) { KSharedConfigPtr lConfig = KSharedConfig::openConfig(QFileInfo(file).absoluteFilePath()); KConfigGroup layoutGroup = KConfigGroup(lConfig, "LayoutSettings"); int version = layoutGroup.readEntry("version", 1); if (version == 2) return Importer::LayoutVersion2; else return Importer::UnknownFileType; } if (!file.endsWith(".latterc")) { return Importer::UnknownFileType; } KTar archive(file, QStringLiteral("application/x-tar")); archive.open(QIODevice::ReadOnly); //! if the file isnt a tar archive if (!archive.isOpen()) { return Importer::UnknownFileType; } QTemporaryDir archiveTempDir; bool version1rc = false; bool version1applets = false; bool version2rc = false; bool version2LatteDir = false; bool version2layout = false; archive.directory()->copyTo(archiveTempDir.path()); //rc file QString rcFile(archiveTempDir.path() + "/lattedockrc"); if (QFile(rcFile).exists()) { KSharedConfigPtr lConfig = KSharedConfig::openConfig(rcFile); KConfigGroup universalGroup = KConfigGroup(lConfig, "UniversalSettings"); int version = universalGroup.readEntry("version", 1); if (version == 1) { version1rc = true; } else if (version == 2) { version2rc = true; } } //applets file QString appletsFile(archiveTempDir.path() + "/lattedock-appletsrc"); if (QFile(appletsFile).exists() && version1rc) { KSharedConfigPtr lConfig = KSharedConfig::openConfig(appletsFile); KConfigGroup generalGroup = KConfigGroup(lConfig, "LayoutSettings"); int version = generalGroup.readEntry("version", 1); if (version == 1) { version1applets = true; } else if (version == 2) { version2layout = true; } } //latte directory QString latteDir(archiveTempDir.path() + "/latte"); if (QDir(latteDir).exists()) { version2LatteDir = true; } if (version1applets && version1applets) { return ConfigVersion1; } else if (version2rc && version2LatteDir) { return ConfigVersion2; } return Importer::UnknownFileType; } bool Importer::importHelper(QString fileName) { LatteFileVersion version = fileVersion(fileName); if ((version != ConfigVersion1) && (version != ConfigVersion2)) { return false; } KTar archive(fileName, QStringLiteral("application/x-tar")); archive.open(QIODevice::ReadOnly); if (!archive.isOpen()) { return false; } QString latteDirPath(QDir::homePath() + "/.config/latte"); QDir latteDir(latteDirPath); if (latteDir.exists()) { latteDir.removeRecursively(); } archive.directory()->copyTo(QString(QDir::homePath() + "/.config")); return true; } QString Importer::importLayoutHelper(QString fileName) { LatteFileVersion version = fileVersion(fileName); if (version != LayoutVersion2) { return QString(); } QString newLayoutName = Layout::AbstractLayout::layoutName(fileName); newLayoutName = uniqueLayoutName(newLayoutName); QString newPath = QDir::homePath() + "/.config/latte/" + newLayoutName + ".layout.latte"; QFile(fileName).copy(newPath); QFileInfo newFileInfo(newPath); if (newFileInfo.exists() && !newFileInfo.isWritable()) { QFile(newPath).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } return newLayoutName; } QStringList Importer::availableLayouts() { QDir layoutDir(QDir::homePath() + "/.config/latte"); QStringList filter; filter.append(QString("*.layout.latte")); QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); QStringList layoutNames; for(const auto &file : files) { layoutNames.append(Layout::AbstractLayout::layoutName(file)); } return layoutNames; } QString Importer::nameOfConfigFile(const QString &fileName) { int lastSlash = fileName.lastIndexOf("/"); QString tempLayoutFile = fileName; QString layoutName = tempLayoutFile.remove(0, lastSlash + 1); int ext = layoutName.lastIndexOf(".latterc"); layoutName = layoutName.remove(ext, 8); return layoutName; } bool Importer::layoutExists(QString layoutName) { return QFile::exists(layoutFilePath(layoutName)); } QString Importer::layoutFilePath(QString layoutName) { return QString(QDir::homePath() + "/.config/latte/" + layoutName + ".layout.latte"); } QString Importer::uniqueLayoutName(QString name) { int pos_ = name.lastIndexOf(QRegExp(QString("[-][0-9]+"))); if (layoutExists(name) && pos_ > 0) { name = name.left(pos_); } int i = 2; QString namePart = name; while (layoutExists(name)) { name = namePart + "-" + QString::number(i); i++; } return name; } QStringList Importer::checkRepairMultipleLayoutsLinkedFile() { QString linkedFilePath = QDir::homePath() + "/.config/latte/" + Layout::AbstractLayout::MultipleLayoutsName + ".layout.latte"; KSharedConfigPtr filePtr = KSharedConfig::openConfig(linkedFilePath); KConfigGroup linkedContainments = KConfigGroup(filePtr, "Containments"); //! layoutName and its Containments QHash linkedLayoutContainmentGroups; for(const auto &cId : linkedContainments.groupList()) { QString layoutName = linkedContainments.group(cId).readEntry("layoutId", QString()); if (!layoutName.isEmpty()) { qDebug() << layoutName; linkedLayoutContainmentGroups[layoutName].append(cId); linkedContainments.group(cId).writeEntry("layoutId", QString()); } } QStringList updatedLayouts; for(const auto &layoutName : linkedLayoutContainmentGroups.uniqueKeys()) { if (layoutName != Layout::AbstractLayout::MultipleLayoutsName && layoutExists(layoutName)) { updatedLayouts << layoutName; KSharedConfigPtr layoutFilePtr = KSharedConfig::openConfig(layoutFilePath(layoutName)); KConfigGroup origLayoutContainments = KConfigGroup(layoutFilePtr, "Containments"); //Clear old containments origLayoutContainments.deleteGroup(); //Update containments for(const auto &cId : linkedLayoutContainmentGroups[layoutName]) { KConfigGroup newContainment = origLayoutContainments.group(cId); linkedContainments.group(cId).copyTo(&newContainment); linkedContainments.group(cId).deleteGroup(); } origLayoutContainments.sync(); } } //! clear all remaining ghost containments for(const auto &cId : linkedContainments.groupList()) { linkedContainments.group(cId).deleteGroup(); } linkedContainments.sync(); return updatedLayouts; } } +} diff --git a/app/importer.h b/app/layouts/importer.h similarity index 99% rename from app/importer.h rename to app/layouts/importer.h index b9e8c59e..916eac6a 100644 --- a/app/importer.h +++ b/app/layouts/importer.h @@ -1,111 +1,113 @@ /* * 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 IMPORTER_H #define IMPORTER_H // Qt #include namespace Latte { namespace Layouts { class Manager; } } namespace Latte { +namespace Layouts { //! This class is responsible to import/export configurations //! and of course to import old configuration to new architecture class Importer : public QObject { Q_OBJECT public: enum LatteFileVersion { UnknownFileType = -1, LayoutVersion1 = 0, ConfigVersion1 = 1, LayoutVersion2 = 2, ConfigVersion2 = 3 }; Q_ENUM(LatteFileVersion); Importer(QObject *parent = nullptr); ~Importer() override; //! updates the old configuration to version: 2 bool updateOldConfiguration(); //! imports an old layout file, //! newName: the layout new name, if it is empty the original is used //! alternative: old files can contain both a Default and an Alternative layout //! false: imports only Default layout //! true: imports only Alternative layout bool importOldLayout(QString oldAppletsPath, QString newName, bool alternative = false, QString exportDirectory = QString()); //! imports and old configuration file (tar archive) that contains //! both an applets file and a latterc file with the screens //! newName: if it is empty the name is extracted from the old config file name bool importOldConfiguration(QString oldConfigPath, QString newName = QString()); bool exportFullConfiguration(QString file); static Importer::LatteFileVersion fileVersion(QString file); static bool importHelper(QString fileName); //! returns the standard path found that contains the subPath //! local paths have higher priority by default static QString standardPath(QString subPath, bool localFirst = true); //! returns all application data standard paths //! local paths have higher priority by default static QStringList standardPaths(bool localfirst = true); //! check if this layout exists already in the latte directory static bool layoutExists(QString layoutName); //! imports the specific layout and return the new layout name. //! if the function didn't succeed return an empty string static QString importLayoutHelper(QString fileName); //! return the file path of a layout either existing or not static QString layoutFilePath(QString layoutName); static QString nameOfConfigFile(const QString &fileName); static QString uniqueLayoutName(QString name); static QStringList availableLayouts(); //! it checks the linked file if there are Containments in it that belong //! to Original Layouts and moves them accordingly. This is used mainly on //! startup and if such state occurs, it basically means that the app didn't //! close correctly, e.g. there was a crash. static QStringList checkRepairMultipleLayoutsLinkedFile(); private: //! checks if this old layout can be imported. If it can it returns //! the new layout path and an empty string if it cant QString layoutCanBeImported(QString oldAppletsPath, QString newName, QString exportDirectory = QString()); Layouts::Manager *m_manager; }; +} } #endif // IMPORTER_H diff --git a/app/layouts/manager.cpp b/app/layouts/manager.cpp index cafc80a6..25c1cc8d 100644 --- a/app/layouts/manager.cpp +++ b/app/layouts/manager.cpp @@ -1,1226 +1,1226 @@ /* * 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 "../importer.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_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); } } Manager::~Manager() { m_importer->deleteLater(); m_launchersSignals->deleteLater(); unload(); m_activitiesController->deleteLater(); } void Manager::load() { int configVer = m_corona->universalSettings()->version(); qDebug() << "Universal Settings version : " << configVer; if (configVer < 2 && QFile(QDir::homePath() + "/.config/lattedockrc").exists()) { qDebug() << "Latte must update its configuration..."; m_importer->updateOldConfiguration(); importPresets(false); } else if (!QFile(QDir::homePath() + "/.config/lattedockrc").exists()) { //startup create what is necessary.... QDir layoutDir(QDir::homePath() + "/.config/latte"); if (!layoutDir.exists()) { QDir(QDir::homePath() + "/.config").mkdir("latte"); } newLayout(i18n("My Layout")); importPresets(false); m_corona->universalSettings()->setCurrentLayoutName(i18n("My Layout")); m_corona->universalSettings()->setVersion(2); } //! Check if the multiple-layouts hidden file is present, add it if it isnt if (!QFile(QDir::homePath() + "/.config/latte/" + Layout::AbstractLayout::MultipleLayoutsName + ".layout.latte").exists()) { importPreset(MultipleLayoutsPresetId, false); } qDebug() << "Latte is loading its layouts..."; connect(m_corona->m_activityConsumer, &KActivities::Consumer::currentActivityChanged, this, &Manager::currentActivityChanged); connect(m_corona->m_activityConsumer, &KActivities::Consumer::runningActivitiesChanged, this, [&]() { if (memoryUsage() == Types::MultipleLayouts) { syncMultipleLayoutsToActivities(); } }); loadLayouts(); } void Manager::unload() { //! Unload all CentralLayouts while (!m_centralLayouts.isEmpty()) { CentralLayout *layout = m_centralLayouts.at(0); unloadCentralLayout(layout); } m_multipleModeInitialized = false; //! Remove no-needed temp files QString temp1File = QDir::homePath() + "/.config/lattedock.copy1.bak"; QString temp2File = QDir::homePath() + "/.config/lattedock.copy2.bak"; QFile file1(temp1File); QFile file2(temp2File); if (file1.exists()) file1.remove(); if (file2.exists()) file2.remove(); } 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; } } Latte::Corona *Manager::corona() { return m_corona; } Importer *Manager::importer() { return m_importer; } LaunchersSignals *Manager::launchersSignals() { return m_launchersSignals; } QString Manager::currentLayoutName() const { if (memoryUsage() == Types::SingleLayout) { return m_corona->universalSettings()->currentLayoutName(); } else if (memoryUsage() == Types::MultipleLayouts) { return m_currentLayoutNameInMultiEnvironment; } return QString(); } 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); } QStringList Manager::layouts() const { return m_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; } void Manager::setMenuLayouts(QStringList layouts) { if (m_menuLayouts == layouts) { return; } m_menuLayouts = layouts; emit menuLayoutsChanged(); } QStringList Manager::activities() { return m_corona->m_activityConsumer->activities(); } QStringList Manager::runningActivities() { return m_corona->m_activityConsumer->runningActivities(); } QStringList Manager::orphanedActivities() { QStringList orphans; for (const auto &activity : activities()) { if (m_assignedLayouts[activity].isEmpty()) { orphans.append(activity); } } return orphans; } 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; } 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; } 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; } QStringList Manager::storedSharedLayouts() const { return m_sharedLayoutIds; } Layout::GenericLayout *Manager::layout(QString id) const { Layout::GenericLayout *l = centralLayout(id); if (!l) { l = sharedLayout(id); } return l; } 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; } 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; } 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; } 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; } 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; } } } 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() { if (m_corona->universalSettings()->showInfoWindow()) { m_dynamicSwitchTimer.setInterval(1800); } else { m_dynamicSwitchTimer.setInterval(2300); } } 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); } } 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) { 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(); } 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"); } } } diff --git a/app/layouts/manager.h b/app/layouts/manager.h index dcd19134..1313b6b8 100644 --- a/app/layouts/manager.h +++ b/app/layouts/manager.h @@ -1,219 +1,219 @@ /* * 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 "settings/settingsdialog.h" // Qt #include #include #include // KDE #include namespace Plasma { class Containment; class Types; } namespace KActivities { class Controller; } namespace Latte { class Corona; -class Importer; class CentralLayout; class SharedLayout; class View; namespace Layout { class GenericLayout; } namespace Layouts { +class Importer; class LaunchersSignals; } } 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 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(); 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); 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; friend class Latte::SettingsDialog; }; } } #endif // LAYOUTSMANAGER_H diff --git a/app/main.cpp b/app/main.cpp index f28c8f6c..bcabf19c 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,333 +1,333 @@ /* * 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 . */ // local #include "config-latte.h" -#include "importer.h" #include "lattecorona.h" +#include "layouts/importer.h" #include "../liblatte2/types.h" // C++ #include #include // Qt #include #include #include #include #include #include #include #include // KDE #include #include #include #include #include //! COLORS #define CNORMAL "\e[0m" #define CIGREEN "\e[1;32m" #define CGREEN "\e[0;32m" #define CICYAN "\e[1;36m" #define CCYAN "\e[0;36m" #define CIRED "\e[1;31m" #define CRED "\e[0;31m" inline void configureAboutData(); inline void detectPlatform(int argc, char **argv); int main(int argc, char **argv) { //Plasma scales itself to font DPI //on X, where we don't have compositor scaling, this generally works fine. //also there are bugs on older Qt, especially when it comes to fractional scaling //there's advantages to disabling, and (other than small context menu icons) few advantages in enabling //On wayland, it's different. Everything is simpler as all co-ordinates are in the same co-ordinate system //we don't have fractional scaling on the client so don't hit most the remaining bugs and //even if we don't use Qt scaling the compositor will try to scale us anyway so we have no choice if (!qEnvironmentVariableIsSet("PLASMA_USE_QT_SCALING")) { qunsetenv("QT_DEVICE_PIXEL_RATIO"); QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); } else { QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); } QQuickWindow::setDefaultAlphaBuffer(true); const bool qpaVariable = qEnvironmentVariableIsSet("QT_QPA_PLATFORM"); detectPlatform(argc, argv); QApplication app(argc, argv); if (!qpaVariable) { // don't leak the env variable to processes we start qunsetenv("QT_QPA_PLATFORM"); } KQuickAddons::QtQuickSettings::init(); KLocalizedString::setApplicationDomain("latte-dock"); app.setWindowIcon(QIcon::fromTheme(QStringLiteral("latte-dock"))); //protect from closing app when changing to "alternative session" and back app.setQuitOnLastWindowClosed(false); configureAboutData(); QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); parser.addOptions({ {{"r", "replace"}, i18nc("command line", "Replace the current Latte instance.")} , {{"d", "debug"}, i18nc("command line", "Show the debugging messages on stdout.")} , {{"cc", "clear-cache"}, i18nc("command line", "Clear qml cache. It can be useful after system upgrades.")} , {"default-layout", i18nc("command line", "Import and load default layout on startup.")} , {"available-layouts", i18nc("command line", "Print available layouts")} , {"layout", i18nc("command line", "Load specific layout on startup."), i18nc("command line: load", "layout_name")} , {"import-layout", i18nc("command line", "Import and load a layout."), i18nc("command line: import", "file_name")} , {"import-full", i18nc("command line", "Import full configuration."), i18nc("command line: import", "file_name")} , {"single", i18nc("command line", "Single layout memory mode. Only one layout is active at any case.")} , {"multiple", i18nc("command line", "Multiple layouts memory mode. Multiple layouts can be active at any time based on Activities running.")} }); //! START: Hidden options for Developer and Debugging usage QCommandLineOption graphicsOption(QStringList() << QStringLiteral("graphics")); graphicsOption.setDescription(QStringLiteral("Draw boxes around of the applets.")); graphicsOption.setHidden(true); parser.addOption(graphicsOption); QCommandLineOption withWindowOption(QStringList() << QStringLiteral("with-window")); withWindowOption.setDescription(QStringLiteral("Open a window with much debug information")); withWindowOption.setHidden(true); parser.addOption(withWindowOption); QCommandLineOption maskOption(QStringList() << QStringLiteral("mask")); maskOption.setDescription(QStringLiteral("Show messages of debugging for the mask (Only useful to devs).")); maskOption.setHidden(true); parser.addOption(maskOption); QCommandLineOption timersOption(QStringList() << QStringLiteral("timers")); timersOption.setDescription(QStringLiteral("Show messages for debugging the timers (Only useful to devs).")); timersOption.setHidden(true); parser.addOption(timersOption); QCommandLineOption spacersOption(QStringList() << QStringLiteral("spacers")); spacersOption.setDescription(QStringLiteral("Show visual indicators for debugging spacers (Only useful to devs).")); spacersOption.setHidden(true); parser.addOption(spacersOption); QCommandLineOption overloadedIconsOption(QStringList() << QStringLiteral("overloaded-icons")); overloadedIconsOption.setDescription(QStringLiteral("Show visual indicators for debugging overloaded applets icons (Only useful to devs).")); overloadedIconsOption.setHidden(true); parser.addOption(overloadedIconsOption); //! END: Hidden options parser.process(app); //! print available-layouts if (parser.isSet(QStringLiteral("available-layouts"))) { - QStringList layouts = Latte::Importer::availableLayouts(); + QStringList layouts = Latte::Layouts::Importer::availableLayouts(); if (layouts.count() > 0) { qInfo() << i18n("Available layouts that can be used to start Latte:"); for (const auto &layout : layouts) { qInfo() << " " << layout; } } else { qInfo() << i18n("There are no available layouts, during startup Default will be used."); } qGuiApp->exit(); return 0; } bool defaultLayoutOnStartup = false; int memoryUsage = -1; QString layoutNameOnStartup = ""; //! --default-layout option if (parser.isSet(QStringLiteral("default-layout"))) { defaultLayoutOnStartup = true; } else if (parser.isSet(QStringLiteral("layout"))) { layoutNameOnStartup = parser.value(QStringLiteral("layout")); - if (!Latte::Importer::layoutExists(layoutNameOnStartup)) { + if (!Latte::Layouts::Importer::layoutExists(layoutNameOnStartup)) { qInfo() << i18nc("layout missing", "This layout doesn't exist in the system."); qGuiApp->exit(); return 0; } } //! --replace option QString username = qgetenv("USER"); if (username.isEmpty()) username = qgetenv("USERNAME"); QLockFile lockFile {QDir::tempPath() + "/latte-dock." + username + ".lock"}; int timeout {100}; if (parser.isSet(QStringLiteral("replace")) || parser.isSet(QStringLiteral("import-full"))) { qint64 pid{ -1}; if (lockFile.getLockInfo(&pid, nullptr, nullptr)) { kill(static_cast(pid), SIGINT); timeout = -1; } } if (!lockFile.tryLock(timeout)) { qInfo() << i18n("An instance is already running!, use --replace to restart Latte"); qGuiApp->exit(); return 0; } //! clear-cache option if (parser.isSet(QStringLiteral("clear-cache"))) { QDir cacheDir(QDir::homePath() + "/.cache/lattedock/qmlcache"); if (cacheDir.exists()) { cacheDir.removeRecursively(); qDebug() << "Cache directory found and cleared..."; } } //! import-full option if (parser.isSet(QStringLiteral("import-full"))) { - bool imported = Latte::Importer::importHelper(parser.value(QStringLiteral("import-full"))); + bool imported = Latte::Layouts::Importer::importHelper(parser.value(QStringLiteral("import-full"))); if (!imported) { qInfo() << i18n("The configuration cannot be imported"); qGuiApp->exit(); return 0; } } //! import-layout option if (parser.isSet(QStringLiteral("import-layout"))) { - QString importedLayout = Latte::Importer::importLayoutHelper(parser.value(QStringLiteral("import-layout"))); + QString importedLayout = Latte::Layouts::Importer::importLayoutHelper(parser.value(QStringLiteral("import-layout"))); if (importedLayout.isEmpty()) { qInfo() << i18n("The layout cannot be imported"); qGuiApp->exit(); return 0; } else { layoutNameOnStartup = importedLayout; } } //! memory usage option if (parser.isSet(QStringLiteral("multiple"))) { memoryUsage = (int)(Latte::Types::MultipleLayouts); } else if (parser.isSet(QStringLiteral("single"))) { memoryUsage = (int)(Latte::Types::SingleLayout); } //! debug/mask options if (parser.isSet(QStringLiteral("debug")) || parser.isSet(QStringLiteral("mask"))) { //! set pattern for debug messages //! [%{type}] [%{function}:%{line}] - %{message} [%{backtrace}] qSetMessagePattern(QStringLiteral( CIGREEN "[%{type} " CGREEN "%{time h:mm:ss.zz}" CIGREEN "]" CNORMAL #ifndef QT_NO_DEBUG CIRED " [" CCYAN "%{function}" CIRED ":" CCYAN "%{line}" CIRED "]" #endif CICYAN " - " CNORMAL "%{message}" CIRED "%{if-fatal}\n%{backtrace depth=8 separator=\"\n\"}%{endif}" "%{if-critical}\n%{backtrace depth=8 separator=\"\n\"}%{endif}" CNORMAL)); } else { const auto noMessageOutput = [](QtMsgType, const QMessageLogContext &, const QString &) {}; qInstallMessageHandler(noMessageOutput); } auto signal_handler = [](int) { qGuiApp->exit(); }; std::signal(SIGKILL, signal_handler); std::signal(SIGINT, signal_handler); KCrash::setDrKonqiEnabled(true); KCrash::setFlags(KCrash::AutoRestart | KCrash::AlwaysDirectly); Latte::Corona corona(defaultLayoutOnStartup, layoutNameOnStartup, memoryUsage); KDBusService service(KDBusService::Unique); return app.exec(); } inline void configureAboutData() { KAboutData about(QStringLiteral("lattedock") , QStringLiteral("Latte Dock") , QStringLiteral(VERSION) , i18n("Latte is a dock based on plasma frameworks that provides an elegant and " "intuitive experience for your tasks and plasmoids. It animates its contents " "by using parabolic zoom effect and tries to be there only when it is needed." "\n\n\"Art in Coffee\"") , KAboutLicense::GPL_V2 , QStringLiteral("\251 2016-2017 Michail Vourlakos, Smith AR")); about.setHomepage(WEBSITE); about.setProgramLogo(QIcon::fromTheme(QStringLiteral("latte-dock"))); about.setDesktopFileName(QStringLiteral("latte-dock")); // Authors about.addAuthor(QStringLiteral("Michail Vourlakos"), QString(), QStringLiteral("mvourlakos@gmail.com")); about.addAuthor(QStringLiteral("Smith AR"), QString(), QStringLiteral("audoban@openmailbox.org")); KAboutData::setApplicationData(about); } //! used the version provided by PW:KWorkspace inline void detectPlatform(int argc, char **argv) { if (qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) { return; } for (int i = 0; i < argc; i++) { if (qstrcmp(argv[i], "-platform") == 0 || qstrcmp(argv[i], "--platform") == 0 || QByteArray(argv[i]).startsWith("-platform=") || QByteArray(argv[i]).startsWith("--platform=")) { return; } } const QByteArray sessionType = qgetenv("XDG_SESSION_TYPE"); if (sessionType.isEmpty()) { return; } if (qstrcmp(sessionType, "wayland") == 0) { qputenv("QT_QPA_PLATFORM", "wayland"); } else if (qstrcmp(sessionType, "x11") == 0) { qputenv("QT_QPA_PLATFORM", "xcb"); } } diff --git a/app/plasma/extended/theme.cpp b/app/plasma/extended/theme.cpp index c6e4cd46..c2fbf53d 100644 --- a/app/plasma/extended/theme.cpp +++ b/app/plasma/extended/theme.cpp @@ -1,584 +1,584 @@ /* * Copyright 2018 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 "theme.h" // local #include "lattecorona.h" #include "schemecolors.h" -#include "../../importer.h" +#include "../../layouts/importer.h" #include "../../view/panelshadows_p.h" #include "../../../liblatte2/commontools.h" // Qt #include #include #include // KDE #include #include #include // X11 #include #define DEFAULTCOLORSCHEME "default.colors" #define REVERSEDCOLORSCHEME "reversed.colors" namespace Latte { namespace PlasmaExtended { Theme::Theme(KSharedConfig::Ptr config, QObject *parent) : QObject(parent), m_themeGroup(KConfigGroup(config, QStringLiteral("PlasmaThemeExtended"))) { m_corona = qobject_cast(parent); //! compositing tracking if (KWindowSystem::isPlatformWayland()) { //! TODO: Wayland compositing active m_compositing = true; } else { connect(KWindowSystem::self(), &KWindowSystem::compositingChanged , this, [&](bool enabled) { if (m_compositing == enabled) return; m_compositing = enabled; emit compositingChanged(); }); m_compositing = KWindowSystem::compositingActive(); } //! loadConfig(); connect(this, &Theme::compositingChanged, this, &Theme::roundnessChanged); connect(this, &Theme::outlineWidthChanged, this, &Theme::saveConfig); connect(&m_theme, &Plasma::Theme::themeChanged, this, &Theme::hasShadowChanged); connect(&m_theme, &Plasma::Theme::themeChanged, this, &Theme::load); connect(&m_theme, &Plasma::Theme::themeChanged, this, &Theme::themeChanged); } void Theme::load() { loadThemePaths(); loadRoundness(); } Theme::~Theme() { saveConfig(); m_defaultScheme->deleteLater(); m_reversedScheme->deleteLater(); } bool Theme::hasShadow() const { return PanelShadows::self()->enabled(); } bool Theme::isLightTheme() const { return m_isLightTheme; } bool Theme::isDarkTheme() const { return !m_isLightTheme; } int Theme::bottomEdgeRoundness() const { return m_bottomEdgeRoundness; } int Theme::leftEdgeRoundness() const { return m_leftEdgeRoundness; } int Theme::topEdgeRoundness() const { return m_topEdgeRoundness; } int Theme::rightEdgeRoundness() const { return m_rightEdgeRoundness; } int Theme::outlineWidth() const { return m_outlineWidth; } void Theme::setOutlineWidth(int width) { if (m_outlineWidth == width) { return; } m_outlineWidth = width; emit outlineWidthChanged(); } float Theme::backgroundMaxOpacity() const { return m_backgroundMaxOpacity; } SchemeColors *Theme::defaultTheme() const { return m_defaultScheme; } SchemeColors *Theme::lightTheme() const { return m_isLightTheme ? m_defaultScheme : m_reversedScheme; } SchemeColors *Theme::darkTheme() const { return !m_isLightTheme ? m_defaultScheme : m_reversedScheme; } void Theme::setOriginalSchemeFile(const QString &file) { if (m_originalSchemePath == file) { return; } m_originalSchemePath = file; qDebug() << "plasma theme original colors ::: " << m_originalSchemePath; updateDefaultScheme(); updateReversedScheme(); loadThemeLightness(); emit themeChanged(); } //! WM records need to be updated based on the colors that //! plasma will use in order to be consistent. Such an example //! are the Breeze color schemes that have different values for //! WM and the plasma theme records void Theme::updateDefaultScheme() { QString defaultFilePath = m_extendedThemeDir.path() + "/" + DEFAULTCOLORSCHEME; if (QFileInfo(defaultFilePath).exists()) { QFile(defaultFilePath).remove(); } QFile(m_originalSchemePath).copy(defaultFilePath); m_defaultSchemePath = defaultFilePath; updateDefaultSchemeValues(); if (m_defaultScheme) { disconnect(m_defaultScheme, &SchemeColors::colorsChanged, this, &Theme::loadThemeLightness); m_defaultScheme->deleteLater(); } m_defaultScheme = new SchemeColors(this, m_defaultSchemePath, true); connect(m_defaultScheme, &SchemeColors::colorsChanged, this, &Theme::loadThemeLightness); qDebug() << "plasma theme default colors ::: " << m_defaultSchemePath; } void Theme::updateDefaultSchemeValues() { //! update WM values based on original scheme KSharedConfigPtr originalPtr = KSharedConfig::openConfig(m_originalSchemePath); KSharedConfigPtr defaultPtr = KSharedConfig::openConfig(m_defaultSchemePath); if (originalPtr && defaultPtr) { KConfigGroup originalViewGroup(originalPtr, "Colors:View"); KConfigGroup defaultWMGroup(defaultPtr, "WM"); defaultWMGroup.writeEntry("activeBackground", originalViewGroup.readEntry("BackgroundNormal", QColor())); defaultWMGroup.writeEntry("activeForeground", originalViewGroup.readEntry("ForegroundNormal", QColor())); defaultWMGroup.sync(); } } void Theme::updateReversedScheme() { QString reversedFilePath = m_extendedThemeDir.path() + "/" + REVERSEDCOLORSCHEME; if (QFileInfo(reversedFilePath).exists()) { QFile(reversedFilePath).remove(); } QFile(m_originalSchemePath).copy(reversedFilePath); m_reversedSchemePath = reversedFilePath; updateReversedSchemeValues(); if (m_reversedScheme) { m_reversedScheme->deleteLater(); } m_reversedScheme = new SchemeColors(this, m_reversedSchemePath, true); qDebug() << "plasma theme reversed colors ::: " << m_reversedSchemePath; } void Theme::updateReversedSchemeValues() { //! reverse values based on original scheme KSharedConfigPtr originalPtr = KSharedConfig::openConfig(m_originalSchemePath); KSharedConfigPtr reversedPtr = KSharedConfig::openConfig(m_reversedSchemePath); if (originalPtr && reversedPtr) { for (const auto &groupName : reversedPtr->groupList()) { if (groupName != "Colors:Button" && groupName != "Colors:Selection") { KConfigGroup reversedGroup(reversedPtr, groupName); if (reversedGroup.keyList().contains("BackgroundNormal") && reversedGroup.keyList().contains("ForegroundNormal")) { //! reverse usual text/background values KConfigGroup originalGroup(originalPtr, groupName); reversedGroup.writeEntry("BackgroundNormal", originalGroup.readEntry("ForegroundNormal", QColor())); reversedGroup.writeEntry("ForegroundNormal", originalGroup.readEntry("BackgroundNormal", QColor())); reversedGroup.sync(); } } } //! update WM group KConfigGroup reversedWMGroup(reversedPtr, "WM"); KConfigGroup originalViewGroup(originalPtr, "Colors:View"); if (reversedWMGroup.keyList().contains("activeBackground") && reversedWMGroup.keyList().contains("activeForeground") && reversedWMGroup.keyList().contains("inactiveBackground") && reversedWMGroup.keyList().contains("inactiveForeground")) { //! reverse usual wm titlebar values KConfigGroup originalGroup(originalPtr, "WM"); reversedWMGroup.writeEntry("activeBackground", originalViewGroup.readEntry("ForegroundNormal", QColor())); reversedWMGroup.writeEntry("activeForeground", originalViewGroup.readEntry("BackgroundNormal", QColor())); reversedWMGroup.writeEntry("inactiveBackground", originalGroup.readEntry("inactiveForeground", QColor())); reversedWMGroup.writeEntry("inactiveForeground", originalGroup.readEntry("inactiveBackground", QColor())); reversedWMGroup.sync(); } if (reversedWMGroup.keyList().contains("activeBlend") && reversedWMGroup.keyList().contains("inactiveBlend")) { KConfigGroup originalGroup(originalPtr, "WM"); reversedWMGroup.writeEntry("activeBlend", originalGroup.readEntry("inactiveBlend", QColor())); reversedWMGroup.writeEntry("inactiveBlend", originalGroup.readEntry("activeBlend", QColor())); reversedWMGroup.sync(); } //! update scheme name QString originalSchemeName = SchemeColors::schemeName(m_originalSchemePath); KConfigGroup generalGroup(reversedPtr, "General"); generalGroup.writeEntry("Name", originalSchemeName + "_reversed"); generalGroup.sync(); } } int Theme::roundness(Plasma::FrameSvg *svg, Plasma::Types::Location edge) { int discovY = (edge == Plasma::Types::TopEdge ? svg->mask().boundingRect().bottom() : svg->mask().boundingRect().top()); int discovX = (edge == Plasma::Types::LeftEdge ? svg->mask().boundingRect().right() : svg->mask().boundingRect().left()); int round{0}; if (edge == Plasma::Types::BottomEdge || edge == Plasma::Types::RightEdge || edge == Plasma::Types::TopEdge) { //! TOPLEFT corner //! first LEFT pixel found for (int x=svg->mask().boundingRect().left(); x<50; ++x) { if (!svg->mask().contains(QPoint(x, discovY))) { discovX++; round++; } else { break; } } } else if (edge == Plasma::Types::LeftEdge) { //! it should be TOPRIGHT corner in that case //! first RIGHT pixel found for (int x=svg->mask().boundingRect().right(); x>50; --x) { if (!svg->mask().contains(QPoint(x, discovY))) { discovX--; round++; } else { break; } } } //! this needs investigation (the x2) I dont know if it is really needed //! but it gives me the impression that returns better results return round*2; } void Theme::loadCompositingRoundness() { Plasma::FrameSvg *svg = new Plasma::FrameSvg(this); svg->setImagePath(QStringLiteral("widgets/panel-background")); svg->setEnabledBorders(Plasma::FrameSvg::AllBorders); svg->resizeFrame(QSize(100,100)); //! bottom roundness if (svg->hasElementPrefix("south")) { svg->setElementPrefix("south"); } m_bottomEdgeRoundness = roundness(svg, Plasma::Types::BottomEdge); //! left roundness if (svg->hasElementPrefix("west")) { svg->setElementPrefix("west"); } else { svg->setElementPrefix(""); } m_leftEdgeRoundness = roundness(svg, Plasma::Types::LeftEdge); //! top roundness if (svg->hasElementPrefix("north")) { svg->setElementPrefix("north"); } else { svg->setElementPrefix(""); } m_topEdgeRoundness = roundness(svg, Plasma::Types::TopEdge); //! right roundness if (svg->hasElementPrefix("east")) { svg->setElementPrefix("east"); } else { svg->setElementPrefix(""); } m_rightEdgeRoundness = roundness(svg, Plasma::Types::RightEdge); qDebug() << " COMPOSITING MASK ::: " << svg->mask(); qDebug() << " COMPOSITING MASK BOUNDING RECT ::: " << svg->mask().boundingRect(); qDebug() << " COMPOSITING ROUNDNESS ::: " << m_bottomEdgeRoundness << " _ " << m_leftEdgeRoundness << " _ " << m_topEdgeRoundness << " _ " << m_rightEdgeRoundness; svg->deleteLater(); } void Theme::loadRoundness() { loadCompositingRoundness(); emit roundnessChanged(); } void Theme::loadThemePaths() { - m_themePath = Importer::standardPath("plasma/desktoptheme/" + m_theme.themeName()); + m_themePath = Layouts::Importer::standardPath("plasma/desktoptheme/" + m_theme.themeName()); if (QDir(m_themePath+"/widgets").exists()) { m_themeWidgetsPath = m_themePath + "/widgets"; } else { - m_themeWidgetsPath = Importer::standardPath("plasma/desktoptheme/default/widgets"); + m_themeWidgetsPath = Layouts::Importer::standardPath("plasma/desktoptheme/default/widgets"); } qDebug() << "current plasma theme ::: " << m_theme.themeName(); qDebug() << "theme path ::: " << m_themePath; qDebug() << "theme widgets path ::: " << m_themeWidgetsPath; //! clear kde connections for (auto &c : m_kdeConnections) { disconnect(c); } //! assign color schemes QString themeColorScheme = m_themePath + "/colors"; if (QFileInfo(themeColorScheme).exists()) { setOriginalSchemeFile(themeColorScheme); } else { //! when plasma theme uses the kde colors //! we track when kde color scheme is changing QString kdeSettingsFile = QDir::homePath() + "/.config/kdeglobals"; KDirWatch::self()->addFile(kdeSettingsFile); m_kdeConnections[0] = connect(KDirWatch::self(), &KDirWatch::dirty, this, [ &, kdeSettingsFile](const QString & path) { if (path == kdeSettingsFile) { this->setOriginalSchemeFile(SchemeColors::possibleSchemeFile("kdeglobals")); } }); m_kdeConnections[1] = connect(KDirWatch::self(), &KDirWatch::created, this, [ &, kdeSettingsFile](const QString & path) { if (path == kdeSettingsFile) { this->setOriginalSchemeFile(SchemeColors::possibleSchemeFile("kdeglobals")); } }); setOriginalSchemeFile(SchemeColors::possibleSchemeFile("kdeglobals")); } //! this is probably not needed at all in order to provide full transparency for all //! plasma themes, so we disable it in order to confirm from user testing //! that it is not needed at all //parseThemeSvgFiles(); } void Theme::parseThemeSvgFiles() { QString origBackgroundSvgFile; QString curBackgroundSvgFile = m_extendedThemeDir.path()+"/widgets/panel-background.svg"; if (QFileInfo(curBackgroundSvgFile).exists()) { QDir(m_extendedThemeDir.path()+"/widgets").remove("panel-background.svg"); } if (!QDir(m_extendedThemeDir.path()+"/widgets").exists()) { QDir(m_extendedThemeDir.path()).mkdir("widgets"); } if (QFileInfo(m_themeWidgetsPath+"/panel-background.svg").exists()) { origBackgroundSvgFile = m_themeWidgetsPath+"/panel-background.svg"; QFile(origBackgroundSvgFile).copy(curBackgroundSvgFile); } else if (QFileInfo(m_themeWidgetsPath+"/panel-background.svgz").exists()) { origBackgroundSvgFile = m_themeWidgetsPath+"/panel-background.svgz"; QString tempBackFile = m_extendedThemeDir.path()+"/widgets/panel-background.svg.gz"; QFile(origBackgroundSvgFile).copy(tempBackFile); //! Identify Plasma Desktop version QProcess process; process.start("gzip -d " + tempBackFile); process.waitForFinished(); QString output(process.readAllStandardOutput()); qDebug() << "plasma theme, background extraction output ::: " << output; qDebug() << "plasma theme, original background svg file was decompressed..."; } if (QFileInfo(curBackgroundSvgFile).exists()) { qDebug() << "plasma theme, panel background ::: " << curBackgroundSvgFile; } else { qDebug() << "plasma theme, panel background ::: was not found..."; } //! Find panel-background transparency QFile svgFile(curBackgroundSvgFile); QString styleSvgStr; if (svgFile.open(QIODevice::ReadOnly)) { QTextStream in(&svgFile); bool centerIdFound{false}; bool styleFound{false}; while (!in.atEnd() && !styleFound) { QString line = in.readLine(); //! each time a rect starts then style can be reset if (line.contains("")) { break; } } svgFile.close(); } if (!styleSvgStr.isEmpty()) { int styleInd = styleSvgStr.indexOf("style="); QString cleanedStr = styleSvgStr.remove(0, styleInd+7); int endInd = cleanedStr.indexOf("\""); styleSvgStr = cleanedStr.mid(0,endInd); QStringList styleValues = styleSvgStr.split(";"); // qDebug() << "plasma theme, discovered svg style ::: " << styleValues; float opacity{1}; float fillOpacity{1}; for (QString &value : styleValues) { if (value.startsWith("opacity:")) { opacity = value.remove(0,8).toFloat(); } if (value.startsWith("fill-opacity:")) { fillOpacity = value.remove(0,13).toFloat(); } } m_backgroundMaxOpacity = opacity * fillOpacity; qDebug() << "plasma theme opacity :: " << m_backgroundMaxOpacity << " from : " << opacity << " * " << fillOpacity; } emit backgroundMaxOpacityChanged(); } void Theme::loadThemeLightness() { float textColorLum = Latte::colorLumina(m_defaultScheme->textColor()); float backColorLum = Latte::colorLumina(m_defaultScheme->backgroundColor()); if (backColorLum > textColorLum) { m_isLightTheme = true; } else { m_isLightTheme = false; } if (m_isLightTheme) { qDebug() << "Plasma theme is light..."; } else { qDebug() << "Plasma theme is dark..."; } } void Theme::loadConfig() { setOutlineWidth(m_themeGroup.readEntry("outlineWidth", 1)); } void Theme::saveConfig() { m_themeGroup.writeEntry("outlineWidth", m_outlineWidth); m_themeGroup.sync(); } } } diff --git a/app/schemecolors.cpp b/app/schemecolors.cpp index 72c27208..6ceb66f7 100644 --- a/app/schemecolors.cpp +++ b/app/schemecolors.cpp @@ -1,249 +1,249 @@ /* * Copyright 2018 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 "schemecolors.h" // local -#include "importer.h" +#include "layouts/importer.h" // Qt #include #include #include // KDE #include #include #include namespace Latte { SchemeColors::SchemeColors(QObject *parent, QString scheme, bool plasmaTheme) : QObject(parent), m_basedOnPlasmaTheme(plasmaTheme) { QString pSchemeFile = possibleSchemeFile(scheme); if (QFileInfo(pSchemeFile).exists()) { setSchemeFile(pSchemeFile); m_schemeName = schemeName(pSchemeFile); //! track scheme file for changes KDirWatch::self()->addFile(m_schemeFile); connect(KDirWatch::self(), &KDirWatch::dirty, this, [ & ](const QString & path) { if (path == m_schemeFile) { updateScheme(); } }); } updateScheme(); } SchemeColors::~SchemeColors() { /// } QColor SchemeColors::backgroundColor() const { return m_activeBackgroundColor; } QColor SchemeColors::textColor() const { return m_activeTextColor; } QColor SchemeColors::inactiveBackgroundColor() const { return m_inactiveBackgroundColor; } QColor SchemeColors::inactiveTextColor() const { return m_inactiveTextColor; } QColor SchemeColors::highlightColor() const { return m_highlightColor; } QColor SchemeColors::highlightedTextColor() const { return m_highlightedTextColor; } QColor SchemeColors::positiveTextColor() const { return m_positiveTextColor; } QColor SchemeColors::neutralTextColor() const { return m_neutralTextColor; } QColor SchemeColors::negativeTextColor() const { return m_negativeTextColor; } QColor SchemeColors::buttonTextColor() const { return m_buttonTextColor; } QColor SchemeColors::buttonBackgroundColor() const { return m_buttonBackgroundColor; } QColor SchemeColors::buttonHoverColor() const { return m_buttonHoverColor; } QColor SchemeColors::buttonFocusColor() const { return m_buttonFocusColor; } QString SchemeColors::schemeName() const { return m_schemeName; } QString SchemeColors::SchemeColors::schemeFile() const { return m_schemeFile; } void SchemeColors::setSchemeFile(QString file) { if (m_schemeFile == file) { return; } m_schemeFile = file; emit schemeFileChanged(); } QString SchemeColors::possibleSchemeFile(QString scheme) { if (scheme.startsWith("/") && scheme.endsWith("colors") && QFileInfo(scheme).exists()) { return scheme; } QString tempScheme = scheme; if (scheme == "kdeglobals") { QString settingsFile = QDir::homePath() + "/.config/kdeglobals"; if (QFileInfo(settingsFile).exists()) { KSharedConfigPtr filePtr = KSharedConfig::openConfig(settingsFile); KConfigGroup generalGroup = KConfigGroup(filePtr, "General"); tempScheme = generalGroup.readEntry("ColorScheme", ""); } } - QString schemePath = Importer::standardPath("color-schemes/" + tempScheme + ".colors"); + QString schemePath = Layouts::Importer::standardPath("color-schemes/" + tempScheme + ".colors"); if (schemePath.isEmpty() || !QFileInfo(schemePath).exists()) { //! remove all whitespaces and "-" from scheme in order to access correctly its file QString schemeNameSimplified = tempScheme.simplified().remove(" ").remove("-"); - schemePath = Importer::standardPath("color-schemes/" + schemeNameSimplified + ".colors"); + schemePath = Layouts::Importer::standardPath("color-schemes/" + schemeNameSimplified + ".colors"); } if (QFileInfo(schemePath).exists()) { return schemePath; } return ""; } QString SchemeColors::schemeName(QString originalFile) { if (!(originalFile.startsWith("/") && originalFile.endsWith("colors") && QFileInfo(originalFile).exists())) { return ""; } QString fileNameNoExt = originalFile; int lastSlash = originalFile.lastIndexOf("/"); if (lastSlash >= 0) { fileNameNoExt.remove(0, lastSlash + 1); } if (fileNameNoExt.endsWith(".colors")) { fileNameNoExt.remove(".colors"); } KSharedConfigPtr filePtr = KSharedConfig::openConfig(originalFile); KConfigGroup generalGroup = KConfigGroup(filePtr, "General"); return generalGroup.readEntry("Name", fileNameNoExt); } void SchemeColors::updateScheme() { if (m_schemeFile.isEmpty() || !QFileInfo(m_schemeFile).exists()) { return; } KSharedConfigPtr filePtr = KSharedConfig::openConfig(m_schemeFile); KConfigGroup wmGroup = KConfigGroup(filePtr, "WM"); KConfigGroup selGroup = KConfigGroup(filePtr, "Colors:Selection"); KConfigGroup viewGroup = KConfigGroup(filePtr, "Colors:View"); //KConfigGroup windowGroup = KConfigGroup(filePtr, "Colors:Window"); KConfigGroup buttonGroup = KConfigGroup(filePtr, "Colors:Button"); if (!m_basedOnPlasmaTheme) { m_activeBackgroundColor = wmGroup.readEntry("activeBackground", QColor()); m_activeTextColor = wmGroup.readEntry("activeForeground", QColor()); m_inactiveBackgroundColor = wmGroup.readEntry("inactiveBackground", QColor()); m_inactiveTextColor = wmGroup.readEntry("inactiveForeground", QColor()); } else { m_activeBackgroundColor = viewGroup.readEntry("BackgroundNormal", QColor()); m_activeTextColor = viewGroup.readEntry("ForegroundNormal", QColor()); m_inactiveBackgroundColor = viewGroup.readEntry("BackgroundAlternate", QColor()); m_inactiveTextColor = viewGroup.readEntry("ForegroundInactive", QColor()); } m_highlightColor = selGroup.readEntry("BackgroundNormal", QColor()); m_highlightedTextColor = selGroup.readEntry("ForegroundNormal", QColor()); m_positiveTextColor = viewGroup.readEntry("ForegroundPositive", QColor()); m_neutralTextColor = viewGroup.readEntry("ForegroundNeutral", QColor());; m_negativeTextColor = viewGroup.readEntry("ForegroundNegative", QColor()); m_buttonTextColor = buttonGroup.readEntry("ForegroundNormal", QColor()); m_buttonBackgroundColor = buttonGroup.readEntry("BackgroundNormal", QColor()); m_buttonHoverColor = buttonGroup.readEntry("DecorationHover", QColor()); m_buttonFocusColor = buttonGroup.readEntry("DecorationFocus", QColor()); emit colorsChanged(); } } diff --git a/app/settings/settingsdialog.cpp b/app/settings/settingsdialog.cpp index 624f43dd..5b89e8ab 100644 --- a/app/settings/settingsdialog.cpp +++ b/app/settings/settingsdialog.cpp @@ -1,1968 +1,1968 @@ /* * 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 "importer.h" #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 "../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()) { - Importer::LatteFileVersion version = Importer::fileVersion(entryFile); + Layouts::Importer::LatteFileVersion version = Layouts::Importer::fileVersion(entryFile); - if (version == Importer::LayoutVersion2) { + 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) { - Importer::LatteFileVersion version = Importer::fileVersion(file); + Layouts::Importer::LatteFileVersion version = Layouts::Importer::fileVersion(file); qDebug() << "VERSION :::: " << version; - if (version == Importer::LayoutVersion2) { + if (version == Layouts::Importer::LayoutVersion2) { addLayoutForFile(file); - } else if (version == Importer::ConfigVersion1) { + } 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 == Importer::ConfigVersion2) { + } 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 = Importer::nameOfConfigFile(file); + 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(); //! 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); } } 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); } } //! 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; } 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::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 diff --git a/app/settings/universalsettings.cpp b/app/settings/universalsettings.cpp index 6b726ce1..1871e653 100644 --- a/app/settings/universalsettings.cpp +++ b/app/settings/universalsettings.cpp @@ -1,513 +1,513 @@ /* * 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 "universalsettings.h" // local -#include "../importer.h" +#include "../layouts/importer.h" #include "../layouts/manager.h" // Qt #include #include #include #include // KDE #include #define KWINMETAFORWARDTOLATTESTRING "org.kde.lattedock,/Latte,org.kde.LatteDock,activateLauncherMenu" #define KWINMETAFORWARDTOPLASMASTRING "org.kde.plasmashell,/PlasmaShell,org.kde.PlasmaShell,activateLauncherMenu" namespace Latte { UniversalSettings::UniversalSettings(KSharedConfig::Ptr config, QObject *parent) : QObject(parent), m_config(config), m_universalGroup(KConfigGroup(config, QStringLiteral("UniversalSettings"))) { m_corona = qobject_cast(parent); connect(this, &UniversalSettings::canDisableBordersChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::currentLayoutNameChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::downloadWindowSizeChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::lastNonAssignedLayoutNameChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::launchersChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::layoutsColumnWidthsChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::layoutsMemoryUsageChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::layoutsWindowSizeChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::metaPressAndHoldEnabledChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::mouseSensitivityChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::screenTrackerIntervalChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::showInfoWindowChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::versionChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::screenScalesChanged, this, &UniversalSettings::saveScalesConfig); } UniversalSettings::~UniversalSettings() { saveConfig(); cleanupSettings(); } void UniversalSettings::load() { //! check if user has set the autostart option bool autostartUserSet = m_universalGroup.readEntry("userConfiguredAutostart", false); if (!autostartUserSet && !autostart()) { setAutostart(true); } //! init screen scales m_screenScalesGroup = m_universalGroup.group("ScreenScales"); //! load configuration loadConfig(); //! this is needed to inform globalshortcuts to update its modifiers tracking emit metaPressAndHoldEnabledChanged(); } bool UniversalSettings::showInfoWindow() const { return m_showInfoWindow; } void UniversalSettings::setShowInfoWindow(bool show) { if (m_showInfoWindow == show) { return; } m_showInfoWindow = show; emit showInfoWindowChanged(); } int UniversalSettings::version() const { return m_version; } void UniversalSettings::setVersion(int ver) { if (m_version == ver) { return; } m_version = ver; emit versionChanged(); } int UniversalSettings::screenTrackerInterval() const { return m_screenTrackerInterval; } void UniversalSettings::setScreenTrackerInterval(int duration) { if (m_screenTrackerInterval == duration) { return; } m_screenTrackerInterval = duration; emit screenTrackerIntervalChanged(); } QString UniversalSettings::currentLayoutName() const { return m_currentLayoutName; } void UniversalSettings::setCurrentLayoutName(QString layoutName) { if (m_currentLayoutName == layoutName) { return; } m_currentLayoutName = layoutName; emit currentLayoutNameChanged(); } QString UniversalSettings::lastNonAssignedLayoutName() const { return m_lastNonAssignedLayoutName; } void UniversalSettings::setLastNonAssignedLayoutName(QString layoutName) { if (m_lastNonAssignedLayoutName == layoutName) { return; } m_lastNonAssignedLayoutName = layoutName; emit lastNonAssignedLayoutNameChanged(); } QSize UniversalSettings::downloadWindowSize() const { return m_downloadWindowSize; } void UniversalSettings::setDownloadWindowSize(QSize size) { if (m_downloadWindowSize == size) { return; } m_downloadWindowSize = size; emit downloadWindowSizeChanged(); } QSize UniversalSettings::layoutsWindowSize() const { return m_layoutsWindowSize; } void UniversalSettings::setLayoutsWindowSize(QSize size) { if (m_layoutsWindowSize == size) { return; } m_layoutsWindowSize = size; emit layoutsWindowSizeChanged(); } QStringList UniversalSettings::layoutsColumnWidths() const { return m_layoutsColumnWidths; } void UniversalSettings::setLayoutsColumnWidths(QStringList widths) { if (m_layoutsColumnWidths == widths) { return; } m_layoutsColumnWidths = widths; emit layoutsColumnWidthsChanged(); } QStringList UniversalSettings::launchers() const { return m_launchers; } void UniversalSettings::setLaunchers(QStringList launcherList) { if (m_launchers == launcherList) { return; } m_launchers = launcherList; emit launchersChanged(); } bool UniversalSettings::autostart() const { QFile autostartFile(QDir::homePath() + "/.config/autostart/org.kde.latte-dock.desktop"); return autostartFile.exists(); } void UniversalSettings::setAutostart(bool state) { //! remove old autostart file QFile oldAutostartFile(QDir::homePath() + "/.config/autostart/latte-dock.desktop"); if (oldAutostartFile.exists()) { oldAutostartFile.remove(); } //! end of removal of old autostart file QFile autostartFile(QDir::homePath() + "/.config/autostart/org.kde.latte-dock.desktop"); - QFile metaFile(Importer::standardPath("applications", false)+"/org.kde.latte-dock.desktop"); + QFile metaFile(Layouts::Importer::standardPath("applications", false)+"/org.kde.latte-dock.desktop"); if (!state && autostartFile.exists()) { //! the first time that the user disables the autostart, this is recorded //! and from now own it will not be recreated it in the beginning if (!m_universalGroup.readEntry("userConfiguredAutostart", false)) { m_universalGroup.writeEntry("userConfiguredAutostart", true); } autostartFile.remove(); emit autostartChanged(); } else if (state && metaFile.exists()) { //! check if autostart folder exists and create otherwise QDir autostartDir(QDir::homePath() + "/.config/autostart"); if (!autostartDir.exists()) { QDir configDir(QDir::homePath() + "/.config"); configDir.mkdir("autostart"); } metaFile.copy(autostartFile.fileName()); //! I haven't added the flag "OnlyShowIn=KDE;" into the autostart file //! because I fall onto a Plasma 5.8 case that this flag //! didn't let the plasma desktop to start emit autostartChanged(); } } bool UniversalSettings::canDisableBorders() const { return m_canDisableBorders; } void UniversalSettings::setCanDisableBorders(bool enable) { if (m_canDisableBorders == enable) { return; } m_canDisableBorders = enable; emit canDisableBordersChanged(); } bool UniversalSettings::metaForwardedToLatte() const { return kwin_metaForwardedToLatte(); } void UniversalSettings::forwardMetaToLatte(bool forward) { kwin_forwardMetaToLatte(forward); } bool UniversalSettings::kwin_metaForwardedToLatte() const { QProcess process; process.start("kreadconfig5 --file kwinrc --group ModifierOnlyShortcuts --key Meta"); process.waitForFinished(); QString output(process.readAllStandardOutput()); output = output.remove("\n"); return (output == KWINMETAFORWARDTOLATTESTRING); } void UniversalSettings::kwin_forwardMetaToLatte(bool forward) { if (kwin_metaForwardedToLatte() == forward) { return; } QProcess process; QStringList parameters; parameters << "--file" << "kwinrc" << "--group" << "ModifierOnlyShortcuts" << "--key" << "Meta"; if (forward) { parameters << KWINMETAFORWARDTOLATTESTRING; } else { parameters << KWINMETAFORWARDTOPLASMASTRING; } process.start("kwriteconfig5", parameters); process.waitForFinished(); QDBusInterface iface("org.kde.KWin", "/KWin", "", QDBusConnection::sessionBus()); if (iface.isValid()) { iface.call("reconfigure"); } } bool UniversalSettings::metaPressAndHoldEnabled() const { return m_metaPressAndHoldEnabled; } void UniversalSettings::setMetaPressAndHoldEnabled(bool enabled) { if (m_metaPressAndHoldEnabled == enabled) { return; } m_metaPressAndHoldEnabled = enabled; emit metaPressAndHoldEnabledChanged(); } Types::LayoutsMemoryUsage UniversalSettings::layoutsMemoryUsage() const { return m_memoryUsage; } void UniversalSettings::setLayoutsMemoryUsage(Types::LayoutsMemoryUsage layoutsMemoryUsage) { if (m_memoryUsage == layoutsMemoryUsage) { return; } m_memoryUsage = layoutsMemoryUsage; emit layoutsMemoryUsageChanged(); } Types::MouseSensitivity UniversalSettings::mouseSensitivity() const { return m_mouseSensitivity; } void UniversalSettings::setMouseSensitivity(Types::MouseSensitivity sensitivity) { if (m_mouseSensitivity == sensitivity) { return; } m_mouseSensitivity = sensitivity; emit mouseSensitivityChanged(); } float UniversalSettings::screenWidthScale(QString screenName) const { if (!m_screenScales.contains(screenName)) { return 1; } return m_screenScales[screenName].first; } float UniversalSettings::screenHeightScale(QString screenName) const { if (!m_screenScales.contains(screenName)) { return 1; } return m_screenScales[screenName].second; } void UniversalSettings::setScreenScales(QString screenName, float widthScale, float heightScale) { if (!m_screenScales.contains(screenName)) { m_screenScales[screenName].first = widthScale; m_screenScales[screenName].second = heightScale; } else { if (m_screenScales[screenName].first == widthScale && m_screenScales[screenName].second == heightScale) { return; } m_screenScales[screenName].first = widthScale; m_screenScales[screenName].second = heightScale; } emit screenScalesChanged(); } void UniversalSettings::loadConfig() { m_version = m_universalGroup.readEntry("version", 1); m_canDisableBorders = m_universalGroup.readEntry("canDisableBorders", false); m_currentLayoutName = m_universalGroup.readEntry("currentLayout", QString()); m_downloadWindowSize = m_universalGroup.readEntry("downloadWindowSize", QSize(800, 550)); m_lastNonAssignedLayoutName = m_universalGroup.readEntry("lastNonAssignedLayout", QString()); m_layoutsWindowSize = m_universalGroup.readEntry("layoutsWindowSize", QSize(700, 450)); m_layoutsColumnWidths = m_universalGroup.readEntry("layoutsColumnWidths", QStringList()); m_launchers = m_universalGroup.readEntry("launchers", QStringList()); m_metaPressAndHoldEnabled = m_universalGroup.readEntry("metaPressAndHoldEnabled", true); m_screenTrackerInterval = m_universalGroup.readEntry("screenTrackerInterval", 2500); m_showInfoWindow = m_universalGroup.readEntry("showInfoWindow", true); m_memoryUsage = static_cast(m_universalGroup.readEntry("memoryUsage", (int)Types::SingleLayout)); m_mouseSensitivity = static_cast(m_universalGroup.readEntry("mouseSensitivity", (int)Types::HighSensitivity)); loadScalesConfig(); } void UniversalSettings::saveConfig() { m_universalGroup.writeEntry("version", m_version); m_universalGroup.writeEntry("canDisableBorders", m_canDisableBorders); m_universalGroup.writeEntry("currentLayout", m_currentLayoutName); m_universalGroup.writeEntry("downloadWindowSize", m_downloadWindowSize); m_universalGroup.writeEntry("lastNonAssignedLayout", m_lastNonAssignedLayoutName); m_universalGroup.writeEntry("layoutsWindowSize", m_layoutsWindowSize); m_universalGroup.writeEntry("layoutsColumnWidths", m_layoutsColumnWidths); m_universalGroup.writeEntry("launchers", m_launchers); m_universalGroup.writeEntry("metaPressAndHoldEnabled", m_metaPressAndHoldEnabled); m_universalGroup.writeEntry("screenTrackerInterval", m_screenTrackerInterval); m_universalGroup.writeEntry("showInfoWindow", m_showInfoWindow); m_universalGroup.writeEntry("memoryUsage", (int)m_memoryUsage); m_universalGroup.writeEntry("mouseSensitivity", (int)m_mouseSensitivity); m_universalGroup.sync(); } void UniversalSettings::cleanupSettings() { KConfigGroup containments = KConfigGroup(m_config, QStringLiteral("Containments")); containments.deleteGroup(); containments.sync(); } QString UniversalSettings::splitterIconPath() { return m_corona->kPackage().filePath("splitter"); } QString UniversalSettings::trademarkIconPath() { return m_corona->kPackage().filePath("trademark"); } QQmlListProperty UniversalSettings::screens() { return QQmlListProperty(this, nullptr, &countScreens, &atScreens); } int UniversalSettings::countScreens(QQmlListProperty *property) { Q_UNUSED(property) return qGuiApp->screens().count(); } QScreen *UniversalSettings::atScreens(QQmlListProperty *property, int index) { Q_UNUSED(property) return qGuiApp->screens().at(index); } void UniversalSettings::loadScalesConfig() { for (const auto &screenName : m_screenScalesGroup.keyList()) { QString scalesStr = m_screenScalesGroup.readEntry(screenName, QString()); QStringList scales = scalesStr.split(";"); if (scales.count() == 2) { m_screenScales[screenName] = qMakePair(scales[0].toFloat(), scales[1].toFloat()); } } } void UniversalSettings::saveScalesConfig() { for (const auto &screenName : m_screenScales.keys()) { QStringList scales; scales << QString::number(m_screenScales[screenName].first) << QString::number(m_screenScales[screenName].second); m_screenScalesGroup.writeEntry(screenName, scales.join(";")); } m_screenScalesGroup.sync(); } }