Commit 3c52fc3b authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇
Browse files

Merge branch 'broulik/libnotificationmanager'

This includes:
libnotificationmanager https://phabricator.kde.org/D20265
New notification plasmoid: https://phabricator.kde.org/D20266
as well as dataengine compat: https://phabricator.kde.org/D20490 and https://phabricator.kde.org/D20491

BUG: 222470
BUG: 402144
BUG: 405570
BUG: 391646
BUG: 401819
BUG: 400811
BUG: 392669
BUG: 390143
BUG: 390864
BUG: 374099
BUG: 360990
BUG: 346458
BUG: 398926
BUG: 390152
BUG: 342355
CCBUG: 402391
CCBUG: 399697
CCBUG: 400871
CCBUG: 398580
FIXED-IN: 5.16.0
parents 1a980bed 972840c9
......@@ -148,6 +148,7 @@ add_subdirectory(libdbusmenuqt)
add_subdirectory(appmenu)
add_subdirectory(libtaskmanager)
add_subdirectory(libnotificationmanager)
add_subdirectory(libcolorcorrect)
add_subdirectory(components)
......@@ -174,7 +175,6 @@ add_subdirectory(wallpapers)
add_subdirectory(kioslave)
add_subdirectory(ktimezoned)
add_subdirectory(kuiserver)
add_subdirectory(menu)
add_subdirectory(phonon)
......
add_subdirectory(lib)
add_subdirectory(plugin)
add_definitions(-DTRANSLATION_DOMAIN=\"plasma_applet_org.kde.plasma.notifications\")
set(notificationapplet_SRCS
notificationapplet.cpp
filemenu.cpp
thumbnailer.cpp
)
add_library(plasma_applet_notifications MODULE ${notificationapplet_SRCS})
kcoreaddons_desktop_to_json(plasma_applet_notifications package/metadata.desktop)
target_link_libraries(plasma_applet_notifications
Qt5::Gui
Qt5::Quick # for QQmlParserStatus
KF5::I18n
KF5::Plasma
KF5::KIOWidgets # for PreviewJob
)
install(TARGETS plasma_applet_notifications DESTINATION ${KDE_INSTALL_PLUGINDIR}/plasma/applets)
plasma_install_package(package org.kde.plasma.notifications)
#! /usr/bin/env bash
$XGETTEXT `find . -name \*.js -o -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_org.kde.plasma.notifications.pot
/*
Copyright (C) 2016 Kai Uwe Broulik <kde@privat.broulik.de>
Copyright (C) 2016,2019 Kai Uwe Broulik <kde@privat.broulik.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
......@@ -16,9 +16,7 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "thumbnailer.h"
#include <KIO/PreviewJob>
#include "filemenu.h"
#include <QApplication>
#include <QClipboard>
......@@ -38,104 +36,90 @@
#include <KIO/OpenFileManagerWindowJob>
Thumbnailer::Thumbnailer(QObject *parent) : QObject(parent)
FileMenu::FileMenu(QObject *parent) : QObject(parent)
{
}
Thumbnailer::~Thumbnailer() = default;
void Thumbnailer::classBegin()
{
}
FileMenu::~FileMenu() = default;
void Thumbnailer::componentComplete()
{
m_inited = true;
generatePreview();
}
QUrl Thumbnailer::url() const
QUrl FileMenu::url() const
{
return m_url;
}
void Thumbnailer::setUrl(const QUrl &url)
void FileMenu::setUrl(const QUrl &url)
{
if (m_url != url) {
m_url = url;
emit urlChanged();
generatePreview();
}
}
QSize Thumbnailer::size() const
QQuickItem *FileMenu::visualParent() const
{
return m_size;
return m_visualParent.data();
}
void Thumbnailer::setSize(const QSize &size)
void FileMenu::setVisualParent(QQuickItem *visualParent)
{
if (m_size != size) {
m_size = size;
emit sizeChanged();
generatePreview();
if (m_visualParent.data() == visualParent) {
return;
}
}
bool Thumbnailer::hasPreview() const
{
return !m_pixmap.isNull();
}
QPixmap Thumbnailer::pixmap() const
{
return m_pixmap;
if (m_visualParent) {
disconnect(m_visualParent.data(), nullptr, this, nullptr);
}
m_visualParent = visualParent;
if (m_visualParent) {
connect(m_visualParent.data(), &QObject::destroyed, this, &FileMenu::visualParentChanged);
}
emit visualParentChanged();
}
QSize Thumbnailer::pixmapSize() const
bool FileMenu::visible() const
{
return m_pixmap.size();
return m_visible;
}
QString Thumbnailer::iconName() const
void FileMenu::setVisible(bool visible)
{
return m_iconName;
}
if (m_visible == visible) {
return;
}
bool Thumbnailer::menuVisible() const
{
return m_menuVisible;
if (visible) {
open(0, 0);
} else {
// TODO warning or close?
}
}
void Thumbnailer::showContextMenu(int x, int y, const QString &path, QQuickItem *ctx)
void FileMenu::open(int x, int y)
{
if (!ctx || !ctx->window()) {
if (!m_visualParent || !m_visualParent->window()) {
return;
}
const QUrl url(path);
if (!url.isValid()) {
if (!m_url.isValid()) {
return;
}
KFileItem fileItem(url);
KFileItem fileItem(m_url);
QMenu *menu = new QMenu();
menu->setAttribute(Qt::WA_DeleteOnClose, true);
connect(menu, &QMenu::triggered, this, &FileMenu::actionTriggered);
connect(menu, &QMenu::aboutToHide, this, [this] {
m_menuVisible = false;
emit menuVisibleChanged();
m_visible = false;
emit visibleChanged();
});
if (KProtocolManager::supportsListing(url)) {
if (KProtocolManager::supportsListing(m_url)) {
QAction *openContainingFolderAction = menu->addAction(QIcon::fromTheme(QStringLiteral("folder-open")), i18n("Open Containing Folder"));
connect(openContainingFolderAction, &QAction::triggered, [url] {
KIO::highlightInFileManager({url});
connect(openContainingFolderAction, &QAction::triggered, [this] {
KIO::highlightInFileManager({m_url});
});
}
......@@ -171,71 +155,31 @@ void Thumbnailer::showContextMenu(int x, int y, const QString &path, QQuickItem
// this causes the next click to go missing
//by releasing manually we avoid that situation
auto ungrabMouseHack = [ctx]() {
if (ctx->window()->mouseGrabberItem()) {
ctx->window()->mouseGrabberItem()->ungrabMouse();
auto ungrabMouseHack = [this]() {
if (m_visualParent && m_visualParent->window() && m_visualParent->window()->mouseGrabberItem()) {
m_visualParent->window()->mouseGrabberItem()->ungrabMouse();
}
};
QTimer::singleShot(0, ctx, ungrabMouseHack);
QTimer::singleShot(0, m_visualParent, ungrabMouseHack);
//end workaround
QPoint pos;
if (x == -1 && y == -1) { // align "bottom left of ctx"
if (x == -1 && y == -1) { // align "bottom left of visualParent"
menu->adjustSize();
pos = ctx->mapToGlobal(QPointF(0, ctx->height())).toPoint();
pos = m_visualParent->mapToGlobal(QPointF(0, m_visualParent->height())).toPoint();
if (!qApp->isRightToLeft()) {
pos.rx() += ctx->width();
pos.rx() += m_visualParent->width();
pos.rx() -= menu->width();
}
} else {
pos = ctx->mapToGlobal(QPointF(x, y)).toPoint();
pos = m_visualParent->mapToGlobal(QPointF(x, y)).toPoint();
}
menu->popup(pos);
m_menuVisible = true;
emit menuVisibleChanged();
}
void Thumbnailer::generatePreview()
{
if (!m_inited) {
return;
}
if (!m_url.isValid() || !m_url.isLocalFile() || !m_size.isValid()) {
return;
}
auto maxSize = qMax(m_size.width(), m_size.height());
KIO::PreviewJob *job = KIO::filePreview(KFileItemList({KFileItem(m_url)}), QSize(maxSize,maxSize));
job->setScaleType(KIO::PreviewJob::Scaled);
job->setIgnoreMaximumSize(true);
connect(job, &KIO::PreviewJob::gotPreview, this, [this](const KFileItem &item, const QPixmap &preview) {
Q_UNUSED(item);
m_pixmap = preview;
emit pixmapChanged();
if (!m_iconName.isEmpty()) {
m_iconName.clear();
emit iconNameChanged();
}
});
connect(job, &KIO::PreviewJob::failed, this, [this](const KFileItem &item) {
m_pixmap = QPixmap();
emit pixmapChanged();
const QString &iconName = item.determineMimeType().iconName();
if (m_iconName != iconName) {
m_iconName = iconName;
emit iconNameChanged();
}
});
job->start();
m_visible = true;
emit visibleChanged();
}
/*
Copyright (C) 2014 Martin Klapetek <mklapetek@kde.org>
Copyright (C) 2016,2019 Kai Uwe Broulik <kde@privat.broulik.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
......@@ -16,20 +16,47 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#ifndef NOTIFICATIONSHELPERPLUGIN_H
#define NOTIFICATIONSHELPERPLUGIN_H
#include <QObject>
#include <QPointer>
#include <QUrl>
#include <QQmlExtensionPlugin>
class QAction;
class QQuickItem;
class NotificationsHelperPlugin : public QQmlExtensionPlugin
class FileMenu : public QObject
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
Q_PROPERTY(QQuickItem *visualParent READ visualParent WRITE setVisualParent NOTIFY visualParentChanged)
Q_PROPERTY(bool visible READ visible WRITE setVisible NOTIFY visibleChanged)
public:
void registerTypes(const char *uri) override;
explicit FileMenu(QObject *parent = nullptr);
~FileMenu() override;
};
QUrl url() const;
void setUrl(const QUrl &url);
QQuickItem *visualParent() const;
void setVisualParent(QQuickItem *visualParent);
bool visible() const;
void setVisible(bool visible);
Q_INVOKABLE void open(int x, int y);
#endif // NOTIFICATIONSHELPERPLUGIN_H
signals:
void actionTriggered(QAction *action);
void urlChanged();
void visualParentChanged();
void visibleChanged();
private:
QUrl m_url;
QPointer<QQuickItem> m_visualParent;
bool m_visible = false;
};
set(notificationsapplet_SRCS
notificationsapplet.cpp
)
add_library(plasma_applet_notifications MODULE ${notificationsapplet_SRCS})
kcoreaddons_desktop_to_json(plasma_applet_notifications ../package/metadata.desktop)
target_link_libraries(plasma_applet_notifications
KF5::WindowSystem
KF5::Plasma
KF5::ConfigCore)
install(TARGETS plasma_applet_notifications DESTINATION ${KDE_INSTALL_PLUGINDIR}/plasma/applets)
/*
* Copyright 2014 (c) Martin Klapetek <mklapetek@kde.org>
*
* This program 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) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 <http://www.gnu.org/licenses/>.
*
*/
#include "notificationsapplet.h"
#include <KConfigGroup>
#include <Plasma/Containment>
#include <Plasma/Corona>
NotificationsApplet::NotificationsApplet(QObject *parent, const QVariantList &data)
: Plasma::Applet(parent, data),
m_availableScreenRect(0,0,0,0)
{
}
NotificationsApplet::~NotificationsApplet()
{
}
void NotificationsApplet::init()
{
m_popupPosition = (NotificationsHelper::PositionOnScreen)configScreenPosition();
connect(this, &Plasma::Applet::locationChanged,
this, &NotificationsApplet::onAppletLocationChanged);
connect(containment(), &Plasma::Containment::screenChanged,
this, &NotificationsApplet::onScreenChanges);
Q_ASSERT(containment());
Q_ASSERT(containment()->corona());
connect(containment()->corona(), &Plasma::Corona::availableScreenRectChanged, this, &NotificationsApplet::onScreenChanges);
Plasma::Applet::init();
onScreenChanges();
onAppletLocationChanged();
}
void NotificationsApplet::onScreenChanges()
{
// when removing the panel the applet is in, the containment is being destroyed but its corona is still
// there, rightfully emitting availableScreenRectChanged and then we blow up if we try to access it.
if (!containment() || !containment()->corona()) {
return;
}
auto newAvailableScreenRect = containment()->corona()->availableScreenRect(containment()->screen());
if (newAvailableScreenRect != m_availableScreenRect) {
m_availableScreenRect = newAvailableScreenRect;
Q_EMIT availableScreenRectChanged(m_availableScreenRect);
}
}
QRect NotificationsApplet::availableScreenRect() const
{
return m_availableScreenRect;
}
void NotificationsApplet::onAppletLocationChanged()
{
if (configScreenPosition() == 0) {
// If the screenPosition is set to default,
// just follow the panel
setScreenPositionFromAppletLocation();
}
}
uint NotificationsApplet::screenPosition() const
{
return m_popupPosition;
}
void NotificationsApplet::onScreenPositionChanged(uint position)
{
KConfigGroup globalGroup = globalConfig();
globalGroup.writeEntry("popupPosition", position);
globalGroup.sync();
// If the position is set to default, let the setScreenPositionFromAppletLocation()
// figure out the effective position, otherwise just set it to m_popupPosition
// and emit the change
if (position == NotificationsHelper::Default) {
setScreenPositionFromAppletLocation();
} else if (m_popupPosition != position) {
m_popupPosition = (NotificationsHelper::PositionOnScreen)position;
Q_EMIT screenPositionChanged(m_popupPosition);
}
}
uint NotificationsApplet::configScreenPosition() const
{
KConfigGroup globalGroup = globalConfig();
return globalGroup.readEntry("popupPosition", 0); //0 is default
}
void NotificationsApplet::setScreenPositionFromAppletLocation()
{
NotificationsHelper::PositionOnScreen newPopupPosition = m_popupPosition;
if (location() == Plasma::Types::TopEdge) {
if (QGuiApplication::isRightToLeft()) {
newPopupPosition = NotificationsHelper::TopLeft;
} else {
newPopupPosition = NotificationsHelper::TopRight;
}
} else {
if (QGuiApplication::isRightToLeft()) {
newPopupPosition = NotificationsHelper::BottomLeft;
} else {
newPopupPosition = NotificationsHelper::BottomRight;
}
}
if (newPopupPosition != m_popupPosition) {
m_popupPosition = newPopupPosition;
Q_EMIT screenPositionChanged(m_popupPosition);
}
}
K_EXPORT_PLASMA_APPLET_WITH_JSON(notifications, NotificationsApplet, "metadata.json")
#include "notificationsapplet.moc"
/*
* Copyright 2018 Kai Uwe Broulik <kde@privat.broulik.de>
*
* This program 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) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 <http://www.gnu.org/licenses/>.
*
*/
#include "notificationapplet.h"
#include <QClipboard>
#include <QDrag>
#include <QMimeData>
#include <QQuickItem>
#include <QQuickWindow>
#include <QScreen>
#include <QStyleHints>
#include "filemenu.h"
#include "thumbnailer.h"
NotificationApplet::NotificationApplet(QObject *parent, const QVariantList &data)
: Plasma::Applet(parent, data)
{
static bool s_typesRegistered = false;
if (!s_typesRegistered) {
const char uri[] = "org.kde.plasma.private.notifications";
qmlRegisterType<FileMenu>(uri, 2, 0, "FileMenu");
qmlRegisterType<Thumbnailer>(uri, 2, 0, "Thumbnailer");
qmlProtectModule(uri, 2);
s_typesRegistered = true;
}
}
NotificationApplet::~NotificationApplet() = default;
void NotificationApplet::init()
{
}
void NotificationApplet::configChanged()
{
}
bool NotificationApplet::dragActive() const
{
return m_dragActive;
}
bool NotificationApplet::isDrag(int oldX, int oldY, int newX, int newY) const
{
return ((QPoint(oldX, oldY) - QPoint(newX, newY)).manhattanLength() >= qApp->styleHints()->startDragDistance());
}
void NotificationApplet::startDrag(QQuickItem *item, const QUrl &url, const QPixmap &pixmap)
{
// This allows the caller to return, making sure we don't crash if
// the caller is destroyed mid-drag
QMetaObject::invokeMethod(this, "doDrag", Qt::QueuedConnection,
Q_ARG(QQuickItem*, item), Q_ARG(QUrl, url), Q_ARG(QPixmap, pixmap));
}
void NotificationApplet::doDrag(QQuickItem *item, const QUrl &url, const QPixmap &pixmap)
{
if (item && item->window() && item->window()->mouseGrabberItem()) {
item->window()->mouseGrabberItem()->ungrabMouse();
}
QDrag *drag = new QDrag(item);
QMimeData *mimeData = new QMimeData();
if (!url.isEmpty()) {
mimeData->setUrls(QList<QUrl>() << url);