1 /*
2  * Copyright (C) by Klaas Freitag <freitag@owncloud.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12  * for more details.
13  */
14 
15 #include <QtGui>
16 #include <QtWidgets>
17 
18 #include "activitylistmodel.h"
19 #include "activitywidget.h"
20 #include "syncresult.h"
21 #include "logger.h"
22 #include "theme.h"
23 #include "folderman.h"
24 #include "syncfileitem.h"
25 #include "folder.h"
26 #include "openfilemanager.h"
27 #include "owncloudpropagator.h"
28 #include "account.h"
29 #include "accountstate.h"
30 #include "accountmanager.h"
31 #include "activityitemdelegate.h"
32 #include "protocolwidget.h"
33 #include "issueswidget.h"
34 #include "QProgressIndicator.h"
35 #include "notificationwidget.h"
36 #include "notificationconfirmjob.h"
37 #include "servernotificationhandler.h"
38 #include "theme.h"
39 #include "ocsjob.h"
40 
41 #include "ui_activitywidget.h"
42 
43 #include <climits>
44 
45 // time span in milliseconds which has to be between two
46 // refreshes of the notifications
47 #define NOTIFICATION_REQUEST_FREE_PERIOD 15000
48 
49 namespace OCC {
50 
ActivityWidget(QWidget * parent)51 ActivityWidget::ActivityWidget(QWidget *parent)
52     : QWidget(parent)
53     , _ui(new Ui::ActivityWidget)
54     , _notificationRequestsRunning(0)
55 {
56     _ui->setupUi(this);
57 
58 // Adjust copyToClipboard() when making changes here!
59 #if defined(Q_OS_MAC)
60     _ui->_activityList->setMinimumWidth(400);
61 #endif
62 
63     _model = new ActivityListModel(this);
64     ActivityItemDelegate *delegate = new ActivityItemDelegate;
65     delegate->setParent(this);
66     _ui->_activityList->setItemDelegate(delegate);
67     _ui->_activityList->setAlternatingRowColors(true);
68     _ui->_activityList->setModel(_model);
69 
70     _ui->_notifyLabel->hide();
71     _ui->_notifyScroll->hide();
72 
73     // Create a widget container for the notifications. The ui file defines
74     // a scroll area that get a widget with a layout as children
75     QWidget *w = new QWidget;
76     _notificationsLayout = new QVBoxLayout;
77     w->setLayout(_notificationsLayout);
78     _notificationsLayout->setAlignment(Qt::AlignTop);
79     _ui->_notifyScroll->setAlignment(Qt::AlignTop);
80     _ui->_notifyScroll->setWidget(w);
81 
82     showLabels();
83 
84     connect(_model, &ActivityListModel::activityJobStatusCode,
85         this, &ActivityWidget::slotAccountActivityStatus);
86 
87     connect(AccountManager::instance(), &AccountManager::accountRemoved, this, [this](AccountState *ast) {
88         if (_accountsWithoutActivities.remove(ast->account()->displayName())) {
89             showLabels();
90         }
91 
92         for (auto it = _widgetForNotifId.cbegin(); it != _widgetForNotifId.cend(); ++it) {
93             if (it.key().second == ast->account()->displayName()) {
94                 scheduleWidgetToRemove(it.value());
95             }
96         }
97     });
98 
99     _copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
100     _copyBtn->setToolTip(tr("Copy the activity list to the clipboard."));
101     connect(_copyBtn, &QAbstractButton::clicked, this, &ActivityWidget::copyToClipboard);
102 
103     connect(_model, &QAbstractItemModel::rowsInserted, this, &ActivityWidget::rowsInserted);
104 
105     connect(_ui->_activityList, &QListView::activated, this, &ActivityWidget::slotOpenFile);
106 
107     connect(&_removeTimer, &QTimer::timeout, this, &ActivityWidget::slotCheckToCleanWidgets);
108     _removeTimer.setInterval(1000);
109 }
110 
~ActivityWidget()111 ActivityWidget::~ActivityWidget()
112 {
113     delete _ui;
114 }
115 
slotRefreshActivities(AccountState * ptr)116 void ActivityWidget::slotRefreshActivities(AccountState *ptr)
117 {
118     _model->slotRefreshActivity(ptr);
119 }
120 
slotRefreshNotifications(AccountState * ptr)121 void ActivityWidget::slotRefreshNotifications(AccountState *ptr)
122 {
123     // start a server notification handler if no notification requests
124     // are running
125     if (_notificationRequestsRunning == 0) {
126         ServerNotificationHandler *snh = new ServerNotificationHandler;
127         connect(snh, &ServerNotificationHandler::newNotificationList,
128             this, &ActivityWidget::slotBuildNotificationDisplay);
129 
130         snh->slotFetchNotifications(ptr);
131     } else {
132         qCWarning(lcActivity) << "Notification request counter not zero.";
133     }
134 }
135 
slotRemoveAccount(AccountState * ptr)136 void ActivityWidget::slotRemoveAccount(AccountState *ptr)
137 {
138     _model->slotRemoveAccount(ptr);
139 }
140 
showLabels()141 void ActivityWidget::showLabels()
142 {
143     QString t = tr("Server Activities");
144     _ui->_headerLabel->setTextFormat(Qt::RichText);
145     _ui->_headerLabel->setText(t);
146 
147     _ui->_notifyLabel->setText(tr("Notifications"));
148 
149     t.clear();
150     QSetIterator<QString> i(_accountsWithoutActivities);
151     while (i.hasNext()) {
152         t.append(tr("<br/>Account %1 does not have activities enabled.").arg(i.next()));
153     }
154     _ui->_bottomLabel->setTextFormat(Qt::RichText);
155     _ui->_bottomLabel->setText(t);
156 }
157 
slotAccountActivityStatus(AccountState * ast,int statusCode)158 void ActivityWidget::slotAccountActivityStatus(AccountState *ast, int statusCode)
159 {
160     if (!(ast && ast->account())) {
161         return;
162     }
163     if (statusCode == 999) {
164         _accountsWithoutActivities.insert(ast->account()->displayName());
165     } else {
166         _accountsWithoutActivities.remove(ast->account()->displayName());
167     }
168 
169     checkActivityTabVisibility();
170     showLabels();
171 }
172 
173 // FIXME: Reused from protocol widget. Move over to utilities.
timeString(QDateTime dt,QLocale::FormatType format) const174 QString ActivityWidget::timeString(QDateTime dt, QLocale::FormatType format) const
175 {
176     const QLocale loc = QLocale::system();
177     QString dtFormat = loc.dateTimeFormat(format);
178     static const QRegExp re("(HH|H|hh|h):mm(?!:s)");
179     dtFormat.replace(re, "\\1:mm:ss");
180     return loc.toString(dt, dtFormat);
181 }
182 
storeActivityList(QTextStream & ts)183 void ActivityWidget::storeActivityList(QTextStream &ts)
184 {
185     ActivityList activities = _model->activityList();
186 
187     foreach (Activity activity, activities) {
188         ts << right
189            // account name
190            << qSetFieldWidth(30)
191            << activity._accName
192            // separator
193            << qSetFieldWidth(0) << ","
194 
195            // date and time
196            << qSetFieldWidth(34)
197            << activity._dateTime.toString()
198            // separator
199            << qSetFieldWidth(0) << ","
200 
201            // file
202            << qSetFieldWidth(30)
203            << activity._file
204            // separator
205            << qSetFieldWidth(0) << ","
206 
207            // subject
208            << qSetFieldWidth(100)
209            << activity._subject
210            // separator
211            << qSetFieldWidth(0) << ","
212 
213            // message (mostly empty)
214            << qSetFieldWidth(55)
215            << activity._message
216            //
217            << qSetFieldWidth(0)
218            << endl;
219     }
220 }
221 
checkActivityTabVisibility()222 void ActivityWidget::checkActivityTabVisibility()
223 {
224     int accountCount = AccountManager::instance()->accounts().count();
225     bool hasAccountsWithActivity =
226         _accountsWithoutActivities.count() != accountCount;
227     bool hasNotifications = !_widgetForNotifId.isEmpty();
228 
229     _ui->_headerLabel->setVisible(hasAccountsWithActivity);
230     _ui->_activityList->setVisible(hasAccountsWithActivity);
231 
232     _ui->_notifyLabel->setVisible(hasNotifications);
233     _ui->_notifyScroll->setVisible(hasNotifications);
234 
235     emit hideActivityTab(!hasAccountsWithActivity && !hasNotifications);
236 }
237 
slotOpenFile(QModelIndex indx)238 void ActivityWidget::slotOpenFile(QModelIndex indx)
239 {
240     qCDebug(lcActivity) << indx.isValid() << indx.data(ActivityItemDelegate::PathRole).toString() << QFile::exists(indx.data(ActivityItemDelegate::PathRole).toString());
241     if (indx.isValid()) {
242         QString fullPath = indx.data(ActivityItemDelegate::PathRole).toString();
243 
244         if (QFile::exists(fullPath)) {
245             showInFileManager(fullPath);
246         }
247     }
248 }
249 
250 // GUI: Display the notifications.
251 // All notifications in list are coming from the same account
252 // but in the _widgetForNotifId hash widgets for all accounts are
253 // collected.
slotBuildNotificationDisplay(const ActivityList & list)254 void ActivityWidget::slotBuildNotificationDisplay(const ActivityList &list)
255 {
256     QHash<QString, int> accNotified;
257     QString listAccountName;
258 
259     // Whether a new notification widget was added to the notificationLayout.
260     bool newNotificationShown = false;
261 
262     foreach (auto activity, list) {
263         if (_blacklistedNotifications.contains(activity)) {
264             qCInfo(lcActivity) << "Activity in blacklist, skip";
265             continue;
266         }
267 
268         NotificationWidget *widget = nullptr;
269 
270         if (_widgetForNotifId.contains(activity.ident())) {
271             widget = _widgetForNotifId[activity.ident()];
272         } else {
273             widget = new NotificationWidget(this);
274             connect(widget, &NotificationWidget::sendNotificationRequest,
275                 this, &ActivityWidget::slotSendNotificationRequest);
276             connect(widget, &NotificationWidget::requestCleanupAndBlacklist,
277                 this, &ActivityWidget::slotRequestCleanupAndBlacklist);
278 
279             _notificationsLayout->addWidget(widget);
280             // _ui->_notifyScroll->setMinimumHeight( widget->height());
281             _ui->_notifyScroll->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
282             _widgetForNotifId[activity.ident()] = widget;
283             newNotificationShown = true;
284         }
285 
286         widget->setActivity(activity);
287 
288         // remember the list account name for the strayCat handling below.
289         listAccountName = activity._accName;
290 
291         // handle gui logs. In order to NOT annoy the user with every fetching of the
292         // notifications the notification id is stored in a Set. Only if an id
293         // is not in the set, it qualifies for guiLog.
294         // Important: The _guiLoggedNotifications set must be wiped regularly which
295         // will repeat the gui log.
296 
297         // after one hour, clear the gui log notification store
298         if (_guiLogTimer.elapsed() > 60 * 60 * 1000) {
299             _guiLoggedNotifications.clear();
300         }
301         if (!_guiLoggedNotifications.contains(activity._id)) {
302             QString host = activity._accName;
303             // store the name of the account that sends the notification to be
304             // able to add it to the tray notification
305             if (!host.isEmpty()) {
306                 if (accNotified.contains(host)) {
307                     accNotified[host] = accNotified[host] + 1;
308                 } else {
309                     accNotified[host] = 1;
310                 }
311             }
312             _guiLoggedNotifications.insert(activity._id);
313         }
314     }
315 
316     // check if there are widgets that have no corresponding activity from
317     // the server any more. Collect them in a list
318     QList<Activity::Identifier> strayCats;
319     foreach (auto id, _widgetForNotifId.keys()) {
320         NotificationWidget *widget = _widgetForNotifId[id];
321 
322         bool found = false;
323         // do not mark widgets of other accounts to delete.
324         if (widget->activity()._accName != listAccountName) {
325             continue;
326         }
327 
328         foreach (auto activity, list) {
329             if (activity.ident() == id) {
330                 // found an activity
331                 found = true;
332                 break;
333             }
334         }
335         if (!found) {
336             // the activity does not exist any more.
337             strayCats.append(id);
338         }
339     }
340 
341     // .. and now delete all these stray cat widgets.
342     foreach (auto strayCatId, strayCats) {
343         NotificationWidget *widgetToGo = _widgetForNotifId[strayCatId];
344         scheduleWidgetToRemove(widgetToGo, 0);
345     }
346 
347     checkActivityTabVisibility();
348 
349     const int newGuiLogCount = accNotified.count();
350 
351     if (newGuiLogCount > 0) {
352         // restart the gui log timer now that we show a notification
353         _guiLogTimer.start();
354 
355         // Assemble a tray notification
356         QString msg;
357         if (newGuiLogCount == 1) {
358             msg = tr("%n notifications(s) for %1.", "", accNotified.begin().value()).arg(accNotified.begin().key());
359         } else if (newGuiLogCount >= 2) {
360             const auto acc1 = accNotified.begin();
361             const auto acc2 = acc1 + 1;
362             if (newGuiLogCount == 2) {
363                 const int notiCount = acc1.value() + acc2.value();
364                 msg = tr("%n notifications(s) for %1 and %2.", "", notiCount).arg(acc1.key(), acc2.key());
365             } else {
366                 msg = tr("New notifications for %1, %2 and other accounts.").arg(acc1.key(), acc2.key());
367             }
368         }
369         const QString log = tr("Open the activity view for details.");
370         emit guiLog(msg, log);
371     }
372 
373     if (newNotificationShown) {
374         emit newNotification();
375     }
376 }
377 
slotSendNotificationRequest(const QString & accountName,const QString & link,const QByteArray & verb)378 void ActivityWidget::slotSendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb)
379 {
380     qCInfo(lcActivity) << "Server Notification Request " << verb << link << "on account" << accountName;
381     NotificationWidget *theSender = qobject_cast<NotificationWidget *>(sender());
382 
383     const QStringList validVerbs = QStringList() << "GET"
384                                                  << "PUT"
385                                                  << "POST"
386                                                  << "DELETE";
387 
388     if (validVerbs.contains(verb)) {
389         AccountStatePtr acc = AccountManager::instance()->account(accountName);
390         if (acc) {
391             NotificationConfirmJob *job = new NotificationConfirmJob(acc->account());
392             QUrl l(link);
393             job->setLinkAndVerb(l, verb);
394             job->setWidget(theSender);
395             connect(job, &AbstractNetworkJob::networkError,
396                 this, &ActivityWidget::slotNotifyNetworkError);
397             connect(job, &NotificationConfirmJob::jobFinished,
398                 this, &ActivityWidget::slotNotifyServerFinished);
399             job->start();
400 
401             // count the number of running notification requests. If this member var
402             // is larger than zero, no new fetching of notifications is started
403             _notificationRequestsRunning++;
404         }
405     } else {
406         qCWarning(lcActivity) << "Notification Links: Invalid verb:" << verb;
407     }
408 }
409 
endNotificationRequest(NotificationWidget * widget,int replyCode)410 void ActivityWidget::endNotificationRequest(NotificationWidget *widget, int replyCode)
411 {
412     _notificationRequestsRunning--;
413     if (widget) {
414         widget->slotNotificationRequestFinished(replyCode);
415     }
416 }
417 
slotNotifyNetworkError(QNetworkReply * reply)418 void ActivityWidget::slotNotifyNetworkError(QNetworkReply *reply)
419 {
420     NotificationConfirmJob *job = qobject_cast<NotificationConfirmJob *>(sender());
421     if (!job) {
422         return;
423     }
424 
425     int resultCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
426 
427     endNotificationRequest(job->widget(), resultCode);
428     qCWarning(lcActivity) << "Server notify job failed with code " << resultCode;
429 }
430 
slotNotifyServerFinished(const QString & reply,int replyCode)431 void ActivityWidget::slotNotifyServerFinished(const QString &reply, int replyCode)
432 {
433     NotificationConfirmJob *job = qobject_cast<NotificationConfirmJob *>(sender());
434     if (!job) {
435         return;
436     }
437 
438     endNotificationRequest(job->widget(), replyCode);
439     qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply;
440 
441     // if the notification was successful start a timer that triggers
442     // removal of the done widgets in a few seconds
443     // Add 200 millisecs to the predefined value to make sure that the timer in
444     // widget's method readyToClose() has elapsed.
445     if (replyCode == OCS_SUCCESS_STATUS_CODE || replyCode == OCS_SUCCESS_STATUS_CODE_V2) {
446         scheduleWidgetToRemove(job->widget());
447     }
448 }
449 
450 // blacklist the activity coming in here.
slotRequestCleanupAndBlacklist(const Activity & blacklistActivity)451 void ActivityWidget::slotRequestCleanupAndBlacklist(const Activity &blacklistActivity)
452 {
453     if (!_blacklistedNotifications.contains(blacklistActivity)) {
454         _blacklistedNotifications.append(blacklistActivity);
455     }
456 
457     NotificationWidget *widget = _widgetForNotifId[blacklistActivity.ident()];
458     scheduleWidgetToRemove(widget);
459 }
460 
scheduleWidgetToRemove(NotificationWidget * widget,int milliseconds)461 void ActivityWidget::scheduleWidgetToRemove(NotificationWidget *widget, int milliseconds)
462 {
463     if (!widget) {
464         return;
465     }
466     // in five seconds from now, remove the widget.
467     QDateTime removeTime = QDateTime::currentDateTimeUtc().addMSecs(milliseconds);
468     QDateTime &it = _widgetsToRemove[widget];
469     if (!it.isValid() || it > removeTime) {
470         it = removeTime;
471     }
472     if (!_removeTimer.isActive()) {
473         _removeTimer.start();
474     }
475 }
476 
477 // Called every second to see if widgets need to be removed.
slotCheckToCleanWidgets()478 void ActivityWidget::slotCheckToCleanWidgets()
479 {
480     auto currentTime = QDateTime::currentDateTimeUtc();
481     auto it = _widgetsToRemove.begin();
482     while (it != _widgetsToRemove.end()) {
483         // loop over all widgets in the to-remove queue
484         QDateTime t = it.value();
485         NotificationWidget *widget = it.key();
486 
487         if (currentTime > t) {
488             // found one to remove!
489             Activity::Identifier id = widget->activity().ident();
490             _widgetForNotifId.remove(id);
491             widget->deleteLater();
492             it = _widgetsToRemove.erase(it);
493         } else {
494             ++it;
495         }
496     }
497 
498     if (_widgetsToRemove.isEmpty()) {
499         _removeTimer.stop();
500     }
501 
502     // check to see if the whole notification pane should be hidden
503     if (_widgetForNotifId.isEmpty()) {
504         _ui->_notifyLabel->setHidden(true);
505         _ui->_notifyScroll->setHidden(true);
506     }
507 }
508 
509 
510 /* ==================================================================== */
511 
ActivitySettings(QWidget * parent)512 ActivitySettings::ActivitySettings(QWidget *parent)
513     : QWidget(parent)
514 {
515     QHBoxLayout *hbox = new QHBoxLayout(this);
516     setLayout(hbox);
517 
518     // create a tab widget for the three activity views
519     _tab = new QTabWidget(this);
520     hbox->addWidget(_tab);
521     _activityWidget = new ActivityWidget(this);
522     _activityTabId = _tab->addTab(_activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity"));
523     connect(_activityWidget, &ActivityWidget::copyToClipboard, this, &ActivitySettings::slotCopyToClipboard);
524     connect(_activityWidget, &ActivityWidget::hideActivityTab, this, &ActivitySettings::setActivityTabHidden);
525     connect(_activityWidget, &ActivityWidget::guiLog, this, &ActivitySettings::guiLog);
526     connect(_activityWidget, &ActivityWidget::newNotification, this, &ActivitySettings::slotShowActivityTab);
527 
528     _protocolWidget = new ProtocolWidget(this);
529     _protocolTabId = _tab->addTab(_protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol"));
530     connect(_protocolWidget, &ProtocolWidget::copyToClipboard, this, &ActivitySettings::slotCopyToClipboard);
531 
532     _issuesWidget = new IssuesWidget(this);
533     _syncIssueTabId = _tab->addTab(_issuesWidget, Theme::instance()->syncStateIcon(SyncResult::Problem), QString());
534     slotShowIssueItemCount(0); // to display the label.
535     connect(_issuesWidget, &IssuesWidget::issueCountUpdated,
536         this, &ActivitySettings::slotShowIssueItemCount);
537     connect(_issuesWidget, &IssuesWidget::copyToClipboard,
538         this, &ActivitySettings::slotCopyToClipboard);
539 
540     // Add a progress indicator to spin if the acitivity list is updated.
541     _progressIndicator = new QProgressIndicator(this);
542     _tab->setCornerWidget(_progressIndicator);
543 
544     connect(&_notificationCheckTimer, &QTimer::timeout,
545         this, &ActivitySettings::slotRegularNotificationCheck);
546 
547     // connect a model signal to stop the animation.
548     connect(_activityWidget, &ActivityWidget::rowsInserted, _progressIndicator, &QProgressIndicator::stopAnimation);
549 
550     // We want the protocol be the default
551     _tab->setCurrentIndex(1);
552 }
553 
setNotificationRefreshInterval(std::chrono::milliseconds interval)554 void ActivitySettings::setNotificationRefreshInterval(std::chrono::milliseconds interval)
555 {
556     qCDebug(lcActivity) << "Starting Notification refresh timer with " << interval.count() / 1000 << " sec interval";
557     _notificationCheckTimer.start(interval.count());
558 }
559 
setActivityTabHidden(bool hidden)560 void ActivitySettings::setActivityTabHidden(bool hidden)
561 {
562     if (hidden && _activityTabId > -1) {
563         _tab->removeTab(_activityTabId);
564         _activityTabId = -1;
565         _protocolTabId -= 1;
566         _syncIssueTabId -= 1;
567     }
568 
569     if (!hidden && _activityTabId == -1) {
570         _activityTabId = _tab->insertTab(0, _activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity"));
571         _protocolTabId += 1;
572         _syncIssueTabId += 1;
573     }
574 }
575 
slotShowIssueItemCount(int cnt)576 void ActivitySettings::slotShowIssueItemCount(int cnt)
577 {
578     QString cntText = tr("Not Synced");
579     if (cnt) {
580         //: %1 is the number of not synced files.
581         cntText = tr("Not Synced (%1)").arg(cnt);
582     }
583     _tab->setTabText(_syncIssueTabId, cntText);
584 }
585 
slotShowActivityTab()586 void ActivitySettings::slotShowActivityTab()
587 {
588     if (_activityTabId != -1) {
589         _tab->setCurrentIndex(_activityTabId);
590     }
591 }
592 
slotShowIssuesTab(const QString & folderAlias)593 void ActivitySettings::slotShowIssuesTab(const QString &folderAlias)
594 {
595     if (_syncIssueTabId == -1)
596         return;
597     _tab->setCurrentIndex(_syncIssueTabId);
598 
599     _issuesWidget->showFolderErrors(folderAlias);
600 }
601 
slotCopyToClipboard()602 void ActivitySettings::slotCopyToClipboard()
603 {
604     QString text;
605     QTextStream ts(&text);
606 
607     int idx = _tab->currentIndex();
608     QString message;
609 
610     if (idx == _activityTabId) {
611         // the activity widget
612         _activityWidget->storeActivityList(ts);
613         message = tr("The server activity list has been copied to the clipboard.");
614     } else if (idx == _protocolTabId) {
615         // the protocol widget
616         _protocolWidget->storeSyncActivity(ts);
617         message = tr("The sync activity list has been copied to the clipboard.");
618     } else if (idx == _syncIssueTabId) {
619         // issues Widget
620         message = tr("The list of unsynced items has been copied to the clipboard.");
621         _issuesWidget->storeSyncIssues(ts);
622     }
623 
624     QApplication::clipboard()->setText(text);
625     emit guiLog(tr("Copied to clipboard"), message);
626 }
627 
slotRemoveAccount(AccountState * ptr)628 void ActivitySettings::slotRemoveAccount(AccountState *ptr)
629 {
630     _activityWidget->slotRemoveAccount(ptr);
631 }
632 
slotRefresh(AccountState * ptr)633 void ActivitySettings::slotRefresh(AccountState *ptr)
634 {
635     // QElapsedTimer isn't actually constructed as invalid.
636     if (!_timeSinceLastCheck.contains(ptr)) {
637         _timeSinceLastCheck[ptr].invalidate();
638     }
639     QElapsedTimer &timer = _timeSinceLastCheck[ptr];
640 
641     // Fetch Activities only if visible and if last check is longer than 15 secs ago
642     if (timer.isValid() && timer.elapsed() < NOTIFICATION_REQUEST_FREE_PERIOD) {
643         qCDebug(lcActivity) << "Do not check as last check is only secs ago: " << timer.elapsed() / 1000;
644         return;
645     }
646     if (ptr && ptr->isConnected()) {
647         if (isVisible() || !timer.isValid()) {
648             _progressIndicator->startAnimation();
649             _activityWidget->slotRefreshActivities(ptr);
650         }
651         _activityWidget->slotRefreshNotifications(ptr);
652         timer.start();
653     }
654 }
655 
slotRegularNotificationCheck()656 void ActivitySettings::slotRegularNotificationCheck()
657 {
658     AccountManager *am = AccountManager::instance();
659     foreach (AccountStatePtr a, am->accounts()) {
660         slotRefresh(a.data());
661     }
662 }
663 
event(QEvent * e)664 bool ActivitySettings::event(QEvent *e)
665 {
666     if (e->type() == QEvent::Show) {
667         slotRegularNotificationCheck();
668     }
669     return QWidget::event(e);
670 }
671 
~ActivitySettings()672 ActivitySettings::~ActivitySettings()
673 {
674 }
675 }
676