File indexing completed on 2024-04-21 15:02:35

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_select, SIGNAL(textChanged(QString)), this, SIGNAL(changed()));
0054     connect(m_ui.TTS_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotTTSComboChanged()));
0055     m_ui.TTS_combo->setEnabled(false);
0056     if (!KNotifyConfigElement::have_tts()) {
0057         m_ui.TTS_check->setVisible(false);
0058         m_ui.TTS_select->setVisible(false);
0059         m_ui.TTS_combo->setVisible(false);
0060     }
0061 }
0062 
0063 KNotifyConfigActionsWidget::~KNotifyConfigActionsWidget()
0064 {
0065 #if HAVE_CANBERRA
0066     if (m_context) {
0067         ca_context_destroy(m_context);
0068     }
0069     m_context = nullptr;
0070 #endif
0071 }
0072 
0073 void KNotifyConfigActionsWidget::setConfigElement(KNotifyConfigElement *config)
0074 {
0075     bool blocked = blockSignals(true); // to block the changed() signal
0076     QString prstring = config->readEntry(QStringLiteral("Action"));
0077     QStringList actions = prstring.split(QLatin1Char('|'));
0078 
0079     m_ui.Sound_check->setChecked(actions.contains(QStringLiteral("Sound")));
0080     m_ui.Popup_check->setChecked(actions.contains(QStringLiteral("Popup")));
0081     m_ui.Logfile_check->setChecked(actions.contains(QStringLiteral("Logfile")));
0082     m_ui.Execute_check->setChecked(actions.contains(QStringLiteral("Execute")));
0083     m_ui.Taskbar_check->setChecked(actions.contains(QStringLiteral("Taskbar")));
0084     m_ui.TTS_check->setChecked(actions.contains(QStringLiteral("TTS")));
0085 
0086     m_ui.Sound_select->setUrl(QUrl(config->readEntry(QStringLiteral("Sound"), true)));
0087     m_ui.Logfile_select->setUrl(QUrl(config->readEntry(QStringLiteral("Logfile"), true)));
0088     m_ui.Execute_select->setUrl(QUrl::fromLocalFile(config->readEntry(QStringLiteral("Execute"))));
0089     m_ui.TTS_select->setText(config->readEntry(QStringLiteral("TTS")));
0090     if (m_ui.TTS_select->text() == QLatin1String("%e")) {
0091         m_ui.TTS_combo->setCurrentIndex(1);
0092     } else if (m_ui.TTS_select->text() == QLatin1String("%m") || m_ui.TTS_select->text() == QLatin1String("%s")) {
0093         m_ui.TTS_combo->setCurrentIndex(0);
0094     } else {
0095         m_ui.TTS_combo->setCurrentIndex(2);
0096     }
0097     blockSignals(blocked);
0098 }
0099 
0100 void KNotifyConfigActionsWidget::save(KNotifyConfigElement *config)
0101 {
0102     QStringList actions;
0103     if (m_ui.Sound_check->isChecked()) {
0104         actions << QStringLiteral("Sound");
0105     }
0106     if (m_ui.Popup_check->isChecked()) {
0107         actions << QStringLiteral("Popup");
0108     }
0109     if (m_ui.Logfile_check->isChecked()) {
0110         actions << QStringLiteral("Logfile");
0111     }
0112     if (m_ui.Execute_check->isChecked()) {
0113         actions << QStringLiteral("Execute");
0114     }
0115     if (m_ui.Taskbar_check->isChecked()) {
0116         actions << QStringLiteral("Taskbar");
0117     }
0118     if (m_ui.TTS_check->isChecked()) {
0119         actions << QStringLiteral("TTS");
0120     }
0121 
0122     config->writeEntry(QStringLiteral("Action"), actions.join(QLatin1Char('|')));
0123 
0124     config->writeEntry(QStringLiteral("Sound"),
0125                        m_ui.Sound_select->text()); // don't use .url() here, .notifyrc files have predefined "static" entries with no path
0126     config->writeEntry(QStringLiteral("Logfile"), m_ui.Logfile_select->url().toString());
0127     config->writeEntry(QStringLiteral("Execute"), m_ui.Execute_select->url().toLocalFile());
0128     switch (m_ui.TTS_combo->currentIndex()) {
0129     case 0:
0130         config->writeEntry(QStringLiteral("TTS"), QStringLiteral("%s"));
0131         break;
0132     case 1:
0133         config->writeEntry(QStringLiteral("TTS"), QStringLiteral("%e"));
0134         break;
0135     case 2:
0136     default:
0137         config->writeEntry(QStringLiteral("TTS"), m_ui.TTS_select->text());
0138     }
0139 }
0140 
0141 void KNotifyConfigActionsWidget::slotPlay()
0142 {
0143     const QString soundFilename = m_ui.Sound_select->text();
0144     QUrl soundURL;
0145     const auto dataLocations = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
0146     for (const QString &dataLocation : dataLocations) {
0147         soundURL = QUrl::fromUserInput(soundFilename, dataLocation + QStringLiteral("/sounds"), QUrl::AssumeLocalFile);
0148         if (soundURL.isLocalFile() && QFile::exists(soundURL.toLocalFile())) {
0149             break;
0150         } else if (!soundURL.isLocalFile() && soundURL.isValid()) {
0151             break;
0152         }
0153         soundURL.clear();
0154     }
0155 
0156 #if HAVE_CANBERRA
0157     if (!m_context) {
0158         int ret = ca_context_create(&m_context);
0159         if (ret != CA_SUCCESS) {
0160             qCWarning(KNOTIFYCONFIG_LOG) << "Failed to initialize canberra context for audio notification:" << ca_strerror(ret);
0161             m_context = nullptr;
0162             return;
0163         }
0164 
0165         QString desktopFileName = QGuiApplication::desktopFileName();
0166         // handle apps which set the desktopFileName property with filename suffix,
0167         // due to unclear API dox (https://bugreports.qt.io/browse/QTBUG-75521)
0168         if (desktopFileName.endsWith(QLatin1String(".desktop"))) {
0169             desktopFileName.chop(8);
0170         }
0171         ret = ca_context_change_props(m_context,
0172                                       CA_PROP_APPLICATION_NAME,
0173                                       qUtf8Printable(qApp->applicationDisplayName()),
0174                                       CA_PROP_APPLICATION_ID,
0175                                       qUtf8Printable(desktopFileName),
0176                                       CA_PROP_APPLICATION_ICON_NAME,
0177                                       qUtf8Printable(qApp->windowIcon().name()),
0178                                       nullptr);
0179         if (ret != CA_SUCCESS) {
0180             qCWarning(KNOTIFYCONFIG_LOG) << "Failed to set application properties on canberra context for audio notification:" << ca_strerror(ret);
0181         }
0182     }
0183 
0184     ca_proplist *props = nullptr;
0185     ca_proplist_create(&props);
0186 
0187     // We'll also want this cached for a time. volatile makes sure the cache is
0188     // dropped after some time or when the cache is under pressure.
0189     ca_proplist_sets(props, CA_PROP_MEDIA_FILENAME, QFile::encodeName(soundURL.toLocalFile()).constData());
0190     ca_proplist_sets(props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile");
0191 
0192     int ret = ca_context_play_full(m_context, 0, props, nullptr, nullptr);
0193 
0194     ca_proplist_destroy(props);
0195 
0196     if (ret != CA_SUCCESS) {
0197         qCWarning(KNOTIFYCONFIG_LOG) << "Failed to play sound with canberra:" << ca_strerror(ret);
0198         return;
0199     }
0200 #elif HAVE_PHONON
0201     Phonon::MediaObject *media = Phonon::createPlayer(Phonon::NotificationCategory, soundURL);
0202     media->play();
0203     connect(media, SIGNAL(finished()), media, SLOT(deleteLater()));
0204 #endif
0205 }
0206 
0207 void KNotifyConfigActionsWidget::slotTTSComboChanged()
0208 {
0209     m_ui.TTS_select->setEnabled(m_ui.TTS_check->isChecked() && m_ui.TTS_combo->currentIndex() == 2);
0210     Q_EMIT changed();
0211 }
0212 
0213 #include "moc_knotifyconfigactionswidget.cpp"