File indexing completed on 2025-01-12 04:20:11

0001 /*
0002     SnoreNotify is a Notification Framework based on Qt
0003     Copyright (C) 2013-2015  Hannah von Reth <vonreth@kde.org>
0004 
0005     SnoreNotify is free software: you can redistribute it and/or modify
0006     it under the terms of the GNU Lesser General Public License as published by
0007     the Free Software Foundation, either version 3 of the License, or
0008     (at your option) any later version.
0009 
0010     SnoreNotify is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013     GNU Lesser General Public License for more details.
0014 
0015     You should have received a copy of the GNU Lesser General Public License
0016     along with SnoreNotify.  If not, see <http://www.gnu.org/licenses/>.
0017 */
0018 
0019 #include "snarl.h"
0020 #include "snarlconstants.h"
0021 
0022 #include "libsnore/snore.h"
0023 #include "libsnore/snore_p.h"
0024 #include "libsnore/utils.h"
0025 #include "libsnore/plugins/plugins.h"
0026 #include "libsnore/plugins/snorebackend.h"
0027 #include "libsnore/notification/notification_p.h"
0028 
0029 #include <QApplication>
0030 #include <QWidget>
0031 
0032 #include <iostream>
0033 
0034 #define SNORENOTIFIER_MESSAGE_ID WM_USER + 238
0035 
0036 using namespace Snarl::V42;
0037 
0038 class SnarlWidget: public QWidget
0039 {
0040     //Q_OBJECT
0041 public:
0042     SnarlWidget(SnorePlugin::Snarl *snarl):
0043         m_snarl(snarl)
0044     {
0045         SNARL_GLOBAL_MESSAGE = SnarlInterface::Broadcast();
0046     }
0047 
0048     bool nativeEvent(const QByteArray &eventType, void *message, long *) override
0049     {
0050         if (eventType == "windows_generic_MSG") {
0051             MSG *msg = static_cast<MSG *>(message);
0052             if (msg->message == SNARL_GLOBAL_MESSAGE) {
0053                 int action = msg->wParam;
0054                 if (action == SnarlEnums::SnarlLaunched) {
0055                     for (const Snore::Application &a : Snore::SnoreCore::instance().aplications()) {
0056                         m_snarl->slotRegisterApplication(a);
0057                     }
0058                 }
0059 
0060             } else if (msg->message == SNORENOTIFIER_MESSAGE_ID) {
0061                 int action = msg->wParam & 0xffff;
0062                 int data = (msg->wParam & 0xffffffff) >> 16;
0063 
0064                 Snore::Notification notification;
0065                 if (msg->lParam != 0) {
0066                     notification =  m_snarl->m_idMap.value(msg->lParam);
0067                 }
0068 
0069                 Snore::Notification::CloseReasons reason = Snore::Notification::None;
0070                 switch (action) {
0071                 case SnarlEnums::CallbackInvoked:
0072                     reason = Snore::Notification::Activated;
0073                     qCDebug(SNORE) << "Notification clicked";
0074                     break;
0075                 case SnarlEnums::NotifyAction:
0076                     reason = Snore::Notification::Activated;
0077                     qCDebug(SNORE) << "Notification action invoked";
0078                     if (notification.isValid()) {
0079                         m_snarl->slotNotificationActionInvoked(notification, notification.actions().value(data));
0080                     }
0081                     break;
0082                 case SnarlEnums::CallbackClosed:
0083                     reason = Snore::Notification::Dismissed;
0084                     qCDebug(SNORE) << "Notification dismissed";
0085                     break;
0086                 case SnarlEnums::CallbackTimedOut:
0087                     reason = Snore::Notification::TimedOut;
0088                     qCDebug(SNORE) << "Notification timed out";
0089                     break;
0090                 //away stuff
0091                 case SnarlEnums::SnarlUserAway:
0092                     qCDebug(SNORE) << "Snalr user has gone away";
0093                     return true;
0094                 case SnarlEnums::SnarlUserBack:
0095                     qCDebug(SNORE) << "Snalr user has returned";
0096                     return true;
0097                 default:
0098                     qCWarning(SNORE) << "Unknown snarl action found!!";
0099                     return false;
0100                 }
0101                 if (notification.isValid()) {
0102                     m_snarl->requestCloseNotification(notification, reason);
0103                     m_snarl->m_idMap.take(msg->lParam);
0104                 } else {
0105                     qCWarning(SNORE) << "Snarl notification already closed" << msg->lParam << action;
0106                     qCWarning(SNORE) << m_snarl->m_idMap;
0107                 }
0108                 return true;
0109             }
0110         }
0111         return false;
0112     }
0113 
0114 private:
0115     uint SNARL_GLOBAL_MESSAGE;
0116     SnorePlugin::Snarl *m_snarl;
0117 
0118 };
0119 
0120 SnorePlugin::Snarl::Snarl()
0121 {
0122 }
0123 
0124 SnorePlugin::Snarl::~Snarl()
0125 {
0126     if (m_eventLoop) {
0127         delete m_eventLoop;
0128     }
0129 }
0130 
0131 bool SnorePlugin::Snarl::canCloseNotification() const
0132 {
0133     return true;
0134 }
0135 
0136 bool SnorePlugin::Snarl::canUpdateNotification() const
0137 {
0138     return true;
0139 }
0140 
0141 bool SnorePlugin::Snarl::isReady()
0142 {
0143     if (!qobject_cast< QApplication * >(qApp)) {
0144         setErrorString(tr("This plugin only works with QApplication"));
0145         return false;
0146     }
0147 
0148     if (!m_eventLoop) {
0149         m_eventLoop = new SnarlWidget(this);
0150     }
0151     bool running = SnarlInterface::IsSnarlRunning();
0152     if (!running) {
0153         setErrorString(tr("%1 is not running.").arg(name()));
0154     }
0155     return running;
0156 }
0157 
0158 void SnorePlugin::Snarl::setDefaultSettings()
0159 {
0160 
0161     setDefaultSettingsValue(SnarlConstants::Password, QString());
0162     SnoreBackend::setDefaultSettings();
0163 }
0164 
0165 void SnorePlugin::Snarl::slotRegisterApplication(const Snore::Application &application)
0166 {
0167     Q_ASSERT(m_eventLoop);
0168 
0169     Q_ASSERT_X(!m_applications.contains(application.name()), Q_FUNC_INFO, "Application already registered");
0170 
0171     SnarlInterface *snarlInterface = new SnarlInterface();
0172     m_applications.insert(application.name(), snarlInterface);
0173 
0174     QString appName = application.name().replace(QLatin1Char(' '), QLatin1Char('_')); //app sig must not contain spaces
0175     QString password = settingsValue(SnarlConstants::Password).toString();
0176     LONG32 result = snarlInterface->Register(appName.toUtf8().constData(),
0177                     application.name().toUtf8().constData(),
0178                     application.icon().localUrl(QSize(128, 128)).toUtf8().constData(),
0179                     password.isEmpty() ? 0 : password.toUtf8().constData(),
0180                     (HWND)m_eventLoop->winId(), SNORENOTIFIER_MESSAGE_ID);
0181     qCDebug(SNORE) << result;
0182 
0183     foreach(const Snore::Alert & alert, application.alerts()) {
0184         snarlInterface->AddClass(alert.name().toUtf8().constData(),
0185                                  alert.name().toUtf8().constData(),
0186                                  0, 0, alert.icon().localUrl(QSize(128, 128)).toUtf8().constData());
0187     }
0188 }
0189 
0190 void SnorePlugin::Snarl::slotDeregisterApplication(const Snore::Application &application)
0191 {
0192     if (!m_applications.contains(application.name())) {
0193         qCDebug(SNORE) << "Unknown apllication: " << application.name();
0194         return;
0195     }
0196     SnarlInterface *snarlInterface = m_applications.take(application.name());
0197     QString appName = application.name().replace(QLatin1Char(' '), QLatin1Char('_')); //app sig must not contain spaces
0198     snarlInterface->Unregister(appName.toUtf8().constData());
0199     delete snarlInterface;
0200 }
0201 
0202 void SnorePlugin::Snarl::slotNotify(Snore::Notification notification)
0203 {
0204     if (!m_applications.contains(notification.application().name())) {
0205         qCDebug(SNORE) << "Unknown apllication: " << notification.application().name();
0206         return;
0207     }
0208 
0209     SnarlInterface *snarlInterface = m_applications.value(notification.application().name());
0210 
0211     ::Snarl::V42::SnarlEnums::MessagePriority priority = ::Snarl::V42::SnarlEnums::PriorityUndefined;
0212     if (notification.priority() > 1) {
0213         priority = ::Snarl::V42::SnarlEnums::PriorityHigh;
0214     } else if (notification.priority() < -1) {
0215         priority = ::Snarl::V42::SnarlEnums::PriorityLow;
0216     } else {
0217         priority = static_cast<::Snarl::V42::SnarlEnums::MessagePriority>(notification.priority());
0218     }
0219 
0220     ULONG32 id = 0;
0221     qCDebug(SNORE) << notification.icon();
0222 
0223     if (!notification.isUpdate()) {
0224         id = snarlInterface->Notify(notification.alert().name().toUtf8().constData(),
0225                                     notification.title().toUtf8().constData(),
0226                                     notification.text().toUtf8().constData(),
0227                                     notification.timeout(),
0228                                     nullptr,
0229                                     Snore::Utils::dataFromImage(notification.icon().pixmap(QSize(128, 128)).toImage()).toBase64().constData(),
0230                                     priority);
0231 
0232         foreach(const Snore::Action & a, notification.actions()) {
0233             snarlInterface->AddAction(id, a.name().toUtf8().constData(), (QLatin1Char('@') + QString::number(a.id())).toUtf8().constData());
0234         }
0235         m_idMap[id] = notification;
0236         notification.hints().setPrivateValue(this, "id", id);
0237     } else {
0238         //update message
0239         id = notification.old().hints().privateValue(this, "id").toUInt();
0240         snarlInterface->Update(id,
0241                                notification.alert().name().toUtf8().constData(),
0242                                notification.title().toUtf8().constData(),
0243                                notification.text().toUtf8().constData(),
0244                                notification.timeout(),
0245                                nullptr,
0246                                Snore::Utils::dataFromImage(notification.icon().pixmap(QSize(128, 128)).toImage()).toBase64().constData(),
0247                                priority);
0248     }
0249 
0250     notification.hints().setPrivateValue(this, "id", id);
0251     slotNotificationDisplayed(notification);//if dnd or away snarl does not timeout atomatically
0252 
0253 }
0254 
0255 void SnorePlugin::Snarl::slotCloseNotification(Snore::Notification notification)
0256 {
0257     if (!m_applications.contains(notification.application().name())) {
0258         qCDebug(SNORE) << "Unknown apllication: " << notification.application().name();
0259         return;
0260     }
0261     ULONG32 id = notification.hints().privateValue(this, "id").toUInt();
0262     m_idMap.remove(id);
0263     m_applications.value(notification.application().name())->Hide(id);
0264 }