1 /***************************************************************************
2 * *
3 * This program is free software; you can redistribute it and/or modify *
4 * it under the terms of the GNU General Public License as published by *
5 * the Free Software Foundation; either version 3 of the License, or *
6 * (at your option) any later version. *
7 * *
8 ***************************************************************************/
9
10 #include "Notification.h"
11
12 #include <QMenu>
13 #include <QList>
14 #include <QSound>
15 #include <QFile>
16
17 #include "WulforUtil.h"
18 #include "WulforSettings.h"
19 #include "MainWindow.h"
20 #include "ShellCommandRunner.h"
21 #include "Settings.h"
22
getBitPos(unsigned eventId)23 static int getBitPos(unsigned eventId){
24 for (unsigned i = 0; i < (sizeof(unsigned)*8); i++){
25 if ((eventId >> i) == 1U)
26 return static_cast<int>(i);
27 }
28
29 return -1;
30 }
31
Notification(QObject * parent)32 Notification::Notification(QObject *parent) :
33 QObject(parent), tray(NULL), notify(NULL), suppressSnd(false), suppressTxt(false)
34 {
35 switchModule(static_cast<unsigned>(WIGET(WI_NOTIFY_MODULE)));
36
37 checkSystemTrayCounter = 0;
38
39 reloadSounds();
40
41 enableTray(WBGET(WB_TRAY_ENABLED));
42
43 connect(MainWindow::getInstance(), SIGNAL(notifyMessage(int,QString,QString)), this, SLOT(showMessage(int,QString,QString)), Qt::QueuedConnection);
44 }
45
~Notification()46 Notification::~Notification(){
47 enableTray(false);
48 delete notify;
49 }
50
enableTray(bool enable)51 void Notification::enableTray(bool enable){
52 if (!enable){
53 if (tray)
54 tray->hide();
55
56 delete tray;
57
58 tray = NULL;
59
60 #if defined(Q_WS_MAC)
61 MainWindow::getInstance()->setUnload(false);
62 #else // defined(Q_WS_MAC)
63 MainWindow::getInstance()->setUnload(true);
64 #endif // defined(Q_WS_MAC)
65
66 //WBSET(WB_TRAY_ENABLED, false);
67 }
68 else {
69 delete tray;
70
71 tray = NULL;
72
73 if (!QSystemTrayIcon::isSystemTrayAvailable() && checkSystemTrayCounter < 12){
74 QTimer *timer = new QTimer(this);
75 timer->setSingleShot(true);
76 timer->setInterval(5000);
77
78 connect(timer, SIGNAL(timeout()), this, SLOT(slotCheckTray()));
79
80 timer->start();
81
82 ++checkSystemTrayCounter;
83
84 return;
85 }
86 else if (!QSystemTrayIcon::isSystemTrayAvailable()){
87 MainWindow::getInstance()->show();
88
89 return;
90 }
91
92 checkSystemTrayCounter = 0;
93
94 tray = new QSystemTrayIcon(this);
95 tray->setIcon(WICON(WulforUtil::eiICON_APPL)
96 .scaled(22, 22, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
97
98 QMenu *menu = new QMenu(MainWindow::getInstance());
99 menu->setTitle("EiskaltDC++");
100
101 QMenu *menuAdditional = new QMenu(tr("Additional"), MainWindow::getInstance());
102 QAction *actSuppressSnd = new QAction(tr("Suppress sound notifications"), menuAdditional);
103 QAction *actSuppressTxt = new QAction(tr("Suppress text notifications"), menuAdditional);
104
105 actSuppressSnd->setCheckable(true);
106 actSuppressSnd->setChecked(false);
107
108 actSuppressTxt->setCheckable(true);
109 actSuppressTxt->setChecked(false);
110
111 menuAdditional->addActions(QList<QAction*>() << actSuppressTxt << actSuppressSnd);
112
113 QAction *show_hide = new QAction(tr("Show/Hide window"), menu);
114 QAction *setup_speed_lim = new QAction(tr("Setup speed limits"), menu);
115 QAction *close_app = new QAction(tr("Exit"), menu);
116 QAction *sep = new QAction(menu);
117 sep->setSeparator(true);
118
119 setup_speed_lim->setIcon(WICON(WulforUtil::eiSPEED_LIMIT_ON));
120 show_hide->setIcon(WICON(WulforUtil::eiHIDEWINDOW));
121 close_app->setIcon(WICON(WulforUtil::eiEXIT));
122
123 connect(show_hide, SIGNAL(triggered()), this, SLOT(slotShowHide()));
124 connect(close_app, SIGNAL(triggered()), this, SLOT(slotExit()));
125 connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
126 this, SLOT(slotTrayMenuTriggered(QSystemTrayIcon::ActivationReason)));
127 connect(actSuppressTxt, SIGNAL(triggered()), this, SLOT(slotSuppressTxt()));
128 connect(actSuppressSnd, SIGNAL(triggered()), this, SLOT(slotSuppressSnd()));
129 connect(setup_speed_lim, SIGNAL(triggered()), this, SLOT(slotShowSpeedLimits()));
130
131 menu->addAction(show_hide);
132 menu->addAction(setup_speed_lim);
133 menu->addMenu(menuAdditional);
134 menu->addActions(QList<QAction*>() << sep << close_app);
135
136 tray->setContextMenu(menu);
137
138 tray->show();
139
140 MainWindow::getInstance()->setUnload(false);
141
142 //WBSET(WB_TRAY_ENABLED, true);
143 }
144 }
145
switchModule(int m)146 void Notification::switchModule(int m){
147 Module t = static_cast<Module>(m);
148
149 delete notify;
150
151 if (t == QtNotify)
152 notify = new QtNotifyModule();
153 #ifdef DBUS_NOTIFY
154 else
155 notify = new DBusNotifyModule();
156 #else
157 else
158 notify = new QtNotifyModule();
159 #endif
160 }
161
showMessage(int t,const QString & title,const QString & msg)162 void Notification::showMessage(int t, const QString &title, const QString &msg){
163 // On Mac OS X, the Growl notification system must be installed for this function to display messages.
164 if (WBGET(WB_NOTIFY_ENABLED) && !suppressTxt){
165 do {
166 if (title.isEmpty() || msg.isEmpty())
167 break;
168
169 if ((MainWindow::getInstance()->isActiveWindow() && !WBGET(WB_NOTIFY_SHOW_ON_ACTIVE)) ||
170 (!MainWindow::getInstance()->isActiveWindow() && MainWindow::getInstance()->isVisible() && !WBGET(WB_NOTIFY_SHOW_ON_VISIBLE)))
171 break;
172
173 if (!(static_cast<unsigned>(WIGET(WI_NOTIFY_EVENTMAP)) & static_cast<unsigned>(t)))
174 break;
175
176 #if defined(Q_WS_MAC)
177 qApp->setWindowIcon(WICON(WulforUtil::eiMESSAGE_TRAY_ICON));
178 qApp->alert(MainWindow::getInstance(), 0);
179 #else // defined(Q_WS_MAC)
180 if (tray && t == PM && (!MainWindow::getInstance()->isVisible() || WBGET(WB_NOTIFY_CH_ICON_ALWAYS))){
181 tray->setIcon(WICON(WulforUtil::eiMESSAGE_TRAY_ICON));
182
183 if (MainWindow::getInstance()->isVisible())
184 QApplication::alert(MainWindow::getInstance(), 0);
185 }
186 #endif // defined(Q_WS_MAC)
187
188 if (notify)
189 notify->showMessage(title, msg, tray);
190 } while (0);
191 }
192
193 if (WBGET(WB_NOTIFY_SND_ENABLED) && !suppressSnd){
194 do {
195 if (!(static_cast<unsigned>(WIGET(WI_NOTIFY_SNDMAP)) & static_cast<unsigned>(t)))
196 break;
197
198 int sound_pos = getBitPos(static_cast<unsigned>(t));
199
200 if (sound_pos >= 0 && sound_pos < sounds.size()){
201 QString sound = sounds.at(sound_pos);
202
203 if (sound.isEmpty() || !QFile::exists(sound))
204 break;
205
206 if (!WBGET(WB_NOTIFY_SND_EXTERNAL))
207 QSound::play(sound);
208 else {
209 QString cmd = WSGET(WS_NOTIFY_SND_CMD);
210
211 if (cmd.isEmpty())
212 break;
213
214 ShellCommandRunner *r = new ShellCommandRunner(cmd, QStringList() << sound, this);
215 connect(r, SIGNAL(finished(bool,QString)), this, SLOT(slotCmdFinished(bool,QString)));
216
217 r->start();
218 }
219 }
220 } while (0);
221 }
222 }
223
setToolTip(const QString & DSPEED,const QString & USPEED,const QString & DOWN,const QString & UP)224 void Notification::setToolTip(const QString &DSPEED, const QString &USPEED, const QString &DOWN, const QString &UP){
225 if (!WBGET(WB_TRAY_ENABLED) || !tray)
226 return;
227
228 #if defined(Q_WS_X11)
229 QString out = tr("<b>Speed</b><br/>"
230 "Download: <font_color=\"green\">%1</font> "
231 "Upload: <font_color=\"red\">%2</font><br/>"
232 "<b>Statistics</b><br/>"
233 "Downloaded: <font_color=\"green\">%3</font> "
234 "Uploaded: <font_color=\"red\">%4</font>")
235 .arg(DSPEED).arg(USPEED).arg(DOWN).arg(UP);
236
237 out.replace(" "," ");
238 out.replace("_"," ");
239 #else
240 QString out = tr("Speed\n"
241 "Download: %1 "
242 "Upload: %2\n"
243 "Statistics\n"
244 "Downloaded: %3 "
245 "Uploaded: %4")
246 .arg(DSPEED).arg(USPEED).arg(DOWN).arg(UP);
247 #endif
248
249 tray->setToolTip(out);
250 }
251
reloadSounds()252 void Notification::reloadSounds(){
253 QString encoded = WSGET(WS_NOTIFY_SOUNDS);
254 QString decoded = QByteArray::fromBase64(encoded.toUtf8());
255
256 sounds = decoded.split("\n");
257 }
258
slotExit()259 void Notification::slotExit(){
260 if (WBGET(WB_EXIT_CONFIRM))
261 MainWindow::getInstance()->show();
262
263 MainWindow::getInstance()->setUnload(true);
264 MainWindow::getInstance()->close();
265 }
266
slotShowHide()267 void Notification::slotShowHide(){
268 MainWindow *MW = MainWindow::getInstance();
269
270 if (MW->isVisible()){
271 #if defined(Q_WS_WIN)
272 MW->hide();
273 #elif defined(Q_WS_MAC)
274 if (!MW->isActiveWindow()){
275 MW->activateWindow();
276 MW->raise();
277 }
278 #else // Linux, FreeBSD, Haiku, Hurd
279 if (MW->isMinimized())
280 MW->show();
281
282 if (!MW->isActiveWindow()){
283 MW->activateWindow();
284 MW->raise();
285 }
286 else {
287 MW->hide();
288 }
289 #endif
290 }
291 else{
292 MW->show();
293 MW->raise();
294 #if defined(Q_WS_MAC)
295 MW->redrawToolPanel();
296 #else // defined(Q_WS_MAC)
297 if (tray)
298 MW->redrawToolPanel();
299 #endif // defined(Q_WS_MAC)
300 }
301 }
302
slotTrayMenuTriggered(QSystemTrayIcon::ActivationReason r)303 void Notification::slotTrayMenuTriggered(QSystemTrayIcon::ActivationReason r){
304 if (r == QSystemTrayIcon::Trigger)
305 slotShowHide();
306 }
307
slotShowSpeedLimits()308 void Notification::slotShowSpeedLimits(){
309 MainWindow::getInstance()->show();
310 MainWindow::getInstance()->raise();
311
312 Settings settings;
313 settings.navigate(Settings::Page::Connection, 1);
314
315 settings.exec();
316 }
317
slotCmdFinished(bool,QString)318 void Notification::slotCmdFinished(bool, QString){
319 ShellCommandRunner *r = reinterpret_cast<ShellCommandRunner*>(sender());
320
321 r->exit(0);
322 r->wait(100);
323
324 if (r->isRunning())
325 r->terminate();
326
327 delete r;
328 }
329
slotCheckTray()330 void Notification::slotCheckTray(){
331 QTimer *timer = qobject_cast<QTimer*>(sender());
332
333 if (!timer)
334 return;
335
336 enableTray(true);
337
338 timer->deleteLater();
339 }
340
slotSuppressTxt()341 void Notification::slotSuppressTxt(){
342 QAction *act = qobject_cast<QAction*>(sender());
343 if (act)
344 setSuppressTxt(act->isChecked());
345 }
346
slotSuppressSnd()347 void Notification::slotSuppressSnd(){
348 QAction *act = qobject_cast<QAction*>(sender());
349 if (act)
350 setSuppressSnd(act->isChecked());
351 }
352
resetTrayIcon()353 void Notification::resetTrayIcon(){
354 if (tray)
355 tray->setIcon(WICON(WulforUtil::eiICON_APPL)
356 .scaled(22, 22, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
357 }
358
showMessage(const QString & title,const QString & msg,QObject * obj)359 void QtNotifyModule::showMessage(const QString &title, const QString &msg, QObject *obj) {
360 QSystemTrayIcon *tray = reinterpret_cast<QSystemTrayIcon*>(obj);
361
362 if (tray)
363 tray->showMessage(title, ((msg.length() > 400)? (msg.left(400) + "...") : msg), QSystemTrayIcon::Information, 5000);
364 }
365
366 #ifdef DBUS_NOTIFY
showMessage(const QString & title,const QString & msg,QObject *)367 void DBusNotifyModule::showMessage(const QString &title, const QString &msg, QObject *) {
368 QDBusInterface iface("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus());
369
370 QVariantList args;
371 args << QString("EiskaltDC++");
372 args << QVariant(QVariant::UInt);
373 args << QVariant(WulforUtil::getInstance()->getIconsPath() + "/" + "icon_appl_big.png");
374 args << QString(title);
375 args << QString(msg);
376 args << QStringList();
377 args << QVariantMap();
378 args << 5000;
379
380 iface.callWithArgumentList(QDBus::NoBlock, "Notify", args);
381 }
382 #endif
383