File indexing completed on 2022-11-29 18:32:23

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2005-2007 Olivier Goffart <ogoffart at kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "knotifyconfigactionswidget.h"
0009 
0010 #include "knotifyconfigelement.h"
0011 #include <knotifyconfig_debug.h>
0012 
0013 #include <QFile>
0014 #include <QStandardPaths>
0015 #include <QUrl>
0016 
0017 #if HAVE_CANBERRA
0018 #include <canberra.h>
0019 #elif HAVE_PHONON
0020 #include <phonon/mediaobject.h>
0021 #endif
0022 
0023 KNotifyConfigActionsWidget::KNotifyConfigActionsWidget(QWidget *parent)
0024     : QWidget(parent)
0025 {
0026     m_ui.setupUi(this);
0027 
0028     // Show sounds directory by default
0029     QStringList soundDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("sounds"), QStandardPaths::LocateDirectory);
0030     if (!soundDirs.isEmpty()) {
0031         m_ui.Sound_select->setStartDir(QUrl::fromLocalFile(soundDirs.last()));
0032     }
0033     m_ui.Sound_select->setMimeTypeFilters({QStringLiteral("audio/x-vorbis+ogg"), QStringLiteral("audio/x-wav")});
0034 
0035     m_ui.Sound_play->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
0036     m_ui.Sound_check->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
0037     m_ui.Popup_check->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
0038     m_ui.Logfile_check->setIcon(QIcon::fromTheme(QStringLiteral("text-x-generic")));
0039     m_ui.Execute_check->setIcon(QIcon::fromTheme(QStringLiteral("system-run")));
0040     m_ui.Taskbar_check->setIcon(QIcon::fromTheme(QStringLiteral("services")));
0041     m_ui.TTS_check->setIcon(QIcon::fromTheme(QStringLiteral("text-speak")));
0042 
0043     connect(m_ui.Execute_check, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
0044     connect(m_ui.Sound_check, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
0045     connect(m_ui.Popup_check, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
0046     connect(m_ui.Logfile_check, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
0047     connect(m_ui.Taskbar_check, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
0048     connect(m_ui.TTS_check, SIGNAL(toggled(bool)), this, SLOT(slotTTSComboChanged()));
0049     connect(m_ui.Execute_select, SIGNAL(textChanged(QString)), this, SIGNAL(changed()));
0050     connect(m_ui.Sound_select, SIGNAL(textChanged(QString)), this, SIGNAL(changed()));
0051     connect(m_ui.Logfile_select, SIGNAL(textChanged(QString)), this, SIGNAL(changed()));
0052     connect(m_ui.Sound_play, SIGNAL(clicked()), this, SLOT(slotPlay()));
0053     connect(m_ui.TTS_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotTTSComboChanged()));
0054     m_ui.TTS_combo->setEnabled(false);
0055     if (!KNotifyConfigElement::have_tts()) {
0056         m_ui.TTS_check->setVisible(false);
0057         m_ui.TTS_select->setVisible(false);
0058         m_ui.TTS_combo->setVisible(false);
0059     }
0060 }
0061 
0062 KNotifyConfigActionsWidget::~KNotifyConfigActionsWidget()
0063 {
0064 #if HAVE_CANBERRA
0065     if (m_context) {
0066         ca_context_destroy(m_context);
0067     }
0068     m_context = nullptr;
0069 #endif
0070 }
0071 
0072 void KNotifyConfigActionsWidget::setConfigElement(KNotifyConfigElement *config)
0073 {
0074     bool blocked = blockSignals(true); // to block the changed() signal
0075     QString prstring = config->readEntry(QStringLiteral("Action"));
0076     QStringList actions = prstring.split(QLatin1Char('|'));
0077 
0078     m_ui.Sound_check->setChecked(actions.contains(QStringLiteral("Sound")));
0079     m_ui.Popup_check->setChecked(actions.contains(QStringLiteral("Popup")));
0080     m_ui.Logfile_check->setChecked(actions.contains(QStringLiteral("Logfile")));
0081     m_ui.Execute_check->setChecked(actions.contains(QStringLiteral("Execute")));
0082     m_ui.Taskbar_check->setChecked(actions.contains(QStringLiteral("Taskbar")));
0083     m_ui.TTS_check->setChecked(actions.contains(QStringLiteral("TTS")));
0084 
0085     m_ui.Sound_select->setUrl(QUrl(config->readEntry(QStringLiteral("Sound"), true)));
0086     m_ui.Logfile_select->setUrl(QUrl(config->readEntry(QStringLiteral("Logfile"), true)));
0087     m_ui.Execute_select->setUrl(QUrl::fromLocalFile(config->readEntry(QStringLiteral("Execute"))));
0088     m_ui.TTS_select->setText(config->readEntry(QStringLiteral("TTS")));
0089     if (m_ui.TTS_select->text() == QLatin1String("%e")) {
0090         m_ui.TTS_combo->setCurrentIndex(1);
0091     } else if (m_ui.TTS_select->text() == QLatin1String("%m") || m_ui.TTS_select->text() == QLatin1String("%s")) {
0092         m_ui.TTS_combo->setCurrentIndex(0);
0093     } else {
0094         m_ui.TTS_combo->setCurrentIndex(2);
0095     }
0096     blockSignals(blocked);
0097 }
0098 
0099 void KNotifyConfigActionsWidget::save(KNotifyConfigElement *config)
0100 {
0101     QStringList actions;
0102     if (m_ui.Sound_check->isChecked()) {
0103         actions << QStringLiteral("Sound");
0104     }
0105     if (m_ui.Popup_check->isChecked()) {
0106         actions << QStringLiteral("Popup");
0107     }
0108     if (m_ui.Logfile_check->isChecked()) {
0109         actions << QStringLiteral("Logfile");
0110     }
0111     if (m_ui.Execute_check->isChecked()) {
0112         actions << QStringLiteral("Execute");
0113     }
0114     if (m_ui.Taskbar_check->isChecked()) {
0115         actions << QStringLiteral("Taskbar");
0116     }
0117     if (m_ui.TTS_check->isChecked()) {
0118         actions << QStringLiteral("TTS");
0119     }
0120 
0121     config->writeEntry(QStringLiteral("Action"), actions.join(QLatin1Char('|')));
0122 
0123     config->writeEntry(QStringLiteral("Sound"),
0124                        m_ui.Sound_select->text()); // don't use .url() here, .notifyrc files have predefined "static" entries with no path
0125     config->writeEntry(QStringLiteral("Logfile"), m_ui.Logfile_select->url().toString());
0126     config->writeEntry(QStringLiteral("Execute"), m_ui.Execute_select->url().toLocalFile());
0127     switch (m_ui.TTS_combo->currentIndex()) {
0128     case 0:
0129         config->writeEntry(QStringLiteral("TTS"), QStringLiteral("%s"));
0130         break;
0131     case 1:
0132         config->writeEntry(QStringLiteral("TTS"), QStringLiteral("%e"));
0133         break;
0134     case 2:
0135     default:
0136         config->writeEntry(QStringLiteral("TTS"), m_ui.TTS_select->text());
0137     }
0138 }
0139 
0140 void KNotifyConfigActionsWidget::slotPlay()
0141 {
0142     const QString soundFilename = m_ui.Sound_select->text();
0143     QUrl soundURL;
0144     const auto dataLocations = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
0145     for (const QString &dataLocation : dataLocations) {
0146         soundURL = QUrl::fromUserInput(soundFilename, dataLocation + QStringLiteral("/sounds"), QUrl::AssumeLocalFile);
0147         if (soundURL.isLocalFile() && QFile::exists(soundURL.toLocalFile())) {
0148             break;
0149         } else if (!soundURL.isLocalFile() && soundURL.isValid()) {
0150             break;
0151         }
0152         soundURL.clear();
0153     }
0154 
0155 #if HAVE_CANBERRA
0156     if (!m_context) {
0157         int ret = ca_context_create(&m_context);
0158         if (ret != CA_SUCCESS) {
0159             qCWarning(KNOTIFYCONFIG_LOG) << "Failed to initialize canberra context for audio notification:" << ca_strerror(ret);
0160             m_context = nullptr;
0161             return;
0162         }
0163 
0164         QString desktopFileName = QGuiApplication::desktopFileName();
0165         // handle apps which set the desktopFileName property with filename suffix,
0166         // due to unclear API dox (https://bugreports.qt.io/browse/QTBUG-75521)
0167         if (desktopFileName.endsWith(QLatin1String(".desktop"))) {
0168             desktopFileName.chop(8);
0169         }
0170         ret = ca_context_change_props(m_context,
0171                                       CA_PROP_APPLICATION_NAME,
0172                                       qUtf8Printable(qApp->applicationDisplayName()),
0173                                       CA_PROP_APPLICATION_ID,
0174                                       qUtf8Printable(desktopFileName),
0175                                       CA_PROP_APPLICATION_ICON_NAME,
0176                                       qUtf8Printable(qApp->windowIcon().name()),
0177                                       nullptr);
0178         if (ret != CA_SUCCESS) {
0179             qCWarning(KNOTIFYCONFIG_LOG) << "Failed to set application properties on canberra context for audio notification:" << ca_strerror(ret);
0180         }
0181     }
0182 
0183     ca_proplist *props = nullptr;
0184     ca_proplist_create(&props);
0185 
0186     // We'll also want this cached for a time. volatile makes sure the cache is
0187     // dropped after some time or when the cache is under pressure.
0188     ca_proplist_sets(props, CA_PROP_MEDIA_FILENAME, QFile::encodeName(soundURL.toLocalFile()).constData());
0189     ca_proplist_sets(props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile");
0190 
0191     int ret = ca_context_play_full(m_context, 0, props, nullptr, nullptr);
0192 
0193     ca_proplist_destroy(props);
0194 
0195     if (ret != CA_SUCCESS) {
0196         qCWarning(KNOTIFYCONFIG_LOG) << "Failed to play sound with canberra:" << ca_strerror(ret);
0197         return;
0198     }
0199 #elif HAVE_PHONON
0200     Phonon::MediaObject *media = Phonon::createPlayer(Phonon::NotificationCategory, soundURL);
0201     media->play();
0202     connect(media, SIGNAL(finished()), media, SLOT(deleteLater()));
0203 #endif
0204 }
0205 
0206 void KNotifyConfigActionsWidget::slotTTSComboChanged()
0207 {
0208     m_ui.TTS_select->setEnabled(m_ui.TTS_check->isChecked() && m_ui.TTS_combo->currentIndex() == 2);
0209     Q_EMIT changed();
0210 }