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 }