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_PHONON
20#include <phonon/mediaobject.h>
21#endif
22
23KNotifyConfigActionsWidget::KNotifyConfigActionsWidget(QWidget *parent)
24 : QWidget(parent)
25{
26 m_ui.setupUi(this);
27
28 // Show sounds directory by default
29 QStringList soundDirs = QStandardPaths::locateAll(type: QStandardPaths::GenericDataLocation, QStringLiteral("sounds"), options: QStandardPaths::LocateDirectory);
30 if (!soundDirs.isEmpty()) {
31 m_ui.Sound_select->setStartDir(QUrl::fromLocalFile(localfile: soundDirs.last()));
32 }
33 m_ui.Sound_select->setMimeTypeFilters({QStringLiteral("audio/x-vorbis+ogg"), QStringLiteral("audio/x-wav")});
34
35 m_ui.Sound_play->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
36 m_ui.Sound_check->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
37 m_ui.Popup_check->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
38
39 connect(sender: m_ui.Sound_check, SIGNAL(toggled(bool)), receiver: this, SIGNAL(changed()));
40 connect(sender: m_ui.Popup_check, SIGNAL(toggled(bool)), receiver: this, SIGNAL(changed()));
41 connect(sender: m_ui.Sound_select, SIGNAL(textChanged(QString)), receiver: this, SIGNAL(changed()));
42 connect(sender: m_ui.Sound_play, SIGNAL(clicked()), receiver: this, SLOT(slotPlay()));
43}
44
45KNotifyConfigActionsWidget::~KNotifyConfigActionsWidget()
46{
47#if HAVE_CANBERRA
48 if (m_context) {
49 ca_context_destroy(c: m_context);
50 }
51 m_context = nullptr;
52#endif
53}
54
55void KNotifyConfigActionsWidget::setConfigElement(KNotifyConfigElement *config)
56{
57 bool blocked = blockSignals(b: true); // to block the changed() signal
58 QString prstring = config->readEntry(QStringLiteral("Action"));
59 QStringList actions = prstring.split(sep: QLatin1Char('|'));
60
61 m_ui.Sound_check->setChecked(actions.contains(QStringLiteral("Sound")));
62 m_ui.Popup_check->setChecked(actions.contains(QStringLiteral("Popup")));
63
64 m_ui.Sound_select->setUrl(QUrl(config->readEntry(QStringLiteral("Sound"), path: true)));
65
66 blockSignals(b: blocked);
67}
68
69void KNotifyConfigActionsWidget::save(KNotifyConfigElement *config)
70{
71 QStringList actions;
72 if (m_ui.Sound_check->isChecked()) {
73 actions << QStringLiteral("Sound");
74 }
75 if (m_ui.Popup_check->isChecked()) {
76 actions << QStringLiteral("Popup");
77 }
78
79 config->writeEntry(QStringLiteral("Action"), data: actions.join(sep: QLatin1Char('|')));
80
81 config->writeEntry(QStringLiteral("Sound"),
82 data: m_ui.Sound_select->text()); // don't use .url() here, .notifyrc files have predefined "static" entries with no path
83}
84
85void KNotifyConfigActionsWidget::slotPlay()
86{
87 const QString soundFilename = m_ui.Sound_select->text();
88 QUrl soundURL;
89 const auto dataLocations = QStandardPaths::standardLocations(type: QStandardPaths::GenericDataLocation);
90 for (const QString &dataLocation : dataLocations) {
91 soundURL = QUrl::fromUserInput(userInput: soundFilename, workingDirectory: dataLocation + QStringLiteral("/sounds"), options: QUrl::AssumeLocalFile);
92 if (soundURL.isLocalFile() && QFile::exists(fileName: soundURL.toLocalFile())) {
93 break;
94 } else if (!soundURL.isLocalFile() && soundURL.isValid()) {
95 break;
96 }
97 soundURL.clear();
98 }
99
100#if HAVE_CANBERRA
101 if (!m_context) {
102 int ret = ca_context_create(c: &m_context);
103 if (ret != CA_SUCCESS) {
104 qCWarning(KNOTIFYCONFIG_LOG) << "Failed to initialize canberra context for audio notification:" << ca_strerror(code: ret);
105 m_context = nullptr;
106 return;
107 }
108
109 QString desktopFileName = QGuiApplication::desktopFileName();
110 // handle apps which set the desktopFileName property with filename suffix,
111 // due to unclear API dox (https://bugreports.qt.io/browse/QTBUG-75521)
112 if (desktopFileName.endsWith(s: QLatin1String(".desktop"))) {
113 desktopFileName.chop(n: 8);
114 }
115 ret = ca_context_change_props(c: m_context,
116 CA_PROP_APPLICATION_NAME,
117 qUtf8Printable(qApp->applicationDisplayName()),
118 CA_PROP_APPLICATION_ID,
119 qUtf8Printable(desktopFileName),
120 CA_PROP_APPLICATION_ICON_NAME,
121 qUtf8Printable(qApp->windowIcon().name()),
122 nullptr);
123 if (ret != CA_SUCCESS) {
124 qCWarning(KNOTIFYCONFIG_LOG) << "Failed to set application properties on canberra context for audio notification:" << ca_strerror(code: ret);
125 }
126 }
127
128 ca_proplist *props = nullptr;
129 ca_proplist_create(p: &props);
130
131 // We'll also want this cached for a time. volatile makes sure the cache is
132 // dropped after some time or when the cache is under pressure.
133 ca_proplist_sets(p: props, CA_PROP_MEDIA_FILENAME, value: QFile::encodeName(fileName: soundURL.toLocalFile()).constData());
134 ca_proplist_sets(p: props, CA_PROP_CANBERRA_CACHE_CONTROL, value: "volatile");
135
136 int ret = ca_context_play_full(c: m_context, id: 0, p: props, cb: nullptr, userdata: nullptr);
137
138 ca_proplist_destroy(p: props);
139
140 if (ret != CA_SUCCESS) {
141 qCWarning(KNOTIFYCONFIG_LOG) << "Failed to play sound with canberra:" << ca_strerror(code: ret);
142 return;
143 }
144#elif HAVE_PHONON
145 Phonon::MediaObject *media = Phonon::createPlayer(Phonon::NotificationCategory, soundURL);
146 media->play();
147 connect(media, SIGNAL(finished()), media, SLOT(deleteLater()));
148#endif
149}
150
151#include "moc_knotifyconfigactionswidget.cpp"
152

source code of knotifyconfig/src/knotifyconfigactionswidget.cpp