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 | |
24 | KNotifyConfigActionsWidget::KNotifyConfigActionsWidget(QWidget *parent) |
25 | : QWidget(parent) |
26 | { |
27 | m_ui.setupUi(this); |
28 | |
29 | // Show sounds directory by default |
30 | QStringList soundDirs = QStandardPaths::locateAll(type: QStandardPaths::GenericDataLocation, QStringLiteral("sounds" ), options: QStandardPaths::LocateDirectory); |
31 | if (!soundDirs.isEmpty()) { |
32 | m_ui.Sound_select->setStartDir(QUrl::fromLocalFile(localfile: 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(sender: m_ui.Sound_check, SIGNAL(toggled(bool)), receiver: this, SIGNAL(changed())); |
41 | connect(sender: m_ui.Popup_check, SIGNAL(toggled(bool)), receiver: this, SIGNAL(changed())); |
42 | connect(sender: m_ui.Sound_select, SIGNAL(textChanged(QString)), receiver: this, SIGNAL(changed())); |
43 | connect(sender: m_ui.Sound_play, SIGNAL(clicked()), receiver: this, SLOT(slotPlay())); |
44 | } |
45 | |
46 | KNotifyConfigActionsWidget::~KNotifyConfigActionsWidget() |
47 | { |
48 | #if HAVE_CANBERRA |
49 | if (m_context) { |
50 | ca_context_destroy(c: m_context); |
51 | } |
52 | m_context = nullptr; |
53 | #endif |
54 | } |
55 | |
56 | void KNotifyConfigActionsWidget::setConfigElement(KNotifyConfigElement *config) |
57 | { |
58 | bool blocked = blockSignals(b: true); // to block the changed() signal |
59 | QString prstring = config->readEntry(QStringLiteral("Action" )); |
60 | QStringList actions = prstring.split(sep: 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" ), path: true))); |
66 | |
67 | blockSignals(b: blocked); |
68 | } |
69 | |
70 | void KNotifyConfigActionsWidget::save(KNotifyConfigElement *config) |
71 | { |
72 | QStringList actions; |
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" ), data: actions.join(sep: QLatin1Char('|'))); |
81 | |
82 | config->writeEntry(QStringLiteral("Sound" ), |
83 | data: m_ui.Sound_select->text()); // don't use .url() here, .notifyrc files have predefined "static" entries with no path |
84 | } |
85 | |
86 | void KNotifyConfigActionsWidget::slotPlay() |
87 | { |
88 | const QString soundFilename = m_ui.Sound_select->text(); |
89 | QUrl soundURL; |
90 | const auto dataLocations = QStandardPaths::standardLocations(type: QStandardPaths::GenericDataLocation); |
91 | for (const QString &dataLocation : dataLocations) { |
92 | soundURL = QUrl::fromUserInput(userInput: soundFilename, workingDirectory: dataLocation + QStringLiteral("/sounds" ), options: QUrl::AssumeLocalFile); |
93 | if (soundURL.isLocalFile() && QFile::exists(fileName: 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(c: &m_context); |
104 | if (ret != CA_SUCCESS) { |
105 | qCWarning(KNOTIFYCONFIG_LOG) << "Failed to initialize canberra context for audio notification:" << ca_strerror(code: 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(s: QLatin1String(".desktop" ))) { |
114 | desktopFileName.chop(n: 8); |
115 | } |
116 | ret = ca_context_change_props(c: 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(code: ret); |
126 | } |
127 | } |
128 | |
129 | ca_proplist *props = nullptr; |
130 | ca_proplist_create(p: &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(p: props, CA_PROP_MEDIA_FILENAME, value: QFile::encodeName(fileName: soundURL.toLocalFile()).constData()); |
135 | ca_proplist_sets(p: props, CA_PROP_CANBERRA_CACHE_CONTROL, value: "volatile" ); |
136 | |
137 | int ret = ca_context_play_full(c: m_context, id: 0, p: props, cb: nullptr, userdata: nullptr); |
138 | |
139 | ca_proplist_destroy(p: props); |
140 | |
141 | if (ret != CA_SUCCESS) { |
142 | qCWarning(KNOTIFYCONFIG_LOG) << "Failed to play sound with canberra:" << ca_strerror(code: 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" |
163 | |