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