KNotifyConfig

knotifyconfigactionswidget.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2005-2007 Olivier Goffart <ogoffart at kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only
6*/
7
8#include "knotifyconfigactionswidget.h"
9
10#include "knotifyconfigelement.h"
11#include <knotifyconfig_debug.h>
12
13#include <QFile>
14#include <QStandardPaths>
15#include <QUrl>
16
17#if HAVE_CANBERRA
18#include <canberra.h>
19#elif HAVE_QTMULTIMEDIA
20#include <QAudioOutput>
21#include <QMediaPlayer>
22#endif
23
24KNotifyConfigActionsWidget::KNotifyConfigActionsWidget(QWidget *parent)
25 : QWidget(parent)
26{
27 m_ui.setupUi(this);
28
29 // Show sounds directory by default
31 if (!soundDirs.isEmpty()) {
32 m_ui.Sound_select->setStartDir(QUrl::fromLocalFile(soundDirs.last()));
33 }
34 m_ui.Sound_select->setMimeTypeFilters({QStringLiteral("audio/x-vorbis+ogg"), QStringLiteral("audio/x-wav")});
35
36 m_ui.Sound_play->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
37 m_ui.Sound_check->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
38 m_ui.Popup_check->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
39
40 connect(m_ui.Sound_check, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
41 connect(m_ui.Popup_check, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
42 connect(m_ui.Sound_select, SIGNAL(textChanged(QString)), this, SIGNAL(changed()));
43 connect(m_ui.Sound_play, SIGNAL(clicked()), this, SLOT(slotPlay()));
44}
45
46KNotifyConfigActionsWidget::~KNotifyConfigActionsWidget()
47{
48#if HAVE_CANBERRA
49 if (m_context) {
50 ca_context_destroy(m_context);
51 }
52 m_context = nullptr;
53#endif
54}
55
56void KNotifyConfigActionsWidget::setConfigElement(KNotifyConfigElement *config)
57{
58 bool blocked = blockSignals(true); // to block the changed() signal
59 QString prstring = config->readEntry(QStringLiteral("Action"));
60 QStringList actions = prstring.split(QLatin1Char('|'));
61
62 m_ui.Sound_check->setChecked(actions.contains(QStringLiteral("Sound")));
63 m_ui.Popup_check->setChecked(actions.contains(QStringLiteral("Popup")));
64
65 m_ui.Sound_select->setUrl(QUrl(config->readEntry(QStringLiteral("Sound"), true)));
66
67 blockSignals(blocked);
68}
69
70void KNotifyConfigActionsWidget::save(KNotifyConfigElement *config)
71{
73 if (m_ui.Sound_check->isChecked()) {
74 actions << QStringLiteral("Sound");
75 }
76 if (m_ui.Popup_check->isChecked()) {
77 actions << QStringLiteral("Popup");
78 }
79
80 config->writeEntry(QStringLiteral("Action"), actions.join(QLatin1Char('|')));
81
82 config->writeEntry(QStringLiteral("Sound"),
83 m_ui.Sound_select->text()); // don't use .url() here, .notifyrc files have predefined "static" entries with no path
84}
85
86void KNotifyConfigActionsWidget::slotPlay()
87{
88 const QString soundFilename = m_ui.Sound_select->text();
89 QUrl soundURL;
91 for (const QString &dataLocation : dataLocations) {
92 soundURL = QUrl::fromUserInput(soundFilename, dataLocation + QStringLiteral("/sounds"), QUrl::AssumeLocalFile);
93 if (soundURL.isLocalFile() && QFile::exists(soundURL.toLocalFile())) {
94 break;
95 } else if (!soundURL.isLocalFile() && soundURL.isValid()) {
96 break;
97 }
98 soundURL.clear();
99 }
100
101#if HAVE_CANBERRA
102 if (!m_context) {
103 int ret = ca_context_create(&m_context);
104 if (ret != CA_SUCCESS) {
105 qCWarning(KNOTIFYCONFIG_LOG) << "Failed to initialize canberra context for audio notification:" << ca_strerror(ret);
106 m_context = nullptr;
107 return;
108 }
109
110 QString desktopFileName = QGuiApplication::desktopFileName();
111 // handle apps which set the desktopFileName property with filename suffix,
112 // due to unclear API dox (https://bugreports.qt.io/browse/QTBUG-75521)
113 if (desktopFileName.endsWith(QLatin1String(".desktop"))) {
114 desktopFileName.chop(8);
115 }
116 ret = ca_context_change_props(m_context,
117 CA_PROP_APPLICATION_NAME,
118 qUtf8Printable(qApp->applicationDisplayName()),
119 CA_PROP_APPLICATION_ID,
120 qUtf8Printable(desktopFileName),
121 CA_PROP_APPLICATION_ICON_NAME,
122 qUtf8Printable(qApp->windowIcon().name()),
123 nullptr);
124 if (ret != CA_SUCCESS) {
125 qCWarning(KNOTIFYCONFIG_LOG) << "Failed to set application properties on canberra context for audio notification:" << ca_strerror(ret);
126 }
127 }
128
129 ca_proplist *props = nullptr;
130 ca_proplist_create(&props);
131
132 // We'll also want this cached for a time. volatile makes sure the cache is
133 // dropped after some time or when the cache is under pressure.
134 ca_proplist_sets(props, CA_PROP_MEDIA_FILENAME, QFile::encodeName(soundURL.toLocalFile()).constData());
135 ca_proplist_sets(props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile");
136
137 int ret = ca_context_play_full(m_context, 0, props, nullptr, nullptr);
138
139 ca_proplist_destroy(props);
140
141 if (ret != CA_SUCCESS) {
142 qCWarning(KNOTIFYCONFIG_LOG) << "Failed to play sound with canberra:" << ca_strerror(ret);
143 return;
144 }
145#elif HAVE_QTMULTIMEDIA
146 auto player = new QMediaPlayer(this);
147 auto audioOutput = new QAudioOutput(player);
148 connect(player, &QMediaPlayer::playingChanged, player, [player](bool playing) {
149 if (!playing) {
150 player->deleteLater();
151 }
152 });
153 connect(player, &QMediaPlayer::errorOccurred, player, [player]() {
154 player->deleteLater();
155 });
156 player->setAudioOutput(audioOutput);
157 player->setSource(soundURL);
158 player->play();
159#endif
160}
161
162#include "moc_knotifyconfigactionswidget.cpp"
Represent the config for an event.
const char * constData() const const
QByteArray encodeName(const QString &fileName)
bool exists() const const
QIcon fromTheme(const QString &name)
bool contains(const AT &value) const const
bool isEmpty() const const
T & last()
bool blockSignals(bool block)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
QStringList standardLocations(StandardLocation type)
void chop(qsizetype n)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
AssumeLocalFile
void clear()
QUrl fromLocalFile(const QString &localFile)
QUrl fromUserInput(const QString &userInput, const QString &workingDirectory, UserInputResolutionOptions options)
bool isLocalFile() const const
bool isValid() const const
QString toLocalFile() const const
QList< QAction * > actions() const const
void setupUi(QWidget *widget)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:09:49 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.