diff --git a/app/dockconfigview.cpp b/app/dockconfigview.cpp index f2ce28ca..9ed54449 100644 --- a/app/dockconfigview.cpp +++ b/app/dockconfigview.cpp @@ -1,448 +1,452 @@ /* * 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" #include "dockview.h" #include "dockcorona.h" #include "panelshadows_p.h" #include "abstractwindowinterface.h" #include "../liblattedock/dock.h" #include #include #include #include #include #include #include #include #include #include namespace Latte { DockConfigView::DockConfigView(Plasma::Containment *containment, DockView *dockView, QWindow *parent) : PlasmaQuick::ConfigView(containment, parent), m_blockFocusLost(false), m_dockView(dockView) { setupWaylandIntegration(); setScreen(m_dockView->screen()); if (containment) { setIcon(qGuiApp->windowIcon()); } m_screenSyncTimer.setSingleShot(true); m_screenSyncTimer.setInterval(100); connections << connect(dockView, SIGNAL(screenChanged(QScreen *)), &m_screenSyncTimer, SLOT(start())); connections << connect(&m_screenSyncTimer, &QTimer::timeout, this, [this]() { setScreen(m_dockView->screen()); setFlags(wFlags()); syncGeometry(); syncSlideEffect(); }); connections << connect(dockView->visibility(), &VisibilityManager::modeChanged, this, &DockConfigView::syncGeometry); connections << connect(containment, &Plasma::Containment::immutabilityChanged, this, &DockConfigView::immutabilityChanged); connections << connect(containment, &Plasma::Containment::locationChanged, [&]() { syncSlideEffect(); QTimer::singleShot(200, this, &DockConfigView::syncGeometry); }); auto *dockCorona = qobject_cast(m_dockView->corona()); if (dockCorona) { connections << connect(this, &DockConfigView::aboutApplication, dockCorona, &DockCorona::aboutApplication); connections << connect(dockCorona, SIGNAL(raiseDocksTemporaryChanged()), this, SIGNAL(raiseDocksTemporaryChanged())); } } DockConfigView::~DockConfigView() { qDebug() << "DockConfigView deleting ..."; 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_dockView); rootContext()->setContextProperty(QStringLiteral("dockConfig"), this); auto *dockCorona = qobject_cast(m_dockView->corona()); if (dockCorona) { rootContext()->setContextProperty(QStringLiteral("universalSettings"), dockCorona->universalSettings()); rootContext()->setContextProperty(QStringLiteral("layoutManager"), dockCorona->layoutManager()); } KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); kdeclarative.setTranslationDomain(QStringLiteral("latte-dock")); kdeclarative.setupBindings(); auto source = QUrl::fromLocalFile(m_dockView->containment()->corona()->kPackage().filePath("lattedockconfigurationui")); setSource(source); syncGeometry(); syncSlideEffect(); qDebug() << "dock config view : initialization ended..."; } inline Qt::WindowFlags DockConfigView::wFlags() const { return (flags() | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) & ~Qt::WindowDoesNotAcceptFocus; } +QString DockConfigView::trademarkPath() +{ + return m_dockView->containment()->corona()->kPackage().filePath("trademark"); +} + void DockConfigView::syncGeometry() { if (!m_dockView->containment() || !rootObject()) return; const auto location = m_dockView->containment()->location(); const auto sGeometry = screen()->geometry(); int clearThickness = m_dockView->normalThickness(); QPoint position{0, 0}; switch (m_dockView->containment()->formFactor()) { case Plasma::Types::Horizontal: { const QSize size(rootObject()->width(), rootObject()->height()); setMaximumSize(size); setMinimumSize(size); resize(size); 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: { const QSize size(rootObject()->width(), rootObject()->height()); setMaximumSize(size); setMinimumSize(size); resize(size); 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; } setPosition(position); if (m_shellSurface) { m_shellSurface->setPosition(position); } } void DockConfigView::syncSlideEffect() { if (!m_dockView->containment()) return; auto slideLocation = WindowSystem::Slide::None; switch (m_dockView->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; } WindowSystem::self().slideWindow(*this, slideLocation); } void DockConfigView::showEvent(QShowEvent *ev) { QQuickWindow::showEvent(ev); WindowSystem::self().setDockExtraFlags(*this); setFlags(wFlags()); WindowSystem::self().enableBlurBehind(*this); syncGeometry(); syncSlideEffect(); if (m_dockView && m_dockView->containment()) m_dockView->containment()->setUserConfiguring(true); m_screenSyncTimer.start(); QTimer::singleShot(400, this, &DockConfigView::syncGeometry); emit showSignal(); } void DockConfigView::hideEvent(QHideEvent *ev) { if (!m_dockView) { QQuickWindow::hideEvent(ev); return; } if (m_dockView->containment()) m_dockView->containment()->setUserConfiguring(false); QQuickWindow::hideEvent(ev); auto recreateDock = [&]() noexcept { auto *dockCorona = qobject_cast(m_dockView->corona()); if (dockCorona) { dockCorona->recreateDock(m_dockView->containment()); } }; const auto mode = m_dockView->visibility()->mode(); const auto previousDockWinBehavior = (m_dockView->flags() & Qt::BypassWindowManagerHint) ? false : true; if (mode == Dock::AlwaysVisible || mode == Dock::WindowsGoBelow) { if (!previousDockWinBehavior) { recreateDock(); } } else if (m_dockView->dockWinBehavior() != previousDockWinBehavior) { recreateDock(); } deleteLater(); } void DockConfigView::focusOutEvent(QFocusEvent *ev) { Q_UNUSED(ev); const auto *focusWindow = qGuiApp->focusWindow(); if (focusWindow && focusWindow->flags().testFlag(Qt::Popup)) return; if (!m_blockFocusLost) hideConfigWindow(); } void DockConfigView::setupWaylandIntegration() { if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_dockView || !m_dockView->containment()) { // already setup return; } if (DockCorona *c = qobject_cast(m_dockView->containment()->corona())) { using namespace KWayland::Client; PlasmaShell *interface = c->waylandDockCoronaInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(this); if (!s) { return; } qDebug() << "wayland dock window surface was created..."; m_shellSurface = interface->createSurface(s, this); 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; } 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; } void DockConfigView::addPanelSpacer() { if (m_dockView && m_dockView->containment()) { m_dockView->containment()->createApplet(QStringLiteral("org.kde.latte.spacer")); } } void DockConfigView::hideConfigWindow() { if (m_shellSurface) { //!NOTE: Avoid crash in wayland enviroment with qt5.9 close(); } else { hide(); } } void DockConfigView::updateLaunchersForGroup(int groupInt) { Dock::LaunchersGroup group = (Dock::LaunchersGroup)groupInt; auto *dockCorona = qobject_cast(m_dockView->corona()); //! when the layout/global launchers list is empty then the current dock launchers are used for them //! as a start point if (dockCorona && dockCorona->layoutManager() && dockCorona->layoutManager()->currentLayout()) { if ((group == Dock::LayoutLaunchers && dockCorona->layoutManager()->currentLayout()->launchers().isEmpty()) || (group == Dock::GlobalLaunchers && dockCorona->universalSettings()->launchers().isEmpty())) { Plasma::Containment *c = m_dockView->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) { dockCorona->layoutManager()->currentLayout()->setLaunchers(launchers.toStringList()); } else if (group == Dock::GlobalLaunchers) { dockCorona->universalSettings()->setLaunchers(launchers.toStringList()); } } } } } } } } } } - } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/dockconfigview.h b/app/dockconfigview.h index 7bf37e48..ebb8cdeb 100644 --- a/app/dockconfigview.h +++ b/app/dockconfigview.h @@ -1,101 +1,102 @@ /* * 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 #include "plasmaquick/configview.h" #include "../liblattedock/dock.h" #include #include #include #include #include namespace Plasma { class Applet; class Containment; class Types; } namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { class DockView; class DockConfigView : public PlasmaQuick::ConfigView { Q_OBJECT public: DockConfigView(Plasma::Containment *containment, DockView *dockView, QWindow *parent = nullptr); ~DockConfigView() override; void init() override; Qt::WindowFlags wFlags() const; bool sticker() const; 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 QString trademarkPath(); signals: void raiseDocksTemporaryChanged(); 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); signals: void aboutApplication(); private: void setupWaylandIntegration(); bool m_blockFocusLost; QPointer m_dockView; QTimer m_screenSyncTimer; QList connections; KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; }; } #endif //DOCKCONFIGVIEW_H // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/dockcorona.cpp b/app/dockcorona.cpp index d29c96ae..15c945b0 100644 --- a/app/dockcorona.cpp +++ b/app/dockcorona.cpp @@ -1,1486 +1,1485 @@ /* * 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 "dockview.h" #include "packageplugins/shell/dockpackage.h" #include "abstractwindowinterface.h" #include "alternativeshelper.h" #include "screenpool.h" //dbus adaptor #include "lattedockadaptor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Latte { DockCorona::DockCorona(QObject *parent) : Plasma::Corona(parent), 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_layoutManager(new LayoutManager(this)) { setupWaylandIntegration(); KPackage::Package package(new DockPackage(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 must be loaded after the package has been set m_universalSettings->load(); qmlRegisterTypes(); - QFontDatabase::addApplicationFont(kPackage().filePath("tangerineFont")); //connect(this, &Corona::containmentAdded, this, &DockCorona::addDock); if (m_activityConsumer && (m_activityConsumer->serviceStatus() == KActivities::Consumer::Running)) { load(); } connect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &DockCorona::load); m_docksScreenSyncTimer.setSingleShot(true); m_docksScreenSyncTimer.setInterval(2500); connect(&m_docksScreenSyncTimer, &QTimer::timeout, this, &DockCorona::syncDockViews); //! Dbus adaptor initialization new LatteDockAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/Latte"), this); } DockCorona::~DockCorona() { m_docksScreenSyncTimer.stop(); cleanConfig(); //qDebug() << "corona config file:" << config()->name(); while (!containments().isEmpty()) { //deleting a containment will remove it from the list due to QObject::destroyed connect in Corona delete containments().first(); } m_globalShortcuts->deleteLater(); m_screenPool->deleteLater(); m_layoutManager->deleteLater(); m_universalSettings->deleteLater(); qDeleteAll(m_dockViews); qDeleteAll(m_waitingDockViews); m_dockViews.clear(); m_waitingDockViews.clear(); disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &DockCorona::load); delete m_activityConsumer; qDebug() << "latte corona deleted..." << this; } void DockCorona::load() { if (m_activityConsumer && (m_activityConsumer->serviceStatus() == KActivities::Consumer::Running) && m_activitiesStarting) { disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &DockCorona::load); m_layoutManager->load(); m_activitiesStarting = false; // connect(qGuiApp, &QGuiApplication::screenAdded, this, &DockCorona::addOutput, Qt::UniqueConnection); connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &DockCorona::primaryOutputChanged, Qt::UniqueConnection); // connect(qGuiApp, &QGuiApplication::screenRemoved, this, &DockCorona::screenRemoved, Qt::UniqueConnection); connect(QApplication::desktop(), &QDesktopWidget::screenCountChanged, this, &DockCorona::screenCountChanged); connect(m_screenPool, &ScreenPool::primaryPoolChanged, this, &DockCorona::screenCountChanged); QString assignedLayout = m_layoutManager->shouldSwitchToLayout(m_activityConsumer->currentActivity()); if (!assignedLayout.isEmpty() && assignedLayout != m_universalSettings->currentLayoutName()) { m_layoutManager->switchToLayout(assignedLayout); } else { m_layoutManager->switchToLayout(m_universalSettings->currentLayoutName()); } /*foreach (auto containment, containments()) addDock(containment);*/ } } void DockCorona::unload() { qDebug() << "unload: removing dockViews and 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(); } qDeleteAll(m_dockViews); qDeleteAll(m_waitingDockViews); m_dockViews.clear(); m_waitingDockViews.clear(); } void DockCorona::loadLatteLayout(QString layoutPath) { if (!layoutPath.isEmpty()) { qDebug() << "corona is unloading the interface..."; unload(); qDebug() << "loading layout:" << layoutPath; loadLayout(layoutPath); m_firstContainmentWithTasks = -1; m_tasksWillBeLoaded = heuresticForLoadingDockWithTasks(); qDebug() << "TASKS WILL BE PRESENT AFTER LOADING ::: " << m_tasksWillBeLoaded; foreach (auto containment, containments()) addDock(containment); } } void DockCorona::setupWaylandIntegration() { using namespace KWayland::Client; if (!KWindowSystem::isPlatformWayland()) { return; } 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); }); connect(qApp, &QCoreApplication::aboutToQuit, this, [this, registry]() { if (m_waylandDockCorona) m_waylandDockCorona->release(); registry->release(); }); registry->setup(); } KWayland::Client::PlasmaShell *DockCorona::waylandDockCoronaInterface() const { return m_waylandDockCorona; } void DockCorona::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 { foreach (auto containment, containments()) { if (id == containment->id()) { return true; } } return false; } bool DockCorona::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; } ScreenPool *DockCorona::screenPool() const { return m_screenPool; } UniversalSettings *DockCorona::universalSettings() const { return m_universalSettings; } LayoutManager *DockCorona::layoutManager() const { return m_layoutManager; } int DockCorona::numScreens() const { return qGuiApp->screens().count(); } QRect DockCorona::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 { 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(); QRegion available(screen->geometry()); for (const auto *view : m_dockViews) { if (view && view->containment() && view->screen() == screen) { int realThickness = view->normalThickness() - view->shadow(); // Usually availableScreenRect is used by the desktop, // but Latte dont 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 { 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 {}; auto available = screen->geometry(); for (const auto *view : m_dockViews) { if (view && view->containment() && view->screen() == screen) { auto dockRect = view->absGeometry(); // Usually availableScreenRect is used by the desktop, // but Latte dont 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.setTopLeft({available.x(), dockRect.bottom()}); break; case Plasma::Types::BottomEdge: available.setBottomLeft({available.x(), dockRect.top()}); break; default: //! bypass clang warnings break; } } } return available; } //! the number of currently running docks containing //! tasks plasmoid int DockCorona::noDocksWithTasks() const { int result = 0; foreach (auto view, m_dockViews) { if (view->tasksPresent()) { result++; } } return result; } void DockCorona::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()); } } void DockCorona::primaryOutputChanged() { /* qDebug() << "primary changed ### "<< qGuiApp->primaryScreen()->name(); foreach(auto scr, qGuiApp->screens()){ qDebug() << "Found screen: "<name(); }*/ //if (m_dockViews.count()==1 && qGuiApp->screens().size()==1) { // foreach(auto view, m_dockViews) { // view->setScreenToFollow(qGuiApp->primaryScreen()); // } // } } void DockCorona::screenRemoved(QScreen *screen) { Q_ASSERT(screen); } void DockCorona::screenCountChanged() { m_docksScreenSyncTimer.start(); } //! the central functions that updates loading/unloading dockviews //! concerning screen changed (for multi-screen setups mainly) void DockCorona::syncDockViews() { qDebug() << "screen count changed -+-+ " << qGuiApp->screens().size(); qDebug() << "adding consideration...."; qDebug() << "dock view running : " << m_dockViews.count(); foreach (auto scr, qGuiApp->screens()) { qDebug() << "Found screen: " << scr->name(); foreach (auto cont, containments()) { int id = cont->screen(); if (id == -1) { id = cont->lastScreen(); } bool onPrimary = cont->config().readEntry("onPrimary", true); Plasma::Types::Location location = static_cast((int)cont->config().readEntry("location", (int)Plasma::Types::BottomEdge)); //! two main situations that a dock must be added when it is not already running //! 1. when a dock is primary, not running and the edge for which is associated is free //! 2. when a dock in explicit, not running and the associated screen currently exists //! e.g. the screen has just been added if (((onPrimary && freeEdges(qGuiApp->primaryScreen()).contains(location)) || (!onPrimary && (m_screenPool->connector(id) == scr->name()))) && (!m_dockViews.contains(cont))) { qDebug() << "screen Count signal: view must be added... for:" << scr->name(); addDock(cont); } } } qDebug() << "removing consideration & updating screen for always on primary docks...."; //! this code tries to find a containment that must not be deleted by //! automatic algorithm. Currently the containment with the minimum id //! containing tasks plasmoid wins int preserveContainmentId{ -1}; bool dockWithTasksWillBeShown{false}; //! associate correct values for preserveContainmentId and //! dockWithTasksWillBeShown foreach (auto view, m_dockViews) { bool found{false}; foreach (auto scr, qGuiApp->screens()) { if (scr->name() == view->currentScreen() || (view->onPrimary() && scr == qGuiApp->primaryScreen())) { found = true; break; } } //!check if a tasks dock will be shown (try to prevent its deletion) if (found && view->tasksPresent()) { dockWithTasksWillBeShown = true; } if (!found && !view->onPrimary() && (m_dockViews.size() > 1) && m_dockViews.contains(view->containment()) && !(view->tasksPresent() && noDocksWithTasks() == 1)) { //do not delete last dock containing tasks if (view->tasksPresent()) { if (preserveContainmentId == -1) preserveContainmentId = view->containment()->id(); else if (view->containment()->id() < preserveContainmentId) preserveContainmentId = view->containment()->id(); } } } //! check which docks must be deleted e.g. when the corresponding //! screen does not exist any more. //! The code is smart enough in order //! to never delete the last tasks dock and also it makes sure that //! the last tasks dock which will exist in the end will be the one //! with the lowest containment id foreach (auto view, m_dockViews) { bool found{false}; foreach (auto scr, qGuiApp->screens()) { if (scr->name() == view->currentScreen() || (view->onPrimary() && scr == qGuiApp->primaryScreen())) { found = true; break; } } //! which explicit docks can be deleted if (!found && !view->onPrimary() && (m_dockViews.size() > 1) && m_dockViews.contains(view->containment()) && !(view->tasksPresent() && noDocksWithTasks() == 1)) { //do not delete last dock containing tasks if (dockWithTasksWillBeShown || preserveContainmentId != view->containment()->id()) { qDebug() << "screen Count signal: view must be deleted... for:" << view->currentScreen(); auto viewToDelete = m_dockViews.take(view->containment()); viewToDelete->deleteLater(); } //!which primary docks can be deleted } else if (view->onPrimary() && !found && !freeEdges(qGuiApp->primaryScreen()).contains(view->location())) { qDebug() << "screen Count signal: primary view must be deleted... for:" << view->currentScreen(); auto viewToDelete = m_dockViews.take(view->containment()); viewToDelete->deleteLater(); } else { //! 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 screens count change...."; } int DockCorona::primaryScreenId() const { //this is not the proper way because kwin probably uses a different //index of screens... //This needs a lot of testing... return m_screenPool->id(qGuiApp->primaryScreen()->name()); } int DockCorona::docksCount(int screen) const { QScreen *scr = m_screenPool->screenForId(screen); int docks{0}; for (const auto &view : m_dockViews) { if (view && view->screen() == scr && !view->containment()->destroyed()) { ++docks; } } // qDebug() << docks << "docks on screen:" << screen; return docks; } int DockCorona::docksCount() const { int docks{0}; for (const auto &view : m_dockViews) { if (view && view->containment() && !view->containment()->destroyed()) { ++docks; } } // qDebug() << docks << "docks on screen:" << screen; return docks; } int DockCorona::docksCount(QScreen *screen) const { int docks{0}; for (const auto &view : m_dockViews) { if (view && view->screen() == screen && !view->containment()->destroyed()) { ++docks; } } // qDebug() << docks << "docks on screen:" << screen; return docks; } void DockCorona::closeApplication() { qGuiApp->quit(); } void DockCorona::aboutApplication() { if (aboutDialog) { aboutDialog->hide(); aboutDialog->deleteLater(); } aboutDialog = new KAboutApplicationDialog(KAboutData::applicationData()); connect(aboutDialog.data(), &QDialog::finished, aboutDialog.data(), &QObject::deleteLater); WindowSystem::self().skipTaskBar(*aboutDialog); aboutDialog->show(); } int DockCorona::noOfDocks() { return m_dockViews.count(); } QList DockCorona::freeEdges(QScreen *screen) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; for (auto *view : m_dockViews) { if (view && view->currentScreen() == screen->name()) { edges.removeOne(view->location()); } } return edges; } QList DockCorona::freeEdges(int screen) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; QScreen *scr = m_screenPool->screenForId(screen); for (auto *view : m_dockViews) { if (view && scr && view->currentScreen() == scr->name()) { edges.removeOne(view->location()); } } return edges; } int DockCorona::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 dockView 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; } } //if the panel views already exist, base upon them DockView *view = m_dockViews.value(containment); if (view && view->screen()) { return m_screenPool->id(view->screen()->name()); } //Failed? fallback on lastScreen() //lastScreen() is the correct screen for panels //It is also correct for desktops *that have the correct activity()* //a containment with lastScreen() == 0 but another activity, //won't be associated to a screen // qDebug() << "ShellCorona screenForContainment: " << containment << " Last screen is " << containment->lastScreen(); for (auto screen : qGuiApp->screens()) { // containment->lastScreen() == m_screenPool->id(screen->name()) to check if the lastScreen refers to a screen that exists/it's known if (containment->lastScreen() == m_screenPool->id(screen->name()) && (containment->activity() == m_activityConsumer->currentActivity() || containment->containmentType() == Plasma::Types::PanelContainment || containment->containmentType() == Plasma::Types::CustomPanelContainment)) { return containment->lastScreen(); } } return -1; } void DockCorona::addDock(Plasma::Containment *containment, int expDockScreen) { if (!containment || !containment->kPackage().isValid()) { qWarning() << "the requested containment plugin can not be located or loaded"; return; } auto metadata = containment->kPackage().metadata(); if (metadata.pluginId() != "org.kde.latte.containment") return; for (auto *dock : m_dockViews) { if (dock->containment() == containment) return; } QScreen *nextScreen{qGuiApp->primaryScreen()}; //! 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 bool forceDockLoading = false; if (!m_tasksWillBeLoaded && m_firstContainmentWithTasks == static_cast(containment->id())) { m_tasksWillBeLoaded = true; //this protects by loading more than one dock at startup forceDockLoading = true; } 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 : " << id << " ,onprimary:" << onPrimary << " ,forceDockLoad:" << forceDockLoading; if (id >= 0 && !onPrimary && !forceDockLoading) { QString connector = m_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() << "adding dock rejected, screen not available : " << connector; return; } } else if (onPrimary) { if (explicitDockOccupyEdge(primaryScreenId(), 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 dockView = new DockView(this, nextScreen, dockWin); dockView->init(); dockView->setContainment(containment); //! force this special dock case to become primary //! even though it isnt if (forceDockLoading) { dockView->setOnPrimary(true); } connect(containment, &QObject::destroyed, this, &DockCorona::dockContainmentDestroyed); connect(containment, &Plasma::Applet::destroyedChanged, this, &DockCorona::destroyedChanged); connect(containment, &Plasma::Applet::locationChanged, this, &DockCorona::dockLocationChanged); connect(containment, &Plasma::Containment::appletAlternativesRequested , this, &DockCorona::showAlternativesForApplet, Qt::QueuedConnection); //! 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()) { dockView->show(); //} m_dockViews[containment] = dockView; emit docksCountChanged(); } bool DockCorona::explicitDockOccupyEdge(int screen, Plasma::Types::Location location) const { foreach (auto containment, containments()) { 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; } void DockCorona::recreateDock(Plasma::Containment *containment) { //! give the time to config window to close itself first and then recreate the dock //! step:1 remove the dockview QTimer::singleShot(350, [this, containment]() { auto view = m_dockViews.take(containment); if (view) { qDebug() << "recreate - step 1: removing dock for containment:" << containment->id(); //! step:2 add the new dockview connect(view, &QObject::destroyed, this, [this, containment]() { QTimer::singleShot(250, this, [this, containment]() { if (!m_dockViews.contains(containment)) { qDebug() << "recreate - step 2: adding dock for containment:" << containment->id(); addDock(containment); } }); }); view->deleteLater(); } }); } void DockCorona::destroyedChanged(bool destroyed) { qDebug() << "dock containment destroyed changed!!!!"; Plasma::Containment *sender = qobject_cast(QObject::sender()); if (!sender) { return; } if (destroyed) { m_waitingDockViews[sender] = m_dockViews.take(static_cast(sender)); } else { m_dockViews[sender] = m_waitingDockViews.take(static_cast(sender)); } emit docksCountChanged(); } void DockCorona::dockContainmentDestroyed(QObject *cont) { qDebug() << "dock containment destroyed!!!!"; auto view = m_dockViews.take(static_cast(cont)); if (!view) { view = m_waitingDockViews.take(static_cast(cont)); } if (view) { view->deleteLater(); } emit docksCountChanged(); } void DockCorona::showAlternativesForApplet(Plasma::Applet *applet) { const QString alternativesQML = kPackage().filePath("appletalternativesui"); if (alternativesQML.isEmpty()) { return; } DockView *dockView = m_dockViews[applet->containment()]; KDeclarative::QmlObject *qmlObj{nullptr}; if (dockView) { dockView->setAlternativesIsShown(true); qmlObj = new KDeclarative::QmlObject(dockView); } 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, [dockView]() { dockView->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) { 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() { 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); QList edges = freeEdges(defaultContainment->screen()); if ((edges.count() > 0)) { defaultContainment->setLocation(edges.at(0)); } else { defaultContainment->setLocation(Plasma::Types::BottomEdge); } defaultContainment->updateConstraints(Plasma::Types::StartupCompletedConstraint); defaultContainment->save(config); requestConfigSync(); defaultContainment->flushPendingConstraintsEvents(); emit containmentAdded(defaultContainment); emit containmentCreated(defaultContainment); addDock(defaultContainment); defaultContainment->createApplet(QStringLiteral("org.kde.latte.plasmoid")); defaultContainment->createApplet(QStringLiteral("org.kde.plasma.analogclock")); } void DockCorona::copyDock(Plasma::Containment *containment) { if (!containment) return; qDebug() << "copying containment layout"; //! Settting mutable for create a containment setImmutability(Plasma::Types::Mutable); QStringList toCopyContainmentIds; QStringList toCopyAppletIds; QString temp1File = QDir::homePath() + "/.config/lattedock.copy1.bak"; QString temp2File = QDir::homePath() + "/.config/lattedock.copy2.bak"; //! WE NEED A WAY TO COPY A CONTAINMENT!!!! QFile copyFile(temp1File); QFile copyFile2(temp2File); if (copyFile.exists()) copyFile.remove(); if (copyFile2.exists()) copyFile2.remove(); KSharedConfigPtr newFile = KSharedConfig::openConfig(QDir::homePath() + "/.config/lattedock.copy1.bak"); 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").toInt(); if (tSysId != -1) { systrayId = tSysId; systrayAppletId = applet; qDebug() << "systray was found in the containment..."; break; } } if (systrayId != -1) { Plasma::Containment *systray{nullptr}; foreach (auto containment, 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 //! BEGIN updating the ids in the temp file QStringList allIds; allIds << containmentsIds(); allIds << appletsIds(); //qDebug() << "Ids:" << allIds; //qDebug() << "to copy containments: " << toCopyContainmentIds; //qDebug() << "to copy applets: " << toCopyAppletIds; QStringList assignedIds; QHash assigned; foreach (auto contId, toCopyContainmentIds) { QString newId = availableId(allIds, assignedIds, 12); assignedIds << newId; assigned[contId] = newId; } foreach (auto appId, toCopyAppletIds) { QString newId = availableId(allIds, assignedIds, 40); assignedIds << newId; assigned[appId] = newId; } qDebug() << "full assignments ::: " << assigned; QString order1 = copied_c1.group("General").readEntry("appletOrder", QString()); QStringList order1Ids = order1.split(";"); QStringList fixedOrder1Ids; //qDebug() << "order1 :: " << order1; for (int i = 0; i < order1Ids.count(); ++i) { fixedOrder1Ids.append(assigned[order1Ids[i]]); } QString fixedOrder1 = fixedOrder1Ids.join(";"); //qDebug() << "fixed order ::: " << fixedOrder1; copied_c1.group("General").writeEntry("appletOrder", fixedOrder1); //! must update also the systray id in its applet if (systrayId > -1) { copied_c1.group("Applets").group(systrayAppletId).group("Configuration").writeEntry("SystrayContainmentId", assigned[QString::number(systrayId)]); copied_systray.sync(); } copied_c1.sync(); QFile(temp1File).copy(temp2File); QFile f(temp2File); if (!f.open(QFile::ReadOnly)) { qDebug() << "temp file couldnt be opened..."; return; } QTextStream in(&f); QString fileText = in.readAll(); foreach (auto contId, toCopyContainmentIds) { fileText = fileText.replace("[Containments][" + contId + "]", "[Containments][" + assigned[contId] + "]"); } foreach (auto appId, toCopyAppletIds) { fileText = fileText.replace("][Applets][" + appId + "]", "][Applets][" + assigned[appId] + "]"); } f.close(); if (!f.open(QFile::WriteOnly)) { qDebug() << "temp file couldnt be opened for writing..."; return; } QTextStream outputStream(&f); outputStream << fileText; f.close(); //! END of updating the ids in the temp file //! Finally import the configuration KSharedConfigPtr newFile2 = KSharedConfig::openConfig(QDir::homePath() + "/.config/lattedock.copy2.bak"); auto nConts = importLayout(KConfigGroup(newFile2, "")); ///Find latte and systray containments qDebug() << " imported containments ::: " << nConts.length(); Plasma::Containment *newContainment{nullptr}; int newSystrayId = -1; foreach (auto containment, nConts) { KPluginMetaData meta = containment->kPackage().metadata(); if (meta.pluginId() == "org.kde.latte.containment") { qDebug() << "new latte containment id: " << containment->id(); newContainment = containment; } else if (meta.pluginId() == "org.kde.plasma.private.systemtray") { qDebug() << "new systray containment id: " << containment->id(); newSystrayId = containment->id(); } } if (!newContainment) return; ///after systray was found we must update in latte the relevant id if (newSystrayId != -1) { applets = newContainment->config().group("Applets"); qDebug() << "systray found with id : " << newSystrayId << " and applets in the containment :" << applets.groupList().count(); foreach (auto applet, applets.groupList()) { KConfigGroup appletSettings = applets.group(applet).group("Configuration"); if (appletSettings.hasKey("SystrayContainmentId")) { qDebug() << "!!! updating systray id to : " << newSystrayId; appletSettings.writeEntry("SystrayContainmentId", newSystrayId); } } } 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_dockViews[containment]; bool setOnExplicitScreen = false; int dockScrId = -1; int copyScrId = -1; if (dock) { dockScrId = m_screenPool->id(dock->currentScreen()); qDebug() << "COPY DOCK SCREEN ::: " << dockScrId; if (dockScrId != -1 && screens.count() > 1) { foreach (auto scr, screens) { copyScrId = m_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, copyScrId); newContainment->reactToScreenChange(); } else { qDebug() << "Copy Dock in current screen..."; addDock(newContainment, dockScrId); } } QString DockCorona::availableId(QStringList all, QStringList assigned, int base) { bool found = false; int i = base; while (!found && i < 30000) { QString iStr = QString::number(i); if (!all.contains(iStr) && !assigned.contains(iStr)) { return iStr; } i++; } return QString(""); } QStringList DockCorona::containmentsIds() { auto containmentsEntries = config()->group("Containments"); return containmentsEntries.groupList(); } QStringList DockCorona::appletsIds() { QStringList ids; auto containmentsEntries = config()->group("Containments"); foreach (auto cId, containmentsEntries.groupList()) { auto appletsEntries = containmentsEntries.group(cId).group("Applets"); ids << appletsEntries.groupList(); } return ids; } //! 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 DockCorona::heuresticForLoadingDockWithTasks() { foreach (auto containment, 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) { m_firstContainmentWithTasks = containment->id(); if (onPrimary) { return true; } else { if (lastScreen >= 0) { QString connector = m_screenPool->connector(lastScreen); foreach (auto scr, qGuiApp->screens()) { if (scr && scr->name() == connector) { return true; break; } } } } } } } return false; } //! Activate launcher menu through dbus interface void DockCorona::activateLauncherMenu() { m_globalShortcuts->activateLauncherMenu(); } //! update badge for specific dock item void DockCorona::updateDockItemBadge(QString identifier, QString value) { m_globalShortcuts->updateDockItemBadge(identifier, value); } inline void DockCorona::qmlRegisterTypes() const { qmlRegisterType(); } } diff --git a/app/packageplugins/shell/dockpackage.cpp b/app/packageplugins/shell/dockpackage.cpp index 1064f9ef..ed2b3359 100644 --- a/app/packageplugins/shell/dockpackage.cpp +++ b/app/packageplugins/shell/dockpackage.cpp @@ -1,82 +1,82 @@ /* * 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 "dockpackage.h" #include #include #include namespace Latte { DockPackage::DockPackage(QObject *parent, const QVariantList &args) : KPackage::PackageStructure(parent, args) { } DockPackage::~DockPackage() { } void DockPackage::initPackage(KPackage::Package *package) { auto fallback = KPackage::PackageLoader::self()->loadPackage("Plasma/Shell", "org.kde.plasma.desktop"); package->setDefaultPackageRoot(QStringLiteral("plasma/shells/")); package->setPath("org.kde.latte.shell"); package->addFileDefinition("lattedockui", QStringLiteral("views/Panel.qml"), i18n("Latte Dock panel")); //Configuration package->addFileDefinition("lattedockconfigurationui", QStringLiteral("configuration/LatteDockConfiguration.qml"), i18n("Dock configuration UI")); package->addFileDefinition("configmodel", QStringLiteral("configuration/config.qml"), i18n("Config model")); - package->addFileDefinition("tangerineFont", QStringLiteral("fonts/tangerine.ttf"), i18n("Tangerine Font")); + package->addFileDefinition("trademark", QStringLiteral("images/trademark.svg"), i18n("Latte Trademark")); package->addFileDefinition("infoviewui", QStringLiteral("views/InfoView.qml"), i18n("Info View Window")); package->addFileDefinition("layout1", QStringLiteral("layouts/Default.latterc"), i18n("default layout file")); package->addFileDefinition("layout2", QStringLiteral("layouts/Plasma.latterc"), i18n("plasma layout file")); package->addFileDefinition("layout3", QStringLiteral("layouts/Unity.latterc"), i18n("unity layout file")); package->addFileDefinition("layout4", QStringLiteral("layouts/Extended.latterc"), i18n("extended layout file")); package->addFileDefinition("preset1", QStringLiteral("presets/Default.layout.latte"), i18n("default preset file")); package->addFileDefinition("preset2", QStringLiteral("presets/Plasma.layout.latte"), i18n("plasma preset file")); package->addFileDefinition("preset3", QStringLiteral("presets/Unity.layout.latte"), i18n("unity preset file")); package->addFileDefinition("preset4", QStringLiteral("presets/Extended.layout.latte"), i18n("extended preset file")); package->addFileDefinition("separator0", QStringLiteral("controls/latte-separator.desktop"), i18n("tasks plasmoid separator")); package->setFallbackPackage(fallback); qDebug() << "package is valid" << package->isValid(); } void DockPackage::pathChanged(KPackage::Package *package) { if (!package->metadata().isValid()) return; const QString pluginName = package->metadata().pluginId(); if (!pluginName.isEmpty() && pluginName != "org.kde.latte.shell") { auto fallback = KPackage::PackageLoader::self()->loadPackage("Plasma/Shell", "org.kde.latte.shell"); package->setFallbackPackage(fallback); } else if (pluginName.isEmpty() || pluginName == "org.kde.latte.shell") { package->setFallbackPackage(KPackage::Package()); } } } diff --git a/shell/package/contents/configuration/LatteDockConfiguration.qml b/shell/package/contents/configuration/LatteDockConfiguration.qml index c3ae9be4..12719deb 100644 --- a/shell/package/contents/configuration/LatteDockConfiguration.qml +++ b/shell/package/contents/configuration/LatteDockConfiguration.qml @@ -1,388 +1,397 @@ /* * 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 . */ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import QtQuick.Window 2.2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import QtQuick.Controls.Styles.Plasma 2.0 as Styles import org.kde.plasma.plasmoid 2.0 import org.kde.kquickcontrolsaddons 2.0 as KQuickControlAddons import org.kde.latte 0.1 as Latte PlasmaCore.FrameSvgItem { id: dialog imagePath: "dialogs/background" property int maxWidth: 34 * theme.defaultFont.pixelSize width: maxWidth + units.smallSpacing * 2 height: content.height + units.smallSpacing * 2 Layout.minimumWidth: width Layout.minimumHeight: height LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true property bool panelIsVertical: plasmoid.formFactor === PlasmaCore.Types.Vertical property int subGroupSpacing: units.largeSpacing + units.smallSpacing * 1.5 PlasmaComponents.ToolButton { id: pinButton anchors.right: parent.right anchors.top: parent.top Layout.fillWidth: false Layout.fillHeight: false Layout.preferredWidth: width Layout.preferredHeight: height Layout.alignment: Qt.AlignRight | Qt.AlignVCenter iconSource: "window-pin" checkable: true width: Math.round(units.gridUnit * 1.25) height: width property bool inStartup: true onClicked: { plasmoid.configuration.configurationSticker = checked dockConfig.setSticker(checked) } Component.onCompleted: { checked = plasmoid.configuration.configurationSticker dockConfig.setSticker(plasmoid.configuration.configurationSticker) } } ColumnLayout { id: content Layout.minimumWidth: width Layout.minimumHeight: height Layout.preferredWidth: width Layout.preferredHeight: height height: header.height + tabBar.height + pagesBackground.height + actionButtons.height + spacing * 3 width: dialog.maxWidth anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top spacing: units.smallSpacing RowLayout { id: header Layout.fillWidth: true spacing: 0 Item { Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter Layout.fillWidth: false Layout.preferredWidth: width Layout.preferredHeight: height width: Qt.application.layoutDirection !== Qt.RightToLeft ? logo.width + latteTxt.width + units.smallSpacing : logo.width + units.smallSpacing height: logo.height PlasmaCore.IconItem { id: logo - width: 1.5 * latteTxt.font.pixelSize + width: 1.5 * latteTxtMetrics.font.pixelSize height: width source: "latte-dock" animated: true usesPlasmaTheme: false active: aboutMouseArea.containsMouse } - PlasmaComponents.Label { + id: latteTxtMetrics + text: "Latte" + font.pointSize: 2 * theme.defaultFont.pointSize + visible: false + } + + PlasmaCore.SvgItem{ id: latteTxt - height: logo.height - verticalAlignment: Text.AlignVCenter - text: "atte" - font.family: "Tangerine" - font.pointSize: 2 * theme.defaultFont.pointSize - font.italic: true + width: 2.2 * height + height: 0.42 * latteTxtMetrics.font.pixelSize + visible: Qt.application.layoutDirection !== Qt.RightToLeft anchors.left: logo.right + anchors.leftMargin: -3 + anchors.verticalCenter: logo.verticalCenter + + svg: PlasmaCore.Svg{ + imagePath: dockConfig.trademarkPath() + } } MouseArea { id: aboutMouseArea acceptedButtons: Qt.LeftButton anchors.fill: parent hoverEnabled: true onClicked: dockConfig.aboutApplication() } } RowLayout { Layout.fillWidth: true Layout.rightMargin: units.smallSpacing Layout.alignment: Qt.AlignRight | Qt.AlignBottom PlasmaComponents.Label { Layout.fillWidth: true Layout.alignment: Qt.AlignRight } PlasmaComponents.Label { text: i18n("Advanced") Layout.alignment: Qt.AlignRight opacity: plasmoid.configuration.advanced ? 1 : 0.3 } Switch { id: advancedSwitch checked: plasmoid.configuration.advanced onPressedChanged: { if(pressed) plasmoid.configuration.advanced = !checked; } style: Styles.SwitchStyle { property bool checked: advancedSwitch.checked } onCheckedChanged: { if (!checked && tabGroup.currentTab === tweaksPage) { if (tasksTabBtn.visible) { tabGroup.currentTab = tasksPage; tabBar.currentTab = tasksTabBtn; } else { tabGroup.currentTab = appearancePage; tabBar.currentTab = appearanceTabBtn; } } } } } } PlasmaComponents.TabBar { id: tabBar Layout.fillWidth: true Layout.maximumWidth: maxWidth PlasmaComponents.TabButton { id: behaviorTabBtn text: i18n("Behavior") tab: behaviorPage } PlasmaComponents.TabButton { id: appearanceTabBtn text: i18n("Appearance") tab: appearancePage } PlasmaComponents.TabButton { id: tasksTabBtn text: i18n("Tasks") tab: tasksPage visible: dock.latteTasksPresent() } PlasmaComponents.TabButton { text: i18n("Tweaks") tab: tweaksPage visible: plasmoid.configuration.advanced } } Rectangle { id: pagesBackground Layout.fillWidth: true Layout.fillHeight: false Layout.minimumWidth: maxWidth - 2*units.smallSpacing Layout.minimumHeight: height Layout.maximumHeight: height width: maxWidth - units.smallSpacing height: behaviorPage.Layout.maximumHeight + units.smallSpacing * 4 property color bC: theme.backgroundColor property color transparentBack: Qt.rgba(bC.r, bC.g, bC.b, 0.7) color: transparentBack border.width: 1 border.color: theme.backgroundColor PlasmaExtras.ScrollArea { id: scrollArea anchors.fill: parent verticalScrollBarPolicy: Qt.ScrollBarAsNeeded horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff flickableItem.flickableDirection: Flickable.VerticalFlick PlasmaComponents.TabGroup { id: tabGroup width: currentTab.Layout.maximumWidth height: currentTab.Layout.maximumHeight BehaviorConfig { id: behaviorPage } AppearanceConfig { id: appearancePage } TasksConfig { id: tasksPage } TweaksConfig { id: tweaksPage } } } } RowLayout { id: actionButtons Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom spacing: units.largeSpacing Connections{ target: dock onDocksCountChanged: actionButtons.updateEnabled(); } function updateEnabled() { addDock.enabled = dock.docksCount < 4 && dock.freeEdges().length > 0 removeDock.enabled = dock.docksCount>1 && !(dock.docksWithTasks()===1 && dock.tasksPresent()) } PlasmaComponents.Button { Layout.alignment: Qt.AlignLeft Layout.fillWidth: true text:" " PlasmaComponents.ComboBox { id: actionsCmb anchors.fill: parent enabled: addDock.enabled function addModel() { var actions = [] actions.push(" " + i18n("Copy Dock")); actionsCmb.model = actions; actionsCmb.currentIndex = -1; } function emptyModel() { var actions = [] actions.push(" "); actionsCmb.model = actions; actionsCmb.currentIndex = -1; } Component.onCompleted:{ addModel(); } onActivated: { if (index==0) { dock.copyDock(); } actionsCmb.currentIndex = -1; } onEnabledChanged: { if (enabled) addModel(); else emptyModel(); } } //overlayed button PlasmaComponents.Button { id: addDock anchors.left: Qt.application.layoutDirection === Qt.RightToLeft ? undefined : parent.left anchors.right: Qt.application.layoutDirection === Qt.RightToLeft ? parent.right : undefined LayoutMirroring.enabled: false width: parent.width - units.iconSizes.medium + 2*units.smallSpacing height: parent.height text: i18n("New Dock") iconSource: "list-add" tooltip: i18n("Add a new dock") onClicked: dock.addNewDock() Component.onCompleted: { enabled = dock.freeEdges().length > 0 } } } PlasmaComponents.Button { id: removeDock Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter text: i18n("Remove") iconSource: "edit-delete" opacity: dock.totalDocksCount > 1 ? 1 : 0 tooltip: i18n("Remove current dock") onClicked: dock.removeDock() } PlasmaComponents.Button { id: quit Layout.fillWidth: true Layout.alignment: Qt.AlignRight text: i18n("Quit") iconSource: "application-exit" tooltip: i18n("Quit Latte") onClicked: dock.closeApplication() } } } } diff --git a/shell/package/contents/fonts/SIL Open Font License.txt b/shell/package/contents/fonts/SIL Open Font License.txt deleted file mode 100644 index 14c043d6..00000000 --- a/shell/package/contents/fonts/SIL Open Font License.txt +++ /dev/null @@ -1,41 +0,0 @@ -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. - -The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the copyright statement(s). - -"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. - -"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. - -5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/shell/package/contents/fonts/tangerine.ttf b/shell/package/contents/fonts/tangerine.ttf deleted file mode 100644 index 2a2a9e17..00000000 Binary files a/shell/package/contents/fonts/tangerine.ttf and /dev/null differ diff --git a/shell/package/contents/images/trademark.svg b/shell/package/contents/images/trademark.svg new file mode 100644 index 00000000..fe650968 --- /dev/null +++ b/shell/package/contents/images/trademark.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + +