mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-27 00:33:48 -05:00
Add API to send native desktop notifications to QgsNative
And use native desktop notifications on Linux (via dbus). Avoids unnecessary system tray icon creation, which is a pre-requisite for the Qt desktop notification implemention. This should avoid annoying notifications on certain Linux desktop environments, and should also allow us to use prettier Windows notifications (when implemented!)
This commit is contained in:
parent
282f95c9f7
commit
64586df297
@ -94,6 +94,9 @@
|
||||
#include "processing/qgs3dalgorithms.h"
|
||||
#endif
|
||||
|
||||
#include "qgsgui.h"
|
||||
#include "qgsnative.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkProxy>
|
||||
#include <QAuthenticator>
|
||||
@ -707,9 +710,12 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
|
||||
}
|
||||
endProfile();
|
||||
|
||||
mTray = new QSystemTrayIcon();
|
||||
mTray->setIcon( QIcon( QgsApplication::appIconPath() ) );
|
||||
mTray->hide();
|
||||
if ( !( QgsGui::nativePlatformInterface()->capabilities() & QgsNative::NativeDesktopNotifications ) )
|
||||
{
|
||||
mTray = new QSystemTrayIcon();
|
||||
mTray->setIcon( QIcon( QgsApplication::appIconPath() ) );
|
||||
mTray->hide();
|
||||
}
|
||||
|
||||
// Create the themes folder for the user
|
||||
startProfile( QStringLiteral( "Creating theme folder" ) );
|
||||
@ -1387,7 +1393,6 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
|
||||
{
|
||||
mCentralContainer->setCurrentIndex( 0 );
|
||||
} );
|
||||
|
||||
} // QgisApp ctor
|
||||
|
||||
QgisApp::QgisApp()
|
||||
@ -13844,13 +13849,34 @@ QMenu *QgisApp::createPopupMenu()
|
||||
}
|
||||
|
||||
|
||||
void QgisApp::showSystemNotification( const QString &title, const QString &message )
|
||||
void QgisApp::showSystemNotification( const QString &title, const QString &message, bool replaceExisting )
|
||||
{
|
||||
// Menubar icon is hidden by default. Show to enable notification bubbles
|
||||
mTray->show();
|
||||
mTray->showMessage( title, message );
|
||||
// Re-hide menubar icon
|
||||
mTray->hide();
|
||||
static QVariant sLastMessageId;
|
||||
|
||||
QgsNative::NotificationSettings settings;
|
||||
settings.transient = true;
|
||||
if ( replaceExisting )
|
||||
settings.messageId = sLastMessageId;
|
||||
settings.svgAppIconPath = QgsApplication::iconsPath() + QStringLiteral( "qgis_icon.svg" );
|
||||
|
||||
QgsNative::NotificationResult result = QgsGui::instance()->nativePlatformInterface()->showDesktopNotification( title, message, settings );
|
||||
|
||||
if ( !result.successful )
|
||||
{
|
||||
// fallback - use system tray notification
|
||||
if ( mTray )
|
||||
{
|
||||
// Menubar icon is hidden by default. Show to enable notification bubbles
|
||||
mTray->show();
|
||||
mTray->showMessage( title, message );
|
||||
// Re-hide menubar icon
|
||||
mTray->hide();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sLastMessageId = result.messageId;
|
||||
}
|
||||
}
|
||||
|
||||
void QgisApp::showStatisticsDockWidget( bool show )
|
||||
|
@ -416,8 +416,9 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
*
|
||||
* \param title
|
||||
* \param message
|
||||
* \param replaceExisting set to true to replace any existing notifications, or false to add a new notification
|
||||
*/
|
||||
void showSystemNotification( const QString &title, const QString &message );
|
||||
void showSystemNotification( const QString &title, const QString &message, bool replaceExisting = true );
|
||||
|
||||
|
||||
//! Actions to be inserted in menus and toolbars
|
||||
|
@ -21,6 +21,12 @@
|
||||
#include <QString>
|
||||
#include <QtDBus/QtDBus>
|
||||
#include <QtDebug>
|
||||
#include <QImage>
|
||||
|
||||
QgsNative::Capabilities QgsLinuxNative::capabilities() const
|
||||
{
|
||||
return NativeDesktopNotifications;
|
||||
}
|
||||
|
||||
void QgsLinuxNative::openFileExplorerAndSelectFile( const QString &path )
|
||||
{
|
||||
@ -41,3 +47,117 @@ void QgsLinuxNative::openFileExplorerAndSelectFile( const QString &path )
|
||||
QgsNative::openFileExplorerAndSelectFile( path );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatic marshaling of a QImage for org.freedesktop.Notifications.Notify
|
||||
*
|
||||
* This function is from the Clementine project (see
|
||||
* http://www.clementine-player.org) and licensed under the GNU General Public
|
||||
* License, version 3 or later.
|
||||
*
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*/
|
||||
QDBusArgument &operator<<( QDBusArgument &arg, const QImage &image )
|
||||
{
|
||||
if ( image.isNull() )
|
||||
{
|
||||
arg.beginStructure();
|
||||
arg << 0 << 0 << 0 << false << 0 << 0 << QByteArray();
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
|
||||
QImage scaled = image.scaledToHeight( 100, Qt::SmoothTransformation );
|
||||
scaled = scaled.convertToFormat( QImage::Format_ARGB32 );
|
||||
|
||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||
// ABGR -> ARGB
|
||||
QImage i = scaled.rgbSwapped();
|
||||
#else
|
||||
// ABGR -> GBAR
|
||||
QImage i( scaled.size(), scaled.format() );
|
||||
for ( int y = 0; y < i.height(); ++y )
|
||||
{
|
||||
QRgb *p = ( QRgb * ) scaled.scanLine( y );
|
||||
QRgb *q = ( QRgb * ) i.scanLine( y );
|
||||
QRgb *end = p + scaled.width();
|
||||
while ( p < end )
|
||||
{
|
||||
*q = qRgba( qGreen( *p ), qBlue( *p ), qAlpha( *p ), qRed( *p ) );
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
arg.beginStructure();
|
||||
arg << i.width();
|
||||
arg << i.height();
|
||||
arg << i.bytesPerLine();
|
||||
arg << i.hasAlphaChannel();
|
||||
int channels = i.isGrayscale() ? 1 : ( i.hasAlphaChannel() ? 4 : 3 );
|
||||
arg << i.depth() / channels;
|
||||
arg << channels;
|
||||
arg << QByteArray( reinterpret_cast<const char *>( i.bits() ), i.numBytes() );
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>( const QDBusArgument &arg, QImage & )
|
||||
{
|
||||
// This is needed to link but shouldn't be called.
|
||||
Q_ASSERT( 0 );
|
||||
return arg;
|
||||
}
|
||||
|
||||
QgsNative::NotificationResult QgsLinuxNative::showDesktopNotification( const QString &summary, const QString &body, const NotificationSettings &settings )
|
||||
{
|
||||
NotificationResult result;
|
||||
result.successful = false;
|
||||
|
||||
if ( !QDBusConnection::sessionBus().isConnected() )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
qDBusRegisterMetaType<QImage>();
|
||||
|
||||
QDBusInterface iface( QStringLiteral( "org.freedesktop.Notifications" ),
|
||||
QStringLiteral( "/org/freedesktop/Notifications" ),
|
||||
QStringLiteral( "org.freedesktop.Notifications" ),
|
||||
QDBusConnection::sessionBus() );
|
||||
|
||||
QVariantMap hints;
|
||||
hints[QStringLiteral( "transient" )] = settings.transient;
|
||||
if ( !settings.image.isNull() )
|
||||
hints[QStringLiteral( "image_data" )] = settings.image;
|
||||
|
||||
QVariantList argumentList;
|
||||
argumentList << "qgis"; //app_name
|
||||
// replace_id
|
||||
if ( settings.messageId.isValid() )
|
||||
argumentList << static_cast< uint >( settings.messageId.toInt() );
|
||||
else
|
||||
argumentList << static_cast< uint >( 0 );
|
||||
// app_icon
|
||||
if ( !settings.svgAppIconPath.isEmpty() )
|
||||
argumentList << settings.svgAppIconPath;
|
||||
else
|
||||
argumentList << "";
|
||||
argumentList << summary; // summary
|
||||
argumentList << body; // body
|
||||
argumentList << QStringList(); // actions
|
||||
argumentList << hints; // hints
|
||||
argumentList << -1; // timeout in ms "If -1, the notification's expiration time is dependent on the notification server's settings, and may vary for the type of notification."
|
||||
|
||||
QDBusMessage reply = iface.callWithArgumentList( QDBus::AutoDetect, QStringLiteral( "Notify" ), argumentList );
|
||||
if ( reply.type() == QDBusMessage::ErrorMessage )
|
||||
{
|
||||
qDebug() << "D-Bus Error:" << reply.errorMessage();
|
||||
return result;
|
||||
}
|
||||
|
||||
result.successful = true;
|
||||
result.messageId = reply.arguments().value( 0 );
|
||||
return result;
|
||||
}
|
||||
|
@ -20,10 +20,22 @@
|
||||
|
||||
#include "qgsnative.h"
|
||||
|
||||
/**
|
||||
* Native implementation for linux platforms.
|
||||
*
|
||||
* Implements the native platform interface for Linux based platforms. This is
|
||||
* intended to expose desktop-agnostic implementations of the QgsNative methods,
|
||||
* so that they work without issue across the wide range of Linux desktop environments
|
||||
* (e.g. Gnome/KDE).
|
||||
*
|
||||
* Typically, this means implementing methods using DBUS calls to freedesktop standards.
|
||||
*/
|
||||
class NATIVE_EXPORT QgsLinuxNative : public QgsNative
|
||||
{
|
||||
public:
|
||||
QgsNative::Capabilities capabilities() const override;
|
||||
void openFileExplorerAndSelectFile( const QString &path ) override;
|
||||
NotificationResult showDesktopNotification( const QString &summary, const QString &body, const NotificationSettings &settings = NotificationSettings() ) override;
|
||||
};
|
||||
|
||||
#endif // QGSLINUXNATIVE_H
|
||||
|
@ -21,6 +21,11 @@
|
||||
#include <QUrl>
|
||||
#include <QFileInfo>
|
||||
|
||||
QgsNative::Capabilities QgsNative::capabilities() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QgsNative::initializeMainWindow( QWindow * )
|
||||
{
|
||||
|
||||
@ -51,3 +56,10 @@ void QgsNative::hideApplicationProgress()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QgsNative::NotificationResult QgsNative::showDesktopNotification( const QString &, const QString &, const NotificationSettings & )
|
||||
{
|
||||
NotificationResult result;
|
||||
result.successful = false;
|
||||
return result;
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
#define QGSNATIVE_H
|
||||
|
||||
#include "qgis_native.h"
|
||||
#include <QImage>
|
||||
#include <QVariant>
|
||||
|
||||
class QString;
|
||||
class QWindow;
|
||||
@ -34,8 +36,20 @@ class NATIVE_EXPORT QgsNative
|
||||
{
|
||||
public:
|
||||
|
||||
//! Native interface capabilities
|
||||
enum Capability
|
||||
{
|
||||
NativeDesktopNotifications = 1 << 1, //!< Native desktop notifications are supported. See showDesktopNotification().
|
||||
};
|
||||
Q_DECLARE_FLAGS( Capabilities, Capability )
|
||||
|
||||
virtual ~QgsNative() = default;
|
||||
|
||||
/**
|
||||
* Returns the native interface's capabilities.
|
||||
*/
|
||||
virtual Capabilities capabilities() const;
|
||||
|
||||
/**
|
||||
* Initializes the native interface, using the specified \a window.
|
||||
*
|
||||
@ -97,6 +111,59 @@ class NATIVE_EXPORT QgsNative
|
||||
*/
|
||||
virtual void hideApplicationProgress();
|
||||
|
||||
|
||||
/**
|
||||
* Notification settings, for use with showDesktopNotification().
|
||||
*/
|
||||
struct NotificationSettings
|
||||
{
|
||||
NotificationSettings() {}
|
||||
|
||||
//! Optional image to show in notification
|
||||
QImage image;
|
||||
|
||||
//! Whether the notification should be transient (the meaning varies depending on platform)
|
||||
bool transient = true;
|
||||
|
||||
//! Path to application icon in SVG format
|
||||
QString svgAppIconPath;
|
||||
|
||||
//! Message ID, used to replace existing messages
|
||||
QVariant messageId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Result of sending a desktop notification, returned by showDesktopNotification().
|
||||
*/
|
||||
struct NotificationResult
|
||||
{
|
||||
NotificationResult() {}
|
||||
|
||||
//! True if notification was successfully sent.
|
||||
bool successful = false;
|
||||
|
||||
//! Unique notification message ID, used by some platforms to identify the notification
|
||||
QVariant messageId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows a native desktop notification.
|
||||
*
|
||||
* The \a summary argument specifies a short title for the notification, and the \a body argument
|
||||
* specifies the complete notification message text.
|
||||
*
|
||||
* The \a settings argument allows fine-tuning of the notification, using a range of settings which
|
||||
* may or may not be respected on all platforms. It is recommended to set as many of these properties
|
||||
* as is applicable, and let the native implementation choose which to use based on the platform's
|
||||
* capabilities.
|
||||
*
|
||||
* This method is only supported when the interface returns the NativeDesktopNotifications flag for capabilities().
|
||||
*
|
||||
* Returns true if notification was successfully sent.
|
||||
*/
|
||||
virtual NotificationResult showDesktopNotification( const QString &summary, const QString &body, const NotificationSettings &settings = NotificationSettings() );
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsNative::Capabilities )
|
||||
|
||||
#endif // QGSNATIVE_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user