diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 1ec90e01..bbb54be3 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,86 +1,86 @@ set(lattedock-app_SRCS ../liblattedock/dock.cpp alternativeshelper.cpp commontools.cpp - dockcorona.cpp globalshortcuts.cpp importer.cpp infoview.cpp + lattecorona.cpp launcherssignals.cpp layout.cpp layoutmanager.cpp plasmathemeextended.cpp schemecolors.cpp screenpool.cpp - packageplugins/shell/lattepackage.cpp + packageplugins/shell/lattepackage.cpp settings/settingsdialog.cpp settings/sortedactivitiesmodel.cpp settings/universalsettings.cpp settings/delegates/activitycmbboxdelegate.cpp settings/delegates/checkboxdelegate.cpp settings/delegates/colorcmbboxdelegate.cpp settings/delegates/colorcmbboxitemdelegate.cpp settings/delegates/layoutnamedelegate.cpp view/contextmenu.cpp view/dockconfigview.cpp view/docksecconfigview.cpp view/effects.cpp view/panelshadows.cpp view/positioner.cpp view/screenedgeghostwindow.cpp - view/view.cpp - view/visibilitymanager.cpp + view/view.cpp + view/visibilitymanager.cpp wm/abstractwindowinterface.cpp wm/waylandinterface.cpp wm/windowinfowrap.cpp wm/xwindowinterface.cpp main.cpp ) set(latte_dbusXML dbus/org.kde.LatteDock.xml) -qt5_add_dbus_adaptor(lattedock-app_SRCS ${latte_dbusXML} dockcorona.h Latte::DockCorona lattedockadaptor) +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::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}) diff --git a/app/globalshortcuts.cpp b/app/globalshortcuts.cpp index 9ba0b9a3..3278e022 100644 --- a/app/globalshortcuts.cpp +++ b/app/globalshortcuts.cpp @@ -1,919 +1,919 @@ /* * 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 "globalshortcuts.h" // local -#include "dockcorona.h" +#include "lattecorona.h" #include "layoutmanager.h" #include "settings/universalsettings.h" #include "view/view.h" // C++ #include // Qt #include #include #include #include #include // KDE #include #include #include #include // Plasma #include #include // X11 #include #include #include //this code is used by activityswitcher in plasma in order to check if the //user has release all the modifier keys from the globalshortcut namespace { bool isPlatformX11() { static const bool isX11 = QX11Info::isPlatformX11(); return isX11; } // Taken from kwin/tabbox/tabbox.cpp Display *x11_display() { static Display *s_display = nullptr; if (!s_display) { s_display = QX11Info::display(); } return s_display; } bool x11_areKeySymXsDepressed(bool bAll, const uint keySyms[], int nKeySyms) { char keymap[32]; XQueryKeymap(x11_display(), keymap); for (int iKeySym = 0; iKeySym < nKeySyms; iKeySym++) { uint keySymX = keySyms[ iKeySym ]; uchar keyCodeX = XKeysymToKeycode(x11_display(), keySymX); int i = keyCodeX / 8; char mask = 1 << (keyCodeX - (i * 8)); // Abort if bad index value, if (i < 0 || i >= 32) return false; // If ALL keys passed need to be depressed, if (bAll) { if ((keymap[i] & mask) == 0) return false; } else { // If we are looking for ANY key press, and this key is depressed, if (keymap[i] & mask) return true; } } // If we were looking for ANY key press, then none was found, return false, // If we were looking for ALL key presses, then all were found, return true. return bAll; } bool x11_areModKeysDepressed(const QKeySequence &seq) { uint rgKeySyms[10]; int nKeySyms = 0; if (seq.isEmpty()) { return false; } int mod = seq[seq.count() - 1] & Qt::KeyboardModifierMask; if (mod & Qt::SHIFT) { rgKeySyms[nKeySyms++] = XK_Shift_L; rgKeySyms[nKeySyms++] = XK_Shift_R; } if (mod & Qt::CTRL) { rgKeySyms[nKeySyms++] = XK_Control_L; rgKeySyms[nKeySyms++] = XK_Control_R; } if (mod & Qt::ALT) { rgKeySyms[nKeySyms++] = XK_Alt_L; rgKeySyms[nKeySyms++] = XK_Alt_R; } if (mod & Qt::META) { // It would take some code to determine whether the Win key // is associated with Super or Meta, so check for both. // See bug #140023 for details. rgKeySyms[nKeySyms++] = XK_Super_L; rgKeySyms[nKeySyms++] = XK_Super_R; rgKeySyms[nKeySyms++] = XK_Meta_L; rgKeySyms[nKeySyms++] = XK_Meta_R; } return x11_areKeySymXsDepressed(false, rgKeySyms, nKeySyms); } } namespace Latte { const int APPLETEXECUTIONDELAY = 400; GlobalShortcuts::GlobalShortcuts(QObject *parent) : QObject(parent) { - m_corona = qobject_cast(parent); + m_corona = qobject_cast(parent); if (m_corona) { init(); } m_hideDocksTimer.setSingleShot(true); if (isPlatformX11()) { //in X11 the timer is a poller that checks to see if the modifier keys //from user global shortcut have been released m_hideDocksTimer.setInterval(300); } else { //on wayland in acting just as simple timer that hides the dock afterwards m_hideDocksTimer.setInterval(2500); } connect(&m_hideDocksTimer, &QTimer::timeout, this, &GlobalShortcuts::hideDocksTimerSlot); } GlobalShortcuts::~GlobalShortcuts() { } void GlobalShortcuts::init() { KActionCollection *generalActions = new KActionCollection(m_corona); //show-hide the main dock in the primary screen QAction *showAction = generalActions->addAction(QStringLiteral("show latte dock")); showAction->setText(i18n("Show Dock")); showAction->setShortcut(QKeySequence(Qt::META + '`')); KGlobalAccel::setGlobalShortcut(showAction, QKeySequence(Qt::META + '`')); connect(showAction, &QAction::triggered, this, [this]() { showDocks(); }); //show-cycle between Latte settings windows QAction *settingsAction = generalActions->addAction(QStringLiteral("show dock settings")); settingsAction->setText(i18n("Show Dock Settings")); KGlobalAccel::setGlobalShortcut(settingsAction, QKeySequence(Qt::META + Qt::Key_A)); connect(settingsAction, &QAction::triggered, this, [this] { showSettings(); }); //show the layouts editor QAction *layoutsAction = generalActions->addAction(QStringLiteral("show layout settings")); layoutsAction->setText(i18n("Show Layout Settings")); layoutsAction->setShortcut(QKeySequence(Qt::META + Qt::Key_W)); KGlobalAccel::setGlobalShortcut(layoutsAction, QKeySequence(Qt::META + Qt::Key_W)); connect(layoutsAction, &QAction::triggered, this, [this]() { m_corona->layoutManager()->showLatteSettingsDialog(Dock::LayoutPage); }); //show the latter universal settings QAction *universalSettingsAction = generalActions->addAction(QStringLiteral("show latte universal settings")); universalSettingsAction->setText(i18n("Show Latte Settings")); universalSettingsAction->setShortcut(QKeySequence(Qt::META + Qt::Key_E)); KGlobalAccel::setGlobalShortcut(universalSettingsAction, QKeySequence(Qt::META + Qt::Key_E)); connect(universalSettingsAction, &QAction::triggered, this, [this]() { m_corona->layoutManager()->showLatteSettingsDialog(Dock::PreferencesPage); }); KActionCollection *taskbarActions = new KActionCollection(m_corona); //activate actions [1-9] for (int i = 1; i < 10; ++i) { const int entryNumber = i; const Qt::Key key = static_cast(Qt::Key_0 + i); QAction *action = taskbarActions->addAction(QStringLiteral("activate entry %1").arg(QString::number(entryNumber))); action->setText(i18n("Activate Entry %1", entryNumber)); action->setShortcut(QKeySequence(Qt::META + key)); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + key)); connect(action, &QAction::triggered, this, [this, i] { // qDebug() << "meta action..."; activateEntry(i, static_cast(Qt::META)); }); } //! Array that is used to register correctly actions for task index>=10 and <19 std::array keysAboveTen{ Qt::Key_0, Qt::Key_Z, Qt::Key_X, Qt::Key_C, Qt::Key_V, Qt::Key_B, Qt::Key_N, Qt::Key_M, Qt::Key_Comma, Qt::Key_Period }; //activate actions [10-19] for (int i = 10; i < 20; ++i) { QAction *action = taskbarActions->addAction(QStringLiteral("activate entry %1").arg(QString::number(i))); action->setText(i18n("Activate Entry %1", i)); action->setShortcut(QKeySequence(Qt::META + keysAboveTen[i - 10])); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + keysAboveTen[i - 10])); connect(action, &QAction::triggered, this, [this, i] { activateEntry(i, static_cast(Qt::META)); }); } //new instance actions [1-9] for (int i = 1; i < 10; ++i) { const int entryNumber = i; const Qt::Key key = static_cast(Qt::Key_0 + i); QAction *action = taskbarActions->addAction(QStringLiteral("new instance for entry %1").arg(QString::number(entryNumber))); action->setText(i18n("New Instance for Entry %1", entryNumber)); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + Qt::CTRL + key)); connect(action, &QAction::triggered, this, [this, i] { // qDebug() << "meta + ctrl + action..."; activateEntry(i, static_cast(Qt::CTRL)); }); } //new instance actions [10-19] for (int i = 10; i < 20; ++i) { QAction *action = taskbarActions->addAction(QStringLiteral("new instance for entry %1").arg(QString::number(i))); action->setText(i18n("New Instance for Entry %1", i)); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + Qt::CTRL + keysAboveTen[i - 10])); connect(action, &QAction::triggered, this, [this, i] { activateEntry(i, static_cast(Qt::CTRL)); }); } m_singleMetaAction = new QAction(this); m_singleMetaAction->setShortcut(QKeySequence(Qt::META)); } //! Activate launcher menu through dbus interface void GlobalShortcuts::activateLauncherMenu() { QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentLatteViews()); foreach (auto view, sortedViews) { const auto applets = view->containment()->applets(); for (auto applet : applets) { const auto provides = applet->kPackage().metadata().value(QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.launchermenu"))) { if (view->visibility()->isHidden()) { m_lastInvokedAction = m_singleMetaAction; m_hideDocks.clear(); m_hideDocks.append(view); view->visibility()->setBlockHiding(true); m_hideDocksTimer.start(); //! delay the execution in order to show first the dock QTimer::singleShot(APPLETEXECUTIONDELAY, [this, view, applet]() { view->toggleAppletExpanded(applet->id()); }); } else { view->toggleAppletExpanded(applet->id()); } return; } } } } bool GlobalShortcuts::activatePlasmaTaskManagerEntryAtContainment(const Plasma::Containment *c, int index, Qt::Key modifier) { const auto &applets = c->applets(); for (auto *applet : applets) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } KPluginMetaData meta = applet->kPackage().metadata(); for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { // not using QMetaObject::invokeMethod to avoid warnings when calling // this on applets that don't have it or other child items since this // is pretty much trial and error. // Also, "var" arguments are treated as QVariant in QMetaObject int methodIndex = modifier == static_cast(Qt::META) ? metaObject->indexOfMethod("activateTaskAtIndex(QVariant)") : metaObject->indexOfMethod("newInstanceForTaskAtIndex(QVariant)"); int methodIndex2 = metaObject->indexOfMethod("setShowTasksNumbers(QVariant)"); if (methodIndex == -1 || (methodIndex2 == -1 && meta.pluginId() == "org.kde.latte.plasmoid")) { continue; } int showMethodIndex = -1; if (!m_calledItems.contains(item)) { m_calledItems.append(item); m_methodsShowNumbers.append(metaObject->method(methodIndex)); showMethodIndex = m_methodsShowNumbers.count() - 1; } else { showMethodIndex = m_methodsShowNumbers.indexOf(metaObject->method(methodIndex)); } QMetaMethod method = metaObject->method(methodIndex); if (method.invoke(item, Q_ARG(QVariant, index - 1))) { if (methodIndex2 != -1) { m_methodsShowNumbers[showMethodIndex].invoke(item, Q_ARG(QVariant, true)); } return true; } } } } } } return false; } bool GlobalShortcuts::activateLatteEntryAtContainment(const Latte::View *view, int index, Qt::Key modifier) { if (QQuickItem *containmentInterface = view->containment()->property("_plasma_graphicObject").value()) { const auto &childItems = containmentInterface->childItems(); for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { // not using QMetaObject::invokeMethod to avoid warnings when calling // this on applets that don't have it or other child items since this // is pretty much trial and error. // Also, "var" arguments are treated as QVariant in QMetaObject int methodIndex = modifier == static_cast(Qt::META) ? metaObject->indexOfMethod("activateEntryAtIndex(QVariant)") : metaObject->indexOfMethod("newInstanceForEntryAtIndex(QVariant)"); int methodIndex2 = metaObject->indexOfMethod("setShowAppletsNumbers(QVariant,QVariant,QVariant)"); if (methodIndex == -1 || (methodIndex2 == -1)) { continue; } int appLauncher = m_corona->universalSettings()->metaForwardedToLatte() ? applicationLauncherId(view->containment()) : -1; int showMethodIndex = -1; if (!m_calledItems.contains(item)) { m_calledItems.append(item); m_methodsShowNumbers.append(metaObject->method(methodIndex2)); showMethodIndex = m_methodsShowNumbers.count() - 1; } else { showMethodIndex = m_methodsShowNumbers.indexOf(metaObject->method(methodIndex2)); } QMetaMethod method = metaObject->method(methodIndex); if (view->visibility()->isHidden()) { //! delay the execution in order to show first the dock if (m_methodsShowNumbers[showMethodIndex].invoke(item, Q_ARG(QVariant, true), Q_ARG(QVariant, true), Q_ARG(QVariant, appLauncher))) { QTimer::singleShot(APPLETEXECUTIONDELAY, [this, item, method, index]() { method.invoke(item, Q_ARG(QVariant, index)); }); } return true; } else { if (method.invoke(item, Q_ARG(QVariant, index))) { m_methodsShowNumbers[showMethodIndex].invoke(item, Q_ARG(QVariant, true), Q_ARG(QVariant, true), Q_ARG(QVariant, appLauncher)); return true; } } } } } return false; } //! Activate task manager entry void GlobalShortcuts::activateEntry(int index, Qt::Key modifier) { m_lastInvokedAction = dynamic_cast(sender()); QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentLatteViews()); foreach (auto view, sortedViews) { if ((!view->latteTasksPresent() && view->tasksPresent() && activatePlasmaTaskManagerEntryAtContainment(view->containment(), index, modifier)) || (activateLatteEntryAtContainment(view, index, modifier))) { if (!m_hideDocks.contains(view)) { m_hideDocks.append(view); } view->visibility()->setBlockHiding(true); m_hideDocksTimer.start(); return; } } } //! update badge for specific dock item void GlobalShortcuts::updateDockItemBadge(QString identifier, QString value) { //qDebug() << "DBUS CALL ::: " << identifier << " - " << value; auto updateBadgeForTaskInContainment = [this](const Plasma::Containment * c, QString identifier, QString value) { const auto &applets = c->applets(); for (auto *applet : applets) { KPluginMetaData meta = applet->kPackage().metadata(); if (meta.pluginId() == "org.kde.latte.plasmoid") { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { // not using QMetaObject::invokeMethod to avoid warnings when calling // this on applets that don't have it or other child items since this // is pretty much trial and error. // Also, "var" arguments are treated as QVariant in QMetaObject int methodIndex = metaObject->indexOfMethod("updateBadge(QVariant,QVariant)"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); if (method.invoke(item, Q_ARG(QVariant, identifier), Q_ARG(QVariant, value))) { return true; } } } } } } return false; }; QHash *views = m_corona->layoutManager()->currentLatteViews(); // update badges in all Latte Tasks plasmoids for (auto it = views->constBegin(), end = views->constEnd(); it != end; ++it) { updateBadgeForTaskInContainment(it.key(), identifier, value); } } bool GlobalShortcuts::isCapableToShowAppletsNumbers(Latte::View *view) { if (!view->latteTasksPresent() && view->tasksPresent()) { return false; } const Plasma::Containment *c = view->containment(); if (QQuickItem *containmentInterface = c->property("_plasma_graphicObject").value()) { const auto &childItems = containmentInterface->childItems(); for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { // not using QMetaObject::invokeMethod to avoid warnings when calling // this on applets that don't have it or other child items since this // is pretty much trial and error. // Also, "var" arguments are treated as QVariant in QMetaObject int methodIndex = metaObject->indexOfMethod("setShowAppletsNumbers(QVariant,QVariant,QVariant)"); if (methodIndex == -1) { continue; } return true; } } } return false; } int GlobalShortcuts::applicationLauncherId(const Plasma::Containment *c) { const auto applets = c->applets(); for (auto applet : applets) { const auto provides = applet->kPackage().metadata().value(QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.launchermenu"))) { return applet->id(); } } return -1; } void GlobalShortcuts::showDocks() { m_lastInvokedAction = dynamic_cast(sender()); auto invokeShowNumbers = [this](const Plasma::Containment * c) { if (QQuickItem *containmentInterface = c->property("_plasma_graphicObject").value()) { const auto &childItems = containmentInterface->childItems(); for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { // not using QMetaObject::invokeMethod to avoid warnings when calling // this on applets that don't have it or other child items since this // is pretty much trial and error. // Also, "var" arguments are treated as QVariant in QMetaObject int methodIndex = metaObject->indexOfMethod("setShowAppletsNumbers(QVariant,QVariant,QVariant)"); if (methodIndex == -1) { continue; } int appLauncher = m_corona->universalSettings()->metaForwardedToLatte() ? applicationLauncherId(c) : -1; int showMethodIndex = -1; if (!m_calledItems.contains(item)) { m_calledItems.append(item); m_methodsShowNumbers.append(metaObject->method(methodIndex)); showMethodIndex = m_methodsShowNumbers.count() - 1; } else { showMethodIndex = m_methodsShowNumbers.indexOf(metaObject->method(methodIndex)); } if (m_methodsShowNumbers[showMethodIndex].invoke(item, Q_ARG(QVariant, true), Q_ARG(QVariant, true), Q_ARG(QVariant, appLauncher))) { return true; } } } } return false; }; auto invokeShowOnlyMeta = [this](const Plasma::Containment * c) { if (QQuickItem *containmentInterface = c->property("_plasma_graphicObject").value()) { const auto &childItems = containmentInterface->childItems(); for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { // not using QMetaObject::invokeMethod to avoid warnings when calling // this on applets that don't have it or other child items since this // is pretty much trial and error. // Also, "var" arguments are treated as QVariant in QMetaObject int methodIndex = metaObject->indexOfMethod("setShowAppletsNumbers(QVariant,QVariant,QVariant)"); if (methodIndex == -1) { continue; } int appLauncher = m_corona->universalSettings()->metaForwardedToLatte() ? applicationLauncherId(c) : -1; int showMethodIndex = -1; if (!m_calledItems.contains(item)) { m_calledItems.append(item); m_methodsShowNumbers.append(metaObject->method(methodIndex)); showMethodIndex = m_methodsShowNumbers.count() - 1; } else { showMethodIndex = m_methodsShowNumbers.indexOf(metaObject->method(methodIndex)); } if (m_methodsShowNumbers[showMethodIndex].invoke(item, Q_ARG(QVariant, false), Q_ARG(QVariant, true), Q_ARG(QVariant, appLauncher))) { return true; } } } } return false; }; QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentLatteViews()); Latte::View *viewWithTasks{nullptr}; Latte::View *viewWithMeta{nullptr}; foreach (auto view, sortedViews) { if (!viewWithTasks && isCapableToShowAppletsNumbers(view)) { viewWithTasks = view; } if (!viewWithMeta && m_corona->universalSettings()->metaForwardedToLatte() && applicationLauncherId(view->containment()) > -1) { viewWithMeta = view; } } bool dockFound{false}; if (!m_hideDocksTimer.isActive()) { m_hideDocks.clear(); } if (viewWithTasks || viewWithMeta) { m_calledItems.clear(); m_methodsShowNumbers.clear(); } if (viewWithTasks && invokeShowNumbers(viewWithTasks->containment())) { dockFound = true; if (!m_hideDocksTimer.isActive()) { m_hideDocks.append(viewWithTasks); viewWithTasks->visibility()->setBlockHiding(true); } } if (viewWithMeta && viewWithMeta != viewWithTasks && invokeShowOnlyMeta(viewWithMeta->containment())) { dockFound = true; if (!m_hideDocksTimer.isActive()) { m_hideDocks.append(viewWithMeta); viewWithMeta->visibility()->setBlockHiding(true); } } if (dockFound) { if (!m_hideDocksTimer.isActive()) { m_hideDocksTimer.start(); } else { m_hideDocksTimer.stop(); hideDocksTimerSlot(); } } } bool GlobalShortcuts::docksToHideAreValid() { foreach (auto view, m_hideDocks) { if (!m_corona->layoutManager()->latteViewExists(view)) { return false; } } return true; } bool GlobalShortcuts::dockAtLowerScreenPriority(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() << "dockAtLowerScreenPriority : shouldn't had reached here..."; return false; } bool GlobalShortcuts::dockAtLowerEdgePriority(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; } QList GlobalShortcuts::sortedViewsList(QHash *views) { QList docks; //! create a docks list to sorted out for (auto it = views->constBegin(), end = views->constEnd(); it != end; ++it) { docks.append(it.value()); } qDebug() << " -------- "; for (int i = 0; i < docks.count(); ++i) { qDebug() << i << ". " << docks[i]->screen()->name() << " - " << docks[i]->location(); } //! sort the docks based on screens and edges priorities //! docks on primary screen have higher priority and //! for docks in the same screen the priority goes to //! Bottom,Left,Top,Right for (int i = 0; i < docks.size(); ++i) { for (int j = 0; j < docks.size() - i - 1; ++j) { if (dockAtLowerScreenPriority(docks[j], docks[j + 1]) || (docks[j]->screen() == docks[j + 1]->screen() && dockAtLowerEdgePriority(docks[j], docks[j + 1]))) { Latte::View *temp = docks[j + 1]; docks[j + 1] = docks[j]; docks[j] = temp; } } } Latte::View *highestPriorityView{nullptr}; for (int i = 0; i < docks.size(); ++i) { if (docks[i]->isPreferredForShortcuts()) { highestPriorityView = docks[i]; docks.removeAt(i); break; } } if (highestPriorityView) { docks.prepend(highestPriorityView); } qDebug() << " -------- sorted -----"; for (int i = 0; i < docks.count(); ++i) { qDebug() << i << ". " << docks[i]->isPreferredForShortcuts() << " - " << docks[i]->screen()->name() << " - " << docks[i]->location(); } return docks; } void GlobalShortcuts::showSettings() { QList docks = sortedViewsList(m_corona->layoutManager()->currentLatteViews()); //! find which is the next dock to show its settings if (docks.count() > 0) { int openSettings = -1; //! check if there is a dock with opened settings window for (int i = 0; i < docks.size(); ++i) { if (docks[i]->settingsWindowIsShown()) { openSettings = i; break; } } if (openSettings >= 0 && docks.count() > 1) { openSettings = openSettings + 1; if (openSettings >= docks.size()) { openSettings = 0; } docks[openSettings]->showSettingsWindow(); } else { docks[0]->showSettingsWindow(); } } } void GlobalShortcuts::hideDocksTimerSlot() { if (!m_lastInvokedAction || m_hideDocks.count() == 0) { return; } // qDebug() << "MEMORY ::: " << m_hideDocks.count() << " _ " << m_calledItems.count() << " _ " << m_methodsShowNumbers.count(); if (isPlatformX11()) { if (!x11_areModKeysDepressed(m_lastInvokedAction->shortcut())) { m_lastInvokedAction = Q_NULLPTR; if (docksToHideAreValid()) { foreach (auto latteView, m_hideDocks) { latteView->visibility()->setBlockHiding(false); } if (m_calledItems.count() > 0) { for (int i = 0; i < m_calledItems.count(); ++i) { m_methodsShowNumbers[i].invoke(m_calledItems[i], Q_ARG(QVariant, false), Q_ARG(QVariant, false), Q_ARG(QVariant, -1)); } } } m_hideDocks.clear(); m_calledItems.clear(); m_methodsShowNumbers.clear(); return; } else { m_hideDocksTimer.start(); } } else { // TODO: This is needs to be fixed in wayland m_lastInvokedAction = Q_NULLPTR; if (docksToHideAreValid()) { foreach (auto latteView, m_hideDocks) { latteView->visibility()->setBlockHiding(false); } if (m_calledItems.count() > 0) { for (int i = 0; i < m_calledItems.count(); ++i) { m_methodsShowNumbers[i].invoke(m_calledItems[i], Q_ARG(QVariant, false), Q_ARG(QVariant, false), Q_ARG(QVariant, -1)); } } } m_hideDocks.clear(); m_calledItems.clear(); m_methodsShowNumbers.clear(); } } } #include "moc_globalshortcuts.cpp" diff --git a/app/globalshortcuts.h b/app/globalshortcuts.h index 525d6a5b..dccd23bf 100644 --- a/app/globalshortcuts.h +++ b/app/globalshortcuts.h @@ -1,89 +1,89 @@ /* * 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 . */ #ifndef GLOBALSHORTCUTS_H #define GLOBALSHORTCUTS_H // local #include "../liblattedock/dock.h" // Qt #include #include #include namespace Plasma { class Containment; } namespace Latte { -class DockCorona; +class Corona; class View; } namespace Latte { class GlobalShortcuts : public QObject { Q_OBJECT public: GlobalShortcuts(QObject *parent = nullptr); ~GlobalShortcuts() override; void activateLauncherMenu(); void updateDockItemBadge(QString identifier, QString value); private slots: void hideDocksTimerSlot(); private: void init(); void activateEntry(int index, Qt::Key modifier); void showDocks(); void showSettings(); bool activateLatteEntryAtContainment(const Latte::View *view, int index, Qt::Key modifier); bool activatePlasmaTaskManagerEntryAtContainment(const Plasma::Containment *c, int index, Qt::Key modifier); bool dockAtLowerEdgePriority(Latte::View *test, Latte::View *base); bool dockAtLowerScreenPriority(Latte::View *test, Latte::View *base); bool docksToHideAreValid(); bool isCapableToShowAppletsNumbers(Latte::View *view); int applicationLauncherId(const Plasma::Containment *c); QList sortedViewsList(QHash *views); QAction *m_lastInvokedAction; //!it is used when the dock is hidden in order to delay the app launcher showing QAction *m_singleMetaAction; QTimer m_hideDocksTimer; QList m_hideDocks; QList m_calledItems; QList m_methodsShowNumbers; - DockCorona *m_corona{nullptr}; + Latte::Corona *m_corona{nullptr}; }; } #endif // GLOBALSHORTCUTS_H diff --git a/app/importer.cpp b/app/importer.cpp index 2d6e7d76..eb62f137 100644 --- a/app/importer.cpp +++ b/app/importer.cpp @@ -1,598 +1,598 @@ /* * 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 "dockcorona.h" +#include "lattecorona.h" #include "layoutmanager.h" #include "layout.h" #include "screenpool.h" #include "settings/universalsettings.h" #include "../liblattedock/dock.h" // Qt #include #include // KDE #include #include #include #include #include #include namespace Latte { 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()); foreach (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 foreach (auto containmentId, containments.groupList()) { KConfigGroup containmentGroup = containments.group(containmentId); QString plugin = containmentGroup.readEntry("plugin", QString()); Dock::SessionType session = (Dock::SessionType)containmentGroup.readEntry("session", (int)Dock::DefaultSession); bool shouldImport = false; if (plugin == "org.kde.latte.containment" && session == Dock::DefaultSession && !alternative) { qDebug() << containmentId << " - " << plugin << " - " << session; shouldImport = true; } else if (plugin == "org.kde.latte.containment" && session == Dock::AlternativeSession && alternative) { qDebug() << containmentId << " - " << plugin << " - " << session; shouldImport = true; } // this latte containment should be imported if (shouldImport) { auto applets = containments.group(containmentId).group("Applets"); foreach (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 foreach (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 newLayout(this, newLayoutPath, newName); newLayout.setVersion(2); newLayout.setLaunchers(layoutLaunchers); newLayout.setShowInMenu(true); if (alternative) { newLayout.setColor("purple"); } else { newLayout.setColor("blue"); } return true; } 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()); foreach (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 foreach (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")); foreach (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::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; foreach (auto file, files) { layoutNames.append(Layout::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::MultipleLayoutsName + ".layout.latte"; KSharedConfigPtr filePtr = KSharedConfig::openConfig(linkedFilePath); KConfigGroup linkedContainments = KConfigGroup(filePtr, "Containments"); //! layoutName and its Containments QHash linkedLayoutContainmentGroups; foreach (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; foreach (auto layoutName, linkedLayoutContainmentGroups.uniqueKeys()) { if (layoutName != Layout::MultipleLayoutsName && layoutExists(layoutName)) { updatedLayouts << layoutName; KSharedConfigPtr layoutFilePtr = KSharedConfig::openConfig(layoutFilePath(layoutName)); KConfigGroup origLayoutContainments = KConfigGroup(layoutFilePtr, "Containments"); //Clear old containments origLayoutContainments.deleteGroup(); //Update containments foreach (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 foreach (auto cId, linkedContainments.groupList()) { linkedContainments.group(cId).deleteGroup(); } linkedContainments.sync(); return updatedLayouts; } } diff --git a/app/infoview.cpp b/app/infoview.cpp index 7542fc4d..4a5c28bb 100644 --- a/app/infoview.cpp +++ b/app/infoview.cpp @@ -1,201 +1,201 @@ /* * 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 "infoview.h" // local #include "wm/abstractwindowinterface.h" #include "view/panelshadows_p.h" // Qt #include #include #include #include // KDE #include #include #include #include #include // Plasma #include namespace Latte { -InfoView::InfoView(DockCorona *corona, QString message, QScreen *screen, QWindow *parent) +InfoView::InfoView(Latte::Corona *corona, QString message, QScreen *screen, QWindow *parent) : QQuickView(parent), m_corona(corona), m_message(message), m_screen(screen) { setupWaylandIntegration(); setResizeMode(QQuickView::SizeViewToRootObject); setColor(QColor(Qt::transparent)); setDefaultAlphaBuffer(true); setIcon(qGuiApp->windowIcon()); setScreen(screen); setFlags(wFlags()); init(); } InfoView::~InfoView() { PanelShadows::self()->removeWindow(this); qDebug() << "InfoView deleting ..."; if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; } } void InfoView::init() { rootContext()->setContextProperty(QStringLiteral("infoWindow"), this); KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); kdeclarative.setTranslationDomain(QStringLiteral("latte-dock")); kdeclarative.setupBindings(); auto source = QUrl::fromLocalFile(m_corona->kPackage().filePath("infoviewui")); setSource(source); rootObject()->setProperty("message", m_message); syncGeometry(); } Plasma::FrameSvg::EnabledBorders InfoView::enabledBorders() const { return m_borders; } inline Qt::WindowFlags InfoView::wFlags() const { return (flags() | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) & ~Qt::WindowDoesNotAcceptFocus; } void InfoView::syncGeometry() { const QSize size(rootObject()->width(), rootObject()->height()); const auto sGeometry = screen()->geometry(); setMaximumSize(size); setMinimumSize(size); resize(size); QPoint position{sGeometry.center().x() - size.width() / 2, sGeometry.center().y() - size.height() / 2 }; setPosition(position); if (m_shellSurface) { m_shellSurface->setPosition(position); } } void InfoView::showEvent(QShowEvent *ev) { QQuickWindow::showEvent(ev); m_corona->wm()->setDockExtraFlags(*this); setFlags(wFlags()); m_corona->wm()->enableBlurBehind(*this); syncGeometry(); QTimer::singleShot(400, this, &InfoView::syncGeometry); PanelShadows::self()->addWindow(this); PanelShadows::self()->setEnabledBorders(this, m_borders); } void InfoView::setupWaylandIntegration() { if (m_shellSurface) { // already setup return; } if (m_corona) { using namespace KWayland::Client; - PlasmaShell *interface = m_corona->waylandDockCoronaInterface(); + PlasmaShell *interface = m_corona->waylandCoronaInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(this); if (!s) { return; } qDebug() << "wayland dock window surface was created..."; m_shellSurface = interface->createSurface(s, this); } } bool InfoView::event(QEvent *e) { if (e->type() == QEvent::PlatformSurface) { if (auto pe = dynamic_cast(e)) { switch (pe->surfaceEventType()) { case QPlatformSurfaceEvent::SurfaceCreated: if (m_shellSurface) { break; } setupWaylandIntegration(); break; case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; } PanelShadows::self()->removeWindow(this); break; } } } return QQuickWindow::event(e); } void InfoView::setOnActivities(QStringList activities) { KWindowSystem::setOnActivities(winId(), activities); } } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/infoview.h b/app/infoview.h index 752dfaa7..75a86613 100644 --- a/app/infoview.h +++ b/app/infoview.h @@ -1,84 +1,83 @@ /* * 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 . */ #ifndef INFOVIEW_H #define INFOVIEW_H // local -#include "dockcorona.h" +#include "lattecorona.h" // Qt #include #include #include namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { class InfoView : public QQuickView { Q_OBJECT Q_PROPERTY(Plasma::FrameSvg::EnabledBorders enabledBorders READ enabledBorders NOTIFY enabledBordersChanged) public: - InfoView(DockCorona *corona, QString message, QScreen *screen = qGuiApp->primaryScreen(), QWindow *parent = nullptr); + InfoView(Latte::Corona *corona, QString message, QScreen *screen = qGuiApp->primaryScreen(), QWindow *parent = nullptr); ~InfoView() override; Plasma::FrameSvg::EnabledBorders enabledBorders() const; void init(); Qt::WindowFlags wFlags() const; void setOnActivities(QStringList activities = {"0"}); public slots: Q_INVOKABLE void syncGeometry(); signals: void enabledBordersChanged(); protected: void showEvent(QShowEvent *ev) override; bool event(QEvent *e) override; private: void setupWaylandIntegration(); private: QString m_message; QScreen *m_screen{nullptr}; Plasma::FrameSvg::EnabledBorders m_borders{Plasma::FrameSvg::TopBorder | Plasma::FrameSvg::BottomBorder}; KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; - DockCorona *m_corona{nullptr}; + Latte::Corona *m_corona{nullptr}; }; } -#endif //DOCKCONFIGVIEW_H -// kate: indent-mode cstyle; indent-width 4; replace-tabs on; +#endif //INFOVIEW_H diff --git a/app/dockcorona.cpp b/app/lattecorona.cpp similarity index 90% rename from app/dockcorona.cpp rename to app/lattecorona.cpp index a3263d04..04774999 100644 --- a/app/dockcorona.cpp +++ b/app/lattecorona.cpp @@ -1,956 +1,956 @@ /* * 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 "dockcorona.h" +#include "lattecorona.h" // local #include "alternativeshelper.h" #include "globalshortcuts.h" #include "importer.h" #include "lattedockadaptor.h" #include "launcherssignals.h" #include "layoutmanager.h" #include "plasmathemeextended.h" #include "screenpool.h" #include "packageplugins/shell/lattepackage.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 { -DockCorona::DockCorona(bool defaultLayoutOnStartup, QString layoutNameOnStartUp, int userSetMemoryUsage, QObject *parent) +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_globalShortcuts(new GlobalShortcuts(this)), m_universalSettings(new UniversalSettings(KSharedConfig::openConfig(), this)), m_themeExtended(new PlasmaThemeExtended(KSharedConfig::openConfig(), this)), m_layoutManager(new LayoutManager(this)) { //! create the window manager if (KWindowSystem::isPlatformWayland()) { m_wm = new WaylandInterface(this); } else { m_wm = new XWindowInterface(this); } setupWaylandIntegration(); KPackage::Package package(new Latte::Package(this)); m_screenPool->load(); if (!package.isValid()) { qWarning() << staticMetaObject.className() << "the package" << package.metadata().rawData() << "is invalid!"; return; } else { qDebug() << staticMetaObject.className() << "the package" << package.metadata().rawData() << "is valid!"; } setKPackage(package); //! universal settings / extendedtheme must be loaded after the package has been set m_universalSettings->load(); m_themeExtended->load(); qmlRegisterTypes(); if (m_activityConsumer && (m_activityConsumer->serviceStatus() == KActivities::Consumer::Running)) { load(); } - connect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &DockCorona::load); + connect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); m_docksScreenSyncTimer.setSingleShot(true); m_docksScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval()); - connect(&m_docksScreenSyncTimer, &QTimer::timeout, this, &DockCorona::syncLatteViewsToScreens); + connect(&m_docksScreenSyncTimer, &QTimer::timeout, this, &Corona::syncLatteViewsToScreens); connect(m_universalSettings, &UniversalSettings::screenTrackerIntervalChanged, this, [this]() { m_docksScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval()); }); //! Dbus adaptor initialization new LatteDockAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/Latte"), this); } -DockCorona::~DockCorona() +Corona::~Corona() { //! BEGIN: Give the time to slide-out docks when closing m_layoutManager->hideAllDocks(); //! 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 docks when closing m_docksScreenSyncTimer.stop(); if (m_layoutManager->memoryUsage() == Dock::SingleLayout) { cleanConfig(); } qDebug() << "Latte Corona - unload: containments ..."; m_layoutManager->unload(); m_wm->deleteLater(); m_globalShortcuts->deleteLater(); m_layoutManager->deleteLater(); m_screenPool->deleteLater(); m_universalSettings->deleteLater(); m_themeExtended->deleteLater(); - disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &DockCorona::load); + disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); delete m_activityConsumer; qDebug() << "Latte Corona - deleted..."; } -void DockCorona::load() +void Corona::load() { if (m_activityConsumer && (m_activityConsumer->serviceStatus() == KActivities::Consumer::Running) && m_activitiesStarting) { - disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &DockCorona::load); + disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); m_layoutManager->load(); m_activitiesStarting = false; - connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &DockCorona::primaryOutputChanged, Qt::UniqueConnection); - connect(QApplication::desktop(), &QDesktopWidget::screenCountChanged, this, &DockCorona::screenCountChanged); + connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &Corona::primaryOutputChanged, Qt::UniqueConnection); + connect(QApplication::desktop(), &QDesktopWidget::screenCountChanged, this, &Corona::screenCountChanged); - connect(m_screenPool, &ScreenPool::primaryPoolChanged, this, &DockCorona::screenCountChanged); + connect(m_screenPool, &ScreenPool::primaryPoolChanged, this, &Corona::screenCountChanged); QString assignedLayout = m_layoutManager->shouldSwitchToLayout(m_activityConsumer->currentActivity()); QString loadLayoutName = ""; if (!m_defaultLayoutOnStartup && m_layoutNameOnStartUp.isEmpty()) { if (!assignedLayout.isEmpty() && assignedLayout != m_universalSettings->currentLayoutName()) { loadLayoutName = assignedLayout; } else { loadLayoutName = m_universalSettings->currentLayoutName(); } if (!m_layoutManager->layoutExists(loadLayoutName)) { loadLayoutName = m_layoutManager->defaultLayoutName(); m_layoutManager->importDefaultLayout(false); } } else if (m_defaultLayoutOnStartup) { loadLayoutName = m_layoutManager->importer()->uniqueLayoutName(m_layoutManager->defaultLayoutName()); m_layoutManager->importDefaultLayout(true); } else { loadLayoutName = m_layoutNameOnStartUp; } if (m_userSetMemoryUsage != -1 && !KWindowSystem::isPlatformWayland()) { Dock::LayoutsMemoryUsage usage = static_cast(m_userSetMemoryUsage); m_universalSettings->setLayoutsMemoryUsage(usage); } if (KWindowSystem::isPlatformWayland()) { m_universalSettings->setLayoutsMemoryUsage(Dock::SingleLayout); } m_layoutManager->loadLayoutOnStartup(loadLayoutName); //! load screens signals such screenGeometryChanged in order to support //! plasmoid.screenGeometry properly for (QScreen *screen : qGuiApp->screens()) { addOutput(screen); } - connect(qGuiApp, &QGuiApplication::screenAdded, this, &DockCorona::addOutput, Qt::UniqueConnection); + connect(qGuiApp, &QGuiApplication::screenAdded, this, &Corona::addOutput, Qt::UniqueConnection); } } -void DockCorona::unload() +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 DockCorona::setupWaylandIntegration() +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_waylandDockCorona = registry->createPlasmaShell(name, version, this); + 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(); } -KWayland::Client::PlasmaShell *DockCorona::waylandDockCoronaInterface() const +KWayland::Client::PlasmaShell *Corona::waylandCoronaInterface() const { - return m_waylandDockCorona; + return m_waylandCorona; } -void DockCorona::cleanConfig() +void Corona::cleanConfig() { auto containmentsEntries = config()->group("Containments"); bool changed = false; foreach (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"); foreach (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 DockCorona::containmentExists(uint id) const +bool Corona::containmentExists(uint id) const { foreach (auto containment, containments()) { if (id == containment->id()) { return true; } } return false; } -bool DockCorona::appletExists(uint containmentId, uint appletId) const +bool Corona::appletExists(uint containmentId, uint appletId) const { Plasma::Containment *containment = nullptr; foreach (auto cont, containments()) { if (containmentId == cont->id()) { containment = cont; break; } } if (!containment) { return false; } foreach (auto applet, containment->applets()) { if (applet->id() == appletId) { return true; } } return false; } -KActivities::Consumer *DockCorona::activitiesConsumer() const +KActivities::Consumer *Corona::activitiesConsumer() const { return m_activityConsumer; } -ScreenPool *DockCorona::screenPool() const +ScreenPool *Corona::screenPool() const { return m_screenPool; } -UniversalSettings *DockCorona::universalSettings() const +UniversalSettings *Corona::universalSettings() const { return m_universalSettings; } -LayoutManager *DockCorona::layoutManager() const +LayoutManager *Corona::layoutManager() const { return m_layoutManager; } -AbstractWindowInterface *DockCorona::wm() const +AbstractWindowInterface *Corona::wm() const { return m_wm; } -PlasmaThemeExtended *DockCorona::themeExtended() const +PlasmaThemeExtended *Corona::themeExtended() const { return m_themeExtended; } -int DockCorona::numScreens() const +int Corona::numScreens() const { return qGuiApp->screens().count(); } -QRect DockCorona::screenGeometry(int id) const +QRect Corona::screenGeometry(int id) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; QString screenName; if (m_screenPool->knownIds().contains(id)) screenName = m_screenPool->connector(id); foreach (auto scr, screens) { if (scr->name() == screenName) { screen = scr; break; } } return screen->geometry(); } -QRegion DockCorona::availableScreenRegion(int id) const +QRegion Corona::availableScreenRegion(int id) const { return availableScreenRegionWithCriteria(id); } -QRegion DockCorona::availableScreenRegionWithCriteria(int id, QString forLayout) const +QRegion Corona::availableScreenRegionWithCriteria(int id, QString forLayout) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; QString screenName; if (m_screenPool->knownIds().contains(id)) screenName = m_screenPool->connector(id); foreach (auto scr, screens) { if (scr->name() == screenName) { screen = scr; break; } } if (!screen) return QRegion(); QHash *views; if (forLayout.isEmpty()) { views = m_layoutManager->currentLatteViews(); } else { views = m_layoutManager->layoutLatteViews(forLayout); } QRegion available(screen->geometry()); if (views) { for (const auto *view : *views) { if (view && view->containment() && view->screen() == screen && view->visibility() && (view->visibility()->mode() != Latte::Dock::AutoHide)) { int realThickness = view->normalThickness() - view->effects()->innerShadow(); // Usually availableScreenRect is used by the desktop, // but Latte don't have desktop, then here just // need calculate available space for top and bottom location, // because the left and right are those who dodge others docks 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::Dock::Left: realGeometry = QRect(view->x(), view->y(), realWidth, realThickness); break; case Latte::Dock::Center: case Latte::Dock::Justify: realGeometry = QRect(qMax(view->geometry().x(), view->geometry().center().x() - realWidth / 2), view->y(), realWidth, realThickness); break; case Latte::Dock::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::Dock::Left: realGeometry = QRect(view->x(), realY, realWidth, realThickness); break; case Latte::Dock::Center: case Latte::Dock::Justify: realGeometry = QRect(qMax(view->geometry().x(), view->geometry().center().x() - realWidth / 2), realY, realWidth, realThickness); break; case Latte::Dock::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 DockCorona::availableScreenRect(int id) const +QRect Corona::availableScreenRect(int id) const { return availableScreenRectWithCriteria(id); } -QRect DockCorona::availableScreenRectWithCriteria(int id, QList modes, QList edges) const +QRect Corona::availableScreenRectWithCriteria(int id, QList modes, QList edges) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; if (m_screenPool->knownIds().contains(id)) { QString scrName = m_screenPool->connector(id); foreach (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(); QHash *views = m_layoutManager->currentLatteViews(); if (views) { 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()))))) { auto dockRect = view->absGeometry(); // 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(dockRect.bottom() + 1); break; case Plasma::Types::BottomEdge: available.setBottom(dockRect.top() - 1); break; case Plasma::Types::LeftEdge: available.setLeft(dockRect.right() + 1); break; case Plasma::Types::RightEdge: available.setRight(dockRect.left() - 1); break; default: //! bypass clang warnings break; } } } } return available; } -void DockCorona::addOutput(QScreen *screen) +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 DockCorona::primaryOutputChanged() +void Corona::primaryOutputChanged() { m_docksScreenSyncTimer.start(); } -void DockCorona::screenRemoved(QScreen *screen) +void Corona::screenRemoved(QScreen *screen) { Q_ASSERT(screen); } -void DockCorona::screenCountChanged() +void Corona::screenCountChanged() { m_docksScreenSyncTimer.start(); } //! the central functions that updates loading/unloading latteviews //! concerning screen changed (for multi-screen setups mainly) -void DockCorona::syncLatteViewsToScreens() +void Corona::syncLatteViewsToScreens() { m_layoutManager->syncLatteViewsToScreens(); } -int DockCorona::primaryScreenId() const +int Corona::primaryScreenId() const { return m_screenPool->id(qGuiApp->primaryScreen()->name()); } -void DockCorona::closeApplication() +void Corona::closeApplication() { //! this code must be called asynchronously because it is called //! also from qml (Settings window). QTimer::singleShot(5, [this]() { m_layoutManager->hideLatteSettingsDialog(); m_layoutManager->hideAllDocks(); }); //! give the time for the docks to hide themselves QTimer::singleShot(500, [this]() { qGuiApp->quit(); }); } -void DockCorona::aboutApplication() +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 DockCorona::screenForContainment(const Plasma::Containment *containment) const +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; } } QHash *views = m_layoutManager->currentLatteViews(); //if the panel views already exist, base upon them Latte::View *view = views ? views->value(containment) : nullptr; 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 DockCorona::showAlternativesForApplet(Plasma::Applet *applet) +void Corona::showAlternativesForApplet(Plasma::Applet *applet) { const QString alternativesQML = kPackage().filePath("appletalternativesui"); if (alternativesQML.isEmpty()) { return; } QHash *views = m_layoutManager->currentLatteViews(); Latte::View *latteView = (*views)[applet->containment()]; KDeclarative::QmlObject *qmlObj{nullptr}; if (latteView) { latteView->setAlternativesIsShown(true); qmlObj = new KDeclarative::QmlObject(latteView); } else { qmlObj = new KDeclarative::QmlObject(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::QmlObject *obj = it.next(); if (obj == qmlObj) { it.remove(); obj->deleteLater(); } } }); } -void DockCorona::alternativesVisibilityChanged(bool visible) +void Corona::alternativesVisibilityChanged(bool visible) { if (visible) { return; } QObject *root = sender(); QMutableListIterator it(m_alternativesObjects); while (it.hasNext()) { KDeclarative::QmlObject *obj = it.next(); if (obj->rootObject() == root) { it.remove(); obj->deleteLater(); } } } -void DockCorona::loadDefaultLayout() +void Corona::loadDefaultLayout() { qDebug() << "loading default layout"; //! Settting 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 *currentLayout = m_layoutManager->activeLayout(m_layoutManager->currentLayoutName()); if (currentLayout) { edges = currentLayout->freeEdges(defaultContainment->screen()); } if ((edges.count() > 0)) { defaultContainment->setLocation(edges.at(0)); } else { defaultContainment->setLocation(Plasma::Types::BottomEdge); } if (m_layoutManager->memoryUsage() == Dock::MultipleLayouts) { config.writeEntry("layoutId", m_layoutManager->currentLayoutName()); } defaultContainment->updateConstraints(Plasma::Types::StartupCompletedConstraint); defaultContainment->save(config); requestConfigSync(); defaultContainment->flushPendingConstraintsEvents(); emit containmentAdded(defaultContainment); emit containmentCreated(defaultContainment); //m_layoutManager->addDock(defaultContainment); defaultContainment->createApplet(QStringLiteral("org.kde.latte.plasmoid")); defaultContainment->createApplet(QStringLiteral("org.kde.plasma.analogclock")); } -QStringList DockCorona::containmentsIds() +QStringList Corona::containmentsIds() { QStringList ids; foreach (auto containment, containments()) { ids << QString::number(containment->id()); } return ids; } -QStringList DockCorona::appletsIds() +QStringList Corona::appletsIds() { QStringList ids; foreach (auto containment, containments()) { auto applets = containment->config().group("Applets"); ids << applets.groupList(); } return ids; } //! Activate launcher menu through dbus interface -void DockCorona::activateLauncherMenu() +void Corona::activateLauncherMenu() { m_globalShortcuts->activateLauncherMenu(); } -void DockCorona::windowColorScheme(QString windowIdAndScheme) +void Corona::windowColorScheme(QString windowIdAndScheme) { int firstSlash = windowIdAndScheme.indexOf("-"); QString windowIdStr = windowIdAndScheme.mid(0, firstSlash); QString schemeStr = windowIdAndScheme.mid(firstSlash + 1); m_wm->setColorSchemeForWindow(windowIdStr, schemeStr); } //! update badge for specific dock item -void DockCorona::updateDockItemBadge(QString identifier, QString value) +void Corona::updateDockItemBadge(QString identifier, QString value) { m_globalShortcuts->updateDockItemBadge(identifier, value); } -void DockCorona::switchToLayout(QString layout) +void Corona::switchToLayout(QString layout) { m_layoutManager->switchToLayout(layout); } -void DockCorona::showSettingsWindow(int page) +void Corona::showSettingsWindow(int page) { Dock::LatteConfigPage p = Dock::LayoutPage; if (page >= Dock::LayoutPage && page <= Dock::PreferencesPage) { p = static_cast(page); } m_layoutManager->showLatteSettingsDialog(p); } -QStringList DockCorona::contextMenuData() +QStringList Corona::contextMenuData() { QStringList data; data << QString::number((int)m_layoutManager->memoryUsage()); data << m_layoutManager->currentLayoutName(); foreach (auto layoutName, m_layoutManager->menuLayouts()) { if (m_layoutManager->activeLayout(layoutName)) { data << QString("1," + layoutName); } else { data << QString("0," + layoutName); } } return data; } -inline void DockCorona::qmlRegisterTypes() const +inline void Corona::qmlRegisterTypes() const { qmlRegisterType(); } } diff --git a/app/dockcorona.h b/app/lattecorona.h similarity index 94% rename from app/dockcorona.h rename to app/lattecorona.h index da63416c..e4c1b365 100644 --- a/app/dockcorona.h +++ b/app/lattecorona.h @@ -1,188 +1,188 @@ /* * 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 . */ -#ifndef DOCKCORONA_H -#define DOCKCORONA_H +#ifndef LATTECORONA_H +#define LATTECORONA_H // local #include "../liblattedock/dock.h" // Qt #include #include // Plasma #include // KDE #include namespace KDeclarative { class QmlObject; } namespace Plasma { class Corona; class Containment; class Types; } namespace PlasmaQuick { class ConfigView; } namespace KActivities { class Consumer; } namespace KWayland { namespace Client { class PlasmaShell; } } namespace Latte { class AbstractWindowInterface; class ScreenPool; class GlobalShortcuts; class UniversalSettings; class LayoutManager; class LaunchersSignals; class PlasmaThemeExtended; } namespace Latte { -class DockCorona : public Plasma::Corona +class Corona : public Plasma::Corona { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.LatteDock") public: - DockCorona(bool defaultLayoutOnStartup = false, + Corona(bool defaultLayoutOnStartup = false, QString layoutNameOnStartUp = QString(), int userSetMemoryUsage = -1, QObject *parent = nullptr); - virtual ~DockCorona(); + virtual ~Corona(); int numScreens() const override; QRect screenGeometry(int id) const override; QRegion availableScreenRegion(int id) const override; QRect availableScreenRect(int id) const override; //! This is a very generic function in order to return the availableScreenRect of specific screen //! by calculating only the user specified visibility modes and edges. Empty QLists for both //! arguments mean that all choices are accepted in calculations QRect availableScreenRectWithCriteria(int id, QList modes = QList(), QList edges = QList()) const; QRegion availableScreenRegionWithCriteria(int id, QString forLayout = QString()) const; int screenForContainment(const Plasma::Containment *containment) const override; void closeApplication(); AbstractWindowInterface *wm() const; KActivities::Consumer *activitiesConsumer() const; ScreenPool *screenPool() const; UniversalSettings *universalSettings() const; LayoutManager *layoutManager() const; PlasmaThemeExtended *themeExtended() const; - KWayland::Client::PlasmaShell *waylandDockCoronaInterface() const; + KWayland::Client::PlasmaShell *waylandCoronaInterface() const; //! these functions are used from context menu through containmentactions void switchToLayout(QString layout); void showSettingsWindow(int page); QStringList contextMenuData(); public slots: void aboutApplication(); void activateLauncherMenu(); //! they are separated with a "-" character void windowColorScheme(QString windowIdAndScheme); void loadDefaultLayout() override; void updateDockItemBadge(QString identifier, QString value); void unload(); signals: void configurationShown(PlasmaQuick::ConfigView *configView); void dockLocationChanged(); void raiseDocksTemporaryChanged(); private slots: void alternativesVisibilityChanged(bool visible); void showAlternativesForApplet(Plasma::Applet *applet); void load(); void addOutput(QScreen *screen); void primaryOutputChanged(); void screenRemoved(QScreen *screen); void screenCountChanged(); void syncLatteViewsToScreens(); private: void cleanConfig(); void qmlRegisterTypes() const; void setupWaylandIntegration(); bool appletExists(uint containmentId, uint appletId) const; bool containmentExists(uint id) const; int primaryScreenId() const; QStringList containmentsIds(); QStringList appletsIds(); bool m_activitiesStarting{true}; bool m_defaultLayoutOnStartup{false}; //! this is used to enforce loading the default layout on startup bool m_quitTimedEnded{false}; //! this is used on destructor in order to delay it and slide-out the docks //!it can be used on startup to change memory usage from command line int m_userSetMemoryUsage{ -1}; QString m_layoutNameOnStartUp; QList m_alternativesObjects; QTimer m_docksScreenSyncTimer; KActivities::Consumer *m_activityConsumer; QPointer aboutDialog; AbstractWindowInterface *m_wm{nullptr}; ScreenPool *m_screenPool{nullptr}; GlobalShortcuts *m_globalShortcuts{nullptr}; UniversalSettings *m_universalSettings{nullptr}; LayoutManager *m_layoutManager{nullptr}; PlasmaThemeExtended *m_themeExtended{nullptr}; - KWayland::Client::PlasmaShell *m_waylandDockCorona{nullptr}; + KWayland::Client::PlasmaShell *m_waylandCorona{nullptr}; friend class GlobalShortcuts; friend class Layout; friend class LayoutManager; friend class LaunchersSignals; }; } -#endif // DOCKCORONA_H +#endif // LATTECORONA_H diff --git a/app/launcherssignals.cpp b/app/launcherssignals.cpp index e073b6fa..d31eea6b 100644 --- a/app/launcherssignals.cpp +++ b/app/launcherssignals.cpp @@ -1,314 +1,314 @@ /* * 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 "launcherssignals.h" // local -#include "dockcorona.h" +#include "lattecorona.h" #include "layout.h" // Qt #include // Plasma #include #include namespace Latte { LaunchersSignals::LaunchersSignals(QObject *parent) : QObject(parent) { m_manager = qobject_cast(parent); } LaunchersSignals::~LaunchersSignals() { } QList LaunchersSignals::lattePlasmoids(QString layoutName) { QList applets; Layout *layout = m_manager->activeLayout(layoutName); QList containments; if (layoutName.isEmpty()) { containments = m_manager->corona()->containments(); } else if (layout) { containments = *(layout->containments()); } foreach (auto containment, containments) { for (auto *applet : containment->applets()) { KPluginMetaData meta = applet->kPackage().metadata(); if (meta.pluginId() == "org.kde.latte.plasmoid") { applets.append(applet); } } } return applets; } void LaunchersSignals::addLauncher(QString layoutName, int launcherGroup, QString launcher) { Dock::LaunchersGroup group = static_cast(launcherGroup); if ((Dock::LaunchersGroup)group == Dock::UniqueLaunchers) { return; } QString lName = (group == Dock::LayoutLaunchers) ? layoutName : ""; foreach (auto applet, lattePlasmoids(lName)) { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { int methodIndex = metaObject->indexOfMethod("extSignalAddLauncher(QVariant,QVariant)"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); method.invoke(item, Q_ARG(QVariant, launcherGroup), Q_ARG(QVariant, launcher)); } } } } } void LaunchersSignals::removeLauncher(QString layoutName, int launcherGroup, QString launcher) { Dock::LaunchersGroup group = static_cast(launcherGroup); if ((Dock::LaunchersGroup)group == Dock::UniqueLaunchers) { return; } QString lName = (group == Dock::LayoutLaunchers) ? layoutName : ""; foreach (auto applet, lattePlasmoids(lName)) { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { int methodIndex = metaObject->indexOfMethod("extSignalRemoveLauncher(QVariant,QVariant)"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); method.invoke(item, Q_ARG(QVariant, launcherGroup), Q_ARG(QVariant, launcher)); } } } } } void LaunchersSignals::addLauncherToActivity(QString layoutName, int launcherGroup, QString launcher, QString activity) { Dock::LaunchersGroup group = static_cast(launcherGroup); if ((Dock::LaunchersGroup)group == Dock::UniqueLaunchers) { return; } QString lName = (group == Dock::LayoutLaunchers) ? layoutName : ""; foreach (auto applet, lattePlasmoids(lName)) { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { int methodIndex = metaObject->indexOfMethod("extSignalAddLauncherToActivity(QVariant,QVariant,QVariant)"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); method.invoke(item, Q_ARG(QVariant, launcherGroup), Q_ARG(QVariant, launcher), Q_ARG(QVariant, activity)); } } } } } void LaunchersSignals::removeLauncherFromActivity(QString layoutName, int launcherGroup, QString launcher, QString activity) { Dock::LaunchersGroup group = static_cast(launcherGroup); if ((Dock::LaunchersGroup)group == Dock::UniqueLaunchers) { return; } QString lName = (group == Dock::LayoutLaunchers) ? layoutName : ""; foreach (auto applet, lattePlasmoids(lName)) { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { int methodIndex = metaObject->indexOfMethod("extSignalRemoveLauncherFromActivity(QVariant,QVariant,QVariant)"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); method.invoke(item, Q_ARG(QVariant, launcherGroup), Q_ARG(QVariant, launcher), Q_ARG(QVariant, activity)); } } } } } void LaunchersSignals::urlsDropped(QString layoutName, int launcherGroup, QStringList urls) { Dock::LaunchersGroup group = static_cast(launcherGroup); if ((Dock::LaunchersGroup)group == Dock::UniqueLaunchers) { return; } QString lName = (group == Dock::LayoutLaunchers) ? layoutName : ""; foreach (auto applet, lattePlasmoids(lName)) { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { int methodIndex = metaObject->indexOfMethod("extSignalUrlsDropped(QVariant,QVariant)"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); method.invoke(item, Q_ARG(QVariant, launcherGroup), Q_ARG(QVariant, urls)); } } } } } void LaunchersSignals::moveTask(QString layoutName, int senderId, int launcherGroup, int from, int to) { Dock::LaunchersGroup group = static_cast(launcherGroup); if ((Dock::LaunchersGroup)group == Dock::UniqueLaunchers) { return; } QString lName = (group == Dock::LayoutLaunchers) ? layoutName : ""; foreach (auto applet, lattePlasmoids(lName)) { if (applet->id() != senderId) { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { int methodIndex = metaObject->indexOfMethod("extSignalMoveTask(QVariant,QVariant,QVariant)"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); method.invoke(item, Q_ARG(QVariant, launcherGroup), Q_ARG(QVariant, from), Q_ARG(QVariant, to)); } } } } } } void LaunchersSignals::validateLaunchersOrder(QString layoutName, int senderId, int launcherGroup, QStringList launchers) { Dock::LaunchersGroup group = static_cast(launcherGroup); if ((Dock::LaunchersGroup)group == Dock::UniqueLaunchers) { return; } QString lName = (group == Dock::LayoutLaunchers) ? layoutName : ""; foreach (auto applet, lattePlasmoids(lName)) { if (applet->id() != senderId) { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { int methodIndex = metaObject->indexOfMethod("extSignalValidateLaunchersOrder(QVariant,QVariant)"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); method.invoke(item, Q_ARG(QVariant, launcherGroup), Q_ARG(QVariant, launchers)); } } } } } } } //end of namespace diff --git a/app/layout.cpp b/app/layout.cpp index 47df2824..77ae5564 100644 --- a/app/layout.cpp +++ b/app/layout.cpp @@ -1,1884 +1,1884 @@ /* * 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 "layout.h" // local -#include "dockcorona.h" #include "importer.h" +#include "lattecorona.h" #include "layoutmanager.h" #include "screenpool.h" #include "view/positioner.h" #include "view/view.h" #include "settings/universalsettings.h" // Qt #include #include #include // KDE #include #include // Plasma #include #include #include namespace Latte { const QString Layout::MultipleLayoutsName = ".multiple-layouts_hidden"; Layout::Layout(QObject *parent, QString layoutFile, QString assignedName) : QObject(parent) { qDebug() << "Layout file to create object: " << layoutFile << " with name: " << assignedName; if (QFile(layoutFile).exists()) { if (assignedName.isEmpty()) { assignedName = layoutName(layoutFile); } //!this order is important because setFile initializes also the m_layoutGroup setFile(layoutFile); setName(assignedName); loadConfig(); init(); } } Layout::~Layout() { if (!m_layoutFile.isEmpty()) { m_layoutGroup.sync(); } } void Layout::syncToLayoutFile(bool removeLayoutId) { if (!m_corona || !isWritable()) { return; } KSharedConfigPtr filePtr = KSharedConfig::openConfig(m_layoutFile); KConfigGroup oldContainments = KConfigGroup(filePtr, "Containments"); oldContainments.deleteGroup(); oldContainments.sync(); qDebug() << " LAYOUT :: " << m_layoutName << " is syncing its original file."; foreach (auto containment, m_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 Layout::unloadContainments() { if (!m_corona) { return; } //!disconnect signals in order to avoid crashes when the layout is unloading disconnect(this, &Layout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged); disconnect(this, &Layout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged); qDebug() << "Layout - " + name() + " unload: containments ... size ::: " << m_containments.size() << " ,latteViews in memory ::: " << m_latteViews.size() << " ,hidden latteViews in memory ::: " << m_waitingLatteViews.size(); foreach (auto view, m_latteViews) { view->disconnectSensitiveSignals(); } foreach (auto view, m_waitingLatteViews) { view->disconnectSensitiveSignals(); } m_unloadedContainmentsIds.clear(); QList systrays; //!identify systrays and unload them first foreach (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 Layout::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(); } void Layout::init() { connect(this, &Layout::activitiesChanged, this, &Layout::saveConfig); connect(this, &Layout::backgroundChanged, this, &Layout::saveConfig); connect(this, &Layout::versionChanged, this, &Layout::saveConfig); connect(this, &Layout::colorChanged, this, &Layout::textColorChanged); connect(this, &Layout::disableBordersForMaximizedWindowsChanged, this, &Layout::saveConfig); connect(this, &Layout::showInMenuChanged, this, &Layout::saveConfig); connect(this, &Layout::textColorChanged, this, &Layout::saveConfig); connect(this, &Layout::launchersChanged, this, &Layout::saveConfig); connect(this, &Layout::lastUsedActivityChanged, this, &Layout::saveConfig); } -void Layout::initToCorona(DockCorona *corona) +void Layout::initToCorona(Latte::Corona *corona) { m_corona = corona; foreach (auto containment, m_corona->containments()) { if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) { addContainment(containment); } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty() && (layoutId == m_layoutName)) { addContainment(containment); } } } qDebug() << "Layout ::::: " << name() << " added contaiments ::: " << m_containments.size(); connect(m_corona->universalSettings(), &UniversalSettings::canDisableBordersChanged, this, [&]() { if (m_corona->universalSettings()->canDisableBorders()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } else { kwin_setDisabledMaximizedBorders(false); } }); if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout && m_corona->universalSettings()->canDisableBorders()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { connect(m_corona->layoutManager(), &LayoutManager::currentLayoutNameChanged, this, [&]() { if (m_corona->universalSettings()->canDisableBorders() && m_corona->layoutManager()->currentLayoutName() == name()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } }); } if (m_layoutName != MultipleLayoutsName) { updateLastUsedActivity(); } connect(m_corona, &Plasma::Corona::containmentAdded, this, &Layout::addContainment); connect(m_corona->m_activityConsumer, &KActivities::Consumer::currentActivityChanged, this, &Layout::updateLastUsedActivity); //!connect signals after adding the containment connect(this, &Layout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged); connect(this, &Layout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged); emit viewsCountChanged(); } int Layout::version() const { return m_version; } void Layout::setVersion(int ver) { if (m_version == ver) { return; } m_version = ver; emit versionChanged(); } bool Layout::blockAutomaticLatteViewCreation() const { return m_blockAutomaticLatteViewCreation; } void Layout::setBlockAutomaticLatteViewCreation(bool block) { if (m_blockAutomaticLatteViewCreation == block) { return; } m_blockAutomaticLatteViewCreation = block; } bool Layout::disableBordersForMaximizedWindows() const { return m_disableBordersForMaximizedWindows; } void Layout::setDisableBordersForMaximizedWindows(bool disable) { if (m_disableBordersForMaximizedWindows == disable) { return; } m_disableBordersForMaximizedWindows = disable; kwin_setDisabledMaximizedBorders(disable); emit disableBordersForMaximizedWindowsChanged(); } bool Layout::kwin_disabledMaximizedBorders() const { //! Indentify 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 Layout::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 Layout::showInMenu() const { return m_showInMenu; } void Layout::setShowInMenu(bool show) { if (m_showInMenu == show) { return; } m_showInMenu = show; emit showInMenuChanged(); } bool Layout::isWritable() const { QFileInfo layoutFileInfo(m_layoutFile); if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) { return false; } else { return true; } } void Layout::lock() { QFileInfo layoutFileInfo(m_layoutFile); if (layoutFileInfo.exists() && layoutFileInfo.isWritable()) { QFile(m_layoutFile).setPermissions(QFileDevice::ReadUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } } void Layout::unlock() { QFileInfo layoutFileInfo(m_layoutFile); if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) { QFile(m_layoutFile).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } } QString Layout::background() const { return m_background; } void Layout::setBackground(QString path) { if (path == m_background) { return; } if (!path.isEmpty() && !QFileInfo(path).exists()) { return; } m_background = path; //! initialize the text color also if (path.isEmpty()) { setTextColor(QString()); } emit backgroundChanged(); } QString Layout::name() const { return m_layoutName; } void Layout::setName(QString name) { if (m_layoutName == name) { return; } qDebug() << "Layout name:" << name; m_layoutName = name; emit nameChanged(); } void Layout::renameLayout(QString newName) { if (m_layoutFile != Importer::layoutFilePath(newName)) { setFile(Importer::layoutFilePath(newName)); } if (m_layoutName != newName) { setName(newName); } //! thus this is a linked file if (m_corona) { foreach (auto containment, m_containments) { containment->config().writeEntry("layoutId", m_layoutName); } } } QString Layout::color() const { return m_color; } void Layout::setColor(QString color) { if (m_color == color) { return; } m_color = color; emit colorChanged(); } QString Layout::textColor() const { //! the user is in default layout theme if (m_background.isEmpty()) { if (m_color == "blue") { return "#D7E3FF"; } else if (m_color == "brown") { return "#F1DECB"; } else if (m_color == "darkgrey") { return "#ECECEC"; } else if (m_color == "gold") { return "#7C3636"; } else if (m_color == "green") { return "#4D7549"; } else if (m_color == "lightskyblue") { return "#0C2A43"; } else if (m_color == "orange") { return "#6F3902"; } else if (m_color == "pink") { return "#743C46"; } else if (m_color == "purple") { return "#ECD9FF"; } else if (m_color == "red") { return "#F3E4E4"; } else if (m_color == "wheat") { return "#6A4E25"; } else { return "#FCFCFC"; } } return "#" + m_textColor; } void Layout::setTextColor(QString color) { //! remove # if someone is trying to set it this way if (color.startsWith("#")) { color.remove(0, 1); } if (m_textColor == color) { return; } m_textColor = color; emit textColorChanged(); } QString Layout::file() const { return m_layoutFile; } void Layout::setFile(QString file) { if (m_layoutFile == file) { return; } qDebug() << "Layout file:" << file; m_layoutFile = file; KSharedConfigPtr filePtr = KSharedConfig::openConfig(m_layoutFile); m_layoutGroup = KConfigGroup(filePtr, "LayoutSettings"); emit fileChanged(); } QStringList Layout::launchers() const { return m_launchers; } void Layout::setLaunchers(QStringList launcherList) { if (m_launchers == launcherList) return; m_launchers = launcherList; emit launchersChanged(); } QStringList Layout::activities() const { return m_activities; } void Layout::setActivities(QStringList activities) { if (m_activities == activities) { return; } m_activities = activities; emit activitiesChanged(); } QStringList Layout::unloadedContainmentsIds() { return m_unloadedContainmentsIds; } bool Layout::isActiveLayout() const { if (!m_corona) { return false; } Layout *activeLayout = m_corona->layoutManager()->activeLayout(m_layoutName); if (activeLayout) { return true; } else { return false; } } bool Layout::isOriginalLayout() const { return m_layoutName != MultipleLayoutsName; } bool Layout::layoutIsBroken() const { if (m_layoutFile.isEmpty() || !QFile(m_layoutFile).exists()) { return false; } QStringList ids; QStringList conts; QStringList applets; KSharedConfigPtr lFile = KSharedConfig::openConfig(m_layoutFile); if (!m_corona) { KConfigGroup containmentsEntries = KConfigGroup(lFile, "Containments"); ids << containmentsEntries.groupList(); conts << ids; foreach (auto cId, containmentsEntries.groupList()) { auto appletsEntries = containmentsEntries.group(cId).group("Applets"); ids << appletsEntries.groupList(); applets << appletsEntries.groupList(); } } else { foreach (auto containment, m_containments) { ids << QString::number(containment->id()); conts << QString::number(containment->id()); foreach (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_layoutName << " ----"; if (!m_corona) { qDebug() << " --- file : " << m_layoutFile; } else { if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { qDebug() << " --- in multiple layouts hidden file : " << Importer::layoutFilePath(Layout::MultipleLayoutsName); } else { qDebug() << " --- in layout file : " << m_layoutFile; } } qDebug() << "Contaiments :: " << conts; qDebug() << "Applets :: " << applets; foreach (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_corona) { KConfigGroup containmentsEntries = KConfigGroup(lFile, "Containments"); foreach (auto cId, containmentsEntries.groupList()) { auto appletsEntries = containmentsEntries.group(cId).group("Applets"); qDebug() << " CONTAINMENT : " << cId << " APPLETS : " << appletsEntries.groupList(); } } else { foreach (auto containment, m_containments) { QStringList appletsIds; foreach (auto applet, containment->applets()) { appletsIds << QString::number(applet->id()); } qDebug() << " CONTAINMENT : " << containment->id() << " APPLETS : " << appletsIds.join(","); } } return true; } return false; } QString Layout::layoutName(const QString &fileName) { int lastSlash = fileName.lastIndexOf("/"); QString tempLayoutFile = fileName; QString layoutName = tempLayoutFile.remove(0, lastSlash + 1); int ext = layoutName.lastIndexOf(".layout.latte"); layoutName = layoutName.remove(ext, 13); return layoutName; } void Layout::loadConfig() { m_version = m_layoutGroup.readEntry("version", 2); m_color = m_layoutGroup.readEntry("color", QString("blue")); m_disableBordersForMaximizedWindows = m_layoutGroup.readEntry("disableBordersForMaximizedWindows", false); m_showInMenu = m_layoutGroup.readEntry("showInMenu", false); m_textColor = m_layoutGroup.readEntry("textColor", QString("fcfcfc")); m_activities = m_layoutGroup.readEntry("activities", QStringList()); m_launchers = m_layoutGroup.readEntry("launchers", QStringList()); m_lastUsedActivity = m_layoutGroup.readEntry("lastUsedActivity", QString()); QString back = m_layoutGroup.readEntry("background", ""); if (!back.isEmpty()) { if (QFileInfo(back).exists()) { m_background = back; } else { m_layoutGroup.writeEntry("background", QString()); } } emit activitiesChanged(); } void Layout::saveConfig() { qDebug() << "layout is saving... for layout:" << m_layoutName; m_layoutGroup.writeEntry("version", m_version); m_layoutGroup.writeEntry("showInMenu", m_showInMenu); m_layoutGroup.writeEntry("color", m_color); m_layoutGroup.writeEntry("disableBordersForMaximizedWindows", m_disableBordersForMaximizedWindows); m_layoutGroup.writeEntry("launchers", m_launchers); m_layoutGroup.writeEntry("background", m_background); m_layoutGroup.writeEntry("activities", m_activities); m_layoutGroup.writeEntry("lastUsedActivity", m_lastUsedActivity); m_layoutGroup.writeEntry("textColor", m_textColor); m_layoutGroup.sync(); } //! Containments Actions void Layout::addContainment(Plasma::Containment *containment) { if (!containment || m_containments.contains(containment)) { return; } bool containmentInLayout{false}; if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) { m_containments.append(containment); containmentInLayout = true; } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty() && (layoutId == m_layoutName)) { m_containments.append(containment); containmentInLayout = true; } } if (containmentInLayout) { if (!blockAutomaticLatteViewCreation()) { addDock(containment); } else { qDebug() << "delaying LatteView creation for containment :: " << containment->id(); } connect(containment, &QObject::destroyed, this, &Layout::containmentDestroyed); } } QHash *Layout::latteViews() { return &m_latteViews; } QList *Layout::containments() { return &m_containments; } const QStringList Layout::appliedActivities() { if (!m_corona) { return {}; } if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) { return {"0"}; } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { if (m_activities.isEmpty()) { return m_corona->layoutManager()->orphanedActivities(); } else { return m_activities; } } else { return {"0"}; } } QString Layout::lastUsedActivity() { return m_lastUsedActivity; } void Layout::clearLastUsedActivity() { m_lastUsedActivity = ""; emit lastUsedActivityChanged(); } void Layout::updateLastUsedActivity() { if (!m_corona) { return; } if (!m_lastUsedActivity.isEmpty() && !m_corona->layoutManager()->activities().contains(m_lastUsedActivity)) { clearLastUsedActivity(); } QString currentId = m_corona->activitiesConsumer()->currentActivity(); QStringList appliedActivitiesIds = appliedActivities(); if (m_lastUsedActivity != currentId && (appliedActivitiesIds.contains(currentId) || m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout)) { m_lastUsedActivity = currentId; emit lastUsedActivityChanged(); } } void Layout::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 Layout::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(); emit viewColorizerChanged(); } } } void Layout::addDock(Plasma::Containment *containment, bool forceOnPrimary, int expDockScreen) { qDebug() << "Layout :::: " << m_layoutName << " ::: addDock 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 (!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 && expDockScreen == -1) { id = containment->lastScreen(); } if (expDockScreen > -1) { id = expDockScreen; } qDebug() << "add dock - containment id: " << containment->id() << " ,screen : " << id << " - " << m_corona->screenPool()->connector(id) << " ,onprimary:" << onPrimary << " - " << qGuiApp->primaryScreen()->name() << " ,forceOnPrimary:" << forceOnPrimary; if (id >= 0 && !onPrimary && !forceOnPrimary) { QString connector = m_corona->screenPool()->connector(id); qDebug() << "add dock - connector : " << connector; bool found{false}; foreach (auto scr, qGuiApp->screens()) { if (scr && scr->name() == connector) { found = true; nextScreen = scr; break; } } if (!found) { qDebug() << "reject : adding explicit dock, 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() << "reject : adding explicit dock, primary dock occupies edge at screen ! : " << connector; return; } } if (id >= 0 && onPrimary) { QString connector = m_corona->screenPool()->connector(id); qDebug() << "add dock - connector : " << connector; foreach (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(); } } } /* old behavior to not add primary docks on explicit one else if (onPrimary) { if (explicitDockOccupyEdge(m_corona->screenPool()->primaryScreenId(), containment->location())) { qDebug() << "CORONA ::: adding dock rejected, the edge is occupied by explicit dock ! : " << containment->location(); //we must check that an onPrimary dock should never catch up the same edge on //the same screen with an explicit dock return; } }*/ qDebug() << "Adding dock for container..."; qDebug() << "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(Dock::DodgeActive))); bool dockWin{true}; if (mode == Dock::AlwaysVisible || mode == Dock::WindowsGoBelow) { dockWin = true; } else { dockWin = containment->config().readEntry("dockWindowBehavior", true); } auto latteView = new Latte::View(m_corona, nextScreen, dockWin); latteView->init(); latteView->setContainment(containment); latteView->setManagedLayout(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, &Layout::containmentDestroyed); connect(containment, &Plasma::Applet::destroyedChanged, this, &Layout::destroyedChanged); - connect(containment, &Plasma::Applet::locationChanged, m_corona, &DockCorona::dockLocationChanged); + connect(containment, &Plasma::Applet::locationChanged, m_corona, &Latte::Corona::dockLocationChanged); connect(containment, &Plasma::Containment::appletAlternativesRequested - , m_corona, &DockCorona::showAlternativesForApplet, Qt::QueuedConnection); + , m_corona, &Latte::Corona::showAlternativesForApplet, Qt::QueuedConnection); if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { connect(containment, &Plasma::Containment::appletCreated, this, &Layout::appletCreated); } connect(latteView->effects(), &ViewPart::Effects::colorizerEnabledChanged, this, &Layout::viewColorizerChanged); //! 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 viewColorizerChanged(); emit viewsCountChanged(); } void Layout::addNewDock() { if (!m_corona) { return; } m_corona->loadDefaultLayout(); } void Layout::copyDock(Plasma::Containment *containment) { if (!containment || !m_corona) return; qDebug() << "copying containment layout"; //! Settting mutable for create a containment m_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"); foreach (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}; foreach (auto containment, m_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 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_latteViews[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) { foreach (auto scr, screens) { copyScrId = m_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 = 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 = 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; addDock(newContainment, false, copyScrId); newContainment->reactToScreenChange(); } else { qDebug() << "Copy Dock in current screen..."; addDock(newContainment, false, dockScrId); } setBlockAutomaticLatteViewCreation(false); } void Layout::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; foreach (auto containment, m_corona->containments()) { if (containment->id() == sId) { containment->config().writeEntry("layoutId", m_layoutName); } addContainment(containment); } } } void Layout::importToCorona() { if (!m_corona) { return; } //! Settting mutable for create a containment m_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 Myltiple 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_layoutFile); 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); } QString Layout::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 Layout::newUniqueIdsLayoutFromFile(QString file) { if (!m_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_corona->containmentsIds(); allIds << m_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 foreach (auto cId, investigate_conts.groupList()) { toInvestigateContainmentIds << cId; auto appletsEntries = investigate_conts.group(cId).group("Applets"); toInvestigateAppletIds << appletsEntries.groupList(); //! investigate for systrays foreach (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 foreach (auto contId, toInvestigateContainmentIds) { QString newId = availableId(allIds, assignedIds, 12); assignedIds << newId; assigned[contId] = newId; } foreach (auto appId, toInvestigateAppletIds) { QString newId = availableId(allIds, assignedIds, 40); assignedIds << newId; assigned[appId] = newId; } qDebug() << "ALL CORONA IDS ::: " << allIds; qDebug() << "FULL ASSIGNMENTS ::: " << assigned; foreach (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; } } } foreach (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 contaiment order and in MultipleLayouts update also the layoutId foreach (auto cId, investigate_conts.groupList()) { //! Update (appletOrder) and (lockedZoomApplets) for (int i = 1; i <= 2; ++i) { QString settingStr = (i == 1) ? "appletOrder" : "lockedZoomApplets"; 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_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { investigate_conts.group(cId).writeEntry("layoutId", m_layoutName); } } //! must update also the systray id in its applet foreach (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"); foreach (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(); foreach (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; } QList Layout::importLayoutFile(QString file) { KSharedConfigPtr filePtr = KSharedConfig::openConfig(file); auto newContainments = m_corona->importLayout(KConfigGroup(filePtr, "")); ///Find latte and systray containments qDebug() << " imported containments ::: " << newContainments.length(); QList importedDocks; //QList systrays; foreach (auto containment, newContainments) { if (isLatteContainment(containment)) { qDebug() << "new latte containment id: " << containment->id(); importedDocks << containment; } } ///after systrays were found we must update in latte the relevant ids /*if (!systrays.isEmpty()) { foreach (auto systray, systrays) { qDebug() << "systray found with id : " << systray->id(); Plasma::Applet *parentApplet = qobject_cast(systray->parent()); if (parentApplet) { KConfigGroup appletSettings = parentApplet->config().group("Configuration"); if (appletSettings.hasKey("SystrayContainmentId")) { qDebug() << "!!! updating systray id to : " << systray->id(); appletSettings.writeEntry("SystrayContainmentId", systray->id()); } } } }*/ return importedDocks; } void Layout::recreateDock(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(); addDock(containment); } }); }); view->deleteLater(); } }); } //! the central functions that updates loading/unloading latteviews //! concerning screen changed (for multi-screen setups mainly) void Layout::syncLatteViewsToScreens() { if (!m_corona) { return; } qDebug() << "start of, syncLatteViewsToScreens ...."; qDebug() << "LAYOUT ::: " << name(); qDebug() << "screen count changed -+-+ " << qGuiApp->screens().size(); QHash> futureDocksLocations; QList futureShownViews; QString prmScreenName = qGuiApp->primaryScreen()->name(); //! first step: primary docks must be placed in primary screen free edges foreach (auto containment, m_containments) { if (isLatteContainment(containment)) { int screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } bool onPrimary = containment->config().readEntry("onPrimary", true); Plasma::Types::Location location = static_cast((int)containment->config().readEntry("location", (int)Plasma::Types::BottomEdge)); if (onPrimary && !futureDocksLocations[prmScreenName].contains(location)) { futureDocksLocations[prmScreenName].append(location); futureShownViews.append(containment->id()); } } } //! second step: explicit docks must be placed in their screens if the screen edge is free foreach (auto containment, m_containments) { if (isLatteContainment(containment)) { int screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } bool onPrimary = containment->config().readEntry("onPrimary", true); Plasma::Types::Location location = static_cast((int)containment->config().readEntry("location", (int)Plasma::Types::BottomEdge)); if (!onPrimary) { QString expScreenName = m_corona->screenPool()->connector(screenId); if (m_corona->screenPool()->screenExists(screenId) && !futureDocksLocations[expScreenName].contains(location)) { futureDocksLocations[expScreenName].append(location); futureShownViews.append(containment->id()); } } } } qDebug() << "PRIMARY SCREEN :: " << prmScreenName; qDebug() << "LATTEVIEWS MUST BE PRESENT AT :: " << futureDocksLocations; qDebug() << "FUTURESHOWNVIEWS MUST BE :: " << futureShownViews; //! add views foreach (auto containment, m_containments) { int screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } if (!latteViewExists(containment) && futureShownViews.contains(containment->id())) { qDebug() << "syncLatteViewsToScreens: view must be added... for containment:" << containment->id() << " at screen:" << m_corona->screenPool()->connector(screenId); addDock(containment); } } //! remove views foreach (auto view, m_latteViews) { if (view->containment() && !futureShownViews.contains(view->containment()->id())) { qDebug() << "syncLatteViewsToScreens: view must be deleted... for containment:" << view->containment()->id() << " at screen:" << view->positioner()->currentScreenName(); auto viewToDelete = m_latteViews.take(view->containment()); viewToDelete->disconnectSensitiveSignals(); viewToDelete->deleteLater(); } } //! reconsider views foreach (auto view, m_latteViews) { if (view->containment() && futureShownViews.contains(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 view->reconsiderScreen(); } } qDebug() << "end of, syncLatteViewsToScreens ...."; } void Layout::assignToLayout(Latte::View *latteView, QList containments) { if (!m_corona) { return; } if (latteView) { m_latteViews[latteView->containment()] = latteView; m_containments << containments; foreach (auto containment, containments) { containment->config().writeEntry("layoutId", name()); connect(containment, &QObject::destroyed, this, &Layout::containmentDestroyed); connect(containment, &Plasma::Applet::destroyedChanged, this, &Layout::destroyedChanged); connect(containment, &Plasma::Containment::appletCreated, this, &Layout::appletCreated); } latteView->setManagedLayout(this); emit viewsCountChanged(); } //! sync the original layout file for integrity if (m_corona && m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { syncToLayoutFile(false); } } QList Layout::unassignFromLayout(Latte::View *latteView) { QList containments; if (!m_corona) { return containments; } containments << latteView->containment(); foreach (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, &Layout::containmentDestroyed); disconnect(containment, &Plasma::Applet::destroyedChanged, this, &Layout::destroyedChanged); disconnect(containment, &Plasma::Containment::appletCreated, this, &Layout::appletCreated); } } foreach (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->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { syncToLayoutFile(false); } return containments; } bool Layout::latteViewExists(Plasma::Containment *containment) { if (!m_corona) { return false; } return m_latteViews.keys().contains(containment); } QList Layout::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; } foreach (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; } QList Layout::qmlFreeEdges(int screen) const { if (!m_corona) { const QList emptyEdges; return emptyEdges; } const auto edges = freeEdges(screen); QList edgesInt; foreach (Plasma::Types::Location edge, edges) { edgesInt.append(static_cast(edge)); } return edgesInt; } QList Layout::freeEdges(QScreen *scr) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } foreach (auto view, m_latteViews) { if (view && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } QList Layout::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); foreach (auto view, m_latteViews) { if (view && scr && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } bool Layout::explicitDockOccupyEdge(int screen, Plasma::Types::Location location) const { if (!m_corona) { return false; } foreach (auto containment, m_containments) { if (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 Layout::primaryDockOccupyEdge(Plasma::Types::Location location) const { if (!m_corona) { return false; } foreach (auto containment, m_containments) { if (isLatteContainment(containment)) { bool onPrimary = containment->config().readEntry("onPrimary", true); Plasma::Types::Location contLocation = containment->location(); if (onPrimary && contLocation == location) { return true; } } } return false; } bool Layout::isLatteContainment(Plasma::Containment *containment) const { if (!containment) { return false; } if (containment->pluginMetaData().pluginId() == "org.kde.latte.containment") { return true; } return false; } int Layout::viewsWithTasks() const { if (!m_corona) { return 0; } int result = 0; foreach (auto view, m_latteViews) { if (view->tasksPresent()) { result++; } } return result; } int Layout::viewsCount(int screen) const { if (!m_corona) { return 0; } QScreen *scr = m_corona->screenPool()->screenForId(screen); int docks{0}; foreach (auto view, m_latteViews) { if (view && view->screen() == scr && !view->containment()->destroyed()) { ++docks; } } return docks; } int Layout::viewsCount(QScreen *screen) const { if (!m_corona) { return 0; } int docks{0}; foreach (auto view, m_latteViews) { if (view && view->screen() == screen && !view->containment()->destroyed()) { ++docks; } } return docks; } int Layout::viewsCount() const { if (!m_corona) { return 0; } int docks{0}; foreach (auto view, m_latteViews) { if (view && view->containment() && !view->containment()->destroyed()) { ++docks; } } return docks; } } diff --git a/app/layout.h b/app/layout.h index 3a443439..2d57f425 100644 --- a/app/layout.h +++ b/app/layout.h @@ -1,256 +1,256 @@ /* * 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 LAYOUT_H #define LAYOUT_H // Qt #include #include // KDE #include #include // Plasma #include namespace Plasma { class Applet; class Containment; class Types; } namespace Latte { -class DockCorona; +class Corona; class View; } namespace Latte { //! This class is responsible to hold the settings for a specific layout. //! It also updates always the relevant layout configuration concerning //! its general settings (no the containments) class Layout : public QObject { Q_OBJECT Q_PROPERTY(bool showInMenu READ showInMenu WRITE setShowInMenu NOTIFY showInMenuChanged) Q_PROPERTY(int viewsCount READ viewsCount NOTIFY viewsCountChanged) Q_PROPERTY(QString background READ background NOTIFY backgroundChanged) Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QString lastUsedActivity READ lastUsedActivity NOTIFY lastUsedActivityChanged) Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString textColor READ textColor NOTIFY textColorChanged) Q_PROPERTY(QStringList launchers READ launchers WRITE setLaunchers NOTIFY launchersChanged) Q_PROPERTY(QStringList activities READ activities WRITE setActivities NOTIFY activitiesChanged) public: Layout(QObject *parent, QString layoutFile, QString layoutName = QString()); ~Layout() override; static const QString MultipleLayoutsName; - void initToCorona(DockCorona *corona); + void initToCorona(Latte::Corona *corona); void syncToLayoutFile(bool removeLayoutId = false); void unloadContainments(); void unloadLatteViews(); bool disableBordersForMaximizedWindows() const; void setDisableBordersForMaximizedWindows(bool disable); bool showInMenu() const; void setShowInMenu(bool show); bool layoutIsBroken() const; //!this layout is loaded and running bool isActiveLayout() const; //!it is original layout compared to pseudo-layouts that are combinations of multiple-original layouts bool isOriginalLayout() const; bool isWritable() const; bool latteViewExists(Plasma::Containment *containment); int version() const; void setVersion(int ver); QString background() const; void setBackground(QString path); QString color() const; void setColor(QString color); QString lastUsedActivity(); void clearLastUsedActivity(); //!e.g. when we export a layout QString name() const; QString file() const; QString textColor() const; void setTextColor(QString color); QStringList activities() const; void setActivities(QStringList activities); QStringList launchers() const; void setLaunchers(QStringList launcherList); static QString layoutName(const QString &fileName); void renameLayout(QString newName); QStringList unloadedContainmentsIds(); //! this function needs the layout to have first set the corona through initToCorona() function void addDock(Plasma::Containment *containment, bool forceOnPrimary = false, int expDockScreen = -1); void copyDock(Plasma::Containment *containment); void recreateDock(Plasma::Containment *containment); void syncLatteViewsToScreens(); void importToCorona(); const QStringList appliedActivities(); QList *containments(); QHash *latteViews(); //! Bind this latteView and its relevant containments(including systrays) //! to this layout. It is used for moving a Latte::View from layout to layout) void assignToLayout(Latte::View *latteView, QList containments); //! Unassign that latteView from this layout (this is used for moving a latteView //! from layout to layout) and returns all the containments relevant to //! that latteView QList unassignFromLayout(Latte::View *latteView); //! Available edges for specific view in that screen QList availableEdgesForView(QScreen *scr, Latte::View *forView) const; //! All free edges in that screen QList freeEdges(QScreen *scr) const; QList freeEdges(int screen) const; //! make it only read-only void lock(); //! make it writable which it should be the default void unlock(); int viewsCount(int screen) const; int viewsCount(QScreen *screen) const; int viewsCount() const; public slots: Q_INVOKABLE int viewsWithTasks() const; //change to types Q_INVOKABLE QList qmlFreeEdges(int screen) const; Q_INVOKABLE void addNewDock(); signals: void activitiesChanged(); void backgroundChanged(); void colorChanged(); void disableBordersForMaximizedWindowsChanged(); void fileChanged(); void lastUsedActivityChanged(); void launchersChanged(); void nameChanged(); void versionChanged(); void showInMenuChanged(); void textColorChanged(); void viewColorizerChanged(); void viewsCountChanged(); //! used from LatteView(s) in order to exist only one each time that has the highest priority //! to use the global shortcuts activations void preferredViewForShortcutsChanged(Latte::View *view); private slots: void loadConfig(); void saveConfig(); void addContainment(Plasma::Containment *containment); void appletCreated(Plasma::Applet *applet); void destroyedChanged(bool destroyed); void containmentDestroyed(QObject *cont); void updateLastUsedActivity(); private: void importLocalLayout(QString file); void init(); void setName(QString name); void setFile(QString file); //! It can be used in order for LatteViews to not be created automatically when //! their corresponding containments are created e.g. copyDock functionality bool blockAutomaticLatteViewCreation() const; void setBlockAutomaticLatteViewCreation(bool block); bool explicitDockOccupyEdge(int screen, Plasma::Types::Location location) const; bool primaryDockOccupyEdge(Plasma::Types::Location location) const; //! Check if a containment is a latte dock/panel bool isLatteContainment(Plasma::Containment *containment) const; bool kwin_disabledMaximizedBorders() const; void kwin_setDisabledMaximizedBorders(bool disable); QString availableId(QStringList all, QStringList assigned, int base); //! provides a new file path based the provided file. The new file //! has updated ids for containments and applets based on the corona //! loaded ones QString newUniqueIdsLayoutFromFile(QString file); //! imports a layout file and returns the containments for the docks QList importLayoutFile(QString file); private: bool m_blockAutomaticLatteViewCreation{false}; bool m_disableBordersForMaximizedWindows{false}; bool m_showInMenu{false}; //if version doesn't exist it is and old layout file int m_version{2}; QString m_background; QString m_color; QString m_lastUsedActivity; //the last used activity for this layout QString m_layoutFile; QString m_layoutName; QString m_textColor; QStringList m_activities; QStringList m_launchers; QStringList m_unloadedContainmentsIds; - DockCorona *m_corona{nullptr}; + Latte::Corona *m_corona{nullptr}; KConfigGroup m_layoutGroup; QList m_containments; QHash m_latteViews; QHash m_waitingLatteViews; }; } #endif // LAYOUT_H diff --git a/app/layoutmanager.cpp b/app/layoutmanager.cpp index d2374edd..c6f7e98c 100644 --- a/app/layoutmanager.cpp +++ b/app/layoutmanager.cpp @@ -1,1216 +1,1216 @@ /* * Copyright 2017 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "layoutmanager.h" // local #include "importer.h" #include "infoview.h" #include "launcherssignals.h" #include "layout.h" #include "screenpool.h" #include "settings/settingsdialog.h" #include "settings/universalsettings.h" #include "view/view.h" // Qt #include #include #include #include #include // KDE #include #include #include #include namespace Latte { const int MultipleLayoutsPresetId = 10; LayoutManager::LayoutManager(QObject *parent) : QObject(parent), m_importer(new Importer(this)), m_launchersSignals(new LaunchersSignals(this)), m_activitiesController(new KActivities::Controller(this)) { - m_corona = qobject_cast(parent); + m_corona = qobject_cast(parent); if (m_corona) { connect(m_corona->universalSettings(), &UniversalSettings::currentLayoutNameChanged, this, &LayoutManager::currentLayoutNameChanged); connect(m_corona->universalSettings(), &UniversalSettings::showInfoWindowChanged, this, &LayoutManager::showInfoWindowChanged); m_dynamicSwitchTimer.setSingleShot(true); showInfoWindowChanged(); connect(&m_dynamicSwitchTimer, &QTimer::timeout, this, &LayoutManager::confirmDynamicSwitch); } } LayoutManager::~LayoutManager() { m_importer->deleteLater(); m_launchersSignals->deleteLater(); while (!m_activeLayouts.isEmpty()) { Layout *layout = m_activeLayouts.at(0); m_activeLayouts.removeFirst(); layout->unloadContainments(); layout->unloadLatteViews(); layout->deleteLater(); } m_activitiesController->deleteLater(); } void LayoutManager::load() { int configVer = m_corona->universalSettings()->version(); qDebug() << "Universal Settings version : " << configVer; if (configVer < 2 && QFile(QDir::homePath() + "/.config/lattedockrc").exists()) { qDebug() << "Latte must update its configuration..."; m_importer->updateOldConfiguration(); importPresets(false); } else if (!QFile(QDir::homePath() + "/.config/lattedockrc").exists()) { //startup create what is necessary.... QDir layoutDir(QDir::homePath() + "/.config/latte"); if (!layoutDir.exists()) { QDir(QDir::homePath() + "/.config").mkdir("latte"); } newLayout(i18n("My Layout")); importPresets(false); m_corona->universalSettings()->setCurrentLayoutName(i18n("My Layout")); m_corona->universalSettings()->setVersion(2); } //! Check if the multiple-layouts hidden file is present, add it if it isnt if (!QFile(QDir::homePath() + "/.config/latte/" + Layout::MultipleLayoutsName + ".layout.latte").exists()) { importPreset(MultipleLayoutsPresetId, false); } qDebug() << "Latte is loading its layouts..."; connect(m_corona->m_activityConsumer, &KActivities::Consumer::currentActivityChanged, this, &LayoutManager::currentActivityChanged); connect(m_corona->m_activityConsumer, &KActivities::Consumer::runningActivitiesChanged, this, [&]() { if (memoryUsage() == Dock::MultipleLayouts) { syncMultipleLayoutsToActivities(); } }); loadLayouts(); } void LayoutManager::unload() { //! Unload all Layouts foreach (auto layout, m_activeLayouts) { if (memoryUsage() == Dock::MultipleLayouts && layout->isOriginalLayout()) { layout->syncToLayoutFile(true); } layout->unloadContainments(); layout->unloadLatteViews(); if (memoryUsage() == Dock::MultipleLayouts && layout->isOriginalLayout()) { clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds()); } layout->deleteLater(); } //! Cleanup pseudo-layout from Containments if (memoryUsage() == Dock::MultipleLayouts) { // auto containmentsEntries = m_corona->config()->group("Containments"); // containmentsEntries.deleteGroup(); // containmentsEntries.sync(); } //! 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(); } -DockCorona *LayoutManager::corona() +Latte::Corona *LayoutManager::corona() { return m_corona; } Importer *LayoutManager::importer() { return m_importer; } LaunchersSignals *LayoutManager::launchersSignals() { return m_launchersSignals; } QString LayoutManager::currentLayoutName() const { if (memoryUsage() == Dock::SingleLayout) { return m_corona->universalSettings()->currentLayoutName(); } else if (memoryUsage() == Dock::MultipleLayouts) { return m_currentLayoutNameInMultiEnvironment; } return QString(); } QString LayoutManager::defaultLayoutName() const { QByteArray presetNameOrig = QString("preset" + QString::number(1)).toUtf8(); QString presetPath = m_corona->kPackage().filePath(presetNameOrig); QString presetName = Layout::layoutName(presetPath); QByteArray presetNameChars = presetName.toUtf8(); presetName = i18n(presetNameChars); return presetName; } bool LayoutManager::hasColorizer() const { foreach (auto layout, m_activeLayouts) { for (const auto *view : *layout->latteViews()) { if (view->effects()->colorizerEnabled()) { return true; } } } return false; } bool LayoutManager::layoutExists(QString layoutName) const { return m_layouts.contains(layoutName); } QStringList LayoutManager::layouts() const { return m_layouts; } QStringList LayoutManager::menuLayouts() const { QStringList fixedMenuLayouts = m_menuLayouts; //! in case the current layout isnt checked to be shown in the menus //! we must add it on top if (!fixedMenuLayouts.contains(currentLayoutName()) && memoryUsage() == Dock::SingleLayout) { fixedMenuLayouts.prepend(currentLayoutName()); } else if (memoryUsage() == Dock::MultipleLayouts) { foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout() && !fixedMenuLayouts.contains(layout->name())) { fixedMenuLayouts.prepend(layout->name()); } } } return fixedMenuLayouts; } void LayoutManager::setMenuLayouts(QStringList layouts) { if (m_menuLayouts == layouts) { return; } m_menuLayouts = layouts; emit menuLayoutsChanged(); } QStringList LayoutManager::activities() { return m_corona->m_activityConsumer->activities(); } QStringList LayoutManager::runningActivities() { return m_corona->m_activityConsumer->runningActivities(); } QStringList LayoutManager::orphanedActivities() { QStringList orphans; foreach (auto activity, activities()) { if (m_assignedLayouts[activity].isEmpty()) { orphans.append(activity); } } return orphans; } QStringList LayoutManager::presetsPaths() const { return m_presetsPaths; } QString LayoutManager::layoutPath(QString layoutName) { QString path = QDir::homePath() + "/.config/latte/" + layoutName + ".layout.latte"; if (!QFile(path).exists()) { path = ""; } return path; } Dock::LayoutsMemoryUsage LayoutManager::memoryUsage() const { return m_corona->universalSettings()->layoutsMemoryUsage(); } int LayoutManager::layoutsMemoryUsage() { return (int)m_corona->universalSettings()->layoutsMemoryUsage(); } void LayoutManager::setMemoryUsage(Dock::LayoutsMemoryUsage memoryUsage) { m_corona->universalSettings()->setLayoutsMemoryUsage(memoryUsage); } void LayoutManager::addDock(Plasma::Containment *containment, bool forceLoading, int expDockScreen) { if (memoryUsage() == Dock::SingleLayout) { m_activeLayouts.at(0)->addDock(containment, forceLoading, expDockScreen); } else if (memoryUsage() == Dock::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty()) { auto layout = activeLayout(layoutId); if (layout) { layout->addDock(containment, forceLoading, expDockScreen); } } } } bool LayoutManager::latteViewExists(Latte::View *view) const { foreach (auto layout, m_activeLayouts) { for (auto it = layout->latteViews()->constBegin(), end = layout->latteViews()->constEnd(); it != end; ++it) { if (it.value() == view) { return true; } } } return false; } QHash *LayoutManager::currentLatteViews() const { if (memoryUsage() == Dock::SingleLayout) { return m_activeLayouts.at(0)->latteViews(); } else { foreach (auto layout, m_activeLayouts) { if (layout->activities().contains(m_corona->m_activityConsumer->currentActivity())) { return layout->latteViews(); } } foreach (auto layout, m_activeLayouts) { if ((layout->name() != Layout::MultipleLayoutsName) && (layout->activities().isEmpty())) { return layout->latteViews(); } } } return nullptr; } QHash *LayoutManager::layoutLatteViews(const QString &layoutName) const { Layout *layout = activeLayout(layoutName); if (layout) { return layout->latteViews(); } return nullptr; } QStringList LayoutManager::activeLayoutsNames() { QStringList names; if (memoryUsage() == Dock::SingleLayout) { names << currentLayoutName(); } else { for (int i = 0; i < m_activeLayouts.size(); ++i) { Layout *layout = m_activeLayouts.at(i); if (layout->isOriginalLayout()) { names << layout->name(); } } } return names; } Layout *LayoutManager::activeLayout(QString id) const { for (int i = 0; i < m_activeLayouts.size(); ++i) { Layout *layout = m_activeLayouts.at(i); if (layout->name() == id) { return layout; } } return nullptr; } int LayoutManager::activeLayoutPos(QString id) const { for (int i = 0; i < m_activeLayouts.size(); ++i) { Layout *layout = m_activeLayouts.at(i); if (layout->name() == id) { return i; } } return -1; } void LayoutManager::updateCurrentLayoutNameInMultiEnvironment() { foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout() && layout->activities().contains(m_corona->activitiesConsumer()->currentActivity())) { m_currentLayoutNameInMultiEnvironment = layout->name(); emit currentLayoutNameChanged(); return; } } foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout() && layout->activities().isEmpty()) { m_currentLayoutNameInMultiEnvironment = layout->name(); emit currentLayoutNameChanged(); return; } } } void LayoutManager::currentActivityChanged(const QString &id) { if (memoryUsage() == Dock::SingleLayout) { qDebug() << "activity changed :: " << id; m_shouldSwitchToLayout = shouldSwitchToLayout(id); m_dynamicSwitchTimer.start(); } else if (memoryUsage() == Dock::MultipleLayouts) { updateCurrentLayoutNameInMultiEnvironment(); } } void LayoutManager::showInfoWindowChanged() { if (m_corona->universalSettings()->showInfoWindow()) { m_dynamicSwitchTimer.setInterval(1800); } else { m_dynamicSwitchTimer.setInterval(2300); } } QString LayoutManager::shouldSwitchToLayout(QString activityId) { if (m_assignedLayouts.contains(activityId) && m_assignedLayouts[activityId] != currentLayoutName()) { return m_assignedLayouts[activityId]; } else if (!m_assignedLayouts.contains(activityId) && !m_corona->universalSettings()->lastNonAssignedLayoutName().isEmpty() && m_corona->universalSettings()->lastNonAssignedLayoutName() != currentLayoutName()) { return m_corona->universalSettings()->lastNonAssignedLayoutName(); } return QString(); } void LayoutManager::confirmDynamicSwitch() { QString tempShouldSwitch = shouldSwitchToLayout(m_corona->m_activityConsumer->currentActivity()); if (tempShouldSwitch.isEmpty()) { return; } if (m_shouldSwitchToLayout == tempShouldSwitch && m_shouldSwitchToLayout != currentLayoutName()) { qDebug() << "dynamic switch to layout :: " << m_shouldSwitchToLayout; emit currentLayoutIsSwitching(currentLayoutName()); if (m_corona->universalSettings()->showInfoWindow()) { showInfoWindow(i18n("Switching to layout %0 ...").arg(m_shouldSwitchToLayout), 4000); } QTimer::singleShot(500, [this, tempShouldSwitch]() { switchToLayout(tempShouldSwitch); }); } else { m_shouldSwitchToLayout = tempShouldSwitch; m_dynamicSwitchTimer.start(); } } void LayoutManager::loadLayouts() { m_layouts.clear(); m_menuLayouts.clear(); m_presetsPaths.clear(); m_assignedLayouts.clear(); QDir layoutDir(QDir::homePath() + "/.config/latte"); QStringList filter; filter.append(QString("*.layout.latte")); QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); foreach (auto layout, files) { Layout layoutSets(this, layoutDir.absolutePath() + "/" + layout); QStringList validActivityIds = validActivities(layoutSets.activities()); layoutSets.setActivities(validActivityIds); foreach (auto activity, validActivityIds) { m_assignedLayouts[activity] = layoutSets.name(); } m_layouts.append(layoutSets.name()); if (layoutSets.showInMenu()) { m_menuLayouts.append(layoutSets.name()); } } m_presetsPaths.append(m_corona->kPackage().filePath("preset1")); m_presetsPaths.append(m_corona->kPackage().filePath("preset2")); m_presetsPaths.append(m_corona->kPackage().filePath("preset3")); m_presetsPaths.append(m_corona->kPackage().filePath("preset4")); emit layoutsChanged(); emit menuLayoutsChanged(); } void LayoutManager::loadLayoutOnStartup(QString layoutName) { // if (memoryUsage() == Dock::MultipleLayouts) { QStringList layouts = m_importer->checkRepairMultipleLayoutsLinkedFile(); //! Latte didn't close correctly, maybe a crash if (layouts.size() > 0) { QMessageBox *msg = new QMessageBox(); msg->setAttribute(Qt::WA_DeleteOnClose); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Multiple Layouts Warning")); msg->setText(i18n("Latte did not close properly in the previous session. The following layout(s) [%0] were updated for consistency!!!").arg(layouts.join(","))); msg->setStandardButtons(QMessageBox::Ok); msg->open(); } //} switchToLayout(layoutName); } void LayoutManager::loadLatteLayout(QString layoutPath) { qDebug() << " -------------------------------------------------------------------- "; qDebug() << " -------------------------------------------------------------------- "; if (m_corona->containments().size() > 0) { qDebug() << "LOAD LATTE LAYOUT ::: There are still containments present !!!! :: " << m_corona->containments().size(); } if (!layoutPath.isEmpty() && m_corona->containments().size() == 0) { cleanupOnStartup(layoutPath); qDebug() << "LOADING CORONA LAYOUT:" << layoutPath; m_corona->loadLayout(layoutPath); //! ~~~ ADDING LATTEVIEWS AND ENFORCE LOADING IF TASKS ARENT PRESENT BASED ON SCREENS ~~~ !// //! this is used to record the first dock having tasks in it. It is used //! to specify which dock will be loaded on startup if a case that no "dock //! with tasks" will be loaded otherwise. Currently the older one dock wins /*int firstContainmentWithTasks = -1; //! this is used to check if a dock with tasks in it will be loaded on startup bool tasksWillBeLoaded = heuresticForLoadingDockWithTasks(&firstContainmentWithTasks); qDebug() << "TASKS WILL BE PRESENT AFTER LOADING ::: " << tasksWillBeLoaded; foreach (auto containment, m_corona->containments()) { //! forceDockLoading is used when a latte configuration based on the //! current running screens does not provide a dock containing tasks. //! in such case the lowest latte containment containing tasks is loaded //! and it forcefully becomes primary dock if (!tasksWillBeLoaded && firstContainmentWithTasks == containment->id()) { tasksWillBeLoaded = true; //this protects by loading more than one dock at startup addDock(containment, true); } else { addDock(containment); } }*/ } } void LayoutManager::cleanupOnStartup(QString path) { KSharedConfigPtr filePtr = KSharedConfig::openConfig(path); KConfigGroup actionGroups = KConfigGroup(filePtr, "ActionPlugins"); QStringList deprecatedActionGroup; foreach (auto actId, actionGroups.groupList()) { QString pluginId = actionGroups.group(actId).readEntry("RightButton;NoModifier", ""); if (pluginId == "org.kde.contextmenu") { deprecatedActionGroup << actId; } } foreach (auto pId, deprecatedActionGroup) { qDebug() << "!!!!!!!!!!!!!!!! !!!!!!!!!!!! !!!!!!! REMOVING :::: " << pId; actionGroups.group(pId).deleteGroup(); } KConfigGroup containmentGroups = KConfigGroup(filePtr, "Containments"); QStringList removeContaimentsList; foreach (auto cId, containmentGroups.groupList()) { QString pluginId = containmentGroups.group(cId).readEntry("plugin", ""); if (pluginId == "org.kde.desktopcontainment") { //!must remove ghost containments first removeContaimentsList << cId; } } foreach (auto cId, removeContaimentsList) { containmentGroups.group(cId).deleteGroup(); } actionGroups.sync(); containmentGroups.sync(); } void LayoutManager::showAboutDialog() { m_corona->aboutApplication(); } void LayoutManager::importLatteLayout(QString layoutPath) { //! This might not be needed as it is Layout responsibility } void LayoutManager::hideAllDocks() { foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout()) { emit currentLayoutIsSwitching(layout->name()); } } } void LayoutManager::addLayout(Layout *layout) { if (!m_activeLayouts.contains(layout)) { m_activeLayouts.append(layout); layout->initToCorona(m_corona); connect(layout, &Layout::viewColorizerChanged, this, &LayoutManager::viewColorizerChanged); } } bool LayoutManager::switchToLayout(QString layoutName, int previousMemoryUsage) { if (m_activeLayouts.size() > 0 && currentLayoutName() == layoutName && previousMemoryUsage == -1) { return false; } //! First Check If that Layout is already present if (memoryUsage() == Dock::MultipleLayouts && previousMemoryUsage == -1) { Layout *layout = activeLayout(layoutName); if (layout) { QStringList appliedActivities = layout->appliedActivities(); QString nextActivity = !layout->lastUsedActivity().isEmpty() ? layout->lastUsedActivity() : appliedActivities[0]; //! it means we are at a foreign activity if (!appliedActivities.contains(m_corona->activitiesConsumer()->currentActivity())) { m_activitiesController->setCurrentActivity(nextActivity); return true; } } } //! When going from memory usage to different memory usage we first //! send the layouts that will be changed. This signal creates the //! nice animation that hides these docks/panels if (previousMemoryUsage != -1) { foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout()) { 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() == Dock::SingleLayout) { emit currentLayoutIsSwitching(currentLayoutName()); } else if (memoryUsage() == Dock::MultipleLayouts && layoutName != Layout::MultipleLayoutsName) { Layout toLayout(this, lPath); QStringList toActivities = toLayout.activities(); Layout *activeForOrphans{nullptr}; foreach (auto fromLayout, m_activeLayouts) { if (fromLayout->isOriginalLayout() && fromLayout->activities().isEmpty()) { activeForOrphans = fromLayout; break; } } if (toActivities.isEmpty() && activeForOrphans && (toLayout.name() != activeForOrphans->name())) { emit currentLayoutIsSwitching(activeForOrphans->name()); } } //! this code must be called asynchronously because it is called //! also from qml (Tasks plasmoid). This change fixes a very important //! crash when switching sessions through the Tasks plasmoid Context menu //! Latte was unstable and was crashing very often during changing //! sessions. QTimer::singleShot(350, [this, layoutName, lPath, previousMemoryUsage]() { qDebug() << layoutName << " - " << lPath; QString fixedLPath = lPath; QString fixedLayoutName = layoutName; bool initializingMultipleLayouts{false}; if (memoryUsage() == Dock::MultipleLayouts && !activeLayout(Layout::MultipleLayoutsName)) { initializingMultipleLayouts = true; } if (memoryUsage() == Dock::SingleLayout || initializingMultipleLayouts || previousMemoryUsage == Dock::MultipleLayouts) { while (!m_activeLayouts.isEmpty()) { Layout *layout = m_activeLayouts.at(0); m_activeLayouts.removeFirst(); if (layout->isOriginalLayout() && previousMemoryUsage == Dock::MultipleLayouts) { layout->syncToLayoutFile(true); } layout->unloadContainments(); layout->unloadLatteViews(); if (layout->isOriginalLayout() && previousMemoryUsage == Dock::MultipleLayouts) { clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds(), true); } delete layout; } if (initializingMultipleLayouts) { fixedLayoutName = QString(Layout::MultipleLayoutsName); fixedLPath = layoutPath(fixedLayoutName); } Layout *newLayout = new Layout(this, fixedLPath, fixedLayoutName); addLayout(newLayout); loadLatteLayout(fixedLPath); emit activeLayoutsChanged(); } if (memoryUsage() == Dock::MultipleLayouts) { if (!initializingMultipleLayouts && !activeLayout(layoutName)) { //! When we are in Multiple Layouts Environment and the user activates //! a Layout that is assigned to specific activities but this //! layout isnt loaded (this means neither of its activities are running) //! is such case we just activate these Activities Layout 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) { foreach (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 foreach (auto assignedActivity, assignedActivities) { if (lastUsedActivity == assignedActivity) { lastUsedActivityFound = true; } } if ((!lastUsedActivityFound && assignedActivities.count() == 0) || !assignedActivities.contains(m_corona->m_activityConsumer->currentActivity())) { //! Starting the activities must be done asynchronous because otherwise //! the activity manager cant close multiple activities QTimer::singleShot(1000, [this, lastUsedActivity, lastUsedActivityFound]() { m_activitiesController->startActivity(lastUsedActivity); m_activitiesController->setCurrentActivity(lastUsedActivity); }); } } if (orphanedLayout) { syncMultipleLayoutsToActivities(layoutName); } else if (!orphanedLayout && !lastUsedActivityFound) { m_activitiesController->setCurrentActivity(layout.activities()[0]); } } else { syncMultipleLayoutsToActivities(layoutName); } } m_corona->universalSettings()->setCurrentLayoutName(layoutName); if (!layoutIsAssigned(layoutName)) { m_corona->universalSettings()->setLastNonAssignedLayoutName(layoutName); } }); } else { qDebug() << "Layout : " << layoutName << " was not found..."; } return true; } void LayoutManager::syncMultipleLayoutsToActivities(QString layoutForOrphans) { qDebug() << " ---- --------- ------ syncMultipleLayoutsToActivities ------- "; qDebug() << " ---- --------- ------ ------------------------------- ------- "; QStringList layoutsToUnload; QStringList layoutsToLoad; layoutsToLoad << Layout::MultipleLayoutsName; bool allRunningActivitiesWillBeReserved{true}; if (layoutForOrphans.isEmpty() || m_assignedLayouts.values().contains(layoutForOrphans)) { layoutForOrphans = m_corona->universalSettings()->lastNonAssignedLayoutName(); } foreach (auto activity, runningActivities()) { if (!m_assignedLayouts[activity].isEmpty()) { if (!layoutsToLoad.contains(m_assignedLayouts[activity])) { layoutsToLoad.append(m_assignedLayouts[activity]); } } else { allRunningActivitiesWillBeReserved = false; } } foreach (auto layout, m_activeLayouts) { QString tempLayoutName; if (!layoutsToLoad.contains(layout->name()) && layout->name() != layoutForOrphans) { tempLayoutName = layout->name(); } else if (layout->activities().isEmpty() && allRunningActivitiesWillBeReserved) { //! in such case the layout for the orphaned must be unloaded tempLayoutName = layout->name(); } if (!tempLayoutName.isEmpty() && !layoutsToUnload.contains(tempLayoutName)) { layoutsToUnload << tempLayoutName; } } //! Unload no needed Layouts foreach (auto layoutName, layoutsToUnload) { if (layoutName != Layout::MultipleLayoutsName) { Layout *layout = activeLayout(layoutName); int posLayout = activeLayoutPos(layoutName); if (posLayout >= 0) { qDebug() << "REMOVING LAYOUT ::::: " << layoutName; m_activeLayouts.removeAt(posLayout); if (layout->isOriginalLayout()) { layout->syncToLayoutFile(true); } layout->unloadContainments(); layout->unloadLatteViews(); clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds()); delete layout; } } } //! Add Layout for orphan activities if (!allRunningActivitiesWillBeReserved) { if (!activeLayout(layoutForOrphans)) { Layout *newLayout = new Layout(this, layoutPath(layoutForOrphans), layoutForOrphans); if (newLayout) { qDebug() << "ACTIVATING ORPHANED LAYOUT ::::: " << layoutForOrphans; addLayout(newLayout); newLayout->importToCorona(); } } } //! Add needed Layouts based on Activities foreach (auto layoutName, layoutsToLoad) { if (!activeLayout(layoutName)) { Layout *newLayout = new Layout(this, QString(layoutPath(layoutName)), layoutName); if (newLayout) { qDebug() << "ACTIVATING LAYOUT ::::: " << layoutName; addLayout(newLayout); newLayout->importToCorona(); if (newLayout->isOriginalLayout() && m_corona->universalSettings()->showInfoWindow()) { showInfoWindow(i18n("Activating layout: %0 ...").arg(newLayout->name()), 5000, newLayout->appliedActivities()); } } } } updateCurrentLayoutNameInMultiEnvironment(); emit activeLayoutsChanged(); } void LayoutManager::pauseLayout(QString layoutName) { if (memoryUsage() == Dock::MultipleLayouts) { Layout *layout = activeLayout(layoutName); if (layout && !layout->activities().isEmpty()) { int i = 0; foreach (auto activityId, layout->activities()) { //! Stopping the activities must be done asynchronous because otherwise //! the activity manager cant close multiple activities QTimer::singleShot(i * 1000, [this, activityId]() { m_activitiesController->stopActivity(activityId); }); i = i + 1; } } } } void LayoutManager::syncActiveLayoutsToOriginalFiles() { if (memoryUsage() == Dock::MultipleLayouts) { foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout()) { layout->syncToLayoutFile(); } } } } void LayoutManager::clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds, bool bypassChecks) { if (!m_corona || (memoryUsage() == Dock::SingleLayout && !bypassChecks)) { return; } auto containments = m_corona->config()->group("Containments"); foreach (auto conId, containmentsIds) { qDebug() << "unloads ::: " << conId; KConfigGroup containment = containments.group(conId); containment.deleteGroup(); } containments.sync(); } void LayoutManager::syncLatteViewsToScreens() { foreach (auto layout, m_activeLayouts) { layout->syncLatteViewsToScreens(); } } QString LayoutManager::newLayout(QString layoutName, QString preset) { QDir layoutDir(QDir::homePath() + "/.config/latte"); QStringList filter; filter.append(QString(layoutName + "*.layout.latte")); QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); //! if the newLayout already exists provide a newName that doesn't if (files.count() >= 1) { int newCounter = files.count() + 1; layoutName = layoutName + "-" + QString::number(newCounter); } QString newLayoutPath = layoutDir.absolutePath() + "/" + layoutName + ".layout.latte"; qDebug() << "adding layout : " << layoutName << " based on preset:" << preset; if (preset == i18n("Default") && !QFile(newLayoutPath).exists()) { qDebug() << "adding layout : succeed"; QFile(m_corona->kPackage().filePath("preset1")).copy(newLayoutPath); } return newLayoutPath; } //! This function figures in the beginning if a dock with tasks //! in it will be loaded taking into account also the screens are present. bool LayoutManager::heuresticForLoadingDockWithTasks(int *firstContainmentWithTasks) { foreach (auto containment, m_corona->containments()) { QString plugin = containment->pluginMetaData().pluginId(); if (plugin == "org.kde.latte.containment") { bool onPrimary = containment->config().readEntry("onPrimary", true); int lastScreen = containment->lastScreen(); qDebug() << "containment values: " << onPrimary << " - " << lastScreen; bool containsTasks = false; foreach (auto applet, containment->applets()) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { containsTasks = true; break; } } if (containsTasks) { *firstContainmentWithTasks = containment->id(); if (onPrimary) { return true; } else { if (lastScreen >= 0) { QString connector = m_corona->screenPool()->connector(lastScreen); foreach (auto scr, qGuiApp->screens()) { if (scr && scr->name() == connector) { return true; break; } } } } } } } return false; } void LayoutManager::importDefaultLayout(bool newInstanceIfPresent) { importPreset(1, newInstanceIfPresent); if (newInstanceIfPresent) { loadLayouts(); } } void LayoutManager::importPresets(bool includeDefault) { int start = 1; if (!includeDefault) { start = 2; } for (int i = start; i <= 4; ++i) { importPreset(i, false); } } void LayoutManager::importPreset(int presetNo, bool newInstanceIfPresent) { QByteArray presetNameOrig = QString("preset" + QString::number(presetNo)).toUtf8(); QString presetPath = m_corona->kPackage().filePath(presetNameOrig); QString presetName = Layout::layoutName(presetPath); QByteArray presetNameChars = presetName.toUtf8(); presetName = i18n(presetNameChars); //! hide the multiple layouts layout file from user presetName = (presetNo == MultipleLayoutsPresetId) ? "." + presetName : presetName; QString newLayoutFile = ""; if (newInstanceIfPresent) { newLayoutFile = QDir::homePath() + "/.config/latte/" + m_importer->uniqueLayoutName(presetName) + ".layout.latte"; } else { newLayoutFile = QDir::homePath() + "/.config/latte/" + presetName + ".layout.latte"; } if (!QFile(newLayoutFile).exists()) { QFile(presetPath).copy(newLayoutFile); QFileInfo newFileInfo(newLayoutFile); if (newFileInfo.exists() && !newFileInfo.isWritable()) { QFile(newLayoutFile).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } } } QStringList LayoutManager::validActivities(QStringList currentList) { QStringList validIds; foreach (auto activity, currentList) { if (activities().contains(activity)) { validIds.append(activity); } } return validIds; } bool LayoutManager::layoutIsAssigned(QString layoutName) { QHashIterator i(m_assignedLayouts); while (i.hasNext()) { i.next(); if (i.value() == layoutName) { return true; } } return false; } void LayoutManager::showLatteSettingsDialog(int page) { if (!m_latteSettingsDialog) { m_latteSettingsDialog = new SettingsDialog(nullptr, m_corona); } m_latteSettingsDialog->show(); if (m_latteSettingsDialog->isMinimized()) { m_latteSettingsDialog->showNormal(); } Dock::LatteConfigPage configPage = static_cast(page); m_latteSettingsDialog->setCurrentPage(configPage); m_latteSettingsDialog->activateWindow(); } void LayoutManager::hideLatteSettingsDialog() { if (m_latteSettingsDialog) { m_latteSettingsDialog->deleteLater(); m_latteSettingsDialog = nullptr; } } void LayoutManager::showInfoWindow(QString info, int duration, QStringList activities) { foreach (auto screen, qGuiApp->screens()) { InfoView *infoView = new InfoView(m_corona, info, screen); infoView->show(); infoView->setOnActivities(activities); QTimer::singleShot(duration, [this, infoView]() { infoView->deleteLater(); }); } } //! it is used just in order to provide translations for the presets void LayoutManager::ghostForTranslatedPresets() { QString preset1 = i18n("Default"); QString preset2 = i18n("Plasma"); QString preset3 = i18n("Unity"); QString preset4 = i18n("Extended"); } } diff --git a/app/layoutmanager.h b/app/layoutmanager.h index 400f5c82..5bcd37a1 100644 --- a/app/layoutmanager.h +++ b/app/layoutmanager.h @@ -1,202 +1,202 @@ /* * Copyright 2017 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LAYOUTMANAGER_H #define LAYOUTMANAGER_H // local #include "launcherssignals.h" #include "settings/settingsdialog.h" // Qt #include #include #include // KDE #include namespace Plasma { class Containment; class Types; } namespace KActivities { class Controller; } namespace Latte { -class DockCorona; +class Corona; class Importer; class Layout; class LaunchersSignals; class View; } namespace Latte { //! This class is responsible to manipulate all layouts. //! add,remove,rename, update configurations etc. class LayoutManager : public QObject { Q_OBJECT Q_PROPERTY(QString currentLayoutName READ currentLayoutName NOTIFY currentLayoutNameChanged) Q_PROPERTY(QStringList layouts READ layouts NOTIFY layoutsChanged) Q_PROPERTY(QStringList menuLayouts READ menuLayouts NOTIFY menuLayoutsChanged) Q_PROPERTY(LaunchersSignals *launchersSignals READ launchersSignals NOTIFY launchersSignalsChanged) public: LayoutManager(QObject *parent = nullptr); ~LayoutManager() override; - DockCorona *corona(); + Latte::Corona *corona(); Importer *importer(); void load(); void loadLayoutOnStartup(QString layoutName); void unload(); void addDock(Plasma::Containment *containment, bool forceLoading = false, int expDockScreen = -1); void hideAllDocks(); void pauseLayout(QString layoutName); void syncLatteViewsToScreens(); void syncActiveLayoutsToOriginalFiles(); bool latteViewExists(Latte::View *view) const; bool hasColorizer() 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; Dock::LayoutsMemoryUsage memoryUsage() const; void setMemoryUsage(Dock::LayoutsMemoryUsage memoryUsage); QHash *currentLatteViews() const; QHash *layoutLatteViews(const QString &layoutName) const; //! returns an active layout with that #id (name), it returns null if such //! layout cant be found Layout *activeLayout(QString id) const; int activeLayoutPos(QString id) const; LaunchersSignals *launchersSignals(); QStringList activities(); QStringList runningActivities(); QStringList orphanedActivities(); //! These are activities that havent been assigned to specific layout void importDefaultLayout(bool newInstanceIfPresent = false); void importPresets(bool includeDefault = false); public slots: void showAboutDialog(); void hideLatteSettingsDialog(); Q_INVOKABLE void showLatteSettingsDialog(int page = Latte::Dock::LayoutPage); //! switch to specified layout, default previousMemoryUsage means that it didn't change Q_INVOKABLE bool switchToLayout(QString layoutName, int previousMemoryUsage = -1); Q_INVOKABLE int layoutsMemoryUsage(); //! creates a new layout with layoutName based on the preset Q_INVOKABLE QString newLayout(QString layoutName, QString preset = i18n("Default")); Q_INVOKABLE QStringList activeLayoutsNames(); signals: void activeLayoutsChanged(); void currentLayoutChanged(); void currentLayoutNameChanged(); void launchersSignalsChanged(); void layoutsChanged(); void menuLayoutsChanged(); void viewColorizerChanged(); void currentLayoutIsSwitching(QString layoutName); private slots: void currentActivityChanged(const QString &id); void showInfoWindowChanged(); void syncMultipleLayoutsToActivities(QString layoutForOrphans = QString()); private: void addLayout(Layout *layout); void cleanupOnStartup(QString path); //!remove deprecated or oldstyle config options void clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds, bool bypassChecks = false); void confirmDynamicSwitch(); //! it is used just in order to provide translations for the presets void ghostForTranslatedPresets(); //! This function figures in the beginning if a dock with tasks //! in it will be loaded taking into account also the screens are present. //! returns true if it will be loaded, false otherwise //! firstContainmentWithTasks = the first containment containing a taskmanager plasmoid bool heuresticForLoadingDockWithTasks(int *firstContainmentWithTasks); void importLatteLayout(QString layoutPath); void importPreset(int presetNo, bool newInstanceIfPresent = false); void loadLatteLayout(QString layoutPath); void loadLayouts(); void setMenuLayouts(QStringList layouts); void showInfoWindow(QString info, int duration, QStringList activities = {"0"}); void updateCurrentLayoutNameInMultiEnvironment(); bool layoutIsAssigned(QString layoutName); QString layoutPath(QString layoutName); QStringList validActivities(QStringList currentList); private: QString m_currentLayoutNameInMultiEnvironment; QString m_shouldSwitchToLayout; QStringList m_layouts; QStringList m_menuLayouts; QStringList m_presetsPaths; QHash m_assignedLayouts; QTimer m_dynamicSwitchTimer; QPointer m_latteSettingsDialog; - DockCorona *m_corona{nullptr}; + Latte::Corona *m_corona{nullptr}; Importer *m_importer{nullptr}; LaunchersSignals *m_launchersSignals{nullptr}; QList m_activeLayouts; KActivities::Controller *m_activitiesController; friend class SettingsDialog; }; } #endif // LAYOUTMANAGER_H diff --git a/app/main.cpp b/app/main.cpp index 035908d5..507e4e73 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,316 +1,316 @@ /* * 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 "dockcorona.h" #include "config-latte.h" #include "importer.h" +#include "lattecorona.h" #include "../liblattedock/dock.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.")} , {"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); if (parser.isSet(QStringLiteral("available-layouts"))) { QStringList layouts = Latte::Importer::availableLayouts(); if (layouts.count() > 0) { qInfo() << i18n("Available layouts that can be used to start Latte:"); foreach (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 = ""; if (parser.isSet(QStringLiteral("default-layout"))) { defaultLayoutOnStartup = true; } else if (parser.isSet(QStringLiteral("layout"))) { layoutNameOnStartup = parser.value(QStringLiteral("layout")); if (!Latte::Importer::layoutExists(layoutNameOnStartup)) { qInfo() << i18nc("layout missing", "This layout doesn't exist in the system."); qGuiApp->exit(); return 0; } } 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 = 3000; } } if (!lockFile.tryLock(timeout)) { qInfo() << i18n("An instance is already running!, use --replace to restart Latte"); qGuiApp->exit(); return 0; } if (parser.isSet(QStringLiteral("import-full"))) { bool imported = Latte::Importer::importHelper(parser.value(QStringLiteral("import-full"))); if (!imported) { qInfo() << i18n("The configuration cannot be imported"); qGuiApp->exit(); return 0; } } if (parser.isSet(QStringLiteral("import-layout"))) { QString importedLayout = Latte::Importer::importLayoutHelper(parser.value(QStringLiteral("import-layout"))); if (importedLayout.isEmpty()) { qInfo() << i18n("The layout cannot be imported"); qGuiApp->exit(); return 0; } else { layoutNameOnStartup = importedLayout; } } if (parser.isSet(QStringLiteral("multiple"))) { memoryUsage = (int)(Latte::Dock::MultipleLayouts); } else if (parser.isSet(QStringLiteral("single"))) { memoryUsage = (int)(Latte::Dock::SingleLayout); } 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::DockCorona corona(defaultLayoutOnStartup, layoutNameOnStartup, memoryUsage); + 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.setBugAddress(BUG_ADDRESS); 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/plasmathemeextended.cpp b/app/plasmathemeextended.cpp index 2ef36c2b..2c04098b 100644 --- a/app/plasmathemeextended.cpp +++ b/app/plasmathemeextended.cpp @@ -1,351 +1,351 @@ /* * 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 "plasmathemeextended.h" // local #include "commontools.h" -#include "dockcorona.h" +#include "lattecorona.h" #include "schemecolors.h" #include "view/panelshadows_p.h" // Qt #include #include // KDE #include #include #include #define REVERSEDCOLORSCHEME "reversed.colors" namespace Latte { PlasmaThemeExtended::PlasmaThemeExtended(KSharedConfig::Ptr config, QObject *parent) : QObject(parent), m_themeGroup(KConfigGroup(config, QStringLiteral("PlasmaThemeExtended"))) { - m_corona = qobject_cast(parent); + m_corona = qobject_cast(parent); loadConfig(); connect(&m_theme, &Plasma::Theme::themeChanged, this, &PlasmaThemeExtended::hasShadowChanged); connect(&m_theme, &Plasma::Theme::themeChanged, this, &PlasmaThemeExtended::load); connect(&m_theme, &Plasma::Theme::themeChanged, this, &PlasmaThemeExtended::themeChanged); } void PlasmaThemeExtended::load() { loadThemePaths(); loadRoundness(); } PlasmaThemeExtended::~PlasmaThemeExtended() { saveConfig(); m_normalScheme->deleteLater(); m_reversedScheme->deleteLater(); } bool PlasmaThemeExtended::hasShadow() const { return PanelShadows::self()->enabled(); } int PlasmaThemeExtended::bottomEdgeRoundness() const { return (themeHasExtendedInfo() ? m_bottomEdgeRoundness : userThemeRoundness()); } int PlasmaThemeExtended::leftEdgeRoundness() const { return (themeHasExtendedInfo() ? m_leftEdgeRoundness : userThemeRoundness()); } int PlasmaThemeExtended::topEdgeRoundness() const { return (themeHasExtendedInfo() ? m_topEdgeRoundness : userThemeRoundness()); } int PlasmaThemeExtended::rightEdgeRoundness() const { return (themeHasExtendedInfo() ? m_rightEdgeRoundness : userThemeRoundness()); } int PlasmaThemeExtended::userThemeRoundness() const { return m_userRoundness; } void PlasmaThemeExtended::setUserThemeRoundness(int roundness) { if (m_userRoundness == roundness) { return; } m_userRoundness = roundness; if (!themeHasExtendedInfo()) { emit roundnessChanged(); } saveConfig(); } bool PlasmaThemeExtended::themeHasExtendedInfo() const { return m_themeHasExtendedInfo; } SchemeColors *PlasmaThemeExtended::lightTheme() const { return m_isLightTheme ? m_normalScheme : m_reversedScheme; } SchemeColors *PlasmaThemeExtended::darkTheme() const { return !m_isLightTheme ? m_normalScheme : m_reversedScheme; } void PlasmaThemeExtended::setNormalSchemeFile(const QString &file) { if (m_normalSchemePath == file) { return; } m_normalSchemePath = file; if (m_normalScheme) { disconnect(m_normalScheme, &SchemeColors::colorsChanged, this, &PlasmaThemeExtended::loadThemeLightness); m_normalScheme->deleteLater(); } m_normalScheme = new SchemeColors(this, m_normalSchemePath, true); connect(m_normalScheme, &SchemeColors::colorsChanged, this, &PlasmaThemeExtended::loadThemeLightness); qDebug() << "plasma theme normal colors ::: " << m_normalSchemePath; updateReversedScheme(); loadThemeLightness(); emit themeChanged(); } void PlasmaThemeExtended::updateReversedScheme() { QString reversedFilePath = m_extendedThemeDir.path() + "/" + REVERSEDCOLORSCHEME; QFile(m_normalSchemePath).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 PlasmaThemeExtended::updateReversedSchemeValues() { //! reverse values based on original scheme KSharedConfigPtr normalPtr = KSharedConfig::openConfig(m_normalSchemePath); KSharedConfigPtr reversedPtr = KSharedConfig::openConfig(m_reversedSchemePath); if (normalPtr && reversedPtr) { foreach (auto groupName, reversedPtr->groupList()) { if (groupName != "Colors:Button") { KConfigGroup reversedGroup(reversedPtr, groupName); if (reversedGroup.keyList().contains("BackgroundNormal") && reversedGroup.keyList().contains("ForegroundNormal")) { //! reverse usual text/background values KConfigGroup normalGroup(normalPtr, groupName); reversedGroup.writeEntry("BackgroundNormal", normalGroup.readEntry("ForegroundNormal", QColor())); reversedGroup.writeEntry("ForegroundNormal", normalGroup.readEntry("BackgroundNormal", QColor())); reversedGroup.sync(); } } } //! update WM group KConfigGroup reversedGroup(reversedPtr, "WM"); if (reversedGroup.keyList().contains("activeBackground") && reversedGroup.keyList().contains("activeForeground") && reversedGroup.keyList().contains("inactiveBackground") && reversedGroup.keyList().contains("inactiveForeground")) { //! reverse usual wm titlebar values KConfigGroup normalGroup(normalPtr, "WM"); reversedGroup.writeEntry("activeBackground", normalGroup.readEntry("activeForeground", QColor())); reversedGroup.writeEntry("activeForeground", normalGroup.readEntry("activeBackground", QColor())); reversedGroup.writeEntry("inactiveBackground", normalGroup.readEntry("inactiveForeground", QColor())); reversedGroup.writeEntry("inactiveForeground", normalGroup.readEntry("inactiveBackground", QColor())); reversedGroup.sync(); } if (reversedGroup.keyList().contains("activeBlend") && reversedGroup.keyList().contains("inactiveBlend")) { KConfigGroup normalGroup(normalPtr, "WM"); reversedGroup.writeEntry("activeBlend", normalGroup.readEntry("inactiveBlend", QColor())); reversedGroup.writeEntry("inactiveBlend", normalGroup.readEntry("activeBlend", QColor())); reversedGroup.sync(); } //! update scheme name QString normalSchemeName = SchemeColors::schemeName(m_normalSchemePath); KConfigGroup generalGroup(reversedPtr, "General"); generalGroup.writeEntry("Name", normalSchemeName + "_reversed"); generalGroup.sync(); } } void PlasmaThemeExtended::loadRoundness() { if (!m_corona) { return; } QString extendedInfoFilePath = m_corona->kPackage().filePath("themesExtendedInfo"); KSharedConfigPtr extInfoPtr = KSharedConfig::openConfig(extendedInfoFilePath); KConfigGroup roundGroup(extInfoPtr, "Roundness"); m_themeHasExtendedInfo = false; foreach (auto key, roundGroup.keyList()) { if (m_theme.themeName().toUpper().startsWith(key.toUpper())) { QStringList rs = roundGroup.readEntry(key, QStringList()); qDebug() << "roundness ::: " << rs; if (rs.size() > 0) { m_themeHasExtendedInfo = true; if (rs.size() <= 3) { //assign same roundness for all edges m_bottomEdgeRoundness = rs[0].toInt(); m_leftEdgeRoundness = m_bottomEdgeRoundness; m_topEdgeRoundness = m_bottomEdgeRoundness; m_rightEdgeRoundness = m_bottomEdgeRoundness; } else if (rs.size() >= 4) { m_bottomEdgeRoundness = rs[0].toInt(); m_leftEdgeRoundness = rs[1].toInt(); m_topEdgeRoundness = rs[2].toInt(); m_rightEdgeRoundness = rs[3].toInt(); } } break; } } emit roundnessChanged(); } void PlasmaThemeExtended::loadThemePaths() { m_themePath = ""; QString localD = QDir::homePath() + "/.local/share/plasma/desktoptheme/" + m_theme.themeName(); QString globalD = "/usr/share/plasma/desktoptheme/" + m_theme.themeName(); if (QDir(localD).exists()) { m_themePath = localD; } else if (QDir(globalD).exists()) { m_themePath = globalD; } qDebug() << "current plasma theme ::: " << m_theme.themeName(); qDebug() << "theme path ::: " << m_themePath; //! clear kde connections for (auto &c : m_kdeConnections) { disconnect(c); } //! assign color schemes QString themeColorScheme = m_themePath + "/colors"; if (QFileInfo(themeColorScheme).exists()) { setNormalSchemeFile(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->setNormalSchemeFile(SchemeColors::possibleSchemeFile("kdeglobals")); } }); m_kdeConnections[1] = connect(KDirWatch::self(), &KDirWatch::created, this, [ &, kdeSettingsFile](const QString & path) { if (path == kdeSettingsFile) { this->setNormalSchemeFile(SchemeColors::possibleSchemeFile("kdeglobals")); } }); setNormalSchemeFile(SchemeColors::possibleSchemeFile("kdeglobals")); } } void PlasmaThemeExtended::loadThemeLightness() { float textColorLum = Latte::colorLumina(m_normalScheme->textColor()); float backColorLum = Latte::colorLumina(m_normalScheme->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 PlasmaThemeExtended::loadConfig() { m_userRoundness = m_themeGroup.readEntry("userSetPlasmaThemeRoundness", 0); } void PlasmaThemeExtended::saveConfig() { m_themeGroup.writeEntry("userSetPlasmaThemeRoundness", m_userRoundness); m_themeGroup.sync(); } } diff --git a/app/plasmathemeextended.h b/app/plasmathemeextended.h index cc0c9f40..73986985 100644 --- a/app/plasmathemeextended.h +++ b/app/plasmathemeextended.h @@ -1,124 +1,126 @@ /* * 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 . * */ #ifndef PLASMATHEMEEXTENDED_H #define PLASMATHEMEEXTENDED_H // local #include "schemecolors.h" // C++ #include // Qt #include #include // KDE #include #include // Plasma #include namespace Latte { +class Corona; +} -class DockCorona; +namespace Latte { class PlasmaThemeExtended: public QObject { Q_OBJECT Q_PROPERTY(bool hasShadow READ hasShadow NOTIFY hasShadowChanged) Q_PROPERTY(int bottomEdgeRoundness READ bottomEdgeRoundness NOTIFY roundnessChanged) Q_PROPERTY(int leftEdgeRoundness READ leftEdgeRoundness NOTIFY roundnessChanged) Q_PROPERTY(int topEdgeRoundness READ topEdgeRoundness NOTIFY roundnessChanged) Q_PROPERTY(int rightEdgeRoundness READ rightEdgeRoundness NOTIFY roundnessChanged) Q_PROPERTY(SchemeColors *lightTheme READ lightTheme NOTIFY themeChanged) Q_PROPERTY(SchemeColors *darkTheme READ darkTheme NOTIFY themeChanged) public: PlasmaThemeExtended(KSharedConfig::Ptr config, QObject *parent); ~PlasmaThemeExtended() override;; bool hasShadow() const; int bottomEdgeRoundness() const; int leftEdgeRoundness() const; int topEdgeRoundness() const; int rightEdgeRoundness() const; int userThemeRoundness() const; void setUserThemeRoundness(int roundness); SchemeColors *lightTheme() const; SchemeColors *darkTheme() const; void load(); signals: void hasShadowChanged(); void roundnessChanged(); void themeChanged(); private slots: void loadConfig(); void saveConfig(); void loadThemeLightness(); private: void loadThemePaths(); void loadRoundness(); void setNormalSchemeFile(const QString &file); void updateReversedScheme(); void updateReversedSchemeValues(); bool themeHasExtendedInfo() const; private: bool m_isLightTheme{false}; bool m_themeHasExtendedInfo{false}; int m_bottomEdgeRoundness{0}; int m_leftEdgeRoundness{0}; int m_topEdgeRoundness{0}; int m_rightEdgeRoundness{0}; int m_userRoundness{0}; QString m_themePath; QString m_normalSchemePath; QString m_reversedSchemePath; std::array m_kdeConnections; QTemporaryDir m_extendedThemeDir; KConfigGroup m_themeGroup; Plasma::Theme m_theme; - DockCorona *m_corona{nullptr}; + Latte::Corona *m_corona{nullptr}; SchemeColors *m_normalScheme{nullptr}; SchemeColors *m_reversedScheme{nullptr}; }; } #endif diff --git a/app/settings/settingsdialog.cpp b/app/settings/settingsdialog.cpp index 07c7f0df..51adec89 100644 --- a/app/settings/settingsdialog.cpp +++ b/app/settings/settingsdialog.cpp @@ -1,1605 +1,1605 @@ /* * 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 "dockcorona.h" #include "layout.h" #include "layoutmanager.h" #include "importer.h" #include "plasmathemeextended.h" #include "universalsettings.h" #include "ui_settingsdialog.h" +#include "../lattecorona.h" #include "../liblattedock/dock.h" #include "delegates/checkboxdelegate.h" #include "delegates/colorcmbboxdelegate.h" #include "delegates/activitycmbboxdelegate.h" #include "delegates/layoutnamedelegate.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 SCREENTRACKERDEFAULTVALUE = 2500; const int THEMEDEFAULTROUNDNESS = 0; //Breeze default value is used 0px. const QChar CheckMark{0x2714}; -SettingsDialog::SettingsDialog(QWidget *parent, DockCorona *corona) +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->layoutManager()->layouts().count(), 6, this); ui->layoutsView->setModel(m_model); ui->layoutsView->horizontalHeader()->setStretchLastSection(true); ui->layoutsView->verticalHeader()->setVisible(false); connect(m_corona->layoutManager(), &LayoutManager::currentLayoutNameChanged, this, &SettingsDialog::layoutsChanged); connect(m_corona->layoutManager(), &LayoutManager::activeLayoutsChanged, 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; foreach (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 ActivityCmbBoxDelegate(this)); m_inMemoryButtons = new QButtonGroup(this); m_inMemoryButtons->addButton(ui->singleToolBtn, Latte::Dock::SingleLayout); m_inMemoryButtons->addButton(ui->multipleToolBtn, Latte::Dock::MultipleLayouts); m_inMemoryButtons->setExclusive(true); if (KWindowSystem::isPlatformWayland()) { m_inMemoryButtons->button(Latte::Dock::MultipleLayouts)->setEnabled(false); } m_mouseSensitivityButtons = new QButtonGroup(this); m_mouseSensitivityButtons->addButton(ui->lowSensitivityBtn, Latte::Dock::LowSensitivity); m_mouseSensitivityButtons->addButton(ui->mediumSensitivityBtn, Latte::Dock::MediumSensitivity); m_mouseSensitivityButtons->addButton(ui->highSensitivityBtn, Latte::Dock::HighSensitivity); m_mouseSensitivityButtons->setExclusive(true); ui->screenTrackerSpinBox->setValue(m_corona->universalSettings()->screenTrackerInterval()); ui->themeRoundnessSpinBox->setValue(m_corona->themeExtended()->userThemeRoundness()); //! 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(); }); connect(m_mouseSensitivityButtons, static_cast(&QButtonGroup::buttonToggled), [ = ](int id, bool checked) { updateApplyButtonsState(); }); connect(ui->screenTrackerSpinBox, QOverload::of(&QSpinBox::valueChanged), [ = ](int i) { updateApplyButtonsState(); }); connect(ui->themeRoundnessSpinBox, QOverload::of(&QSpinBox::valueChanged), [ = ](int i) { updateApplyButtonsState(); }); connect(ui->autostartChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState); connect(ui->metaChkBox, &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, &DockCorona::aboutApplication); - connect(quitAction, &QAction::triggered, m_corona, &DockCorona::closeApplication); + 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)); m_corona->universalSettings()->setLayoutsColumnWidths(columnWidths); } m_inMemoryButtons->deleteLater(); m_mouseSensitivityButtons->deleteLater(); foreach (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->layoutManager()->activities(); } QStringList SettingsDialog::availableActivities() { return m_availableActivities; } void SettingsDialog::setCurrentPage(Dock::LatteConfigPage page) { if (page == Dock::LayoutPage) { ui->tabWidget->setCurrentIndex(0); } else if (page == Dock::PreferencesPage) { ui->tabWidget->setCurrentIndex(1); } } void SettingsDialog::on_newButton_clicked() { qDebug() << Q_FUNC_INFO; //! find Default preset path foreach (auto preset, m_corona->layoutManager()->presetsPaths()) { QString presetName = Layout::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->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { QString lName = (m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole)).toString(); if (Importer::layoutExists(lName)) { Layout *layout = m_corona->layoutManager()->activeLayout(lName); if (layout && layout->isOriginalLayout()) { layout->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); } Layout *settings = new Layout(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()) { foreach (auto entry, dialog.installedEntries()) { foreach (auto entryFile, entry.installedFiles()) { Importer::LatteFileVersion version = Importer::fileVersion(entryFile); if (version == 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->layoutManager()->activeLayout(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_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); qDebug() << "VERSION :::: " << version; if (version == Importer::LayoutVersion2) { addLayoutForFile(file); } else if (version == 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) { 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(); foreach (auto &name, archiveRootDir->entries()) { auto fileEntry = archiveRootDir->file(name); fileEntry->copyTo(tempDir.absolutePath()); } QString name = Importer::nameOfConfigFile(file); QString applets(tempDir.absolutePath() + "/" + "lattedock-appletsrc"); if (QFile(applets).exists()) { if (m_corona->layoutManager()->importer()->importOldLayout(applets, name, false, tempDir.absolutePath())) { addLayoutForFile(tempDir.absolutePath() + "/" + name + ".layout.latte", name, false); } QString alternativeName = name + "-" + i18nc("layout", "Alternative"); if (m_corona->layoutManager()->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->layoutManager()->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); } Layout 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->layoutManager()->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 foreach (auto preset, m_corona->layoutManager()->presetsPaths()) { QString presetName = Layout::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->metaChkBox->setChecked(false); ui->noBordersForMaximizedChkBox->setChecked(false); ui->highSensitivityBtn->setChecked(true); ui->screenTrackerSpinBox->setValue(SCREENTRACKERDEFAULTVALUE); ui->themeRoundnessSpinBox->setValue(THEMEDEFAULTROUNDNESS); } } void SettingsDialog::addLayoutForFile(QString file, QString layoutName, bool newTempDirectory, bool showNotification) { if (layoutName.isEmpty()) { layoutName = Layout::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)) { Layout *oldSettings = m_layouts.take(copiedId); delete oldSettings; } Layout *settings = new Layout(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(); int i = 0; QStringList brokenLayouts; if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { m_corona->layoutManager()->syncActiveLayoutsToOriginalFiles(); } foreach (auto layout, m_corona->layoutManager()->layouts()) { QString layoutPath = QDir::homePath() + "/.config/latte/" + layout + ".layout.latte"; m_initLayoutPaths.append(layoutPath); Layout *layoutSets = new Layout(this, layoutPath); m_layouts[layoutPath] = layoutSets; QString background = layoutSets->background(); if (background.isEmpty()) { insertLayoutInfoAtRow(i, layoutPath, layoutSets->color(), QString(), layoutSets->name(), layoutSets->showInMenu(), layoutSets->disableBordersForMaximizedWindows(), layoutSets->activities(), !layoutSets->isWritable()); } else { insertLayoutInfoAtRow(i, layoutPath, background, layoutSets->textColor(), layoutSets->name(), layoutSets->showInMenu(), layoutSets->disableBordersForMaximizedWindows(), layoutSets->activities(), !layoutSets->isWritable()); } qDebug() << "counter:" << i << " total:" << m_model->rowCount(); i++; if (layoutSets->name() == m_corona->layoutManager()->currentLayoutName()) { ui->layoutsView->selectRow(i - 1); } Layout *activeLayout = m_corona->layoutManager()->activeLayout(layoutSets->name()); if ((activeLayout && activeLayout->layoutIsBroken()) || (!activeLayout && layoutSets->layoutIsBroken())) { brokenLayouts.append(layoutSets->name()); } } //! Check Multiple Layouts Integrity /*if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { m_corona->layoutManager()->syncActiveLayoutsToOriginalFiles(); QString multipleLayoutPath = QDir::homePath() + "/.config/latte/" + Layout::MultipleLayoutsName + ".layout.latte"; Layout multipleHiddenLayouts(this, multipleLayoutPath, "Multiple Layouts File"); if (multipleHiddenLayouts.layoutIsBroken()) { qDebug() << "Intergrity Error ::: Multiple Layouts Hidden file is broken!!!!"; brokenLayouts.append(multipleHiddenLayouts.name()); } }*/ recalculateAvailableActivities(); m_model->setHorizontalHeaderItem(IDCOLUMN, new QStandardItem(QString("#path"))); m_model->setHorizontalHeaderItem(COLORCOLUMN, new QStandardItem(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(QString(i18nc("column for layout to show which activities is assigned to", "Activities")))); //! 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() && columnWidths.count() == 4) { ui->layoutsView->setColumnWidth(COLORCOLUMN, columnWidths[0].toInt()); ui->layoutsView->setColumnWidth(NAMECOLUMN, columnWidths[1].toInt()); ui->layoutsView->setColumnWidth(MENUCOLUMN, columnWidths[2].toInt()); ui->layoutsView->setColumnWidth(BORDERSCOLUMN, columnWidths[3].toInt()); } if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) { ui->singleToolBtn->setChecked(true); } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { ui->multipleToolBtn->setChecked(true); } updatePerLayoutButtonsState(); ui->autostartChkBox->setChecked(m_corona->universalSettings()->autostart()); ui->infoWindowChkBox->setChecked(m_corona->universalSettings()->showInfoWindow()); ui->metaChkBox->setChecked(m_corona->universalSettings()->metaForwardedToLatte()); ui->noBordersForMaximizedChkBox->setChecked(m_corona->universalSettings()->canDisableBorders()); if (m_corona->universalSettings()->mouseSensitivity() == Dock::LowSensitivity) { ui->lowSensitivityBtn->setChecked(true); } else if (m_corona->universalSettings()->mouseSensitivity() == Dock::MediumSensitivity) { ui->mediumSensitivityBtn->setChecked(true); } else if (m_corona->universalSettings()->mouseSensitivity() == Dock::HighSensitivity) { ui->highSensitivityBtn->setChecked(true); } o_settings = currentSettings(); o_settingsLayouts = currentLayoutsSettings(); updateApplyButtonsState(); //! 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->metaChkBox->isChecked(); settings << (int)ui->noBordersForMaximizedChkBox->isChecked(); settings << m_mouseSensitivityButtons->checkedId(); settings << ui->screenTrackerSpinBox->value(); settings << ui->themeRoundnessSpinBox->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(); 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; } 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(",")); QList items; items.append(pathItem); items.append(hiddenTextItem); items.append(colorItem); items.append(nameItem); items.append(menuItem); items.append(bordersItem); items.append(activitiesItem); 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->layoutManager()->activeLayout(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::Dock::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->layoutManager()->switchToLayout(lName); } } else { QVariant value = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), NAMECOLUMN), Qt::DisplayRole); if (value.isValid()) { m_corona->layoutManager()->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(); Layout *layout = m_layouts[id]; if (layout) { m_corona->layoutManager()->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->layoutManager()->currentLayoutName() == name) { font.setBold(true); // ui->layoutsView->selectRow(i); } else { Layout *layout = m_corona->layoutManager()->activeLayout(name); if (layout && (m_corona->layoutManager()->memoryUsage() == Dock::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_layouts[id]->name() != name) { font.setItalic(true); m_model->setData(m_model->index(currentRow, NAMECOLUMN), font, Qt::FontRole); } else { font.setItalic(false); m_model->setData(m_model->index(currentRow, NAMECOLUMN), font, Qt::FontRole); } } 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}; foreach (auto preset, m_corona->layoutManager()->presetsPaths()) { QString presetName = Layout::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->metaChkBox->isChecked() || !ui->infoWindowChkBox->isChecked() || ui->noBordersForMaximizedChkBox->isChecked() || !ui->highSensitivityBtn->isChecked() || ui->screenTrackerSpinBox->value() != SCREENTRACKERDEFAULTVALUE || ui->themeRoundnessSpinBox->value() != THEMEDEFAULTROUNDNESS) { 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(); //! Switch Button if (id.startsWith("/tmp/") || originalName != nameInModel) { ui->switchButton->setEnabled(false); } else { ui->switchButton->setEnabled(true); } //! Pause Button if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) { ui->pauseButton->setVisible(false); } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { ui->pauseButton->setVisible(true); QStringList lActivities = m_model->data(m_model->index(currentRow, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); Layout *layout = m_layouts[id]; if (!lActivities.isEmpty() && layout && m_corona->layoutManager()->activeLayout(layout->name())) { ui->pauseButton->setEnabled(true); } else { ui->pauseButton->setEnabled(false); } } //! Remove Layout Button if (originalName != nameInModel || (originalName == m_corona->layoutManager()->currentLayoutName()) || (m_corona->layoutManager()->activeLayout(originalName)) || lockedInModel) { ui->removeButton->setEnabled(false); } else { ui->removeButton->setEnabled(true); } if (lockedInModel) { ui->lockedButton->setChecked(true); } else { ui->lockedButton->setChecked(false); } } void SettingsDialog::recalculateAvailableActivities() { QStringList tempActivities = m_corona->layoutManager()->activities(); for (int i = 0; i < m_model->rowCount(); ++i) { QStringList assigned = m_model->data(m_model->index(i, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); foreach (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::Dock::MouseSensitivity sensitivity = static_cast(m_mouseSensitivityButtons->checkedId()); bool autostart = ui->autostartChkBox->isChecked(); bool forwardMeta = ui->metaChkBox->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(forwardMeta); m_corona->universalSettings()->setShowInfoWindow(showInfoWindow); m_corona->universalSettings()->setCanDisableBorders(noBordersForMaximized); m_corona->universalSettings()->setScreenTrackerInterval(ui->screenTrackerSpinBox->value()); m_corona->themeExtended()->setUserThemeRoundness(ui->themeRoundnessSpinBox->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 foreach (auto initLayout, m_initLayoutPaths) { if (!idExistsInModel(initLayout)) { QFile(initLayout).remove(); if (m_layouts.contains(initLayout)) { Layout *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 foreach (auto activity, lActivities) { if (knownActivities.contains(activity)) { cleanedActivities.append(activity); } } //qDebug() << i << ". " << id << " - " << color << " - " << name << " - " << menu << " - " << lActivities; Layout *activeLayout = m_corona->layoutManager()->activeLayout(m_layouts[id]->name()); Layout *layout = activeLayout ? activeLayout : m_layouts[id]; //! unlock read-only layout if (!layout->isWritable()) { layout->unlock(); } if (color.startsWith("/")) { //it is image file in such case if (color != layout->background()) { layout->setBackground(color); } if (layout->textColor() != textColor) { layout->setTextColor(textColor); } } else { if (color != layout->color()) { layout->setColor(color); layout->setBackground(QString()); layout->setTextColor(QString()); } } if (layout->showInMenu() != menu) { layout->setShowInMenu(menu); } if (layout->disableBordersForMaximizedWindows() != disabledBorders) { layout->setDisableBordersForMaximizedWindows(disabledBorders); } if (layout->activities() != cleanedActivities) { layout->setActivities(cleanedActivities); } //! If the layout name changed OR the layout path is a temporary one if (layout->name() != name || (id.startsWith("/tmp/"))) { //! If the layout is Active in MultipleLayouts if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts && activeLayout) { qDebug() << " Active Layout Should Be Renamed From : " << layout->name() << " TO :: " << name; activeLayoutsToRename[name] = layout; } QString tempFile = layoutTempDir.path() + "/" + QString(layout->name() + ".layout.latte"); qDebug() << "new temp file ::: " << tempFile; if ((m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) && (layout->name() == m_corona->layoutManager()->currentLayoutName())) { switchToLayout = name; } layout = m_layouts.take(id); delete layout; 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); Layout *nLayout = new Layout(this, newFile); m_layouts[newFile] = nLayout; 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->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { foreach (auto newLayoutName, activeLayoutsToRename.keys()) { qDebug() << " Active Layout Is Renamed From : " << activeLayoutsToRename[newLayoutName]->name() << " TO :: " << newLayoutName; Layout *layout = activeLayoutsToRename[newLayoutName]; layout->renameLayout(newLayoutName); //! that means it is an active layout for orphaned Activities if (layout->activities().isEmpty()) { orphanedLayout = newLayoutName; } } } //! 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 *activeLayout = m_corona->layoutManager()->activeLayout(m_layouts[id]->name()); Layout *layout = activeLayout ? activeLayout : m_layouts[id]; if (layout && locked && layout->isWritable()) { layout->lock(); } } m_corona->layoutManager()->loadLayouts(); Latte::Dock::LayoutsMemoryUsage inMemoryOption = static_cast(m_inMemoryButtons->checkedId()); if (m_corona->layoutManager()->memoryUsage() != inMemoryOption) { Dock::LayoutsMemoryUsage previousMemoryUsage = m_corona->layoutManager()->memoryUsage(); m_corona->layoutManager()->setMemoryUsage(inMemoryOption); QVariant value = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), NAMECOLUMN), Qt::DisplayRole); QString layoutName = value.toString(); m_corona->layoutManager()->switchToLayout(layoutName, previousMemoryUsage); } else { if (!switchToLayout.isEmpty()) { m_corona->layoutManager()->switchToLayout(switchToLayout); } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { m_corona->layoutManager()->syncMultipleLayoutsToActivities(orphanedLayout); } } return true; } 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::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; } 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(); } 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/settingsdialog.h b/app/settings/settingsdialog.h index cd1fe101..3cbc3138 100644 --- a/app/settings/settingsdialog.h +++ b/app/settings/settingsdialog.h @@ -1,138 +1,138 @@ /* * 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 SETTINGSDIALOG_H #define SETTINGSDIALOG_H // local #include "../liblattedock/dock.h" // Qt #include #include #include #include #include #include namespace Ui { class SettingsDialog; } namespace KActivities { class Controller; } namespace Latte { -class DockCorona; +class Corona; class Layout; } namespace Latte { class SettingsDialog : public QDialog { Q_OBJECT public: - SettingsDialog(QWidget *parent, DockCorona *corona); + SettingsDialog(QWidget *parent, Latte::Corona *corona); ~SettingsDialog(); void setCurrentPage(Dock::LatteConfigPage page); QStringList activities(); QStringList availableActivities(); void requestImagesDialog(int row); void requestColorsDialog(int row); private slots: // auto connections void on_newButton_clicked(); void on_copyButton_clicked(); void on_downloadButton_clicked(); void on_lockedButton_clicked(); void on_pauseButton_clicked(); void on_removeButton_clicked(); void on_switchButton_clicked(); void on_importButton_clicked(); void on_exportButton_clicked(); void accept() override; void reject() override; void apply(); void restoreDefaults(); void updatePerLayoutButtonsState(); void layoutsChanged(); void itemChanged(QStandardItem *item); private: void addLayoutForFile(QString file, QString layoutName = QString(), bool newTempDirectory = true, bool showNotification = true); //! When an activity is closed for some reason the window manager hides and reshows //! the windows. This function prevents this because we don't want to delete the window //! on reject in such case. void blockDeleteOnActivityStopped(); void loadSettings(); void recalculateAvailableActivities(); void insertLayoutInfoAtRow(int row, QString path, QString color, QString textColor, QString name, bool menu, bool disabledBorders, QStringList activities, bool locked = false); void updateApplyButtonsState(); bool dataAreAccepted(); bool idExistsInModel(QString id); bool importLayoutsFromV1ConfigFile(QString file); bool nameExistsInModel(QString name); bool saveAllChanges(); int ascendingRowFor(QString name); QString uniqueTempDirectory(); QString uniqueLayoutName(QString name); QList currentSettings(); QStringList currentLayoutsSettings(); private: QStringList m_availableActivities; QStringList m_tempDirectories; QStringList m_initLayoutPaths; QButtonGroup *m_inMemoryButtons; QButtonGroup *m_mouseSensitivityButtons; QTimer m_activityClosedTimer; bool m_blockDeleteOnReject{false}; - DockCorona *m_corona{nullptr}; + Latte::Corona *m_corona{nullptr}; QStandardItemModel *m_model{nullptr}; Ui::SettingsDialog *ui; QHash m_layouts; QList o_settings; QStringList o_settingsLayouts; }; } #endif // SETTINGSDIALOG_H diff --git a/app/settings/universalsettings.cpp b/app/settings/universalsettings.cpp index b5be6807..af3450c3 100644 --- a/app/settings/universalsettings.cpp +++ b/app/settings/universalsettings.cpp @@ -1,573 +1,561 @@ /* * 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 "layoutmanager.h" #include "sortedactivitiesmodel.h" // Qt #include #include #include // KDE #include #include #define GLOBALSHORTCUTSCONFIG "kglobalshortcutsrc" #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); + 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::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); } UniversalSettings::~UniversalSettings() { saveConfig(); cleanupSettings(); if (m_runningActivitiesModel) { m_runningActivitiesModel->deleteLater(); } } void UniversalSettings::initGlobalShortcutsWatcher() { const QString globalShortcutsFilePath = QDir::homePath() + "/.config/" + GLOBALSHORTCUTSCONFIG; m_shortcutsConfigPtr = KSharedConfig::openConfig(globalShortcutsFilePath); KDirWatch::self()->addFile(globalShortcutsFilePath); connect(KDirWatch::self(), &KDirWatch::dirty, this, &UniversalSettings::shortcutsFileChanged, Qt::QueuedConnection); connect(KDirWatch::self(), &KDirWatch::created, this, &UniversalSettings::shortcutsFileChanged, Qt::QueuedConnection); } void UniversalSettings::shortcutsFileChanged(const QString &file) { if (!file.endsWith(GLOBALSHORTCUTSCONFIG)) { return; } m_shortcutsConfigPtr->reparseConfiguration(); parseGlobalShortcuts(); } void UniversalSettings::parseGlobalShortcuts() { KConfigGroup latteGroup = KConfigGroup(m_shortcutsConfigPtr, "lattedock"); //! make sure that latte dock records in global shortcuts where found correctly bool recordsExist{true}; if (!latteGroup.exists()) { recordsExist = false; } if (recordsExist) { for (int i = 1; i <= 19; ++i) { QString entry = "activate entry " + QString::number(i); if (!latteGroup.hasKey(entry)) { recordsExist = false; break; } } } if (recordsExist) { m_badgesForActivate.clear(); for (int i = 1; i <= 19; ++i) { QString entry = "activate entry " + QString::number(i); QStringList records = latteGroup.readEntry(entry, QStringList()); QString badge; if (records[0] != "none") { QStringList modifiers = records[0].split("+"); if (modifiers.count() >= 1) { badge = modifiers[modifiers.count() - 1]; //! when shortcut follows Meta+"Character" scheme if (modifiers.count() == 2 && modifiers[0] == "Meta") { badge = badge.toLower(); } else { badge = badge.toUpper(); } } } m_badgesForActivate << badge; } emit badgesForActivateChanged(); qDebug() << "badges updated to :: " << m_badgesForActivate; } } void UniversalSettings::load() { //! check if user has set the autostart option bool autostartUserSet = m_universalGroup.readEntry("userConfiguredAutostart", false); if (!autostartUserSet && !autostart()) { setAutostart(true); } //! load configuration loadConfig(); //! connections with other classes connect(m_corona->layoutManager(), &LayoutManager::viewColorizerChanged, this, &UniversalSettings::reconsiderActivitiesModel); //! load global shortcuts badges at startup initGlobalShortcutsWatcher(); parseGlobalShortcuts(); } 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::badgesForActivate() const { return m_badgesForActivate; } 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("/usr/share/applications/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()) { metaFile.copy(autostartFile.fileName()); //! I havent 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"); } } Dock::LayoutsMemoryUsage UniversalSettings::layoutsMemoryUsage() const { return m_memoryUsage; } void UniversalSettings::setLayoutsMemoryUsage(Dock::LayoutsMemoryUsage layoutsMemoryUsage) { if (m_memoryUsage == layoutsMemoryUsage) { return; } m_memoryUsage = layoutsMemoryUsage; emit layoutsMemoryUsageChanged(); } Dock::MouseSensitivity UniversalSettings::mouseSensitivity() const { return m_mouseSensitivity; } void UniversalSettings::setMouseSensitivity(Dock::MouseSensitivity sensitivity) { if (m_mouseSensitivity == sensitivity) { return; } m_mouseSensitivity = sensitivity; emit mouseSensitivityChanged(); } 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_screenTrackerInterval = m_universalGroup.readEntry("screenTrackerInterval", 2500); m_showInfoWindow = m_universalGroup.readEntry("showInfoWindow", true); m_memoryUsage = static_cast(m_universalGroup.readEntry("memoryUsage", (int)Dock::SingleLayout)); m_mouseSensitivity = static_cast(m_universalGroup.readEntry("mouseSensitivity", (int)Dock::HighSensitivity)); } 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("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() { - auto *dockCorona = qobject_cast(parent()); - - if (dockCorona) { - return dockCorona->kPackage().filePath("splitter"); - } - - return ""; + return m_corona->kPackage().filePath("splitter"); } QString UniversalSettings::trademarkIconPath() { - auto *dockCorona = qobject_cast(parent()); - - if (dockCorona) { - return dockCorona->kPackage().filePath("trademark"); - } - - return ""; + return m_corona->kPackage().filePath("trademark"); } QAbstractItemModel *UniversalSettings::runningActivitiesModel() const { return m_runningActivitiesModel; } void UniversalSettings::setRunningActivitiesModel(SortedActivitiesModel *model) { if (m_runningActivitiesModel == model) { return; } if (m_runningActivitiesModel) { m_runningActivitiesModel->deleteLater(); } m_runningActivitiesModel = model; emit runningActivitiesModelChanged(); } void UniversalSettings::enableActivitiesModel() { if (!m_runningActivitiesModel) { setRunningActivitiesModel(new SortedActivitiesModel({KActivities::Info::Running, KActivities::Info::Stopping}, this)); } } void UniversalSettings::disableActivitiesModel() { if (m_runningActivitiesModel) { setRunningActivitiesModel(nullptr); } } void UniversalSettings::reconsiderActivitiesModel() { if (m_corona->layoutManager()->hasColorizer()) { enableActivitiesModel(); } else { disableActivitiesModel(); } } float UniversalSettings::luminasFromFile(QString imageFile, int edge) { enableActivitiesModel(); return m_runningActivitiesModel->luminasFromFile(imageFile, edge); } 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); } } diff --git a/app/settings/universalsettings.h b/app/settings/universalsettings.h index 37153f35..5dad4047 100644 --- a/app/settings/universalsettings.h +++ b/app/settings/universalsettings.h @@ -1,198 +1,198 @@ /* * 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 UNIVERSALSETTINGS_H #define UNIVERSALSETTINGS_H // local -#include "dockcorona.h" +#include "../lattecorona.h" #include "../liblattedock/dock.h" // Qt #include #include #include #include #include // KDE #include #include class SortedActivitiesModel; namespace Latte { class LayoutManager; //! This class holds all the settings that are universally available //! independent of layouts class UniversalSettings : public QObject { Q_OBJECT Q_PROPERTY(bool autostart READ autostart WRITE setAutostart NOTIFY autostartChanged) Q_PROPERTY(bool showInfoWindow READ showInfoWindow WRITE setShowInfoWindow NOTIFY showInfoWindowChanged) Q_PROPERTY(QString currentLayoutName READ currentLayoutName WRITE setCurrentLayoutName NOTIFY currentLayoutNameChanged) Q_PROPERTY(QStringList badgesForActivate READ badgesForActivate NOTIFY badgesForActivateChanged) Q_PROPERTY(QStringList launchers READ launchers WRITE setLaunchers NOTIFY launchersChanged) Q_PROPERTY(Latte::Dock::MouseSensitivity mouseSensitivity READ mouseSensitivity WRITE setMouseSensitivity NOTIFY mouseSensitivityChanged) Q_PROPERTY(QAbstractItemModel *runningActivitiesModel READ runningActivitiesModel NOTIFY runningActivitiesModelChanged) Q_PROPERTY(QQmlListProperty screens READ screens) public: UniversalSettings(KSharedConfig::Ptr config, QObject *parent = nullptr); ~UniversalSettings() override; void load(); bool autostart() const; void setAutostart(bool state); bool canDisableBorders() const; void setCanDisableBorders(bool enable); bool metaForwardedToLatte() const; void forwardMetaToLatte(bool forward); bool showInfoWindow() const; void setShowInfoWindow(bool show); int version() const; void setVersion(int ver); int screenTrackerInterval() const; void setScreenTrackerInterval(int duration); QString currentLayoutName() const; void setCurrentLayoutName(QString layoutName); QString lastNonAssignedLayoutName() const; void setLastNonAssignedLayoutName(QString layoutName); QSize downloadWindowSize() const; void setDownloadWindowSize(QSize size); QSize layoutsWindowSize() const; void setLayoutsWindowSize(QSize size); QStringList badgesForActivate() const; QStringList layoutsColumnWidths() const; void setLayoutsColumnWidths(QStringList widths); QStringList launchers() const; void setLaunchers(QStringList launcherList); Dock::MouseSensitivity mouseSensitivity() const; void setMouseSensitivity(Dock::MouseSensitivity sensitivity); QAbstractItemModel *runningActivitiesModel() const; void setRunningActivitiesModel(SortedActivitiesModel *model); QQmlListProperty screens(); static int countScreens(QQmlListProperty *property); //! is needed by screens() static QScreen *atScreens(QQmlListProperty *property, int index); //! is needed by screens() public slots: Q_INVOKABLE QString splitterIconPath(); Q_INVOKABLE QString trademarkIconPath(); Q_INVOKABLE float luminasFromFile(QString imageFile, int edge); signals: void autostartChanged(); void badgesForActivateChanged(); void canDisableBordersChanged(); void currentLayoutNameChanged(); void downloadWindowSizeChanged(); void lastNonAssignedLayoutNameChanged(); void layoutsColumnWidthsChanged(); void layoutsWindowSizeChanged(); void launchersChanged(); void layoutsMemoryUsageChanged(); void mouseSensitivityChanged(); void runningActivitiesModelChanged(); void screenTrackerIntervalChanged(); void showInfoWindowChanged(); void versionChanged(); private slots: void loadConfig(); void saveConfig(); void reconsiderActivitiesModel(); void shortcutsFileChanged(const QString &file); private: void cleanupSettings(); void enableActivitiesModel(); void disableActivitiesModel(); void initGlobalShortcutsWatcher(); //! access user set global shortcuts for activate entries void parseGlobalShortcuts(); bool kwin_metaForwardedToLatte() const; void kwin_forwardMetaToLatte(bool forward); Dock::LayoutsMemoryUsage layoutsMemoryUsage() const; void setLayoutsMemoryUsage(Dock::LayoutsMemoryUsage layoutsMemoryUsage); private: bool m_canDisableBorders{false}; bool m_showInfoWindow{true}; //when there isnt a version it is an old universal file int m_version{1}; int m_screenTrackerInterval{2500}; QString m_currentLayoutName; QString m_lastNonAssignedLayoutName; QSize m_downloadWindowSize{800, 550}; QSize m_layoutsWindowSize{700, 450}; QStringList m_badgesForActivate{"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "z", "x", "c", "v", "b", "n", "m", ",", "."}; QStringList m_layoutsColumnWidths; QStringList m_launchers; Dock::LayoutsMemoryUsage m_memoryUsage; Dock::MouseSensitivity m_mouseSensitivity{Dock::HighSensitivity}; SortedActivitiesModel *m_runningActivitiesModel{nullptr}; - QPointer m_corona; + QPointer m_corona; KConfigGroup m_universalGroup; KSharedConfig::Ptr m_config; KSharedConfig::Ptr m_shortcutsConfigPtr; friend class LayoutManager; - friend class DockCorona; + friend class Latte::Corona; }; } #endif //UNIVERSALSETTINGS_H diff --git a/app/view/contextmenu.cpp b/app/view/contextmenu.cpp index 31837d56..db15a0ab 100644 --- a/app/view/contextmenu.cpp +++ b/app/view/contextmenu.cpp @@ -1,472 +1,472 @@ /* * 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 "contextmenu.h" // local #include "view.h" #include "visibilitymanager.h" -#include "../dockcorona.h" +#include "../lattecorona.h" #include "../layoutmanager.h" // Qt #include #include // KDE #include #include #include // Plasma #include #include #include #include #include namespace Latte { namespace ViewPart { ContextMenu::ContextMenu(Latte::View *view) : QObject(view), m_latteView(view) { } ContextMenu::~ContextMenu() { } QMenu *ContextMenu::menu() { return m_contextMenu; } void ContextMenu::menuAboutToHide() { if (!m_latteView) { return; } m_contextMenu = 0; if (!m_latteView->containment()->isUserConfiguring()) { m_latteView->visibility()->setBlockHiding(false); } emit menuChanged(); } bool ContextMenu::mousePressEvent(QMouseEvent *event) { //qDebug() << "Step -1 ..."; if (!event || !m_latteView->containment()) { return false; } //qDebug() << "Step 0..."; //even if the menu is executed synchronously, other events may be processed //by the qml incubator when plasma is loading, so we need to guard there if (m_contextMenu) { //qDebug() << "Step 0.5 ..."; m_contextMenu->close(); m_contextMenu = 0; emit menuChanged(); // PlasmaQuick::ContainmentView::mousePressEvent(event); return false; } //qDebug() << "1 ..."; QString trigger = Plasma::ContainmentActions::eventToString(event); if (trigger == "RightButton;NoModifier") { Plasma::ContainmentActions *plugin = m_latteView->containment()->containmentActions().value(trigger); if (!plugin || plugin->contextualActions().isEmpty()) { event->setAccepted(false); return false; } //qDebug() << "2 ..."; //the plugin can be a single action or a context menu //Don't have an action list? execute as single action //and set the event position as action data /*if (plugin->contextualActions().length() == 1) { QAction *action = plugin->contextualActions().at(0); action->setData(event->pos()); action->trigger(); event->accept(); return; }*/ //FIXME: very inefficient appletAt() implementation Plasma::Applet *applet = 0; bool inSystray = false; //! initialize the appletContainsMethod on the first right click if (!m_appletContainsMethod.isValid()) { updateAppletContainsMethod(); } foreach (Plasma::Applet *appletTemp, m_latteView->containment()->applets()) { PlasmaQuick::AppletQuickItem *ai = appletTemp->property("_plasma_graphicObject").value(); bool appletContainsMouse = false; if (m_appletContainsMethod.isValid()) { QVariant retVal; m_appletContainsMethod.invoke(m_appletContainsMethodItem, Qt::DirectConnection, Q_RETURN_ARG(QVariant, retVal) , Q_ARG(QVariant, appletTemp->id()), Q_ARG(QVariant, event->pos())); appletContainsMouse = retVal.toBool(); } else { appletContainsMouse = ai->contains(ai->mapFromItem(m_latteView->contentItem(), event->pos())); } if (ai && ai->isVisible() && appletContainsMouse) { applet = ai->applet(); KPluginMetaData meta = applet->kPackage().metadata(); //Try to find applets inside a systray if (meta.pluginId() == "org.kde.plasma.systemtray" || meta.pluginId() == "org.nomad.systemtray") { auto systrayId = applet->config().readEntry("SystrayContainmentId"); applet = 0; inSystray = true; Plasma::Containment *cont = containmentById(systrayId.toInt()); if (cont) { foreach (Plasma::Applet *appletCont, cont->applets()) { PlasmaQuick::AppletQuickItem *ai2 = appletCont->property("_plasma_graphicObject").value(); if (ai2 && ai2->isVisible() && ai2->contains(ai2->mapFromItem(m_latteView->contentItem(), event->pos()))) { applet = ai2->applet(); break; } } } break; } else { ai = 0; } } } if (!applet && !inSystray) { applet = m_latteView->containment(); } //qDebug() << "3 ..."; if (applet) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); //qDebug() << "3.5 ..."; if (!provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { //qDebug() << "4..."; QMenu *desktopMenu = new QMenu; //this is a workaround where Qt now creates the menu widget //in .exec before oxygen can polish it and set the following attribute desktopMenu->setAttribute(Qt::WA_TranslucentBackground); //end workaround if (desktopMenu->winId()) { desktopMenu->windowHandle()->setTransientParent(m_latteView); } desktopMenu->setAttribute(Qt::WA_DeleteOnClose); m_contextMenu = desktopMenu; //! deprecated old code that can be removed if the following plasma approach doesn't //! create any issues with context menu creation in Latte /*if (m_latteView->mouseGrabberItem()) { //workaround, this fixes for me most of the right click menu behavior m_latteView->mouseGrabberItem()->ungrabMouse(); return; }*/ //!plasma official code //this is a workaround where Qt will fail to realise a mouse has been released // this happens if a window which does not accept focus spawns a new window that takes focus and X grab // whilst the mouse is depressed // https://bugreports.qt.io/browse/QTBUG-59044 // this causes the next click to go missing //by releasing manually we avoid that situation auto ungrabMouseHack = [this]() { if (m_latteView->mouseGrabberItem()) { m_latteView->mouseGrabberItem()->ungrabMouse(); } }; //pre 5.8.0 QQuickWindow code is "item->grabMouse(); sendEvent(item, mouseEvent)" //post 5.8.0 QQuickWindow code is sendEvent(item, mouseEvent); item->grabMouse() if (QVersionNumber::fromString(qVersion()) > QVersionNumber(5, 8, 0)) { QTimer::singleShot(0, this, ungrabMouseHack); } else { ungrabMouseHack(); } //end workaround //!end of plasma official code(workaround) //qDebug() << "5 ..."; if (applet && applet != m_latteView->containment()) { //qDebug() << "5.3 ..."; emit applet->contextualActionsAboutToShow(); addAppletActions(desktopMenu, applet, event); } else { //qDebug() << "5.6 ..."; emit m_latteView->containment()->contextualActionsAboutToShow(); addContainmentActions(desktopMenu, event); } //this is a workaround where Qt now creates the menu widget //in .exec before oxygen can polish it and set the following attribute desktopMenu->setAttribute(Qt::WA_TranslucentBackground); //end workaround QPoint pos = event->globalPos(); if (applet) { //qDebug() << "6 ..."; desktopMenu->adjustSize(); if (m_latteView->screen()) { const QRect scr = m_latteView->screen()->geometry(); int smallStep = 3; int x = event->globalPos().x() + smallStep; int y = event->globalPos().y() + smallStep; //qDebug()<globalPos().x() > scr.center().x()) { x = event->globalPos().x() - desktopMenu->width() - smallStep; } if (event->globalPos().y() > scr.center().y()) { y = event->globalPos().y() - desktopMenu->height() - smallStep; } pos = QPoint(x, y); } } //qDebug() << "7..."; if (desktopMenu->isEmpty()) { //qDebug() << "7.5 ..."; delete desktopMenu; event->accept(); return false; } connect(desktopMenu, SIGNAL(aboutToHide()), this, SLOT(menuAboutToHide())); m_latteView->visibility()->setBlockHiding(true); desktopMenu->popup(pos); event->setAccepted(true); emit menuChanged(); return false; } //qDebug() << "8 ..."; } //qDebug() << "9 ..."; } //qDebug() << "10 ..."; emit menuChanged(); return true; // PlasmaQuick::ContainmentView::mousePressEvent(event); } //! update the appletContainsPos method from Panel view void ContextMenu::updateAppletContainsMethod() { for (QQuickItem *item : m_latteView->contentItem()->childItems()) { if (auto *metaObject = item->metaObject()) { // not using QMetaObject::invokeMethod to avoid warnings when calling // this on applets that don't have it or other child items since this // is pretty much trial and error. // Also, "var" arguments are treated as QVariant in QMetaObject int methodIndex = metaObject->indexOfMethod("appletContainsPos(QVariant,QVariant)"); if (methodIndex == -1) { continue; } m_appletContainsMethod = metaObject->method(methodIndex); m_appletContainsMethodItem = item; } } } void ContextMenu::addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event) { if (!m_latteView->containment()) { return; } foreach (QAction *action, applet->contextualActions()) { if (action) { desktopMenu->addAction(action); } } if (!applet->failedToLaunch()) { QAction *runAssociatedApplication = applet->actions()->action(QStringLiteral("run associated application")); if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { desktopMenu->addAction(runAssociatedApplication); } QAction *configureApplet = applet->actions()->action(QStringLiteral("configure")); if (configureApplet && configureApplet->isEnabled()) { desktopMenu->addAction(configureApplet); } QAction *appletAlternatives = applet->actions()->action(QStringLiteral("alternatives")); if (appletAlternatives && appletAlternatives->isEnabled() && m_latteView->containment()->isUserConfiguring()) { desktopMenu->addAction(appletAlternatives); } } QAction *containmentAction = desktopMenu->menuAction(); containmentAction->setText(i18nc("%1 is the name of the containment", "%1 Options", m_latteView->containment()->title())); addContainmentActions(containmentAction->menu(), event); if (!containmentAction->menu()->isEmpty()) { int enabled = 0; //count number of real actions QListIterator actionsIt(containmentAction->menu()->actions()); while (enabled < 3 && actionsIt.hasNext()) { QAction *action = actionsIt.next(); if (action->isVisible() && !action->isSeparator()) { ++enabled; } } desktopMenu->addSeparator(); if (enabled) { //if there is only one, don't create a submenu // if (enabled < 2) { foreach (QAction *action, containmentAction->menu()->actions()) { if (action->isVisible()) { desktopMenu->addAction(action); } } // } else { // desktopMenu->addMenu(containmentMenu); // } } } if (m_latteView->containment()->immutability() == Plasma::Types::Mutable && (m_latteView->containment()->containmentType() != Plasma::Types::PanelContainment || m_latteView->containment()->isUserConfiguring())) { QAction *closeApplet = applet->actions()->action(QStringLiteral("remove")); //qDebug() << "checking for removal" << closeApplet; if (closeApplet) { if (!desktopMenu->isEmpty()) { desktopMenu->addSeparator(); } //qDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible(); desktopMenu->addAction(closeApplet); } } } void ContextMenu::addContainmentActions(QMenu *desktopMenu, QEvent *event) { if (!m_latteView->containment()) { return; } if (m_latteView->containment()->corona()->immutability() != Plasma::Types::Mutable && !KAuthorized::authorizeAction(QStringLiteral("plasma/containment_actions"))) { //qDebug() << "immutability"; return; } //this is what ContainmentPrivate::prepareContainmentActions was const QString trigger = Plasma::ContainmentActions::eventToString(event); //"RightButton;NoModifier" Plasma::ContainmentActions *plugin = m_latteView->containment()->containmentActions().value(trigger); if (!plugin) { return; } if (plugin->containment() != m_latteView->containment()) { plugin->setContainment(m_latteView->containment()); // now configure it KConfigGroup cfg(m_latteView->containment()->corona()->config(), "ActionPlugins"); cfg = KConfigGroup(&cfg, QString::number(m_latteView->containment()->containmentType())); KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } QList actions = plugin->contextualActions(); foreach (auto act, actions) { if (act->menu()) { //this is a workaround where Qt now creates the menu widget //in .exec before oxygen can polish it and set the following attribute act->menu()->setAttribute(Qt::WA_TranslucentBackground); //end workaround if (act->menu()->winId()) { act->menu()->windowHandle()->setTransientParent(m_latteView); } } } desktopMenu->addActions(actions); return; } Plasma::Containment *ContextMenu::containmentById(uint id) { foreach (auto containment, m_latteView->corona()->containments()) { if (id == containment->id()) { return containment; } } return 0; } } } diff --git a/app/view/dockconfigview.cpp b/app/view/dockconfigview.cpp index 28b8f431..9f2909ce 100644 --- a/app/view/dockconfigview.cpp +++ b/app/view/dockconfigview.cpp @@ -1,585 +1,585 @@ /* * 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 "dockconfigview.h" // local #include "view.h" #include "panelshadows_p.h" -#include "../dockcorona.h" +#include "../lattecorona.h" #include "../layoutmanager.h" #include "../settings/universalsettings.h" #include "../wm/abstractwindowinterface.h" // Qt #include #include #include #include #include // KDE #include #include #include #include #include // Plasma #include namespace Latte { DockConfigView::DockConfigView(Plasma::Containment *containment, Latte::View *view, QWindow *parent) : PlasmaQuick::ConfigView(containment, parent), m_latteView(view) { - m_corona = qobject_cast(m_latteView->containment()->corona()); + m_corona = qobject_cast(m_latteView->containment()->corona()); setupWaylandIntegration(); setScreen(m_latteView->screen()); if (containment) { setIcon(qGuiApp->windowIcon()); } m_screenSyncTimer.setSingleShot(true); m_screenSyncTimer.setInterval(100); connections << connect(&m_screenSyncTimer, &QTimer::timeout, this, [this]() { setScreen(m_latteView->screen()); setFlags(wFlags()); syncGeometry(); syncSlideEffect(); }); connections << connect(m_latteView->visibility(), &VisibilityManager::modeChanged, this, &DockConfigView::syncGeometry); connections << connect(containment, &Plasma::Containment::immutabilityChanged, this, &DockConfigView::immutabilityChanged); m_thicknessSyncTimer.setSingleShot(true); m_thicknessSyncTimer.setInterval(200); connections << connect(&m_thicknessSyncTimer, &QTimer::timeout, this, [this]() { syncGeometry(); }); connections << connect(m_latteView, &Latte::View::normalThicknessChanged, [&]() { m_thicknessSyncTimer.start(); }); if (m_corona) { connections << connect(m_corona, SIGNAL(raiseDocksTemporaryChanged()), this, SIGNAL(raiseDocksTemporaryChanged())); } } DockConfigView::~DockConfigView() { qDebug() << "DockConfigView deleting ..."; deleteSecondaryWindow(); foreach (auto var, connections) { QObject::disconnect(var); } if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; } } void DockConfigView::init() { qDebug() << "dock config view : initialization started..."; setDefaultAlphaBuffer(true); setColor(Qt::transparent); PanelShadows::self()->addWindow(this); rootContext()->setContextProperty(QStringLiteral("dock"), m_latteView); rootContext()->setContextProperty(QStringLiteral("dockConfig"), this); if (m_corona) { rootContext()->setContextProperty(QStringLiteral("universalSettings"), m_corona->universalSettings()); rootContext()->setContextProperty(QStringLiteral("layoutManager"), m_corona->layoutManager()); } KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); kdeclarative.setTranslationDomain(QStringLiteral("latte-dock")); kdeclarative.setupBindings(); QByteArray tempFilePath = "lattedockconfigurationui"; updateEnabledBorders(); auto source = QUrl::fromLocalFile(m_latteView->containment()->corona()->kPackage().filePath(tempFilePath)); setSource(source); syncGeometry(); syncSlideEffect(); qDebug() << "dock config view : initialization ended..."; } inline Qt::WindowFlags DockConfigView::wFlags() const { return (flags() | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) & ~Qt::WindowDoesNotAcceptFocus; } QWindow *DockConfigView::secondaryWindow() { return m_secConfigView; } void DockConfigView::setAdvanced(bool advanced) { if (m_advanced == advanced) { return; } m_advanced = advanced; if (m_advanced) { createSecondaryWindow(); } else { deleteSecondaryWindow(); } } void DockConfigView::createSecondaryWindow() { //! do not proceed when secondary window is already created //! or when main dock settings window has not updated yet //! its geometry if (m_secConfigView || geometryWhenVisible().isNull()) { return; } QRect geometry = m_latteView->screenGeometry(); m_secConfigView = new DockSecConfigView(m_latteView, this); m_secConfigView->init(); if (m_secConfigView->geometryWhenVisible().intersects(geometryWhenVisible())) { setShowInlineProperties(true); m_secConfigView->hideConfigWindow(); } else { if (!KWindowSystem::isPlatformWayland()) { QTimer::singleShot(150, m_secConfigView, SLOT(show())); } else { QTimer::singleShot(150, [this]() { m_secConfigView->setVisible(true); }); } setShowInlineProperties(false); } } void DockConfigView::deleteSecondaryWindow() { if (m_secConfigView) { m_secConfigView->deleteLater(); } } QRect DockConfigView::geometryWhenVisible() const { return m_geometryWhenVisible; } void DockConfigView::syncGeometry() { if (!m_latteView->managedLayout() || !m_latteView->containment() || !rootObject()) return; const QSize size(rootObject()->width(), rootObject()->height()); setMaximumSize(size); setMinimumSize(size); resize(size); const auto location = m_latteView->containment()->location(); const auto sGeometry = m_latteView->screenGeometry(); int clearThickness = m_latteView->normalThickness() + m_latteView->fontPixelSize(); QPoint position{0, 0}; switch (m_latteView->containment()->formFactor()) { case Plasma::Types::Horizontal: { if (location == Plasma::Types::TopEdge) { position = {sGeometry.center().x() - size.width() / 2 , sGeometry.y() + clearThickness }; } else if (location == Plasma::Types::BottomEdge) { position = {sGeometry.center().x() - size.width() / 2 , sGeometry.y() + sGeometry.height() - clearThickness - size.height() }; } } break; case Plasma::Types::Vertical: { if (location == Plasma::Types::LeftEdge) { position = {sGeometry.x() + clearThickness , sGeometry.center().y() - size.height() / 2 }; } else if (location == Plasma::Types::RightEdge) { position = {sGeometry.x() + sGeometry.width() - clearThickness - size.width() , sGeometry.center().y() - size.height() / 2 }; } } break; default: qWarning() << "no sync geometry, wrong formFactor"; break; } updateEnabledBorders(); m_geometryWhenVisible = QRect(position.x(), position.y(), size.width(), size.height()); setPosition(position); if (m_shellSurface) { m_shellSurface->setPosition(position); } if (m_advanced) { //! consider even the secondary window can be create createSecondaryWindow(); } } void DockConfigView::syncSlideEffect() { if (!m_latteView->containment()) return; auto slideLocation = WindowSystem::Slide::None; switch (m_latteView->containment()->location()) { case Plasma::Types::TopEdge: slideLocation = WindowSystem::Slide::Top; break; case Plasma::Types::RightEdge: slideLocation = WindowSystem::Slide::Right; break; case Plasma::Types::BottomEdge: slideLocation = WindowSystem::Slide::Bottom; break; case Plasma::Types::LeftEdge: slideLocation = WindowSystem::Slide::Left; break; default: qDebug() << staticMetaObject.className() << "wrong location"; break; } m_corona->wm()->slideWindow(*this, slideLocation); } void DockConfigView::showEvent(QShowEvent *ev) { QQuickWindow::showEvent(ev); m_corona->wm()->setDockExtraFlags(*this); setFlags(wFlags()); m_corona->wm()->enableBlurBehind(*this); syncGeometry(); syncSlideEffect(); if (m_latteView && m_latteView->containment()) m_latteView->containment()->setUserConfiguring(true); m_screenSyncTimer.start(); QTimer::singleShot(400, this, &DockConfigView::syncGeometry); emit showSignal(); } void DockConfigView::hideEvent(QHideEvent *ev) { if (!m_latteView) { QQuickWindow::hideEvent(ev); return; } if (m_latteView->containment()) m_latteView->containment()->setUserConfiguring(false); QQuickWindow::hideEvent(ev); const auto mode = m_latteView->visibility()->mode(); const auto previousDockWinBehavior = (m_latteView->flags() & Qt::BypassWindowManagerHint) ? false : true; if (mode == Dock::AlwaysVisible || mode == Dock::WindowsGoBelow) { if (!previousDockWinBehavior) { m_latteView->managedLayout()->recreateDock(m_latteView->containment()); } } else if (m_latteView->dockWinBehavior() != previousDockWinBehavior) { m_latteView->managedLayout()->recreateDock(m_latteView->containment()); } deleteLater(); } void DockConfigView::focusOutEvent(QFocusEvent *ev) { Q_UNUSED(ev); const auto *focusWindow = qGuiApp->focusWindow(); if (focusWindow && (focusWindow->flags().testFlag(Qt::Popup) || focusWindow->flags().testFlag(Qt::ToolTip))) return; if (!m_blockFocusLost && (!m_secConfigView || (m_secConfigView && !m_secConfigView->isActive()))) { hideConfigWindow(); } } void DockConfigView::setupWaylandIntegration() { if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_latteView || !m_latteView->containment()) { // already setup return; } if (m_corona) { using namespace KWayland::Client; - PlasmaShell *interface = m_corona->waylandDockCoronaInterface(); + PlasmaShell *interface = m_corona->waylandCoronaInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(this); if (!s) { return; } qDebug() << "wayland dock window surface was created..."; m_shellSurface = interface->createSurface(s, this); m_shellSurface->setSkipTaskbar(true); syncGeometry(); } } bool DockConfigView::event(QEvent *e) { if (e->type() == QEvent::PlatformSurface) { if (auto pe = dynamic_cast(e)) { switch (pe->surfaceEventType()) { case QPlatformSurfaceEvent::SurfaceCreated: if (m_shellSurface) { break; } setupWaylandIntegration(); break; case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; qDebug() << "WAYLAND config window surface was deleted..."; PanelShadows::self()->removeWindow(this); } break; } } } return PlasmaQuick::ConfigView::event(e); } void DockConfigView::immutabilityChanged(Plasma::Types::ImmutabilityType type) { if (type != Plasma::Types::Mutable && isVisible()) hideConfigWindow(); } bool DockConfigView::sticker() const { return m_blockFocusLost; } void DockConfigView::setSticker(bool blockFocusLost) { if (m_blockFocusLost == blockFocusLost) return; m_blockFocusLost = blockFocusLost; } bool DockConfigView::showInlineProperties() const { return m_showInlineProperties; } void DockConfigView::setShowInlineProperties(bool show) { if (m_showInlineProperties == show) { return; } m_showInlineProperties = show; emit showInlinePropertiesChanged(); } void DockConfigView::addPanelSpacer() { if (m_latteView && m_latteView->containment()) { m_latteView->containment()->createApplet(QStringLiteral("org.kde.latte.spacer")); } } void DockConfigView::hideConfigWindow() { if (m_shellSurface) { //!NOTE: Avoid crash in wayland environment with qt5.9 close(); } else { hide(); } } void DockConfigView::updateLaunchersForGroup(int groupInt) { Dock::LaunchersGroup group = (Dock::LaunchersGroup)groupInt; //! when the layout/global launchers list is empty then the current dock launchers are used for them //! as a start point if (m_corona && m_latteView->managedLayout()) { if ((group == Dock::LayoutLaunchers && m_latteView->managedLayout()->launchers().isEmpty()) || (group == Dock::GlobalLaunchers && m_corona->universalSettings()->launchers().isEmpty())) { Plasma::Containment *c = m_latteView->containment(); const auto &applets = c->applets(); for (auto *applet : applets) { KPluginMetaData meta = applet->kPackage().metadata(); if (meta.pluginId() == "org.kde.latte.plasmoid") { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { // not using QMetaObject::invokeMethod to avoid warnings when calling // this on applets that don't have it or other child items since this // is pretty much trial and error. // Also, "var" arguments are treated as QVariant in QMetaObject int methodIndex = metaObject->indexOfMethod("getLauncherList()"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); QVariant launchers; if (method.invoke(item, Q_RETURN_ARG(QVariant, launchers))) { if (group == Dock::LayoutLaunchers) { m_latteView->managedLayout()->setLaunchers(launchers.toStringList()); } else if (group == Dock::GlobalLaunchers) { m_corona->universalSettings()->setLaunchers(launchers.toStringList()); } } } } } } } } } } //!BEGIN borders Plasma::FrameSvg::EnabledBorders DockConfigView::enabledBorders() const { return m_enabledBorders; } void DockConfigView::updateEnabledBorders() { if (!this->screen()) { return; } Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders; switch (m_latteView->location()) { case Plasma::Types::TopEdge: borders &= m_inReverse ? ~Plasma::FrameSvg::BottomBorder : ~Plasma::FrameSvg::TopBorder; break; case Plasma::Types::LeftEdge: borders &= ~Plasma::FrameSvg::LeftBorder; break; case Plasma::Types::RightEdge: borders &= ~Plasma::FrameSvg::RightBorder; break; case Plasma::Types::BottomEdge: borders &= m_inReverse ? ~Plasma::FrameSvg::TopBorder : ~Plasma::FrameSvg::BottomBorder; break; default: break; } if (m_enabledBorders != borders) { m_enabledBorders = borders; PanelShadows::self()->addWindow(this, m_enabledBorders); emit enabledBordersChanged(); } } //!END borders } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/view/dockconfigview.h b/app/view/dockconfigview.h index a7325b09..e2cecd1f 100644 --- a/app/view/dockconfigview.h +++ b/app/view/dockconfigview.h @@ -1,145 +1,145 @@ /* * 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 . */ #ifndef DOCKCONFIGVIEW_H #define DOCKCONFIGVIEW_H // local #include "docksecconfigview.h" #include "../plasmaquick/configview.h" #include "../../liblattedock/dock.h" //Qt #include #include #include #include // Plasma #include #include namespace Plasma { class Applet; class Containment; class FrameSvg; class Types; } namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { -class DockCorona; +class Corona; class View; } namespace Latte { class DockConfigView : public PlasmaQuick::ConfigView { Q_OBJECT //! used when the secondary config window can not be shown Q_PROPERTY(bool showInlineProperties READ showInlineProperties WRITE setShowInlineProperties NOTIFY showInlinePropertiesChanged) Q_PROPERTY(Plasma::FrameSvg::EnabledBorders enabledBorders READ enabledBorders NOTIFY enabledBordersChanged) public: enum ConfigViewType { PrimaryConfig = 0, SecondaryConfig }; DockConfigView(Plasma::Containment *containment, Latte::View *view, QWindow *parent = nullptr); ~DockConfigView() override; void init() override; Qt::WindowFlags wFlags() const; bool showInlineProperties() const; void setShowInlineProperties(bool show); bool sticker() const; QRect geometryWhenVisible() const; Plasma::FrameSvg::EnabledBorders enabledBorders() const; QWindow *secondaryWindow(); public slots: Q_INVOKABLE void addPanelSpacer(); Q_INVOKABLE void hideConfigWindow(); Q_INVOKABLE void setSticker(bool blockFocusLost); Q_INVOKABLE void syncGeometry(); Q_INVOKABLE void updateLaunchersForGroup(int groupInt); Q_INVOKABLE void setAdvanced(bool advanced); signals: void enabledBordersChanged(); void raiseDocksTemporaryChanged(); void showInlinePropertiesChanged(); void showSignal(); protected: void showEvent(QShowEvent *ev) override; void hideEvent(QHideEvent *ev) override; void focusOutEvent(QFocusEvent *ev) override; bool event(QEvent *e) override; void syncSlideEffect(); private slots: void immutabilityChanged(Plasma::Types::ImmutabilityType type); void updateEnabledBorders(); void createSecondaryWindow(); void deleteSecondaryWindow(); private: void setupWaylandIntegration(); bool m_advanced{false}; bool m_blockFocusLost{false}; bool m_blockFocusLostOnStartup{true}; bool m_inReverse{false}; //! it is used by the borders bool m_showInlineProperties{false}; QRect m_geometryWhenVisible; QPointer m_latteView; QPointer m_secConfigView; QTimer m_screenSyncTimer; QTimer m_thicknessSyncTimer; QList connections; Plasma::FrameSvg::EnabledBorders m_enabledBorders{Plasma::FrameSvg::AllBorders}; - DockCorona *m_corona{nullptr}; + Latte::Corona *m_corona{nullptr}; KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; }; } #endif //DOCKCONFIGVIEW_H // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/view/docksecconfigview.cpp b/app/view/docksecconfigview.cpp index c3a2452d..52445e5f 100644 --- a/app/view/docksecconfigview.cpp +++ b/app/view/docksecconfigview.cpp @@ -1,389 +1,389 @@ /* * 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 "docksecconfigview.h" // local #include "dockconfigview.h" #include "panelshadows_p.h" #include "view.h" -#include "../dockcorona.h" +#include "../lattecorona.h" #include "../wm/abstractwindowinterface.h" // Qt #include #include #include #include #include // KDE #include #include #include #include #include // Plasma #include namespace Latte { DockSecConfigView::DockSecConfigView(Latte::View *view, QWindow *parent) : QQuickView(nullptr), m_parent(parent), m_latteView(view) { - m_corona = qobject_cast(m_latteView->containment()->corona()); + m_corona = qobject_cast(m_latteView->containment()->corona()); setupWaylandIntegration(); setResizeMode(QQuickView::SizeViewToRootObject); setScreen(m_latteView->screen()); if (m_latteView && m_latteView->containment()) { setIcon(qGuiApp->windowIcon()); } m_screenSyncTimer.setSingleShot(true); m_screenSyncTimer.setInterval(100); connections << connect(&m_screenSyncTimer, &QTimer::timeout, this, [this]() { setScreen(m_latteView->screen()); setFlags(wFlags()); syncGeometry(); syncSlideEffect(); }); connections << connect(m_latteView->visibility(), &VisibilityManager::modeChanged, this, &DockSecConfigView::syncGeometry); m_thicknessSyncTimer.setSingleShot(true); m_thicknessSyncTimer.setInterval(200); connections << connect(&m_thicknessSyncTimer, &QTimer::timeout, this, [this]() { syncGeometry(); }); connections << connect(m_latteView, &Latte::View::normalThicknessChanged, [&]() { m_thicknessSyncTimer.start(); }); } DockSecConfigView::~DockSecConfigView() { qDebug() << "SecDockConfigView deleting ..."; foreach (auto var, connections) { QObject::disconnect(var); } if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; } } void DockSecConfigView::init() { qDebug() << "dock secondary config view : initialization started..."; setDefaultAlphaBuffer(true); setColor(Qt::transparent); PanelShadows::self()->addWindow(this); rootContext()->setContextProperty(QStringLiteral("dock"), m_latteView); rootContext()->setContextProperty(QStringLiteral("dockConfig"), this); rootContext()->setContextProperty(QStringLiteral("plasmoid"), m_latteView->containment()->property("_plasma_graphicObject").value()); KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); kdeclarative.setTranslationDomain(QStringLiteral("latte-dock")); kdeclarative.setupBindings(); QByteArray tempFilePath = "lattedocksecondaryconfigurationui"; updateEnabledBorders(); auto source = QUrl::fromLocalFile(m_latteView->containment()->corona()->kPackage().filePath(tempFilePath)); setSource(source); syncGeometry(); syncSlideEffect(); m_parent->requestActivate(); qDebug() << "dock secondary config view : initialization ended..."; } inline Qt::WindowFlags DockSecConfigView::wFlags() const { return (flags() | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) & ~Qt::WindowDoesNotAcceptFocus; } QRect DockSecConfigView::geometryWhenVisible() const { return m_geometryWhenVisible; } void DockSecConfigView::syncGeometry() { if (!m_latteView->managedLayout() || !m_latteView->containment() || !rootObject()) return; const QSize size(rootObject()->width(), rootObject()->height()); setMaximumSize(size); setMinimumSize(size); resize(size); const auto location = m_latteView->containment()->location(); const auto sGeometry = m_latteView->screenGeometry(); int clearThickness = m_latteView->normalThickness() + m_latteView->fontPixelSize(); int secondaryConfigSpacing = 2 * m_latteView->fontPixelSize(); QPoint position{0, 0}; switch (m_latteView->containment()->formFactor()) { case Plasma::Types::Horizontal: { if (location == Plasma::Types::TopEdge) { int yPos = m_latteView->y() + clearThickness; position = {m_latteView->x() + secondaryConfigSpacing, yPos}; } else if (location == Plasma::Types::BottomEdge) { int yPos; yPos = sGeometry.y() + sGeometry.height() - clearThickness - size.height(); position = {m_latteView->x() + m_latteView->width() - secondaryConfigSpacing - size.width(), yPos}; } } break; case Plasma::Types::Vertical: { if (location == Plasma::Types::LeftEdge) { position = {sGeometry.x() + clearThickness , m_latteView->y() + secondaryConfigSpacing }; } else if (location == Plasma::Types::RightEdge) { position = {sGeometry.x() + sGeometry.width() - clearThickness - size.width() , m_latteView->y() + secondaryConfigSpacing }; } } break; default: qWarning() << "no sync geometry, wrong formFactor"; break; } updateEnabledBorders(); m_geometryWhenVisible = QRect(position.x(), position.y(), size.width(), size.height()); setPosition(position); if (m_shellSurface) { m_shellSurface->setPosition(position); } } void DockSecConfigView::syncSlideEffect() { if (!m_latteView->containment()) return; auto slideLocation = WindowSystem::Slide::None; switch (m_latteView->containment()->location()) { case Plasma::Types::TopEdge: slideLocation = WindowSystem::Slide::Top; break; case Plasma::Types::RightEdge: slideLocation = WindowSystem::Slide::Right; break; case Plasma::Types::BottomEdge: slideLocation = WindowSystem::Slide::Bottom; break; case Plasma::Types::LeftEdge: slideLocation = WindowSystem::Slide::Left; break; default: qDebug() << staticMetaObject.className() << "wrong location"; break; } m_corona->wm()->slideWindow(*this, slideLocation); } void DockSecConfigView::showEvent(QShowEvent *ev) { QQuickWindow::showEvent(ev); m_corona->wm()->setDockExtraFlags(*this); setFlags(wFlags()); m_corona->wm()->enableBlurBehind(*this); syncGeometry(); syncSlideEffect(); m_screenSyncTimer.start(); QTimer::singleShot(400, this, &DockSecConfigView::syncGeometry); emit showSignal(); } void DockSecConfigView::focusOutEvent(QFocusEvent *ev) { Q_UNUSED(ev); const auto *focusWindow = qGuiApp->focusWindow(); if (focusWindow && (focusWindow->flags().testFlag(Qt::Popup) || focusWindow->flags().testFlag(Qt::ToolTip))) return; const auto parent = qobject_cast(m_parent); if (parent && !parent->sticker() && !parent->isActive()) { parent->hideConfigWindow(); } } void DockSecConfigView::setupWaylandIntegration() { if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_latteView || !m_latteView->containment()) { // already setup return; } if (m_corona) { using namespace KWayland::Client; - PlasmaShell *interface = m_corona->waylandDockCoronaInterface(); + PlasmaShell *interface = m_corona->waylandCoronaInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(this); if (!s) { return; } qDebug() << "wayland dock window surface was created..."; m_shellSurface = interface->createSurface(s, this); m_shellSurface->setSkipTaskbar(true); syncGeometry(); } } bool DockSecConfigView::event(QEvent *e) { if (e->type() == QEvent::PlatformSurface) { if (auto pe = dynamic_cast(e)) { switch (pe->surfaceEventType()) { case QPlatformSurfaceEvent::SurfaceCreated: if (m_shellSurface) { break; } setupWaylandIntegration(); break; case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; qDebug() << "WAYLAND secondary config window surface was deleted..."; PanelShadows::self()->removeWindow(this); } break; } } } return QQuickView::event(e); } void DockSecConfigView::hideConfigWindow() { if (m_shellSurface) { //!NOTE: Avoid crash in wayland environment with qt5.9 close(); } else { hide(); } } //!BEGIN borders Plasma::FrameSvg::EnabledBorders DockSecConfigView::enabledBorders() const { return m_enabledBorders; } void DockSecConfigView::updateEnabledBorders() { if (!this->screen()) { return; } Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders; switch (m_latteView->location()) { case Plasma::Types::TopEdge: borders &= ~Plasma::FrameSvg::TopBorder; break; case Plasma::Types::LeftEdge: borders &= ~Plasma::FrameSvg::LeftBorder; break; case Plasma::Types::RightEdge: borders &= ~Plasma::FrameSvg::RightBorder; break; case Plasma::Types::BottomEdge: borders &= ~Plasma::FrameSvg::BottomBorder; break; default: break; } if (m_enabledBorders != borders) { m_enabledBorders = borders; PanelShadows::self()->addWindow(this, m_enabledBorders); emit enabledBordersChanged(); } } //!END borders } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/view/docksecconfigview.h b/app/view/docksecconfigview.h index 894cb381..ed2a43aa 100644 --- a/app/view/docksecconfigview.h +++ b/app/view/docksecconfigview.h @@ -1,110 +1,110 @@ /* * 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 . */ #ifndef DOCKSECCONFIGVIEW_H #define DOCKSECCONFIGVIEW_H // local #include "../../liblattedock/dock.h" //Qt #include #include #include #include // Plasma #include #include namespace Plasma { class Applet; class Containment; class FrameSvg; class Types; } namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { -class DockCorona; +class Corona; class View; } namespace Latte { class DockSecConfigView : public QQuickView { Q_OBJECT Q_PROPERTY(Plasma::FrameSvg::EnabledBorders enabledBorders READ enabledBorders NOTIFY enabledBordersChanged) public: DockSecConfigView(Latte::View *view, QWindow *parent); ~DockSecConfigView() override; void init(); Qt::WindowFlags wFlags() const; QRect geometryWhenVisible() const; Plasma::FrameSvg::EnabledBorders enabledBorders() const; public slots: Q_INVOKABLE void hideConfigWindow(); Q_INVOKABLE void syncGeometry(); signals: void enabledBordersChanged(); void showSignal(); protected: void showEvent(QShowEvent *ev) override; void focusOutEvent(QFocusEvent *ev) override; bool event(QEvent *e) override; void syncSlideEffect(); private slots: void updateEnabledBorders(); private: void setupWaylandIntegration(); QRect m_geometryWhenVisible; QPointer m_latteView; QPointer m_parent; QTimer m_screenSyncTimer; QTimer m_thicknessSyncTimer; QList connections; Plasma::FrameSvg::EnabledBorders m_enabledBorders{Plasma::FrameSvg::AllBorders}; - DockCorona *m_corona{nullptr}; + Latte::Corona *m_corona{nullptr}; KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; }; } #endif //DOCKSECCONFIGVIEW_H // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/view/positioner.cpp b/app/view/positioner.cpp index 355cec3f..64293085 100644 --- a/app/view/positioner.cpp +++ b/app/view/positioner.cpp @@ -1,664 +1,664 @@ /* * 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 "positioner.h" // local #include "effects.h" #include "view.h" -#include "../dockcorona.h" +#include "../lattecorona.h" #include "../screenpool.h" #include "../settings/universalsettings.h" #include "../../liblattedock/dock.h" // Qt #include // KDE #include #include #include namespace Latte { namespace ViewPart { Positioner::Positioner(Latte::View *parent) : QObject(parent), m_view(parent) { m_screenSyncTimer.setSingleShot(true); m_screenSyncTimer.setInterval(2000); connect(&m_screenSyncTimer, &QTimer::timeout, this, &Positioner::reconsiderScreen); //! under X11 it was identified that windows many times especially under screen changes //! don't end up at the correct position and size. This timer will enforce repositionings //! and resizes every 500ms if the window hasn't end up to correct values and until this //! is achieved m_validateGeometryTimer.setSingleShot(true); m_validateGeometryTimer.setInterval(500); connect(&m_validateGeometryTimer, &QTimer::timeout, this, &Positioner::syncGeometry); - auto *dockCorona = qobject_cast(m_view->corona()); + auto *latteCorona = qobject_cast(m_view->corona()); - if (dockCorona) { - m_screenSyncTimer.setInterval(qMax(dockCorona->universalSettings()->screenTrackerInterval() - 500, 1000)); - connect(dockCorona->universalSettings(), &UniversalSettings::screenTrackerIntervalChanged, this, [this, dockCorona]() { - m_screenSyncTimer.setInterval(qMax(dockCorona->universalSettings()->screenTrackerInterval() - 500, 1000)); + if (latteCorona) { + m_screenSyncTimer.setInterval(qMax(latteCorona->universalSettings()->screenTrackerInterval() - 500, 1000)); + connect(latteCorona->universalSettings(), &UniversalSettings::screenTrackerIntervalChanged, this, [this, latteCorona]() { + m_screenSyncTimer.setInterval(qMax(latteCorona->universalSettings()->screenTrackerInterval() - 500, 1000)); }); - connect(dockCorona, &DockCorona::dockLocationChanged, this, [&]() { + connect(latteCorona, &Latte::Corona::dockLocationChanged, this, [&]() { //! check if an edge has been freed for a primary dock //! from another screen if (m_view->onPrimary()) { m_screenSyncTimer.start(); } }); } init(); } Positioner::~Positioner() { m_inDelete = true; m_screenSyncTimer.stop(); m_validateGeometryTimer.stop(); } void Positioner::init() { //! connections connect(this, &Positioner::screenGeometryChanged, this, &Positioner::syncGeometry); connect(m_view, &QQuickWindow::xChanged, this, &Positioner::validateDockGeometry); connect(m_view, &QQuickWindow::yChanged, this, &Positioner::validateDockGeometry); connect(m_view, &QQuickWindow::widthChanged, this, &Positioner::validateDockGeometry); connect(m_view, &QQuickWindow::heightChanged, this, &Positioner::validateDockGeometry); connect(m_view, &QQuickWindow::screenChanged, this, &Positioner::currentScreenChanged); connect(m_view, &QQuickWindow::screenChanged, this, &Positioner::screenChanged); connect(m_view, &Latte::View::absGeometryChanged, this, &Positioner::syncGeometry); connect(m_view, &Latte::View::behaveAsPlasmaPanelChanged, this, &Positioner::syncGeometry); connect(m_view, &Latte::View::maxThicknessChanged, this, &Positioner::syncGeometry); connect(m_view, &Latte::View::maxLengthChanged, this, &Positioner::syncGeometry); connect(m_view, &Latte::View::offsetChanged, this, &Positioner::syncGeometry); connect(m_view, &Latte::View::locationChanged, this, [&]() { updateFormFactor(); syncGeometry(); }); connect(m_view, &Latte::View::normalThicknessChanged, this, [&]() { if (m_view->behaveAsPlasmaPanel()) { syncGeometry(); } }); connect(m_view->effects(), &Latte::ViewPart::Effects::drawShadowsChanged, this, [&]() { if (!m_view->behaveAsPlasmaPanel()) { syncGeometry(); } }); connect(m_view->effects(), &Latte::ViewPart::Effects::innerShadowChanged, this, [&]() { if (m_view->behaveAsPlasmaPanel()) { syncGeometry(); } }); connect(qGuiApp, &QGuiApplication::screenAdded, this, &Positioner::screenChanged); connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &Positioner::screenChanged); initSignalingForLocationChangeSliding(); } int Positioner::currentScreenId() const { - auto *dockCorona = qobject_cast(m_view->corona()); + auto *latteCorona = qobject_cast(m_view->corona()); - if (dockCorona) { - return dockCorona->screenPool()->id(m_screenToFollowId); + if (latteCorona) { + return latteCorona->screenPool()->id(m_screenToFollowId); } return -1; } QString Positioner::currentScreenName() const { return m_screenToFollowId; } bool Positioner::setCurrentScreen(const QString id) { QScreen *nextScreen{qGuiApp->primaryScreen()}; if (id != "primary") { foreach (auto scr, qGuiApp->screens()) { if (scr && scr->name() == id) { nextScreen = scr; break; } } } if (m_screenToFollow == nextScreen) { return true; } if (nextScreen) { if (m_view->managedLayout()) { auto freeEdges = m_view->managedLayout()->freeEdges(nextScreen); if (!freeEdges.contains(m_view->location())) { return false; } else { m_goToScreen = nextScreen; //! asynchronous call in order to not crash from configwindow //! deletion from sliding out animation QTimer::singleShot(100, [this]() { emit hideDockDuringScreenChangeStarted(); }); } } } return true; } //! this function updates the dock's associated screen. //! updateScreenId = true, update also the m_screenToFollowId //! updateScreenId = false, do not update the m_screenToFollowId //! that way an explicit dock can be shown in another screen when //! there isnt a tasks dock running in the system and for that //! dock its first origin screen is stored and that way when //! that screen is reconnected the dock will return to its original //! place void Positioner::setScreenToFollow(QScreen *scr, bool updateScreenId) { if (!scr || (scr && (m_screenToFollow == scr) && (m_view->screen() == scr))) { return; } qDebug() << "setScreenToFollow() called for screen:" << scr->name() << " update:" << updateScreenId; m_screenToFollow = scr; if (updateScreenId) { m_screenToFollowId = scr->name(); } qDebug() << "adapting to screen..."; m_view->setScreen(scr); if (m_view->containment()) { m_view->containment()->reactToScreenChange(); } connect(scr, &QScreen::geometryChanged, this, &Positioner::screenGeometryChanged); syncGeometry(); m_view->updateAbsDockGeometry(true); qDebug() << "setScreenToFollow() ended..."; emit screenGeometryChanged(); emit currentScreenChanged(); } //! the main function which decides if this dock is at the //! correct screen void Positioner::reconsiderScreen() { if (m_inDelete) { return; } qDebug() << "reconsiderScreen() called..."; qDebug() << " Delayer "; foreach (auto scr, qGuiApp->screens()) { qDebug() << " D, found screen: " << scr->name(); } bool screenExists{false}; //!check if the associated screen is running foreach (auto scr, qGuiApp->screens()) { if (m_screenToFollowId == scr->name() || (m_view->onPrimary() && scr == qGuiApp->primaryScreen())) { screenExists = true; } } qDebug() << "dock screen exists ::: " << screenExists; //! 1.a primary dock must be always on the primary screen if (m_view->onPrimary() && (m_screenToFollowId != qGuiApp->primaryScreen()->name() || m_screenToFollow != qGuiApp->primaryScreen() || m_view->screen() != qGuiApp->primaryScreen())) { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; edges = m_view->managedLayout() ? m_view->managedLayout()->availableEdgesForView(qGuiApp->primaryScreen(), m_view) : edges; //change to primary screen only if the specific edge is free qDebug() << "updating the primary screen for dock..."; qDebug() << "available primary screen edges:" << edges; qDebug() << "dock location:" << m_view->location(); if (edges.contains(m_view->location())) { //! case 1 qDebug() << "reached case 1: of updating dock primary screen..."; setScreenToFollow(qGuiApp->primaryScreen()); } } else if (!m_view->onPrimary()) { //! 2.an explicit dock must be always on the correct associated screen //! there are cases that window manager misplaces the dock, this function //! ensures that this dock will return at its correct screen foreach (auto scr, qGuiApp->screens()) { if (scr && scr->name() == m_screenToFollowId) { qDebug() << "reached case 2: updating the explicit screen for dock..."; setScreenToFollow(scr); break; } } } syncGeometry(); qDebug() << "reconsiderScreen() ended..."; } void Positioner::screenChanged(QScreen *scr) { m_screenSyncTimer.start(); //! this is needed in order to update the struts on screen change //! and even though the geometry has been set correctly the offsets //! of the screen must be updated to the new ones if (m_view->visibility() && m_view->visibility()->mode() == Latte::Dock::AlwaysVisible) { m_view->updateAbsDockGeometry(true); } } void Positioner::syncGeometry() { if (!(m_view->screen() && m_view->containment()) || m_inDelete) { return; } bool found{false}; qDebug() << "syncGeometry() called..."; //! before updating the positioning and geometry of the dock //! we make sure that the dock is at the correct screen if (m_view->screen() != m_screenToFollow) { qDebug() << "Sync Geometry screens inconsistent!!!! "; if (m_screenToFollow) { qDebug() << "Sync Geometry screens inconsistent for m_screenToFollow:" << m_screenToFollow->name() << " dock screen:" << m_view->screen()->name(); } if (!m_screenSyncTimer.isActive()) { m_screenSyncTimer.start(); } } else { found = true; } //! if the dock isnt at the correct screen the calculations //! are not executed if (found) { //! compute the free screen rectangle for vertical panels only once //! this way the costly QRegion computations are calculated only once //! instead of two times (both inside the resizeWindow and the updatePosition) QRegion freeRegion;; QRect maximumRect; QRect availableScreenRect{m_view->screen()->geometry()}; if (m_view->formFactor() == Plasma::Types::Vertical) { QString layoutName = m_view->managedLayout() ? m_view->managedLayout()->name() : QString(); - auto dockCorona = qobject_cast(m_view->corona()); - int fixedScreen = m_view->onPrimary() ? dockCorona->screenPool()->primaryScreenId() : m_view->containment()->screen(); + auto latteCorona = qobject_cast(m_view->corona()); + int fixedScreen = m_view->onPrimary() ? latteCorona->screenPool()->primaryScreenId() : m_view->containment()->screen(); - freeRegion = dockCorona->availableScreenRegionWithCriteria(fixedScreen, layoutName); + freeRegion = latteCorona->availableScreenRegionWithCriteria(fixedScreen, layoutName); maximumRect = maximumNormalGeometry(); QRegion availableRegion = freeRegion.intersected(maximumRect); availableScreenRect = freeRegion.intersected(maximumRect).boundingRect(); float area = 0; //! it is used to choose which or the availableRegion rectangles will //! be the one representing dock geometry for (int i = 0; i < availableRegion.rectCount(); ++i) { QRect rect = availableRegion.rects().at(i); //! the area of each rectangle in calculated in squares of 50x50 //! this is a way to avoid enourmous numbers for area value float tempArea = (float)(rect.width() * rect.height()) / 2500; if (tempArea > area) { availableScreenRect = rect; area = tempArea; } } if (availableRegion.rectCount() > 1 && m_view->behaveAsPlasmaPanel()) { m_view->effects()->setForceDrawCenteredBorders(true); } else { m_view->effects()->setForceDrawCenteredBorders(false); } } else { m_view->effects()->setForceDrawCenteredBorders(false); } m_view->effects()->updateEnabledBorders(); resizeWindow(availableScreenRect); updatePosition(availableScreenRect); qDebug() << "syncGeometry() calculations for screen: " << m_view->screen()->name() << " _ " << m_view->screen()->geometry(); } qDebug() << "syncGeometry() ended..."; // qDebug() << "dock geometry:" << qRectToStr(geometry()); } void Positioner::validateDockGeometry() { if (m_view->geometry() != m_validGeometry) { m_validateGeometryTimer.start(); } } //! this is used mainly from vertical panels in order to //! to get the maximum geometry that can be used from the dock //! based on their alignment type and the location dock QRect Positioner::maximumNormalGeometry() { int xPos = 0; int yPos = 0; int maxHeight = m_view->maxLength() * m_view->screen()->geometry().height(); int maxWidth = m_view->normalThickness(); QRect maxGeometry; maxGeometry.setRect(0, 0, maxWidth, maxHeight); switch (m_view->location()) { case Plasma::Types::LeftEdge: xPos = m_view->screen()->geometry().x(); switch (m_view->alignment()) { case Latte::Dock::Top: yPos = m_view->screen()->geometry().y(); break; case Latte::Dock::Center: case Latte::Dock::Justify: yPos = qMax(m_view->screen()->geometry().center().y() - maxHeight / 2, m_view->screen()->geometry().y()); break; case Latte::Dock::Bottom: yPos = m_view->screen()->geometry().bottom() - maxHeight + 1; break; } maxGeometry.setRect(xPos, yPos, maxWidth, maxHeight); break; case Plasma::Types::RightEdge: xPos = m_view->screen()->geometry().right() - maxWidth + 1; switch (m_view->alignment()) { case Latte::Dock::Top: yPos = m_view->screen()->geometry().y(); break; case Latte::Dock::Center: case Latte::Dock::Justify: yPos = qMax(m_view->screen()->geometry().center().y() - maxHeight / 2, m_view->screen()->geometry().y()); break; case Latte::Dock::Bottom: yPos = m_view->screen()->geometry().bottom() - maxHeight + 1; break; } maxGeometry.setRect(xPos, yPos, maxWidth, maxHeight); break; default: //! bypass clang warnings break; } //! this is needed in order to preserve that the top dock will be above //! the others in case flag bypasswindowmanagerhint hasn't be set, //! such a case is the AlwaysVisible mode if (m_view->location() == Plasma::Types::TopEdge) { KWindowSystem::setState(m_view->winId(), NET::KeepAbove); } else { KWindowSystem::clearState(m_view->winId(), NET::KeepAbove); } return maxGeometry; } void Positioner::updatePosition(QRect availableScreenRect) { QRect screenGeometry{availableScreenRect}; QPoint position; position = {0, 0}; const auto length = [&](int length) -> int { float offs = static_cast(m_view->offset()); return static_cast(length * ((1 - m_view->maxLength()) / 2) + length * (offs / 100)); }; int cleanThickness = m_view->normalThickness() - m_view->effects()->innerShadow(); switch (m_view->location()) { case Plasma::Types::TopEdge: if (m_view->behaveAsPlasmaPanel()) { position = {screenGeometry.x() + length(screenGeometry.width()), screenGeometry.y()}; } else { position = {screenGeometry.x(), screenGeometry.y()}; } break; case Plasma::Types::BottomEdge: if (m_view->behaveAsPlasmaPanel()) { position = {screenGeometry.x() + length(screenGeometry.width()), screenGeometry.y() + screenGeometry.height() - cleanThickness }; } else { position = {screenGeometry.x(), screenGeometry.y() + screenGeometry.height() - m_view->height()}; } break; case Plasma::Types::RightEdge: if (m_view->behaveAsPlasmaPanel() && !m_view->mask().isNull()) { position = {availableScreenRect.right() - cleanThickness + 1, availableScreenRect.y() + length(availableScreenRect.height()) }; } else { position = {availableScreenRect.right() - m_view->width() + 1, availableScreenRect.y()}; } break; case Plasma::Types::LeftEdge: if (m_view->behaveAsPlasmaPanel() && !m_view->mask().isNull()) { position = {availableScreenRect.x(), availableScreenRect.y() + length(availableScreenRect.height())}; } else { position = {availableScreenRect.x(), availableScreenRect.y()}; } break; default: qWarning() << "wrong location, couldn't update the panel position" << m_view->location(); } m_validGeometry.setTopLeft(position); m_view->setPosition(position); if (m_view->surface()) { m_view->surface()->setPosition(position); } } void Positioner::resizeWindow(QRect availableScreenRect) { QSize screenSize = m_view->screen()->size(); QSize size = (m_view->formFactor() == Plasma::Types::Vertical) ? QSize(m_view->maxThickness(), availableScreenRect.height()) : QSize(screenSize.width(), m_view->maxThickness()); if (m_view->formFactor() == Plasma::Types::Vertical) { //qDebug() << "MAXIMUM RECT :: " << maximumRect << " - AVAILABLE RECT :: " << availableRect; if (m_view->behaveAsPlasmaPanel()) { size.setWidth(m_view->normalThickness()); size.setHeight(static_cast(m_view->maxLength() * availableScreenRect.height())); } } else { if (m_view->behaveAsPlasmaPanel()) { size.setWidth(static_cast(m_view->maxLength() * screenSize.width())); size.setHeight(m_view->normalThickness()); } } m_validGeometry.setSize(size); m_view->setMinimumSize(size); m_view->setMaximumSize(size); m_view->resize(size); if (m_view->formFactor() == Plasma::Types::Horizontal && m_view->corona()) { emit m_view->corona()->availableScreenRectChanged(); } } void Positioner::updateFormFactor() { if (!m_view->containment()) return; switch (m_view->location()) { case Plasma::Types::TopEdge: case Plasma::Types::BottomEdge: m_view->containment()->setFormFactor(Plasma::Types::Horizontal); break; case Plasma::Types::LeftEdge: case Plasma::Types::RightEdge: m_view->containment()->setFormFactor(Plasma::Types::Vertical); break; default: qWarning() << "wrong location, couldn't update the panel position" << m_view->location(); } } void Positioner::initSignalingForLocationChangeSliding() { //! signals to handle the sliding-in/out during location changes connect(this, &Positioner::hideDockDuringLocationChangeStarted, this, &Positioner::onHideWindowsForSlidingOut); connect(m_view, &View::locationChanged, this, [&]() { if (m_goToLocation != Plasma::Types::Floating) { m_goToLocation = Plasma::Types::Floating; QTimer::singleShot(100, [this]() { m_view->effects()->setAnimationsBlocked(false); emit showDockAfterLocationChangeFinished(); m_view->showSettingsWindow(); if (m_view->managedLayout()) { m_view->managedLayout()->syncLatteViewsToScreens(); } }); } }); //! signals to handle the sliding-in/out during screen changes connect(this, &Positioner::hideDockDuringScreenChangeStarted, this, &Positioner::onHideWindowsForSlidingOut); connect(this, &Positioner::currentScreenChanged, this, [&]() { if (m_goToScreen) { m_goToScreen = nullptr; QTimer::singleShot(100, [this]() { m_view->effects()->setAnimationsBlocked(false); emit showDockAfterScreenChangeFinished(); m_view->showSettingsWindow(); if (m_view->managedLayout()) { m_view->managedLayout()->syncLatteViewsToScreens(); } }); } }); //! signals to handle the sliding-in/out during moving to another layout connect(this, &Positioner::hideDockDuringMovingToLayoutStarted, this, &Positioner::onHideWindowsForSlidingOut); connect(m_view, &View::managedLayoutChanged, this, [&]() { if (!m_moveToLayout.isEmpty() && m_view->managedLayout()) { m_moveToLayout = ""; QTimer::singleShot(100, [this]() { m_view->effects()->setAnimationsBlocked(false); emit showDockAfterMovingToLayoutFinished(); m_view->showSettingsWindow(); }); } }); //! ---- both cases ---- !// //! this is used for both location and screen change cases, this signal //! is send when the sliding-out animation has finished connect(this, &Positioner::hideDockDuringLocationChangeFinished, this, [&]() { m_view->effects()->setAnimationsBlocked(true); if (m_goToLocation != Plasma::Types::Floating) { m_view->setLocation(m_goToLocation); } else if (m_goToScreen) { setScreenToFollow(m_goToScreen); } else if (!m_moveToLayout.isEmpty()) { m_view->moveToLayout(m_moveToLayout); } }); } bool Positioner::inLocationChangeAnimation() { return ((m_goToLocation != Plasma::Types::Floating) || (m_moveToLayout != "") || m_goToScreen); } void Positioner::hideDockDuringLocationChange(int goToLocation) { m_goToLocation = static_cast(goToLocation); emit hideDockDuringLocationChangeStarted(); } void Positioner::hideDockDuringMovingToLayout(QString layoutName) { m_moveToLayout = layoutName; emit hideDockDuringMovingToLayoutStarted(); } } } diff --git a/app/view/screenedgeghostwindow.cpp b/app/view/screenedgeghostwindow.cpp index 6d5b4215..e44a133f 100644 --- a/app/view/screenedgeghostwindow.cpp +++ b/app/view/screenedgeghostwindow.cpp @@ -1,241 +1,241 @@ /* * 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 "screenedgeghostwindow.h" // local #include "view.h" -#include "../dockcorona.h" +#include "../lattecorona.h" // Qt #include #include #include #include // KDE #include #include #include // X11 #include namespace Latte { ScreenEdgeGhostWindow::ScreenEdgeGhostWindow(Latte::View *view) : m_latteView(view) { setColor(QColor(Qt::transparent)); setDefaultAlphaBuffer(true); setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus); m_fixGeometryTimer.setSingleShot(true); m_fixGeometryTimer.setInterval(500); connect(&m_fixGeometryTimer, &QTimer::timeout, this, &ScreenEdgeGhostWindow::fixGeometry); connect(this, &QQuickView::xChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); connect(this, &QQuickView::yChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); connect(this, &QQuickView::widthChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); connect(this, &QQuickView::heightChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); connect(m_latteView, &Latte::View::absGeometryChanged, this, &ScreenEdgeGhostWindow::updateGeometry); connect(m_latteView, &Latte::View::screenGeometryChanged, this, &ScreenEdgeGhostWindow::updateGeometry); connect(m_latteView, &Latte::View::locationChanged, this, &ScreenEdgeGhostWindow::updateGeometry); connect(m_latteView, &QQuickView::screenChanged, this, [this]() { setScreen(m_latteView->screen()); updateGeometry(); }); if (!KWindowSystem::isPlatformWayland()) { connect(this, &QWindow::visibleChanged, this, [&]() { //! IMPORTANT!!! ::: This fixes a bug when closing an Activity all docks from all Activities are //! disappearing! With this they reappear!!! if (m_latteView && m_latteView->managedLayout()) { if (!isVisible()) { QTimer::singleShot(100, [this]() { if (!m_inDelete && m_latteView && m_latteView->managedLayout() && !isVisible()) { setVisible(true); } }); QTimer::singleShot(1500, [this]() { if (!m_inDelete && m_latteView && m_latteView->managedLayout() && !isVisible()) { setVisible(true); } }); } else { //! For some reason when the window is hidden in the edge under X11 afterwards //! is losing its window flags if (!m_inDelete) { KWindowSystem::setType(winId(), NET::Dock); KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); KWindowSystem::setOnAllDesktops(winId(), true); } } } }); } setupWaylandIntegration(); setScreen(m_latteView->screen()); setVisible(true); updateGeometry(); hideWithMask(); } ScreenEdgeGhostWindow::~ScreenEdgeGhostWindow() { m_inDelete = true; m_latteView = nullptr; if (m_shellSurface) { delete m_shellSurface; } } int ScreenEdgeGhostWindow::location() { return (int)m_latteView->location(); } Latte::View *ScreenEdgeGhostWindow::parentDock() { return m_latteView; } KWayland::Client::PlasmaShellSurface *ScreenEdgeGhostWindow::surface() { return m_shellSurface; } void ScreenEdgeGhostWindow::updateGeometry() { QRect newGeometry; int thickness{1}; if (m_latteView->location() == Plasma::Types::BottomEdge) { newGeometry.setX(m_latteView->absGeometry().left()); newGeometry.setY(m_latteView->screenGeometry().bottom() - thickness); } else if (m_latteView->location() == Plasma::Types::TopEdge) { newGeometry.setX(m_latteView->absGeometry().left()); newGeometry.setY(m_latteView->screenGeometry().top()); } else if (m_latteView->location() == Plasma::Types::LeftEdge) { newGeometry.setX(m_latteView->screenGeometry().left()); newGeometry.setY(m_latteView->absGeometry().top()); } else if (m_latteView->location() == Plasma::Types::RightEdge) { newGeometry.setX(m_latteView->screenGeometry().right() - thickness); newGeometry.setY(m_latteView->absGeometry().top()); } if (m_latteView->formFactor() == Plasma::Types::Horizontal) { newGeometry.setWidth(qMin(m_latteView->absGeometry().width(), m_latteView->screenGeometry().width() - 1)); newGeometry.setHeight(thickness + 1); } else { newGeometry.setWidth(thickness + 1); newGeometry.setHeight(qMin(m_latteView->absGeometry().height(), m_latteView->screenGeometry().height() - 1)); } m_calculatedGeometry = newGeometry; fixGeometry(); } void ScreenEdgeGhostWindow::fixGeometry() { if (!m_calculatedGeometry.isEmpty() && (m_calculatedGeometry.x() != x() || m_calculatedGeometry.y() != y() || m_calculatedGeometry.width() != width() || m_calculatedGeometry.height() != height())) { setMinimumSize(m_calculatedGeometry.size()); setMaximumSize(m_calculatedGeometry.size()); resize(m_calculatedGeometry.size()); setPosition(m_calculatedGeometry.x(), m_calculatedGeometry.y()); if (m_shellSurface) { m_shellSurface->setPosition(m_calculatedGeometry.topLeft()); } } } void ScreenEdgeGhostWindow::startGeometryTimer() { m_fixGeometryTimer.start(); } void ScreenEdgeGhostWindow::setupWaylandIntegration() { if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_latteView || !m_latteView->containment()) { // already setup return; } - if (DockCorona *c = qobject_cast(m_latteView->containment()->corona())) { + if (Latte::Corona *c = qobject_cast(m_latteView->containment()->corona())) { using namespace KWayland::Client; - PlasmaShell *interface = c->waylandDockCoronaInterface(); + PlasmaShell *interface = c->waylandCoronaInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(this); if (!s) { return; } qDebug() << "wayland screen edge ghost window surface was created..."; m_shellSurface = interface->createSurface(s, this); m_shellSurface->setSkipTaskbar(true); m_shellSurface->setPanelTakesFocus(false); m_shellSurface->setRole(PlasmaShellSurface::Role::Panel); m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AutoHide); } } bool ScreenEdgeGhostWindow::event(QEvent *e) { if (e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) { emit containsMouseChanged(true); } else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave) { emit containsMouseChanged(false); } return QQuickView::event(e); } void ScreenEdgeGhostWindow::hideWithMask() { QRect maskGeometry{0, 0, 1, 1}; setMask(maskGeometry); } void ScreenEdgeGhostWindow::showWithMask() { setMask(QRect()); } } diff --git a/app/view/view.cpp b/app/view/view.cpp index 551e3bee..7aef45d1 100644 --- a/app/view/view.cpp +++ b/app/view/view.cpp @@ -1,1071 +1,1071 @@ /* * 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 "view.h" // local #include "contextmenu.h" #include "dockconfigview.h" #include "effects.h" #include "positioner.h" #include "visibilitymanager.h" -#include "../dockcorona.h" +#include "../lattecorona.h" #include "../layout.h" #include "../layoutmanager.h" #include "../screenpool.h" #include "../plasmathemeextended.h" #include "../settings/universalsettings.h" #include "../../liblattedock/extras.h" // Qt #include #include #include #include #include #include // KDe #include #include #include #include #include // Plasma #include #include #include namespace Latte { //! both alwaysVisible and dockWinBehavior are passed through corona because //! during the dock window creation containment hasn't been set, but these variables //! are needed in order for window flags to be set correctly View::View(Plasma::Corona *corona, QScreen *targetScreen, bool dockWindowBehavior) : PlasmaQuick::ContainmentView(corona), m_contextMenu(new ViewPart::ContextMenu(this)), m_effects(new ViewPart::Effects(this)), m_positioner(new ViewPart::Positioner(this)) //needs to be created after Effects becuase it catches some of its signals { setTitle(corona->kPackage().metadata().name()); setIcon(qGuiApp->windowIcon()); setResizeMode(QuickViewSharedEngine::SizeRootObjectToView); setColor(QColor(Qt::transparent)); setClearBeforeRendering(true); const auto flags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus; if (dockWindowBehavior) { setFlags(flags); } else { setFlags(flags | Qt::BypassWindowManagerHint); } KWindowSystem::setOnAllDesktops(winId(), true); if (targetScreen) m_positioner->setScreenToFollow(targetScreen); else m_positioner->setScreenToFollow(qGuiApp->primaryScreen()); connect(this, &View::containmentChanged , this, [ &, dockWindowBehavior]() { qDebug() << "dock view c++ containment changed 1..."; if (!this->containment()) return; qDebug() << "dock view c++ containment changed 2..."; //! First load default values from file restoreConfig(); //! Afterwards override that values in case during creation something different is needed setDockWinBehavior(dockWindowBehavior); //! Check the screen assigned to this dock reconsiderScreen(); if (!m_visibility) { m_visibility = new VisibilityManager(this); connect(m_visibility, &VisibilityManager::isHiddenChanged, this, [&]() { if (m_visibility->isHidden()) { deactivateApplets(); } }); } connect(this->containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), SLOT(statusChanged(Plasma::Types::ItemStatus))); }, Qt::DirectConnection); - auto *dockCorona = qobject_cast(this->corona()); + auto *latteCorona = qobject_cast(this->corona()); - if (dockCorona) { - connect(dockCorona, &DockCorona::dockLocationChanged, this, &View::dockLocationChanged); + if (latteCorona) { + connect(latteCorona, &Latte::Corona::dockLocationChanged, this, &View::dockLocationChanged); } } View::~View() { m_inDelete = true; disconnect(corona(), &Plasma::Corona::availableScreenRectChanged, this, &View::availableScreenRectChanged); disconnect(containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), this, SLOT(statusChanged(Plasma::Types::ItemStatus))); qDebug() << "dock view deleting..."; rootContext()->setContextProperty(QStringLiteral("dock"), nullptr); rootContext()->setContextProperty(QStringLiteral("themeExtended"), nullptr); rootContext()->setContextProperty(QStringLiteral("universalSettings"), nullptr); rootContext()->setContextProperty(QStringLiteral("layoutManager"), nullptr); //! this disconnect does not free up connections correctly when //! latteView is deleted. A crash for this example is the following: //! switch to Alternative Session and disable compositing, //! the signal creating the crash was probably from deleted //! windows. //! this->disconnect(); if (m_configView) { m_configView->setVisible(false);//hide(); } if (m_contextMenu) { delete m_contextMenu; } //needs to be deleted before Effects becuase it catches some of its signals if (m_positioner) { delete m_positioner; } if (m_effects) { delete m_effects; } if (m_visibility) delete m_visibility; } void View::init() { connect(this, &QQuickWindow::xChanged, this, &View::xChanged); connect(this, &QQuickWindow::xChanged, this, &View::updateAbsDockGeometry); connect(this, &QQuickWindow::yChanged, this, &View::yChanged); connect(this, &QQuickWindow::yChanged, this, &View::updateAbsDockGeometry); connect(this, &QQuickWindow::widthChanged, this, &View::widthChanged); connect(this, &QQuickWindow::widthChanged, this, &View::updateAbsDockGeometry); connect(this, &QQuickWindow::heightChanged, this, &View::heightChanged); connect(this, &QQuickWindow::heightChanged, this, &View::updateAbsDockGeometry); connect(corona(), &Plasma::Corona::availableScreenRectChanged, this, &View::availableScreenRectChanged); connect(this, &View::dockWinBehaviorChanged, this, &View::saveConfig); connect(this, &View::onPrimaryChanged, this, &View::saveConfig); connect(this, &View::isPreferredForShortcutsChanged, this, &View::saveConfig); connect(this, SIGNAL(normalThicknessChanged()), corona(), SIGNAL(availableScreenRectChanged())); connect(m_positioner, &ViewPart::Positioner::onHideWindowsForSlidingOut, this, &View::hideWindowsForSlidingOut); connect(m_positioner, &ViewPart::Positioner::screenGeometryChanged, this, &View::screenGeometryChanged); connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::contextMenuIsShownChanged); ///!!!!! rootContext()->setContextProperty(QStringLiteral("dock"), this); - auto *dockCorona = qobject_cast(this->corona()); + auto *latteCorona = qobject_cast(this->corona()); - if (dockCorona) { - rootContext()->setContextProperty(QStringLiteral("universalSettings"), dockCorona->universalSettings()); - rootContext()->setContextProperty(QStringLiteral("layoutManager"), dockCorona->layoutManager()); - rootContext()->setContextProperty(QStringLiteral("themeExtended"), dockCorona->themeExtended()); + if (latteCorona) { + rootContext()->setContextProperty(QStringLiteral("universalSettings"), latteCorona->universalSettings()); + rootContext()->setContextProperty(QStringLiteral("layoutManager"), latteCorona->layoutManager()); + rootContext()->setContextProperty(QStringLiteral("themeExtended"), latteCorona->themeExtended()); } setSource(corona()->kPackage().filePath("lattedockui")); // setVisible(true); m_positioner->syncGeometry(); if (!KWindowSystem::isPlatformWayland()) { setVisible(true); } qDebug() << "SOURCE:" << source(); } bool View::inDelete() const { return m_inDelete; } void View::disconnectSensitiveSignals() { disconnect(corona(), &Plasma::Corona::availableScreenRectChanged, this, &View::availableScreenRectChanged); setManagedLayout(nullptr); if (visibility()) { visibility()->setEnabledDynamicBackground(false); } } void View::availableScreenRectChanged() { if (m_inDelete) return; if (formFactor() == Plasma::Types::Vertical) { m_positioner->syncGeometry(); } } void View::setupWaylandIntegration() { if (m_shellSurface) return; - if (DockCorona *c = qobject_cast(corona())) { + if (Latte::Corona *c = qobject_cast(corona())) { using namespace KWayland::Client; - PlasmaShell *interface {c->waylandDockCoronaInterface()}; + PlasmaShell *interface {c->waylandCoronaInterface()}; if (!interface) return; Surface *s{Surface::fromWindow(this)}; if (!s) return; m_shellSurface = interface->createSurface(s, this); qDebug() << "WAYLAND dock window surface was created..."; m_shellSurface->setSkipTaskbar(true); m_shellSurface->setRole(PlasmaShellSurface::Role::Panel); m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::WindowsGoBelow); } } KWayland::Client::PlasmaShellSurface *View::surface() { return m_shellSurface; } //! the main function which decides if this dock is at the //! correct screen void View::reconsiderScreen() { m_positioner->reconsiderScreen(); } void View::copyDock() { m_managedLayout->copyDock(containment()); } void View::removeDock() { if (m_managedLayout && m_managedLayout->viewsCount() > 1) { QAction *removeAct = this->containment()->actions()->action(QStringLiteral("remove")); if (removeAct) { removeAct->trigger(); } } } bool View::settingsWindowIsShown() { auto configView = qobject_cast(m_configView); return (configView != nullptr); } void View::showSettingsWindow() { showConfigurationInterface(containment()); applyActivitiesToWindows(); } void View::showConfigurationInterface(Plasma::Applet *applet) { if (!applet || !applet->containment()) return; Plasma::Containment *c = qobject_cast(applet); if (m_configView && c && c->isContainment() && c == this->containment()) { if (m_configView->isVisible()) { m_configView->setVisible(false); //m_configView->hide(); } else { m_configView->setVisible(true); //m_configView->show(); } return; } else if (m_configView) { if (m_configView->applet() == applet) { m_configView->setVisible(true); //m_configView->show(); m_configView->requestActivate(); return; } else { m_configView->setVisible(false); //m_configView->hide(); m_configView->deleteLater(); } } bool delayConfigView = false; if (c && containment() && c->isContainment() && c->id() == this->containment()->id()) { m_configView = new DockConfigView(c, this); delayConfigView = true; } else { m_configView = new PlasmaQuick::ConfigView(applet); } m_configView.data()->init(); if (!delayConfigView) { m_configView->setVisible(true); //m_configView.data()->show(); } else { //add a timer for showing the configuration window the first time it is //created in order to give the containmnent's layouts the time to //calculate the window's height if (!KWindowSystem::isPlatformWayland()) { QTimer::singleShot(150, m_configView, SLOT(show())); } else { QTimer::singleShot(150, [this]() { m_configView->setVisible(true); }); } } } QRect View::localGeometry() const { return m_localGeometry; } void View::setLocalGeometry(const QRect &geometry) { if (m_localGeometry == geometry) { return; } m_localGeometry = geometry; emit localGeometryChanged(); updateAbsDockGeometry(); } void View::updateAbsDockGeometry(bool bypassChecks) { //! there was a -1 in height and width here. The reason of this //! if I remember correctly was related to multi-screen but I cant //! remember exactly the reason, something related to rigth edge in //! multi screen environment. BUT this was breaking the entire AlwaysVisible //! experience with struts. Removing them in order to restore correct //! behavior and keeping this comment in order to check for //! multi-screen breakage QRect absGeometry {x() + m_localGeometry.x(), y() + m_localGeometry.y() , m_localGeometry.width(), m_localGeometry.height()}; if (m_absGeometry == absGeometry && !bypassChecks) return; m_absGeometry = absGeometry; emit absGeometryChanged(m_absGeometry); //! this is needed in order to update correctly the screenGeometries if (visibility() && corona() && visibility()->mode() == Dock::AlwaysVisible) { emit corona()->availableScreenRectChanged(); emit corona()->availableScreenRegionChanged(); } } void View::statusChanged(Plasma::Types::ItemStatus status) { if (containment()) { if (containment()->status() >= Plasma::Types::NeedsAttentionStatus && containment()->status() != Plasma::Types::HiddenStatus) { setBlockHiding(true); } else { setBlockHiding(false); } } } bool View::alternativesIsShown() const { return m_alternativesIsShown; } void View::setAlternativesIsShown(bool show) { if (m_alternativesIsShown == show) { return; } m_alternativesIsShown = show; setBlockHiding(show); emit alternativesIsShownChanged(); } bool View::contextMenuIsShown() const { if (!m_contextMenu) { return false; } return m_contextMenu->menu(); } int View::currentThickness() const { if (formFactor() == Plasma::Types::Vertical) { return m_effects->mask().isNull() ? width() : m_effects->mask().width() - m_effects->innerShadow(); } else { return m_effects->mask().isNull() ? height() : m_effects->mask().height() - m_effects->innerShadow(); } } int View::normalThickness() const { return m_normalThickness; } void View::setNormalThickness(int thickness) { if (m_normalThickness == thickness) { return; } m_normalThickness = thickness; emit normalThicknessChanged(); } bool View::dockWinBehavior() const { return m_dockWinBehavior; } void View::setDockWinBehavior(bool dock) { if (m_dockWinBehavior == dock) { return; } m_dockWinBehavior = dock; emit dockWinBehaviorChanged(); } bool View::behaveAsPlasmaPanel() const { return m_behaveAsPlasmaPanel; } void View::setBehaveAsPlasmaPanel(bool behavior) { if (m_behaveAsPlasmaPanel == behavior) { return; } m_behaveAsPlasmaPanel = behavior; emit behaveAsPlasmaPanelChanged(); } bool View::inEditMode() const { return m_inEditMode; } void View::setInEditMode(bool edit) { if (m_inEditMode == edit) { return; } m_inEditMode = edit; emit inEditModeChanged(); } bool View::isPreferredForShortcuts() const { return m_isPreferredForShortcuts; } void View::setIsPreferredForShortcuts(bool preferred) { if (m_isPreferredForShortcuts == preferred) { return; } m_isPreferredForShortcuts = preferred; emit isPreferredForShortcutsChanged(); if (m_isPreferredForShortcuts && m_managedLayout) { emit m_managedLayout->preferredViewForShortcutsChanged(this); } } void View::preferredViewForShortcutsChangedSlot(Latte::View *view) { if (view != this) { setIsPreferredForShortcuts(false); } } bool View::onPrimary() const { return m_onPrimary; } void View::setOnPrimary(bool flag) { if (m_onPrimary == flag) { return; } m_onPrimary = flag; emit onPrimaryChanged(); } float View::maxLength() const { return m_maxLength; } void View::setMaxLength(float length) { if (m_maxLength == length) { return; } m_maxLength = length; emit maxLengthChanged(); } int View::maxThickness() const { return m_maxThickness; } void View::setMaxThickness(int thickness) { if (m_maxThickness == thickness) return; m_maxThickness = thickness; emit maxThicknessChanged(); } int View::alignment() const { return m_alignment; } void View::setAlignment(int alignment) { Dock::Alignment align = static_cast(alignment); if (m_alignment == alignment) { return; } m_alignment = align; emit alignmentChanged(); } QRect View::absGeometry() const { return m_absGeometry; } QRect View::screenGeometry() const { if (this->screen()) { QRect geom = this->screen()->geometry(); return geom; } return QRect(); } int View::offset() const { return m_offset; } void View::setOffset(int offset) { if (m_offset == offset) { return; } m_offset = offset; emit offsetChanged(); } int View::fontPixelSize() const { return m_fontPixelSize; } void View::setFontPixelSize(int size) { if (m_fontPixelSize == size) { return; } m_fontPixelSize = size; emit fontPixelSizeChanged(); } void View::applyActivitiesToWindows() { if (m_visibility) { QStringList activities = m_managedLayout->appliedActivities(); m_visibility->setWindowOnActivities(*this, activities); if (m_configView) { m_visibility->setWindowOnActivities(*m_configView, activities); auto configView = qobject_cast(m_configView); if (configView && configView->secondaryWindow()) { m_visibility->setWindowOnActivities(*configView->secondaryWindow(), activities); } } if (m_visibility->supportsKWinEdges()) { m_visibility->applyActivitiesToHiddenWindows(activities); } } } Layout *View::managedLayout() const { return m_managedLayout; } void View::setManagedLayout(Layout *layout) { if (m_managedLayout == layout) { return; } // clear mode for (auto &c : connectionsManagedLayout) { disconnect(c); } m_managedLayout = layout; if (m_managedLayout) { //! Sometimes the activity isnt completely ready, by adding a delay //! we try to catch up QTimer::singleShot(100, [this]() { if (m_managedLayout && m_visibility) { qDebug() << "DOCK VIEW FROM LAYOUT ::: " << m_managedLayout->name() << " - activities: " << m_managedLayout->appliedActivities(); applyActivitiesToWindows(); emit activitiesChanged(); } }); connectionsManagedLayout[0] = connect(m_managedLayout, &Layout::preferredViewForShortcutsChanged, this, &View::preferredViewForShortcutsChangedSlot); } - DockCorona *dockCorona = qobject_cast(this->corona()); + Latte::Corona *latteCorona = qobject_cast(this->corona()); - if (dockCorona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { - connectionsManagedLayout[1] = connect(dockCorona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() { + if (latteCorona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { + connectionsManagedLayout[1] = connect(latteCorona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() { if (m_managedLayout && m_visibility) { qDebug() << "DOCK VIEW FROM LAYOUT (runningActivitiesChanged) ::: " << m_managedLayout->name() << " - activities: " << m_managedLayout->appliedActivities(); applyActivitiesToWindows(); emit activitiesChanged(); } }); connectionsManagedLayout[2] = connect(m_managedLayout, &Layout::activitiesChanged, this, [&]() { if (m_managedLayout) { applyActivitiesToWindows(); emit activitiesChanged(); } }); - connectionsManagedLayout[3] = connect(dockCorona->layoutManager(), &LayoutManager::layoutsChanged, this, [&]() { + connectionsManagedLayout[3] = connect(latteCorona->layoutManager(), &LayoutManager::layoutsChanged, this, [&]() { if (m_managedLayout) { applyActivitiesToWindows(); emit activitiesChanged(); } }); //!IMPORTANT!!! ::: This fixes a bug when closing an Activity all docks from all Activities are //! disappearing! With this they reappear!!! connectionsManagedLayout[4] = connect(this, &QWindow::visibleChanged, this, [&]() { if (!isVisible() && m_managedLayout) { QTimer::singleShot(100, [this]() { if (m_managedLayout && containment() && !containment()->destroyed()) { setVisible(true); applyActivitiesToWindows(); emit activitiesChanged(); } }); QTimer::singleShot(1500, [this]() { if (m_managedLayout && containment() && !containment()->destroyed()) { setVisible(true); applyActivitiesToWindows(); emit activitiesChanged(); } }); } }); } emit managedLayoutChanged(); } void View::moveToLayout(QString layoutName) { if (!m_managedLayout) { return; } QList containments = m_managedLayout->unassignFromLayout(this); - DockCorona *dockCorona = qobject_cast(this->corona()); + Latte::Corona *latteCorona = qobject_cast(this->corona()); - if (dockCorona && containments.size() > 0) { - Layout *newLayout = dockCorona->layoutManager()->activeLayout(layoutName); + if (latteCorona && containments.size() > 0) { + Layout *newLayout = latteCorona->layoutManager()->activeLayout(layoutName); if (newLayout) { newLayout->assignToLayout(this, containments); } } } void View::setBlockHiding(bool block) { if (!block) { auto *configView = qobject_cast(m_configView); if (m_alternativesIsShown || (configView && configView->sticker() && configView->isVisible())) { return; } if (m_visibility) { m_visibility->setBlockHiding(false); } } else { if (m_visibility) { m_visibility->setBlockHiding(true); } } } void View::hideWindowsForSlidingOut() { setBlockHiding(false); if (m_configView) { auto configDialog = qobject_cast(m_configView); if (configDialog) { configDialog->hideConfigWindow(); } } } //! remove latte tasks plasmoid void View::removeTasksPlasmoid() { if (!tasksPresent() || !containment()) { return; } foreach (Plasma::Applet *applet, containment()->applets()) { KPluginMetaData meta = applet->kPackage().metadata(); if (meta.pluginId() == "org.kde.latte.plasmoid") { QAction *closeApplet = applet->actions()->action(QStringLiteral("remove")); if (closeApplet) { closeApplet->trigger(); //! remove only the first found return; } } } } //! check if the tasks plasmoid exist in the dock bool View::tasksPresent() { if (!this->containment()) { return false; } foreach (Plasma::Applet *applet, this->containment()->applets()) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { return true; } } return false; } //! check if the tasks plasmoid exist in the dock bool View::latteTasksPresent() { if (!this->containment()) { return false; } foreach (Plasma::Applet *applet, this->containment()->applets()) { KPluginMetaData metadata = applet->pluginMetaData(); if (metadata.pluginId() == "org.kde.latte.plasmoid") { return true; } } return false; } //!check if the plasmoid with _name_ exists in the midedata bool View::mimeContainsPlasmoid(QMimeData *mimeData, QString name) { if (!mimeData) { return false; } if (mimeData->hasFormat(QStringLiteral("text/x-plasmoidservicename"))) { QString data = mimeData->data(QStringLiteral("text/x-plasmoidservicename")); const QStringList appletNames = data.split('\n', QString::SkipEmptyParts); foreach (const QString &appletName, appletNames) { if (appletName == name) return true; } } return false; } ViewPart::Effects *View::effects() const { return m_effects; } ViewPart::Positioner *View::positioner() const { return m_positioner; } VisibilityManager *View::visibility() const { return m_visibility; } bool View::event(QEvent *e) { if (!m_inDelete) { emit eventTriggered(e); switch (e->type()) { case QEvent::Leave: engine()->trimComponentCache(); break; case QEvent::PlatformSurface: if (auto pe = dynamic_cast(e)) { switch (pe->surfaceEventType()) { case QPlatformSurfaceEvent::SurfaceCreated: setupWaylandIntegration(); if (m_shellSurface) { m_positioner->syncGeometry(); m_effects->updateShadows(); } break; case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; qDebug() << "WAYLAND dock window surface was deleted..."; m_effects->clearShadows(); } break; } } break; default: break; } } return ContainmentView::event(e);; } void View::deactivateApplets() { if (!containment()) { return; } foreach (auto applet, containment()->applets()) { PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value(); if (ai) { ai->setExpanded(false); } } } void View::toggleAppletExpanded(const int id) { if (!containment()) { return; } foreach (auto applet, containment()->applets()) { if (applet->id() == id) { PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value(); if (ai) { if (!ai->isActivationTogglesExpanded()) { ai->setActivationTogglesExpanded(true); } emit applet->activated(); } } } } QVariantList View::containmentActions() { QVariantList actions; /*if (containment()->corona()->immutability() != Plasma::Types::Mutable) { return actions; }*/ //FIXME: the trigger string it should be better to be supported this way //const QString trigger = Plasma::ContainmentActions::eventToString(event); const QString trigger = "RightButton;NoModifier"; Plasma::ContainmentActions *plugin = this->containment()->containmentActions().value(trigger); if (!plugin) { return actions; } if (plugin->containment() != this->containment()) { plugin->setContainment(this->containment()); // now configure it KConfigGroup cfg(this->containment()->corona()->config(), "ActionPlugins"); cfg = KConfigGroup(&cfg, QString::number(this->containment()->containmentType())); KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } foreach (QAction *ac, plugin->contextualActions()) { actions << QVariant::fromValue(ac); } return actions; } void View::disableGrabItemBehavior() { setMouseGrabEnabled(false); } void View::restoreGrabItemBehavior() { setMouseGrabEnabled(true); if (mouseGrabberItem()) { mouseGrabberItem()->ungrabMouse(); } } //!BEGIN overriding context menus behavior void View::mousePressEvent(QMouseEvent *event) { bool result = m_contextMenu->mousePressEvent(event); emit contextMenuIsShownChanged(); if (result) { PlasmaQuick::ContainmentView::mousePressEvent(event); } } //!END overriding context menus behavior //!BEGIN configuration functions void View::saveConfig() { if (!this->containment()) return; auto config = this->containment()->config(); config.writeEntry("onPrimary", onPrimary()); config.writeEntry("dockWindowBehavior", dockWinBehavior()); config.writeEntry("isPreferredForShortcuts", isPreferredForShortcuts()); config.sync(); } void View::restoreConfig() { if (!this->containment()) return; auto config = this->containment()->config(); m_onPrimary = config.readEntry("onPrimary", true); m_dockWinBehavior = config.readEntry("dockWindowBehavior", true); m_isPreferredForShortcuts = config.readEntry("isPreferredForShortcuts", false); //! Send changed signals at the end in order to be sure that saveConfig //! wont rewrite default/invalid values emit onPrimaryChanged(); emit dockWinBehaviorChanged(); } //!END configuration functions } //!END namespace diff --git a/app/view/visibilitymanager.cpp b/app/view/visibilitymanager.cpp index 7ca40adb..0fe98b50 100644 --- a/app/view/visibilitymanager.cpp +++ b/app/view/visibilitymanager.cpp @@ -1,1254 +1,1254 @@ /* * 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 "visibilitymanager.h" #include "visibilitymanager_p.h" // local #include "positioner.h" #include "screenedgeghostwindow.h" #include "view.h" -#include "../dockcorona.h" +#include "../lattecorona.h" #include "../layoutmanager.h" #include "../screenpool.h" #include "../wm/windowinfowrap.h" #include "../../liblattedock/extras.h" // Qt #include // KDE #include #include namespace Latte { //! BEGIN: VisiblityManagerPrivate implementation VisibilityManagerPrivate::VisibilityManagerPrivate(PlasmaQuick::ContainmentView *view, VisibilityManager *q) : QObject(nullptr), q(q), view(view) { m_latteView = qobject_cast(view); - dockCorona = qobject_cast(view->corona()); - wm = dockCorona->wm(); + m_corona = qobject_cast(view->corona()); + wm = m_corona->wm(); if (m_latteView) { connect(m_latteView, &Latte::View::eventTriggered, this, &VisibilityManagerPrivate::viewEventManager); connect(m_latteView, &Latte::View::absGeometryChanged, this, &VisibilityManagerPrivate::setDockGeometry); } timerStartUp.setInterval(5000); timerStartUp.setSingleShot(true); timerCheckWindows.setInterval(350); timerCheckWindows.setSingleShot(true); timerShow.setSingleShot(true); timerHide.setSingleShot(true); connect(&timerCheckWindows, &QTimer::timeout, this, &VisibilityManagerPrivate::checkAllWindows); connect(&timerShow, &QTimer::timeout, this, [this, q]() { if (isHidden) { // qDebug() << "must be shown"; emit this->q->mustBeShown(VisibilityManager::QPrivateSignal{}); } }); connect(&timerHide, &QTimer::timeout, this, [this]() { if (!blockHiding && !isHidden && !dragEnter) { // qDebug() << "must be hide"; emit this->q->mustBeHide(VisibilityManager::QPrivateSignal{}); } }); wm->setDockExtraFlags(*view); wm->addDock(view->winId()); restoreConfig(); } VisibilityManagerPrivate::~VisibilityManagerPrivate() { qDebug() << "VisibilityManagerPrivate deleting..."; wm->removeDockStruts(*view); wm->removeDock(view->winId()); if (edgeGhostWindow) { edgeGhostWindow->deleteLater(); } } inline void VisibilityManagerPrivate::setMode(Dock::Visibility mode) { if (this->mode == mode) return; Q_ASSERT_X(mode != Dock::None, q->staticMetaObject.className(), "set visibility to Dock::None"); // clear mode for (auto &c : connections) { disconnect(c); } if (mode != Dock::DodgeAllWindows && !enabledDynamicBackgroundFlag) { windows.clear(); } if (this->mode == Dock::AlwaysVisible) { wm->removeDockStruts(*view); } else { connections[3] = connect(wm, &WindowSystem::currentDesktopChanged , this, [&] { if (raiseOnDesktopChange) raiseDockTemporarily(); }); connections[4] = connect(wm, &WindowSystem::currentActivityChanged , this, [&]() { if (raiseOnActivityChange) raiseDockTemporarily(); else updateHiddenState(); }); } timerShow.stop(); timerHide.stop(); timerCheckWindows.stop(); this->mode = mode; switch (this->mode) { case Dock::AlwaysVisible: { //set wayland visibility mode if (m_latteView->surface()) { m_latteView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::WindowsGoBelow); } if (view->containment() && !m_latteView->inEditMode() && view->screen()) { updateStrutsBasedOnLayoutsAndActivities(); } connections[0] = connect(view->containment(), &Plasma::Containment::locationChanged , this, [&]() { if (m_latteView->inEditMode()) wm->removeDockStruts(*view); }); connections[1] = connect(m_latteView, &Latte::View::inEditModeChanged , this, [&]() { if (!m_latteView->inEditMode() && !m_latteView->positioner()->inLocationChangeAnimation() && view->screen()) wm->setDockStruts(*view, dockGeometry, view->containment()->location()); }); - if (dockCorona && dockCorona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { - connections[2] = connect(dockCorona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, [&]() { + if (m_corona && m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { + connections[2] = connect(m_corona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, [&]() { updateStrutsBasedOnLayoutsAndActivities(); }); connections[3] = connect(m_latteView, &Latte::View::activitiesChanged, this, [&]() { updateStrutsBasedOnLayoutsAndActivities(); }); } raiseDock(true); } break; case Dock::AutoHide: { //set wayland visibility mode if (m_latteView->surface()) { m_latteView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::AutoHide); } raiseDock(containsMouse); } break; case Dock::DodgeActive: { //set wayland visibility mode if (m_latteView->surface()) { m_latteView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::AutoHide); } connections[0] = connect(wm, &WindowSystem::activeWindowChanged , this, &VisibilityManagerPrivate::dodgeActive); connections[1] = connect(wm, &WindowSystem::windowChanged , this, &VisibilityManagerPrivate::dodgeActive); dodgeActive(wm->activeWindow()); } break; case Dock::DodgeMaximized: { //set wayland visibility mode if (m_latteView->surface()) { m_latteView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::AutoHide); } connections[0] = connect(wm, &WindowSystem::activeWindowChanged , this, &VisibilityManagerPrivate::dodgeMaximized); connections[1] = connect(wm, &WindowSystem::windowChanged , this, &VisibilityManagerPrivate::dodgeMaximized); dodgeMaximized(wm->activeWindow()); } break; case Dock::DodgeAllWindows: { //set wayland visibility mode if (m_latteView->surface()) { m_latteView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::AutoHide); } for (const auto &wid : wm->windows()) { windows.insert(wid, wm->requestInfo(wid)); } connections[0] = connect(wm, &WindowSystem::windowChanged , this, &VisibilityManagerPrivate::dodgeWindows); connections[1] = connect(wm, &WindowSystem::windowRemoved , this, [&](WindowId wid) { windows.remove(wid); timerCheckWindows.start(); }); connections[2] = connect(wm, &WindowSystem::windowAdded , this, [&](WindowId wid) { windows.insert(wid, wm->requestInfo(wid)); timerCheckWindows.start(); }); timerCheckWindows.start(); } break; case Dock::WindowsGoBelow: //set wayland visibility mode if (m_latteView->surface()) { m_latteView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::WindowsGoBelow); } break; default: break; } view->containment()->config().writeEntry("visibility", static_cast(mode)); updateKWinEdgesSupport(); emit q->modeChanged(); } void VisibilityManagerPrivate::updateStrutsBasedOnLayoutsAndActivities() { - bool multipleLayoutsAndCurrent = (dockCorona->layoutManager()->memoryUsage() == Dock::MultipleLayouts + bool multipleLayoutsAndCurrent = (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts && m_latteView->managedLayout() && !m_latteView->positioner()->inLocationChangeAnimation() - && m_latteView->managedLayout()->name() == dockCorona->layoutManager()->currentLayoutName()); + && m_latteView->managedLayout()->name() == m_corona->layoutManager()->currentLayoutName()); - if (dockCorona->layoutManager()->memoryUsage() == Dock::SingleLayout || multipleLayoutsAndCurrent) { + if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout || multipleLayoutsAndCurrent) { wm->setDockStruts(*view, dockGeometry, view->location()); } else { wm->removeDockStruts(*view); } } void VisibilityManagerPrivate::setRaiseOnDesktop(bool enable) { if (enable == raiseOnDesktopChange) return; raiseOnDesktopChange = enable; emit q->raiseOnDesktopChanged(); } void VisibilityManagerPrivate::setRaiseOnActivity(bool enable) { if (enable == raiseOnActivityChange) return; raiseOnActivityChange = enable; emit q->raiseOnActivityChanged(); } inline void VisibilityManagerPrivate::setIsHidden(bool isHidden) { if (this->isHidden == isHidden) return; if (blockHiding && isHidden) { qWarning() << "isHidden property is blocked, ignoring update"; return; } this->isHidden = isHidden; if (q->supportsKWinEdges()) { - bool inCurrentLayout = (dockCorona->layoutManager()->memoryUsage() == Dock::SingleLayout || - (dockCorona->layoutManager()->memoryUsage() == Dock::MultipleLayouts + bool inCurrentLayout = (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout || + (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts && m_latteView->managedLayout() && !m_latteView->positioner()->inLocationChangeAnimation() - && m_latteView->managedLayout()->name() == dockCorona->layoutManager()->currentLayoutName())); + && m_latteView->managedLayout()->name() == m_corona->layoutManager()->currentLayoutName())); if (inCurrentLayout) { wm->setEdgeStateFor(edgeGhostWindow, isHidden); } else { wm->setEdgeStateFor(edgeGhostWindow, false); } } emit q->isHiddenChanged(); } void VisibilityManagerPrivate::setBlockHiding(bool blockHiding) { if (this->blockHiding == blockHiding) return; this->blockHiding = blockHiding; // qDebug() << "blockHiding:" << blockHiding; if (this->blockHiding) { timerHide.stop(); if (isHidden) { emit q->mustBeShown(VisibilityManager::QPrivateSignal{}); } } else { updateHiddenState(); } emit q->blockHidingChanged(); } inline void VisibilityManagerPrivate::setTimerShow(int msec) { timerShow.setInterval(msec); emit q->timerShowChanged(); } inline void VisibilityManagerPrivate::setTimerHide(int msec) { timerHide.setInterval(msec); emit q->timerHideChanged(); } inline void VisibilityManagerPrivate::raiseDock(bool raise) { if (blockHiding) return; if (raise) { timerHide.stop(); if (!timerShow.isActive()) { timerShow.start(); } } else if (!dragEnter) { timerShow.stop(); if (hideNow) { hideNow = false; emit q->mustBeHide(VisibilityManager::QPrivateSignal{}); } else if (!timerHide.isActive()) { timerHide.start(); } } } void VisibilityManagerPrivate::raiseDockTemporarily() { if (raiseTemporarily) return; raiseTemporarily = true; timerHide.stop(); timerShow.stop(); if (isHidden) emit q->mustBeShown(VisibilityManager::QPrivateSignal{}); QTimer::singleShot(qBound(1800, 2 * timerHide.interval(), 3000), this, [&]() { raiseTemporarily = false; hideNow = true; updateHiddenState(); }); } void VisibilityManagerPrivate::updateHiddenState() { if (dragEnter) return; switch (mode) { case Dock::AutoHide: raiseDock(containsMouse); break; case Dock::DodgeActive: dodgeActive(wm->activeWindow()); break; case Dock::DodgeMaximized: dodgeMaximized(wm->activeWindow()); break; case Dock::DodgeAllWindows: dodgeWindows(wm->activeWindow()); break; default: break; } } inline void VisibilityManagerPrivate::setDockGeometry(const QRect &geometry) { if (!view->containment()) return; this->dockGeometry = geometry; if (mode == Dock::AlwaysVisible && !m_latteView->inEditMode() && view->screen()) { updateStrutsBasedOnLayoutsAndActivities(); } } void VisibilityManagerPrivate::setWindowOnActivities(QWindow &window, const QStringList &activities) { wm->setWindowOnActivities(window, activities); } void VisibilityManagerPrivate::applyActivitiesToHiddenWindows(const QStringList &activities) { if (edgeGhostWindow) { wm->setWindowOnActivities(*edgeGhostWindow, activities); } } void VisibilityManagerPrivate::dodgeActive(WindowId wid) { if (raiseTemporarily) return; //!don't send false raiseDock signal when containing mouse if (containsMouse) { raiseDock(true); return; } auto winfo = wm->requestInfo(wid); if (!winfo.isValid() || !winfo.isActive()) { winfo = wm->requestInfo(wm->activeWindow()); if (!winfo.isValid()) { //! very rare case that window manager doesnt have any active window at all raiseDock(true); return; } } //! don't send false raiseDock signal when containing mouse, // Johan comment //! I dont know why that wasnt winfo.wid() //active window, but just wid//the window that made the call if (wm->isOnCurrentDesktop(winfo.wid()) && wm->isOnCurrentActivity(winfo.wid())) { bool overlaps{intersects(winfo)}; raiseDock(!overlaps); } } void VisibilityManagerPrivate::dodgeMaximized(WindowId wid) { if (raiseTemporarily) return; //!don't send false raiseDock signal when containing mouse if (containsMouse) { raiseDock(true); return; } auto winfo = wm->requestInfo(wid); if (!winfo.isValid() || !winfo.isActive()) { winfo = wm->requestInfo(wm->activeWindow()); if (!winfo.isValid()) { //! very rare case that window manager doesnt have any active window at all raiseDock(true); return; } } auto intersectsMaxVert = [&]() noexcept -> bool { return ((winfo.isMaxVert() || (view->screen() && view->screen()->availableSize().height() <= winfo.geometry().height())) && intersects(winfo)); }; auto intersectsMaxHoriz = [&]() noexcept -> bool { return ((winfo.isMaxHoriz() || (view->screen() && view->screen()->availableSize().width() <= winfo.geometry().width())) && intersects(winfo)); }; //! don't send false raiseDock signal when containing mouse, // Johan comment //! I dont know why that wasnt winfo.wid() //active window, but just wid//the window that made the call if (wm->isOnCurrentDesktop(winfo.wid()) && wm->isOnCurrentActivity(winfo.wid())) { bool overlapsMaximized{view->formFactor() == Plasma::Types::Vertical ? intersectsMaxHoriz() : intersectsMaxVert()}; raiseDock(!overlapsMaximized); } } void VisibilityManagerPrivate::dodgeWindows(WindowId wid) { if (raiseTemporarily) return; if (windows.find(wid) == std::end(windows)) return; //!don't send false raiseDock signal when containing mouse if (containsMouse) { raiseDock(true); return; } windows[wid] = wm->requestInfo(wid); auto &winfo = windows[wid]; if (!winfo.isValid() || !wm->isOnCurrentDesktop(wid) || !wm->isOnCurrentActivity(wid)) return; if (intersects(winfo)) raiseDock(false); else timerCheckWindows.start(); } void VisibilityManagerPrivate::checkAllWindows() { if (raiseTemporarily) return; bool raise{true}; bool existsFaultyWindow{false}; for (const auto &winfo : windows) { // if (winfo.geometry() == QRect(0, 0, 0, 0)) { existsFaultyWindow = true; } if (!winfo.isValid() || !wm->isOnCurrentDesktop(winfo.wid()) || !wm->isOnCurrentActivity(winfo.wid())) continue; if (winfo.isFullscreen()) { raise = false; break; } else if (intersects(winfo)) { raise = false; break; } } cleanupFaultyWindows(); raiseDock(raise); } inline bool VisibilityManagerPrivate::intersects(const WindowInfoWrap &winfo) { return (!winfo.isMinimized() && winfo.geometry().intersects(dockGeometry) && !winfo.isShaded()); } inline void VisibilityManagerPrivate::saveConfig() { if (!view->containment()) return; auto config = view->containment()->config(); config.writeEntry("enableKWinEdges", enableKWinEdgesFromUser); config.writeEntry("timerShow", timerShow.interval()); config.writeEntry("timerHide", timerHide.interval()); config.writeEntry("raiseOnDesktopChange", raiseOnDesktopChange); config.writeEntry("raiseOnActivityChange", raiseOnActivityChange); view->containment()->configNeedsSaving(); } inline void VisibilityManagerPrivate::restoreConfig() { if (!view->containment()) return; auto config = view->containment()->config(); timerShow.setInterval(config.readEntry("timerShow", 0)); timerHide.setInterval(config.readEntry("timerHide", 700)); emit q->timerShowChanged(); emit q->timerHideChanged(); enableKWinEdgesFromUser = config.readEntry("enableKWinEdges", true); emit q->enableKWinEdgesChanged(); setRaiseOnDesktop(config.readEntry("raiseOnDesktopChange", false)); setRaiseOnActivity(config.readEntry("raiseOnActivityChange", false)); auto mode = [&]() { return static_cast(view->containment()->config() .readEntry("visibility", static_cast(Dock::DodgeActive))); }; if (mode() == Dock::AlwaysVisible) { setMode(Dock::AlwaysVisible); } else { connect(&timerStartUp, &QTimer::timeout, this, [ &, mode]() { setMode(mode()); }); connect(view->containment(), &Plasma::Containment::userConfiguringChanged , this, [&](bool configuring) { if (configuring && timerStartUp.isActive()) timerStartUp.start(100); }); timerStartUp.start(); } connect(view->containment(), &Plasma::Containment::userConfiguringChanged , this, [&](bool configuring) { if (!configuring) saveConfig(); }); } void VisibilityManagerPrivate::setContainsMouse(bool contains) { if (containsMouse == contains) { return; } containsMouse = contains; emit q->containsMouseChanged(); if (contains && mode != Dock::AlwaysVisible) { raiseDock(true); } } void VisibilityManagerPrivate::viewEventManager(QEvent *ev) { switch (ev->type()) { case QEvent::Enter: setContainsMouse(true); break; case QEvent::Leave: setContainsMouse(false); updateHiddenState(); break; case QEvent::DragEnter: dragEnter = true; if (isHidden) emit q->mustBeShown(VisibilityManager::QPrivateSignal{}); break; case QEvent::DragLeave: case QEvent::Drop: dragEnter = false; updateHiddenState(); break; case QEvent::Show: wm->setDockExtraFlags(*view); break; default: break; } } void VisibilityManagerPrivate::cleanupFaultyWindows() { foreach (auto key, windows.keys()) { auto winfo = windows[key]; //! garbage windows removing if (winfo.geometry() == QRect(0, 0, 0, 0)) { //qDebug() << "Faulty Geometry ::: " << winfo.wid(); windows.remove(key); } } } //! Dynamic Background functions void VisibilityManagerPrivate::setEnabledDynamicBackground(bool active) { if (enabledDynamicBackgroundFlag == active) { return; } enabledDynamicBackgroundFlag = active; if (active) { if (mode != Dock::DodgeAllWindows) { for (const auto &wid : wm->windows()) { windows.insert(wid, wm->requestInfo(wid)); } } connectionsDynBackground[0] = connect(view->corona(), &Plasma::Corona::availableScreenRectChanged, this, &VisibilityManagerPrivate::updateAvailableScreenGeometry); connectionsDynBackground[1] = connect(wm, &WindowSystem::windowChanged, this, [&](WindowId wid) { windows[wid] = wm->requestInfo(wid); updateDynamicBackgroundWindowFlags(); }); connectionsDynBackground[2] = connect(wm, &WindowSystem::windowRemoved, this, [&](WindowId wid) { windows.remove(wid); }); connectionsDynBackground[3] = connect(wm, &WindowSystem::windowAdded, this, [&](WindowId wid) { windows.insert(wid, wm->requestInfo(wid)); updateDynamicBackgroundWindowFlags(); }); connectionsDynBackground[4] = connect(wm, &WindowSystem::activeWindowChanged, this, [&](WindowId wid) { if (windows.contains(lastActiveWindowWid)) { windows[lastActiveWindowWid] = wm->requestInfo(lastActiveWindowWid); } windows[wid] = wm->requestInfo(wid); lastActiveWindowWid = wid; updateDynamicBackgroundWindowFlags(); }); connectionsDynBackground[5] = connect(wm, &WindowSystem::currentDesktopChanged, this, [&] { updateDynamicBackgroundWindowFlags(); }); connectionsDynBackground[6] = connect(wm, &WindowSystem::currentActivityChanged, this, [&] { updateDynamicBackgroundWindowFlags(); }); updateAvailableScreenGeometry(); updateDynamicBackgroundWindowFlags(); } else { // clear mode for (auto &c : connectionsDynBackground) { disconnect(c); } if (mode != Dock::DodgeAllWindows) { windows.clear(); } // ATTENTION: this was creating a crash under wayland environment through the blur effect // setExistsWindowMaximized(false); // setExistsWindowSnapped(false); } emit q->enabledDynamicBackgroundChanged(); } void VisibilityManagerPrivate::setExistsWindowMaximized(bool windowMaximized) { if (windowIsMaximizedFlag == windowMaximized) { return; } windowIsMaximizedFlag = windowMaximized; emit q->existsWindowMaximizedChanged(); } void VisibilityManagerPrivate::setExistsWindowSnapped(bool windowSnapped) { if (windowIsSnappedFlag == windowSnapped) { return; } windowIsSnappedFlag = windowSnapped; emit q->existsWindowSnappedChanged(); } void VisibilityManagerPrivate::setTouchingWindowScheme(SchemeColors *scheme) { if (touchingScheme == scheme) { return; } touchingScheme = scheme; emit q->touchingWindowSchemeChanged(); } void VisibilityManagerPrivate::updateAvailableScreenGeometry() { if (!view || !view->containment()) { return; } int currentScrId = m_latteView->positioner()->currentScreenId(); - QRect tempAvailableScreenGeometry = dockCorona->availableScreenRectWithCriteria(currentScrId, {Dock::AlwaysVisible}, {}); + QRect tempAvailableScreenGeometry = m_corona->availableScreenRectWithCriteria(currentScrId, {Dock::AlwaysVisible}, {}); if (tempAvailableScreenGeometry != availableScreenGeometry) { availableScreenGeometry = tempAvailableScreenGeometry; snappedWindowsGeometries.clear(); //! for top dock the snapped geometries would be int halfWidth1 = std::floor(availableScreenGeometry.width() / 2); int halfWidth2 = availableScreenGeometry.width() - halfWidth1; int halfHeight1 = std::floor((availableScreenGeometry.height()) / 2); int halfHeight2 = availableScreenGeometry.height() - halfHeight1; int x1 = availableScreenGeometry.x(); int x2 = availableScreenGeometry.x() + halfWidth1; int y1 = availableScreenGeometry.y(); int y2 = availableScreenGeometry.y() + halfHeight1; QRect snap1; QRect snap2; QRect snap3; QRect snap4; if (view->formFactor() == Plasma::Types::Horizontal) { if (view->location() == Plasma::Types::TopEdge) { snap1 = QRect(x1, y1, halfWidth1, halfHeight1); snap3 = QRect(x2, y1, halfWidth2, halfHeight1); } else if ((view->location() == Plasma::Types::BottomEdge)) { snap1 = QRect(x1, y2, halfWidth1, halfHeight2); snap3 = QRect(x2, y2, halfWidth2, halfHeight2); } snap2 = QRect(x1, y1, halfWidth1, availableScreenGeometry.height()); snap4 = QRect(x2, y1, halfWidth2, availableScreenGeometry.height()); } else if (view->formFactor() == Plasma::Types::Vertical) { QRect snap5; if (view->location() == Plasma::Types::LeftEdge) { snap1 = QRect(x1, y1, halfWidth1, halfHeight1); snap3 = QRect(x1, y2, halfWidth1, halfHeight2); snap5 = QRect(x1, y1, halfWidth1, availableScreenGeometry.height()); } else if ((view->location() == Plasma::Types::RightEdge)) { snap1 = QRect(x2, y1, halfWidth2, halfHeight1); snap3 = QRect(x2, y2, halfWidth2, halfHeight2); snap5 = QRect(x2, y1, halfWidth2, availableScreenGeometry.height()); } snap2 = QRect(x1, y1, availableScreenGeometry.width(), halfHeight1); snap4 = QRect(x1, y2, availableScreenGeometry.width(), halfHeight2); snappedWindowsGeometries.append(snap5); } snappedWindowsGeometries.append(snap1); snappedWindowsGeometries.append(snap2); snappedWindowsGeometries.append(snap3); snappedWindowsGeometries.append(snap4); updateDynamicBackgroundWindowFlags(); } } bool VisibilityManagerPrivate::isMaximizedInCurrentScreen(const WindowInfoWrap &winfo) { //! updated implementation to identify the screen that the maximized window is present //! in order to avoid: https://bugs.kde.org/show_bug.cgi?id=397700 if (winfo.isValid() && !winfo.isMinimized() && wm->isOnCurrentDesktop(winfo.wid()) && wm->isOnCurrentActivity(winfo.wid())) { if (winfo.isMaximized() && availableScreenGeometry.contains(winfo.geometry().center())) { return true; } } return false; } bool VisibilityManagerPrivate::isTouchingPanelEdge(const WindowInfoWrap &winfo) { if (winfo.isValid() && !winfo.isMinimized() && wm->isOnCurrentDesktop(winfo.wid()) && wm->isOnCurrentActivity(winfo.wid())) { bool touchingPanelEdge{false}; QRect screenGeometry = m_latteView->screenGeometry(); bool inCurrentScreen{screenGeometry.contains(winfo.geometry().topLeft()) || screenGeometry.contains(winfo.geometry().bottomRight())}; if (inCurrentScreen) { if (view->location() == Plasma::Types::TopEdge) { touchingPanelEdge = (winfo.geometry().y() == availableScreenGeometry.y()); } else if (view->location() == Plasma::Types::BottomEdge) { touchingPanelEdge = (winfo.geometry().bottom() == availableScreenGeometry.bottom()); } else if (view->location() == Plasma::Types::LeftEdge) { touchingPanelEdge = (winfo.geometry().x() == availableScreenGeometry.x()); } else if (view->location() == Plasma::Types::RightEdge) { touchingPanelEdge = (winfo.geometry().right() == availableScreenGeometry.right()); } } return touchingPanelEdge; } return false; } void VisibilityManagerPrivate::updateDynamicBackgroundWindowFlags() { bool foundSnap{false}; bool foundMaximized{false}; //! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0), //! maybe a garbage collector here is a good idea!!! bool existsFaultyWindow{false}; WindowId maxWinId; WindowId snapWinId; for (const auto &winfo : windows) { if (isMaximizedInCurrentScreen(winfo)) { foundMaximized = true; maxWinId = winfo.wid(); } if (winfo.isActive() && isTouchingPanelEdge(winfo)) { foundSnap = true; snapWinId = winfo.wid(); } if (!existsFaultyWindow && winfo.geometry() == QRect(0, 0, 0, 0)) { existsFaultyWindow = true; } //qDebug() << "window geometry ::: " << winfo.geometry(); } //! active windows that are touching the panel edge should have a higher priority //! this is why are identified first if (!foundSnap) { for (const auto &winfo : windows) { if ((winfo.isKeepAbove() && isTouchingPanelEdge(winfo)) || (!winfo.isActive() && snappedWindowsGeometries.contains(winfo.geometry()))) { foundSnap = true; snapWinId = winfo.wid(); break; } } } if (existsFaultyWindow) { cleanupFaultyWindows(); } /*if (!foundMaximized && !foundSnap) { qDebug() << "SCREEN GEOMETRY : " << availableScreenGeometry; qDebug() << "SNAPS ::: " << snappedWindowsGeometries; } qDebug() << " FOUND ::: " << foundMaximized << foundSnap;*/ setExistsWindowMaximized(foundMaximized); setExistsWindowSnapped(foundSnap); //! update color scheme for touching window if (foundSnap) { //! first the snap one because that would mean it is active setTouchingWindowScheme(wm->schemeForWindow(snapWinId)); } else if (foundMaximized) { setTouchingWindowScheme(wm->schemeForWindow(maxWinId)); } else { setTouchingWindowScheme(nullptr); } } //! KWin Edges Support functions void VisibilityManagerPrivate::setEnableKWinEdges(bool enable) { if (enableKWinEdgesFromUser == enable) { return; } enableKWinEdgesFromUser = enable; emit q->enableKWinEdgesChanged(); updateKWinEdgesSupport(); } void VisibilityManagerPrivate::updateKWinEdgesSupport() { if (mode == Dock::AutoHide || mode == Dock::DodgeActive || mode == Dock::DodgeAllWindows || mode == Dock::DodgeMaximized) { if (enableKWinEdgesFromUser) { createEdgeGhostWindow(); } else if (!enableKWinEdgesFromUser) { deleteEdgeGhostWindow(); } } else if (mode == Dock::AlwaysVisible || mode == Dock::WindowsGoBelow) { deleteEdgeGhostWindow(); } } void VisibilityManagerPrivate::createEdgeGhostWindow() { if (!edgeGhostWindow) { edgeGhostWindow = new ScreenEdgeGhostWindow(m_latteView); wm->setDockExtraFlags(*edgeGhostWindow); connect(edgeGhostWindow, &ScreenEdgeGhostWindow::containsMouseChanged, this, [ = ](bool contains) { if (contains) { emit this->q->mustBeShown(VisibilityManager::QPrivateSignal{}); } }); connectionsKWinEdges[0] = connect(wm, &WindowSystem::currentActivityChanged, this, [&]() { - bool inCurrentLayout = (dockCorona->layoutManager()->memoryUsage() == Dock::SingleLayout || - (dockCorona->layoutManager()->memoryUsage() == Dock::MultipleLayouts + bool inCurrentLayout = (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout || + (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts && m_latteView->managedLayout() && !m_latteView->positioner()->inLocationChangeAnimation() - && m_latteView->managedLayout()->name() == dockCorona->layoutManager()->currentLayoutName())); + && m_latteView->managedLayout()->name() == m_corona->layoutManager()->currentLayoutName())); if (edgeGhostWindow) { if (inCurrentLayout) { wm->setEdgeStateFor(edgeGhostWindow, isHidden); } else { wm->setEdgeStateFor(edgeGhostWindow, false); } } }); emit q->supportsKWinEdgesChanged(); } } void VisibilityManagerPrivate::deleteEdgeGhostWindow() { if (edgeGhostWindow) { edgeGhostWindow->deleteLater(); edgeGhostWindow = nullptr; for (auto &c : connectionsKWinEdges) { disconnect(c); } emit q->supportsKWinEdgesChanged(); } } //! Window Functions void VisibilityManagerPrivate::requestToggleMaximizeForActiveWindow() { WindowInfoWrap actInfo = wm->requestInfoActive(); //active window can be toggled only when it is in the same screen if (actInfo.isValid() && !actInfo.geometry().isNull() && m_latteView->screenGeometry().contains(actInfo.geometry().center())) { wm->requestToggleMaximized(actInfo.wid()); } } void VisibilityManagerPrivate::requestMoveActiveWindow(int localX, int localY) { WindowInfoWrap actInfo = wm->requestInfoActive(); //active window can be dragged only when it is in the same screen if (actInfo.isValid() && !actInfo.geometry().isNull() && m_latteView->screenGeometry().contains(actInfo.geometry().center())) { QPoint globalPoint{m_latteView->x() + localX, m_latteView->y() + localY}; wm->requestMoveWindow(actInfo.wid(), globalPoint); } } bool VisibilityManagerPrivate::activeWindowCanBeDragged() { WindowInfoWrap actInfo = wm->requestInfoActive(); //active window can be dragged only when it is in the same screen if (actInfo.isValid() && !actInfo.geometry().isNull() && m_latteView->screenGeometry().contains(actInfo.geometry().center())) { return wm->windowCanBeDragged(actInfo.wid()); } return false; } //! END: VisibilityManagerPrivate implementation //! BEGIN: VisibilityManager implementation VisibilityManager::VisibilityManager(PlasmaQuick::ContainmentView *view) : d(new VisibilityManagerPrivate(view, this)) { Latte::View *m_latteView = qobject_cast(view); if (m_latteView) { connect(this, &VisibilityManager::modeChanged, m_latteView->corona(), &Plasma::Corona::availableScreenRectChanged); } } VisibilityManager::~VisibilityManager() { qDebug() << "VisibilityManager deleting..."; delete d; } Dock::Visibility VisibilityManager::mode() const { return d->mode; } void VisibilityManager::setMode(Dock::Visibility mode) { d->setMode(mode); } void VisibilityManager::setWindowOnActivities(QWindow &window, const QStringList &activities) { d->setWindowOnActivities(window, activities); } void VisibilityManager::applyActivitiesToHiddenWindows(const QStringList &activities) { d->applyActivitiesToHiddenWindows(activities); } bool VisibilityManager::raiseOnDesktop() const { return d->raiseOnDesktopChange; } void VisibilityManager::setRaiseOnDesktop(bool enable) { d->setRaiseOnDesktop(enable); } bool VisibilityManager::raiseOnActivity() const { return d->raiseOnActivityChange; } void VisibilityManager::setRaiseOnActivity(bool enable) { d->setRaiseOnActivity(enable); } bool VisibilityManager::isHidden() const { return d->isHidden; } void VisibilityManager::setIsHidden(bool isHidden) { d->setIsHidden(isHidden); } bool VisibilityManager::blockHiding() const { return d->blockHiding; } void VisibilityManager::setBlockHiding(bool blockHiding) { d->setBlockHiding(blockHiding); } bool VisibilityManager::containsMouse() const { return d->containsMouse; } int VisibilityManager::timerShow() const { return d->timerShow.interval(); } void VisibilityManager::setTimerShow(int msec) { d->setTimerShow(msec); } int VisibilityManager::timerHide() const { return d->timerHide.interval(); } void VisibilityManager::setTimerHide(int msec) { d->setTimerHide(msec); } //! Dynamic Background functions bool VisibilityManager::enabledDynamicBackground() const { return d->enabledDynamicBackgroundFlag; } void VisibilityManager::setEnabledDynamicBackground(bool active) { d->setEnabledDynamicBackground(active); } bool VisibilityManager::existsWindowMaximized() const { return d->windowIsMaximizedFlag; } bool VisibilityManager::existsWindowSnapped() const { return d->windowIsSnappedFlag; } SchemeColors *VisibilityManager::touchingWindowScheme() const { return d->touchingScheme; } //! KWin Edges Support functions bool VisibilityManager::enableKWinEdges() const { return d->enableKWinEdgesFromUser; } void VisibilityManager::setEnableKWinEdges(bool enable) { d->setEnableKWinEdges(enable); } bool VisibilityManager::supportsKWinEdges() const { return (d->edgeGhostWindow != nullptr); } //! Window Functions void VisibilityManager::requestToggleMaximizeForActiveWindow() { d->requestToggleMaximizeForActiveWindow(); } void VisibilityManager::requestMoveActiveWindow(int localX, int localY) { d->requestMoveActiveWindow(localX, localY); } bool VisibilityManager::activeWindowCanBeDragged() { return d->activeWindowCanBeDragged(); } //! END: VisibilityManager implementation } diff --git a/app/view/visibilitymanager_p.h b/app/view/visibilitymanager_p.h index b2874f57..3cc24201 100644 --- a/app/view/visibilitymanager_p.h +++ b/app/view/visibilitymanager_p.h @@ -1,142 +1,142 @@ #ifndef VISIBILITYMANAGERPRIVATE_H #define VISIBILITYMANAGERPRIVATE_H // local #include "../schemecolors.h" #include "../wm/abstractwindowinterface.h" #include "../wm/windowinfowrap.h" #include "../../liblattedock/dock.h" // C++ #include #include // Qt #include #include #include #include #include // Plasma #include namespace Latte { -class DockCorona; +class Corona; class View; class VisibilityManager; class ScreenEdgeGhostWindow; /*! * \brief The Latte::VisibilityManagerPrivate is a class d-pointer */ class VisibilityManagerPrivate : public QObject { Q_GADGET public: VisibilityManagerPrivate(PlasmaQuick::ContainmentView *view, VisibilityManager *q); ~VisibilityManagerPrivate(); void setMode(Dock::Visibility mode); void setRaiseOnDesktop(bool enable); void setRaiseOnActivity(bool enable); void setContainsMouse(bool contains); void setIsHidden(bool isHidden); void setBlockHiding(bool blockHiding); void setTimerShow(int msec); void setTimerHide(int msec); void raiseDock(bool raise); void raiseDockTemporarily(); void updateHiddenState(); //! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0), //! this is a garbage collector to collect such windows in order to not break the windows array validity. void cleanupFaultyWindows(); //! Dynamic Background Feature void setEnabledDynamicBackground(bool active); void setExistsWindowMaximized(bool windowMaximized); void setExistsWindowSnapped(bool windowSnapped); void setTouchingWindowScheme(SchemeColors *scheme); void updateAvailableScreenGeometry(); void updateDynamicBackgroundWindowFlags(); //! KWin Edges Support functions void createEdgeGhostWindow(); void deleteEdgeGhostWindow(); void setEnableKWinEdges(bool enable); void updateKWinEdgesSupport(); void setDockGeometry(const QRect &rect); void setWindowOnActivities(QWindow &window, const QStringList &activities); void applyActivitiesToHiddenWindows(const QStringList &activities); void windowAdded(WindowId id); void dodgeActive(WindowId id); void dodgeMaximized(WindowId id); void dodgeWindows(WindowId id); void checkAllWindows(); bool intersects(const WindowInfoWrap &winfo); bool isMaximizedInCurrentScreen(const WindowInfoWrap &winfo); bool isTouchingPanelEdge(const WindowInfoWrap &winfo); void updateStrutsBasedOnLayoutsAndActivities(); void requestToggleMaximizeForActiveWindow(); void requestMoveActiveWindow(int localX, int localY); bool activeWindowCanBeDragged(); void saveConfig(); void restoreConfig(); void viewEventManager(QEvent *ev); VisibilityManager *q; PlasmaQuick::ContainmentView *view; AbstractWindowInterface *wm; Dock::Visibility mode{Dock::None}; std::array connections; QMap windows; QTimer timerShow; QTimer timerHide; QTimer timerCheckWindows; QTimer timerStartUp; QRect dockGeometry; bool isHidden{false}; bool dragEnter{false}; bool blockHiding{false}; bool containsMouse{false}; bool raiseTemporarily{false}; bool raiseOnDesktopChange{false}; bool raiseOnActivityChange{false}; bool hideNow{false}; //! Dynamic Background flags and needed information bool enabledDynamicBackgroundFlag{false}; bool windowIsSnappedFlag{false}; bool windowIsMaximizedFlag{false}; QRect availableScreenGeometry; QList snappedWindowsGeometries; std::array connectionsDynBackground; WindowId lastActiveWindowWid; SchemeColors *touchingScheme{nullptr}; //! KWin Edges bool enableKWinEdgesFromUser{true}; std::array connectionsKWinEdges; ScreenEdgeGhostWindow *edgeGhostWindow{nullptr}; - DockCorona *dockCorona{nullptr}; + Latte::Corona *m_corona{nullptr}; Latte::View *m_latteView{nullptr}; }; } #endif // VISIBILITYMANAGERPRIVATE_H diff --git a/app/wm/waylandinterface.cpp b/app/wm/waylandinterface.cpp index 562951de..7024a369 100644 --- a/app/wm/waylandinterface.cpp +++ b/app/wm/waylandinterface.cpp @@ -1,440 +1,440 @@ /* * 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 "waylandinterface.h" // local -#include "dockcorona.h" #include "view/screenedgeghostwindow.h" #include "view/view.h" +#include "../lattecorona.h" #include "../liblattedock/extras.h" // Qt #include #include #include #include #include #include // KDE #include #include #include // X11 #include using namespace KWayland::Client; namespace Latte { class Private::GhostWindow : public QRasterWindow { Q_OBJECT public: GhostWindow(WaylandInterface *waylandInterface) : m_waylandInterface(waylandInterface) { setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus); setupWaylandIntegration(); show(); } ~GhostWindow() { delete m_shellSurface; } void setGeometry(const QRect &rect) { QWindow::setGeometry(rect); setMaximumSize(rect.size()); m_shellSurface->setPosition(rect.topLeft()); } void setupWaylandIntegration() { using namespace KWayland::Client; if (m_shellSurface) return; Surface *s{Surface::fromWindow(this)}; if (!s) return; - m_shellSurface = m_waylandInterface->waylandDockCoronaInterface()->createSurface(s, this); + m_shellSurface = m_waylandInterface->waylandCoronaInterface()->createSurface(s, this); qDebug() << "wayland ghost window surface was created..."; m_shellSurface->setSkipTaskbar(true); m_shellSurface->setPanelTakesFocus(false); m_shellSurface->setRole(PlasmaShellSurface::Role::Panel); m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible); } KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; WaylandInterface *m_waylandInterface{nullptr}; }; WaylandInterface::WaylandInterface(QObject *parent) : AbstractWindowInterface(parent) { - m_corona = qobject_cast(parent); + m_corona = qobject_cast(parent); m_activities = new KActivities::Consumer(this); connect(m_activities.data(), &KActivities::Consumer::currentActivityChanged , this, &WaylandInterface::currentActivityChanged); } WaylandInterface::~WaylandInterface() { } void WaylandInterface::init() { } void WaylandInterface::initWindowManagement(KWayland::Client::PlasmaWindowManagement *windowManagement) { m_windowManagement = windowManagement; connect(m_windowManagement, &PlasmaWindowManagement::windowCreated, this, &WaylandInterface::windowCreatedProxy); connect(m_windowManagement, &PlasmaWindowManagement::activeWindowChanged, this, [&]() noexcept { auto w = m_windowManagement->activeWindow(); emit activeWindowChanged(w ? w->internalId() : 0); }, Qt::QueuedConnection); } -KWayland::Client::PlasmaShell *WaylandInterface::waylandDockCoronaInterface() const +KWayland::Client::PlasmaShell *WaylandInterface::waylandCoronaInterface() const { - return m_corona->waylandDockCoronaInterface(); + return m_corona->waylandCoronaInterface(); } void WaylandInterface::setDockExtraFlags(QWindow &view) { Q_UNUSED(view) } void WaylandInterface::setDockStruts(QWindow &view, const QRect &rect, Plasma::Types::Location location) { if (!m_ghostWindows.contains(view.winId())) m_ghostWindows[view.winId()] = new Private::GhostWindow(this); auto w = m_ghostWindows[view.winId()]; switch (location) { case Plasma::Types::TopEdge: case Plasma::Types::BottomEdge: w->setGeometry({rect.x() + rect.width() / 2, rect.y(), 1, rect.height()}); break; case Plasma::Types::LeftEdge: case Plasma::Types::RightEdge: w->setGeometry({rect.x(), rect.y() + rect.height() / 2, rect.width(), 1}); break; default: break; } } void WaylandInterface::setWindowOnActivities(QWindow &window, const QStringList &activities) { //! needs to updated to wayland case // KWindowSystem::setOnActivities(view.winId(), activities); } void WaylandInterface::removeDockStruts(QWindow &view) const { delete m_ghostWindows.take(view.winId()); } WindowId WaylandInterface::activeWindow() const { if (!m_windowManagement) { return 0; } auto wid = m_windowManagement->activeWindow(); return wid ? wid->internalId() : 0; } const std::list &WaylandInterface::windows() const { return m_windows; } void WaylandInterface::setKeepAbove(const QDialog &dialog, bool above) const { if (above) { KWindowSystem::setState(dialog.winId(), NET::KeepAbove); } else { KWindowSystem::clearState(dialog.winId(), NET::KeepAbove); } } void WaylandInterface::skipTaskBar(const QDialog &dialog) const { KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar); } void WaylandInterface::slideWindow(QWindow &view, AbstractWindowInterface::Slide location) const { auto slideLocation = KWindowEffects::NoEdge; switch (location) { case Slide::Top: slideLocation = KWindowEffects::TopEdge; break; case Slide::Bottom: slideLocation = KWindowEffects::BottomEdge; break; case Slide::Left: slideLocation = KWindowEffects::LeftEdge; break; case Slide::Right: slideLocation = KWindowEffects::RightEdge; break; default: break; } KWindowEffects::slideWindow(view.winId(), slideLocation, -1); } void WaylandInterface::enableBlurBehind(QWindow &view) const { KWindowEffects::enableBlurBehind(view.winId()); } void WaylandInterface::setEdgeStateFor(QWindow *view, bool active) const { ScreenEdgeGhostWindow *window = qobject_cast(view); if (!window) { return; } if (window->parentDock()->surface() && window->parentDock()->visibility() && (window->parentDock()->visibility()->mode() == Dock::DodgeActive || window->parentDock()->visibility()->mode() == Dock::DodgeMaximized || window->parentDock()->visibility()->mode() == Dock::DodgeAllWindows || window->parentDock()->visibility()->mode() == Dock::AutoHide)) { if (active) { window->showWithMask(); window->surface()->requestHideAutoHidingPanel(); } else { window->hideWithMask(); window->surface()->requestShowAutoHidingPanel(); } } } WindowInfoWrap WaylandInterface::requestInfoActive() const { if (!m_windowManagement) { return {}; } auto w = m_windowManagement->activeWindow(); if (!w) return {}; WindowInfoWrap winfoWrap; winfoWrap.setIsValid(true); winfoWrap.setWid(w->internalId()); winfoWrap.setIsActive(w->isActive()); winfoWrap.setIsMinimized(w->isMinimized()); winfoWrap.setIsMaxVert(w->isMaximized()); winfoWrap.setIsMaxHoriz(w->isMaximized()); winfoWrap.setIsFullscreen(w->isFullscreen()); winfoWrap.setIsShaded(w->isShaded()); winfoWrap.setGeometry(w->geometry()); winfoWrap.setIsKeepAbove(w->isKeepAbove()); winfoWrap.setHasSkipTaskbar(w->skipTaskbar()); return winfoWrap; } bool WaylandInterface::isOnCurrentDesktop(WindowId wid) const { if (!m_windowManagement) { return false; } auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept { return w->isValid() && w->internalId() == wid; }); //qDebug() << "desktop:" << (it != m_windowManagement->windows().constEnd() ? (*it)->virtualDesktop() : -1) << KWindowSystem::currentDesktop(); //return true; return it != m_windowManagement->windows().constEnd() && ((*it)->virtualDesktop() == KWindowSystem::currentDesktop() || (*it)->isOnAllDesktops()); } bool WaylandInterface::isOnCurrentActivity(WindowId wid) const { auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept { return w->isValid() && w->internalId() == wid; }); //TODO: Not yet implemented return it != m_windowManagement->windows().constEnd() && true; } WindowInfoWrap WaylandInterface::requestInfo(WindowId wid) const { WindowInfoWrap winfoWrap; auto w = windowFor(wid); if (w) { if (isValidWindow(w)) { winfoWrap.setIsValid(true); winfoWrap.setWid(wid); winfoWrap.setIsActive(w->isActive()); winfoWrap.setIsMinimized(w->isMinimized()); winfoWrap.setIsMaxVert(w->isMaximized()); winfoWrap.setIsMaxHoriz(w->isMaximized()); winfoWrap.setIsFullscreen(w->isFullscreen()); winfoWrap.setIsShaded(w->isShaded()); winfoWrap.setGeometry(w->geometry()); winfoWrap.setHasSkipTaskbar(w->skipTaskbar()); } else if (w->appId() == QLatin1String("org.kde.plasmashell")) { winfoWrap.setIsValid(true); winfoWrap.setIsPlasmaDesktop(true); winfoWrap.setWid(wid); } } else { return {}; } return winfoWrap; } KWayland::Client::PlasmaWindow *WaylandInterface::windowFor(WindowId wid) const { auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept { return w->isValid() && w->internalId() == wid; }); if (it == m_windowManagement->windows().constEnd()) { return nullptr; } return *it; } bool WaylandInterface::windowCanBeDragged(WindowId wid) const { WindowInfoWrap winfo = requestInfo(wid); return (winfo.isValid() && !winfo.isPlasmaDesktop() && !winfo.hasSkipTaskbar()); } void WaylandInterface::requestMoveWindow(WindowId wid, QPoint from) const { if (windowCanBeDragged(wid)) { auto w = windowFor(wid); if (w && isValidWindow(w)) { w->requestMove(); } } } void WaylandInterface::requestToggleMaximized(WindowId wid) const { auto w = windowFor(wid); if (w && isValidWindow(w)) { w->requestToggleMaximized(); } } inline bool WaylandInterface::isValidWindow(const KWayland::Client::PlasmaWindow *w) const { //! because wayland does not have any way yet to identify the window type //! a trick is to just consider windows as valid when they can be shown in the //! taskbar. Of course that creates issues with plasma native dialogs //! e.g. widgets explorer, Activities etc. that are not used to hide //! the dodge docks/panels appropriately return w->isValid() && !w->skipTaskbar(); } void WaylandInterface::windowCreatedProxy(KWayland::Client::PlasmaWindow *w) { if (!isValidWindow(w)) return; if (!mapper) mapper = new QSignalMapper(this); mapper->setMapping(w, w); connect(w, &PlasmaWindow::unmapped, this, [ &, win = w]() noexcept { mapper->removeMappings(win); m_windows.remove(win->internalId()); emit windowRemoved(win->internalId()); }); connect(w, SIGNAL(activeChanged()), mapper, SLOT(map())); connect(w, SIGNAL(fullscreenChanged()), mapper, SLOT(map())); connect(w, SIGNAL(geometryChanged()), mapper, SLOT(map())); connect(w, SIGNAL(maximizedChanged()), mapper, SLOT(map())); connect(w, SIGNAL(minimizedChanged()), mapper, SLOT(map())); connect(w, SIGNAL(shadedChanged()), mapper, SLOT(map())); connect(w, SIGNAL(skipTaskbarChanged()), mapper, SLOT(map())); connect(w, SIGNAL(onAllDesktopsChanged()), mapper, SLOT(map())); connect(w, SIGNAL(virtualDesktopChanged()), mapper, SLOT(map())); connect(mapper, static_cast(&QSignalMapper::mapped) , this, [&](QObject * w) noexcept { //qDebug() << "window changed:" << qobject_cast(w)->appId(); emit windowChanged(qobject_cast(w)->internalId()); }); m_windows.push_back(w->internalId()); emit windowAdded(w->internalId()); } } #include "waylandinterface.moc" diff --git a/app/wm/waylandinterface.h b/app/wm/waylandinterface.h index bf1cd3cf..fea3e25c 100644 --- a/app/wm/waylandinterface.h +++ b/app/wm/waylandinterface.h @@ -1,110 +1,110 @@ /* * 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 . */ #ifndef WAYLANDINTERFACE_H #define WAYLANDINTERFACE_H // local #include "abstractwindowinterface.h" #include "windowinfowrap.h" // Qt #include #include #include // KDE #include #include #include #include #include #include #include namespace Latte { -class DockCorona; +class Corona; namespace Private { /** * @brief this class is use for create the struts inside wayland */ class GhostWindow; } class WaylandInterface : public AbstractWindowInterface { Q_OBJECT public: explicit WaylandInterface(QObject *parent = nullptr); ~WaylandInterface() override; void setDockExtraFlags(QWindow &view) override; void setDockStruts(QWindow &view, const QRect &rect , Plasma::Types::Location location) override; void setWindowOnActivities(QWindow &view, const QStringList &activities) override; void removeDockStruts(QWindow &view) const override; WindowId activeWindow() const override; WindowInfoWrap requestInfo(WindowId wid) const override; WindowInfoWrap requestInfoActive() const override; bool isOnCurrentDesktop(WindowId wid) const override; bool isOnCurrentActivity(WindowId wid) const override; const std::list &windows() const override; void setKeepAbove(const QDialog &dialog, bool above = true) const override; void skipTaskBar(const QDialog &dialog) const override; void slideWindow(QWindow &view, Slide location) const override; void enableBlurBehind(QWindow &view) const override; void requestToggleMaximized(WindowId wid) const override; void requestMoveWindow(WindowId wid, QPoint from) const override; bool windowCanBeDragged(WindowId wid) const; void setEdgeStateFor(QWindow *view, bool active) const override; void initWindowManagement(KWayland::Client::PlasmaWindowManagement *windowManagement); private: void init(); inline bool isValidWindow(const KWayland::Client::PlasmaWindow *w) const; void windowCreatedProxy(KWayland::Client::PlasmaWindow *w); KWayland::Client::PlasmaWindow *windowFor(WindowId wid) const; - KWayland::Client::PlasmaShell *waylandDockCoronaInterface() const; + KWayland::Client::PlasmaShell *waylandCoronaInterface() const; QSignalMapper *mapper{nullptr}; friend class Private::GhostWindow; mutable QMap m_ghostWindows; KWayland::Client::PlasmaWindowManagement *m_windowManagement{nullptr}; - DockCorona *m_corona{nullptr}; + Latte::Corona *m_corona{nullptr}; }; } #endif // WAYLANDINTERFACE_H