(sender());
+ if (!job) {
+ return;
+ }
+
+ slotEndNotificationRequest(replyCode);
+ qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply;
+}
+
+void User::slotProgressInfo(const QString &folder, const ProgressInfo &progress)
+{
+ if (progress.status() == ProgressInfo::Reconcile) {
+ // Wipe all non-persistent entries - as well as the persistent ones
+ // in cases where a local discovery was done.
+ auto f = FolderMan::instance()->folder(folder);
+ if (!f)
+ return;
+ const auto &engine = f->syncEngine();
+ const auto style = engine.lastLocalDiscoveryStyle();
+ foreach (Activity activity, _activityModel->errorsList()) {
+ if (activity._folder != folder) {
+ continue;
+ }
+
+ if (style == LocalDiscoveryStyle::FilesystemOnly) {
+ _activityModel->removeActivityFromActivityList(activity);
+ continue;
+ }
+
+ if (activity._status == SyncFileItem::Conflict && !QFileInfo(f->path() + activity._file).exists()) {
+ _activityModel->removeActivityFromActivityList(activity);
+ continue;
+ }
+
+ if (activity._status == SyncFileItem::FileLocked && !QFileInfo(f->path() + activity._file).exists()) {
+ _activityModel->removeActivityFromActivityList(activity);
+ continue;
+ }
+
+
+ if (activity._status == SyncFileItem::FileIgnored && !QFileInfo(f->path() + activity._file).exists()) {
+ _activityModel->removeActivityFromActivityList(activity);
+ continue;
+ }
+
+
+ if (!QFileInfo(f->path() + activity._file).exists()) {
+ _activityModel->removeActivityFromActivityList(activity);
+ continue;
+ }
+
+ auto path = QFileInfo(activity._file).dir().path().toUtf8();
+ if (path == ".")
+ path.clear();
+
+ if (engine.shouldDiscoverLocally(path))
+ _activityModel->removeActivityFromActivityList(activity);
+ }
+ }
+
+ if (progress.status() == ProgressInfo::Done) {
+ // We keep track very well of pending conflicts.
+ // Inform other components about them.
+ QStringList conflicts;
+ foreach (Activity activity, _activityModel->errorsList()) {
+ if (activity._folder == folder
+ && activity._status == SyncFileItem::Conflict) {
+ conflicts.append(activity._file);
+ }
+ }
+
+ emit ProgressDispatcher::instance()->folderConflicts(folder, conflicts);
+ }
+}
+
+void User::slotAddError(const QString &folderAlias, const QString &message, ErrorCategory category)
+{
+ auto folderInstance = FolderMan::instance()->folder(folderAlias);
+ if (!folderInstance)
+ return;
+
+ if (folderInstance->accountState() == _account.data()) {
+ qCWarning(lcActivity) << "Item " << folderInstance->shortGuiLocalPath() << " retrieved resulted in " << message;
+
+ Activity activity;
+ activity._type = Activity::SyncResultType;
+ activity._status = SyncResult::Error;
+ activity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate);
+ activity._subject = message;
+ activity._message = folderInstance->shortGuiLocalPath();
+ activity._link = folderInstance->shortGuiLocalPath();
+ activity._accName = folderInstance->accountState()->account()->displayName();
+ activity._folder = folderAlias;
+
+
+ if (category == ErrorCategory::InsufficientRemoteStorage) {
+ ActivityLink link;
+ link._label = tr("Retry all uploads");
+ link._link = folderInstance->path();
+ link._verb = "";
+ link._isPrimary = true;
+ activity._links.append(link);
+ }
+
+ // add 'other errors' to activity list
+ _activityModel->addErrorToActivityList(activity);
+ }
+}
+
+void User::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item)
+{
+ auto folderInstance = FolderMan::instance()->folder(folder);
+
+ if (!folderInstance)
+ return;
+
+ // check if we are adding it to the right account and if it is useful information (protocol errors)
+ if (folderInstance->accountState() == _account.data()) {
+ qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in " << item->_errorString;
+
+ Activity activity;
+ activity._type = Activity::SyncFileItemType; //client activity
+ activity._status = item->_status;
+ activity._dateTime = QDateTime::currentDateTime();
+ activity._message = item->_originalFile;
+ activity._link = folderInstance->accountState()->account()->url();
+ activity._accName = folderInstance->accountState()->account()->displayName();
+ activity._file = item->_file;
+ activity._folder = folder;
+ activity._fileAction = "";
+
+ if (item->_instruction == CSYNC_INSTRUCTION_REMOVE) {
+ activity._fileAction = "file_deleted";
+ } else if (item->_instruction == CSYNC_INSTRUCTION_NEW) {
+ activity._fileAction = "file_created";
+ } else if (item->_instruction == CSYNC_INSTRUCTION_RENAME) {
+ activity._fileAction = "file_renamed";
+ } else {
+ activity._fileAction = "file_changed";
+ }
+
+
+ if (item->_status == SyncFileItem::NoStatus || item->_status == SyncFileItem::Success) {
+ qCWarning(lcActivity) << "Item " << item->_file << " retrieved successfully.";
+
+ if (activity._fileAction == "file_renamed") {
+ activity._message.prepend(tr("You renamed") + " ");
+ } else if (activity._fileAction == "file_deleted") {
+ activity._message.prepend(tr("You deleted") + " ");
+ } else if (activity._fileAction == "file_created") {
+ activity._message.prepend(tr("You created") + " ");
+ } else {
+ activity._message.prepend(tr("You changed") + " ");
+ }
+
+ _activityModel->addSyncFileItemToActivityList(activity);
+ } else {
+ qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in error " << item->_errorString;
+ activity._subject = item->_errorString;
+
+ if (item->_status == SyncFileItem::Status::FileIgnored) {
+ _activityModel->addIgnoredFileToList(activity);
+ } else {
+ // add 'protocol error' to activity list
+ _activityModel->addErrorToActivityList(activity);
+ }
+ }
+ }
+}
+
+AccountPtr User::account() const
+{
+ return _account->account();
+}
+
+void User::setCurrentUser(const bool &isCurrent)
+{
+ _isCurrentUser = isCurrent;
+}
+
+Folder *User::getFolder()
+{
+ foreach (Folder *folder, FolderMan::instance()->map()) {
+ if (folder->accountState() == _account.data()) {
+ return folder;
+ }
+ }
+}
+
+ActivityListModel *User::getActivityModel()
+{
+ return _activityModel;
+}
+
+void User::openLocalFolder()
+{
+#ifdef Q_OS_WIN
+ QString path = "file:///" + this->getFolder()->path();
+#else
+ QString path = "file://" + this->getFolder()->path();
+#endif
+ QDesktopServices::openUrl(path);
+}
+
+void User::login() const
+{
+ _account->account()->resetRejectedCertificates();
+ _account->signIn();
+}
+
+void User::logout() const
+{
+ _account->signOutByUi();
+}
+
+QString User::name() const
+{
+ // If davDisplayName is empty (can be several reasons, simplest is missing login at startup), fall back to username
+ QString name = _account->account()->davDisplayName();
+ if (name == "") {
+ name = _account->account()->credentials()->user();
+ }
+ return name;
+}
+
+QString User::server(bool shortened) const
+{
+ QString serverUrl = _account->account()->url().toString();
+ if (shortened) {
+ serverUrl.replace(QLatin1String("https://"), QLatin1String(""));
+ serverUrl.replace(QLatin1String("http://"), QLatin1String(""));
+ }
+ return serverUrl;
+}
+
+QImage User::avatar(bool whiteBg) const
+{
+ QImage img = AvatarJob::makeCircularAvatar(_account->account()->avatar());
+ if (img.isNull()) {
+ QImage image(128, 128, QImage::Format_ARGB32);
+ image.fill(Qt::GlobalColor::transparent);
+ QPainter painter(&image);
+
+ QSvgRenderer renderer(QString(whiteBg ? ":/client/theme/black/user.svg" : ":/client/theme/white/user.svg"));
+ renderer.render(&painter);
+
+ return image;
+ } else {
+ return img;
+ }
+}
+
+bool User::serverHasTalk() const
+{
+ return _account->hasTalk();
+}
+
+bool User::hasActivities() const
+{
+ return _account->account()->capabilities().hasActivities();
+}
+
+AccountAppList User::appList() const
+{
+ return _account->appList();
+}
+
+bool User::isCurrentUser() const
+{
+ return _isCurrentUser;
+}
+
+bool User::isConnected() const
+{
+ return (_account->connectionStatus() == AccountState::ConnectionStatus::Connected);
+}
+
+void User::removeAccount() const
+{
+ AccountManager::instance()->deleteAccount(_account.data());
+ AccountManager::instance()->save();
+}
+
+/*-------------------------------------------------------------------------------------*/
+
+UserModel *UserModel::_instance = nullptr;
+
+UserModel *UserModel::instance()
+{
+ if (_instance == nullptr) {
+ _instance = new UserModel();
+ }
+ return _instance;
+}
+
+UserModel::UserModel(QObject *parent)
+ : QAbstractListModel(parent)
+ , _currentUserId()
+{
+ // TODO: Remember selected user from last quit via settings file
+ if (AccountManager::instance()->accounts().size() > 0) {
+ buildUserList();
+ }
+
+ connect(AccountManager::instance(), &AccountManager::accountAdded,
+ this, &UserModel::buildUserList);
+}
+
+void UserModel::buildUserList()
+{
+ for (int i = 0; i < AccountManager::instance()->accounts().size(); i++) {
+ auto user = AccountManager::instance()->accounts().at(i);
+ addUser(user);
+ }
+ if (_init) {
+ _users.first()->setCurrentUser(true);
+ _init = false;
+ }
+}
+
+Q_INVOKABLE int UserModel::numUsers()
+{
+ return _users.size();
+}
+
+Q_INVOKABLE int UserModel::currentUserId()
+{
+ return _currentUserId;
+}
+
+Q_INVOKABLE bool UserModel::isUserConnected(const int &id)
+{
+ return _users[id]->isConnected();
+}
+
+Q_INVOKABLE QImage UserModel::currentUserAvatar()
+{
+ if (_users.count() >= 1) {
+ return _users[_currentUserId]->avatar();
+ } else {
+ QImage image(128, 128, QImage::Format_ARGB32);
+ image.fill(Qt::GlobalColor::transparent);
+ QPainter painter(&image);
+ QSvgRenderer renderer(QString(":/client/theme/white/user.svg"));
+ renderer.render(&painter);
+
+ return image;
+ }
+}
+
+QImage UserModel::avatarById(const int &id)
+{
+ return _users[id]->avatar(true);
+}
+
+Q_INVOKABLE QString UserModel::currentUserName()
+{
+ if (_users.count() >= 1) {
+ return _users[_currentUserId]->name();
+ } else {
+ return QString("No users");
+ }
+}
+
+Q_INVOKABLE QString UserModel::currentUserServer()
+{
+ if (_users.count() >= 1) {
+ return _users[_currentUserId]->server();
+ } else {
+ return QString("");
+ }
+}
+
+Q_INVOKABLE bool UserModel::currentServerHasTalk()
+{
+ if (_users.count() >= 1) {
+ return _users[_currentUserId]->serverHasTalk();
+ } else {
+ return false;
+ }
+}
+
+void UserModel::addUser(AccountStatePtr &user, const bool &isCurrent)
+{
+ bool containsUser = false;
+ for (int i = 0; i < _users.size(); i++) {
+ if (_users[i]->account() == user->account()) {
+ containsUser = true;
+ continue;
+ }
+ }
+
+ if (!containsUser) {
+ beginInsertRows(QModelIndex(), rowCount(), rowCount());
+ _users << new User(user, isCurrent);
+ if (isCurrent) {
+ _currentUserId = _users.indexOf(_users.last());
+ }
+ endInsertRows();
+ ConfigFile cfg;
+ _users.last()->setNotificationRefreshInterval(cfg.notificationRefreshInterval());
+ }
+}
+
+int UserModel::currentUserIndex()
+{
+ return _currentUserId;
+}
+
+Q_INVOKABLE void UserModel::openCurrentAccountLocalFolder()
+{
+ _users[_currentUserId]->openLocalFolder();
+}
+
+Q_INVOKABLE void UserModel::openCurrentAccountTalk()
+{
+ QString url = _users[_currentUserId]->server(false) + "/apps/spreed";
+ if (!(url.contains("http://") || url.contains("https://"))) {
+ url = "https://" + _users[_currentUserId]->server(false) + "/apps/spreed";
+ }
+ QDesktopServices::openUrl(QUrl(url));
+}
+
+Q_INVOKABLE void UserModel::openCurrentAccountServer()
+{
+ // Don't open this URL when the QML appMenu pops up on click (see Window.qml)
+ if(appList().count() > 0)
+ return;
+
+ QString url = _users[_currentUserId]->server(false);
+ if (!(url.contains("http://") || url.contains("https://"))) {
+ url = "https://" + _users[_currentUserId]->server(false);
+ }
+ QDesktopServices::openUrl(QUrl(url));
+}
+
+Q_INVOKABLE void UserModel::switchCurrentUser(const int &id)
+{
+ _users[_currentUserId]->setCurrentUser(false);
+ _users[id]->setCurrentUser(true);
+ _currentUserId = id;
+ emit refreshCurrentUserGui();
+ emit newUserSelected();
+}
+
+Q_INVOKABLE void UserModel::login(const int &id)
+{
+ _users[id]->login();
+ emit refreshCurrentUserGui();
+}
+
+Q_INVOKABLE void UserModel::logout(const int &id)
+{
+ _users[id]->logout();
+ emit refreshCurrentUserGui();
+}
+
+Q_INVOKABLE void UserModel::removeAccount(const int &id)
+{
+ QMessageBox messageBox(QMessageBox::Question,
+ tr("Confirm Account Removal"),
+ tr("Do you really want to remove the connection to the account %1?
"
+ "Note: This will not delete any files.
")
+ .arg(_users[id]->name()),
+ QMessageBox::NoButton);
+ QPushButton *yesButton =
+ messageBox.addButton(tr("Remove connection"), QMessageBox::YesRole);
+ messageBox.addButton(tr("Cancel"), QMessageBox::NoRole);
+
+ messageBox.exec();
+ if (messageBox.clickedButton() != yesButton) {
+ return;
+ }
+
+ if (_users[id]->isCurrentUser() && _users.count() > 1) {
+ id == 0 ? switchCurrentUser(1) : switchCurrentUser(0);
+ }
+
+ _users[id]->logout();
+ _users[id]->removeAccount();
+
+ beginRemoveRows(QModelIndex(), id, id);
+ _users.removeAt(id);
+ endRemoveRows();
+
+ emit refreshCurrentUserGui();
+}
+
+int UserModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return _users.count();
+}
+
+QVariant UserModel::data(const QModelIndex &index, int role) const
+{
+ if (index.row() < 0 || index.row() >= _users.count()) {
+ return QVariant();
+ }
+
+ if (role == NameRole) {
+ return _users[index.row()]->name();
+ } else if (role == ServerRole) {
+ return _users[index.row()]->server();
+ } else if (role == AvatarRole) {
+ return _users[index.row()]->avatar();
+ } else if (role == IsCurrentUserRole) {
+ return _users[index.row()]->isCurrentUser();
+ } else if (role == IsConnectedRole) {
+ return _users[index.row()]->isConnected();
+ } else if (role == IdRole) {
+ return index.row();
+ }
+ return QVariant();
+}
+
+QHash UserModel::roleNames() const
+{
+ QHash roles;
+ roles[NameRole] = "name";
+ roles[ServerRole] = "server";
+ roles[AvatarRole] = "avatar";
+ roles[IsCurrentUserRole] = "isCurrentUser";
+ roles[IsConnectedRole] = "isConnected";
+ roles[IdRole] = "id";
+ return roles;
+}
+
+ActivityListModel *UserModel::currentActivityModel()
+{
+ return _users[currentUserIndex()]->getActivityModel();
+}
+
+bool UserModel::currentUserHasActivities()
+{
+ return _users[currentUserIndex()]->hasActivities();
+}
+
+void UserModel::fetchCurrentActivityModel()
+{
+ _users[currentUserId()]->slotRefresh();
+}
+
+AccountAppList UserModel::appList() const
+{
+ if (_users.count() >= 1) {
+ return _users[_currentUserId]->appList();
+ } else {
+ return AccountAppList();
+ }
+}
+
+/*-------------------------------------------------------------------------------------*/
+
+ImageProvider::ImageProvider()
+ : QQuickImageProvider(QQuickImageProvider::Image)
+{
+}
+
+QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
+{
+ Q_UNUSED(size)
+ Q_UNUSED(requestedSize)
+
+ if (id == "currentUser") {
+ return UserModel::instance()->currentUserAvatar();
+ } else {
+ int uid = id.toInt();
+ return UserModel::instance()->avatarById(uid);
+ }
+}
+
+/*-------------------------------------------------------------------------------------*/
+
+UserAppsModel *UserAppsModel::_instance = nullptr;
+
+UserAppsModel *UserAppsModel::instance()
+{
+ if (_instance == nullptr) {
+ _instance = new UserAppsModel();
+ }
+ return _instance;
+}
+
+UserAppsModel::UserAppsModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+void UserAppsModel::buildAppList()
+{
+ if (rowCount() > 0) {
+ beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
+ _apps.clear();
+ endRemoveRows();
+ }
+
+ if(UserModel::instance()->appList().count() > 0) {
+ foreach(AccountApp *app, UserModel::instance()->appList()) {
+ // Filter out Talk because we have a dedicated button for it
+ if(app->id() == QLatin1String("spreed"))
+ continue;
+
+ beginInsertRows(QModelIndex(), rowCount(), rowCount());
+ _apps << app;
+ endInsertRows();
+ }
+ }
+}
+
+void UserAppsModel::openAppUrl(const QUrl &url)
+{
+ QDesktopServices::openUrl(url);
+}
+
+int UserAppsModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return _apps.count();
+}
+
+QVariant UserAppsModel::data(const QModelIndex &index, int role) const
+{
+ if (index.row() < 0 || index.row() >= _apps.count()) {
+ return QVariant();
+ }
+
+ if (role == NameRole) {
+ return _apps[index.row()]->name();
+ } else if (role == UrlRole) {
+ return _apps[index.row()]->url();
+ } else if (role == IconUrlRole) {
+ return _apps[index.row()]->iconUrl().toString();
+ }
+ return QVariant();
+}
+
+QHash UserAppsModel::roleNames() const
+{
+ QHash roles;
+ roles[NameRole] = "appName";
+ roles[UrlRole] = "appUrl";
+ roles[IconUrlRole] = "appIconUrl";
+ return roles;
+}
+
+}
diff --git a/src/gui/tray/UserModel.h b/src/gui/tray/UserModel.h
new file mode 100644
index 000000000000..36f5af4b3942
--- /dev/null
+++ b/src/gui/tray/UserModel.h
@@ -0,0 +1,183 @@
+#ifndef USERMODEL_H
+#define USERMODEL_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include "ActivityListModel.h"
+#include "accountmanager.h"
+#include "folderman.h"
+#include
+
+namespace OCC {
+
+class User : public QObject
+{
+ Q_OBJECT
+public:
+ User(AccountStatePtr &account, const bool &isCurrent = false, QObject* parent = 0);
+
+ AccountPtr account() const;
+
+ bool isConnected() const;
+ bool isCurrentUser() const;
+ void setCurrentUser(const bool &isCurrent);
+ Folder *getFolder();
+ ActivityListModel *getActivityModel();
+ void openLocalFolder();
+ QString name() const;
+ QString server(bool shortened = true) const;
+ bool serverHasTalk() const;
+ bool hasActivities() const;
+ AccountAppList appList() const;
+ QImage avatar(bool whiteBg = false) const;
+ QString id() const;
+ void login() const;
+ void logout() const;
+ void removeAccount() const;
+
+signals:
+ void guiLog(const QString &, const QString &);
+
+public slots:
+ void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
+ void slotProgressInfo(const QString &folder, const ProgressInfo &progress);
+ void slotAddError(const QString &folderAlias, const QString &message, ErrorCategory category);
+ void slotNotificationRequestFinished(int statusCode);
+ void slotNotifyNetworkError(QNetworkReply *reply);
+ void slotEndNotificationRequest(int replyCode);
+ void slotNotifyServerFinished(const QString &reply, int replyCode);
+ void slotSendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
+ void slotBuildNotificationDisplay(const ActivityList &list);
+ void slotRefreshNotifications();
+ void slotRefreshActivities();
+ void slotRefresh();
+ void slotRefreshImmediately();
+ void setNotificationRefreshInterval(std::chrono::milliseconds interval);
+ void slotRebuildNavigationAppList();
+
+private:
+ AccountStatePtr _account;
+ bool _isCurrentUser;
+ ActivityListModel *_activityModel;
+ ActivityList _blacklistedNotifications;
+
+ QTimer _notificationCheckTimer;
+ QHash _timeSinceLastCheck;
+
+ QElapsedTimer _guiLogTimer;
+ QSet _guiLoggedNotifications;
+
+ // number of currently running notification requests. If non zero,
+ // no query for notifications is started.
+ int _notificationRequestsRunning;
+};
+
+class UserModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ static UserModel *instance();
+ virtual ~UserModel() {};
+
+ void addUser(AccountStatePtr &user, const bool &isCurrent = false);
+ int currentUserIndex();
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ QImage avatarById(const int &id);
+
+ Q_INVOKABLE void fetchCurrentActivityModel();
+ Q_INVOKABLE void openCurrentAccountLocalFolder();
+ Q_INVOKABLE void openCurrentAccountTalk();
+ Q_INVOKABLE void openCurrentAccountServer();
+ Q_INVOKABLE QImage currentUserAvatar();
+ Q_INVOKABLE int numUsers();
+ Q_INVOKABLE QString currentUserName();
+ Q_INVOKABLE QString currentUserServer();
+ Q_INVOKABLE bool currentUserHasActivities();
+ Q_INVOKABLE bool currentServerHasTalk();
+ Q_INVOKABLE int currentUserId();
+ Q_INVOKABLE bool isUserConnected(const int &id);
+ Q_INVOKABLE void switchCurrentUser(const int &id);
+ Q_INVOKABLE void login(const int &id);
+ Q_INVOKABLE void logout(const int &id);
+ Q_INVOKABLE void removeAccount(const int &id);
+
+ ActivityListModel *currentActivityModel();
+
+ enum UserRoles {
+ NameRole = Qt::UserRole + 1,
+ ServerRole,
+ AvatarRole,
+ IsCurrentUserRole,
+ IsConnectedRole,
+ IdRole
+ };
+
+ AccountAppList appList() const;
+
+signals:
+ Q_INVOKABLE void addAccount();
+ Q_INVOKABLE void refreshCurrentUserGui();
+ Q_INVOKABLE void newUserSelected();
+
+protected:
+ QHash roleNames() const override;
+
+private:
+ static UserModel *_instance;
+ UserModel(QObject *parent = 0);
+ QList _users;
+ int _currentUserId;
+ bool _init = true;
+
+ void buildUserList();
+};
+
+class ImageProvider : public QQuickImageProvider
+{
+public:
+ ImageProvider();
+ QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
+};
+
+class UserAppsModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ static UserAppsModel *instance();
+ virtual ~UserAppsModel() {};
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ enum UserAppsRoles {
+ NameRole = Qt::UserRole + 1,
+ UrlRole,
+ IconUrlRole
+ };
+
+ void buildAppList();
+
+public slots:
+ void openAppUrl(const QUrl &url);
+
+protected:
+ QHash roleNames() const override;
+
+private:
+ static UserAppsModel *_instance;
+ UserAppsModel(QObject *parent = 0);
+
+ AccountAppList _apps;
+};
+
+}
+#endif // USERMODEL_H
diff --git a/src/gui/tray/Window.qml b/src/gui/tray/Window.qml
new file mode 100644
index 000000000000..5a040c970989
--- /dev/null
+++ b/src/gui/tray/Window.qml
@@ -0,0 +1,612 @@
+import QtQml 2.1
+import QtQml.Models 2.1
+import QtQuick 2.9
+import QtQuick.Window 2.2
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.2
+import QtGraphicalEffects 1.0
+
+Window {
+
+ id: trayWindow
+ visible: true
+ width: 400
+ height: 510
+ color: "transparent"
+ flags: Qt.FramelessWindowHint
+
+ onActiveChanged: {
+ if(!active) {
+ trayWindow.hide();
+ systrayBackend.setClosed();
+ }
+ }
+
+ onVisibleChanged: {
+ currentAccountAvatar.source = ""
+ currentAccountAvatar.source = "image://avatars/currentUser"
+ currentAccountUser.text = userModelBackend.currentUserName();
+ currentAccountServer.text = userModelBackend.currentUserServer();
+ trayWindowTalkButton.visible = userModelBackend.currentServerHasTalk() ? true : false;
+ currentAccountStateIndicator.source = ""
+ currentAccountStateIndicator.source = userModelBackend.isUserConnected(userModelBackend.currentUserId()) ? "qrc:///client/theme/colored/state-ok.svg" : "qrc:///client/theme/colored/state-offline.svg"
+
+ userLineInstantiator.active = false;
+ userLineInstantiator.active = true;
+ }
+
+ Connections {
+ target: userModelBackend
+ onRefreshCurrentUserGui: {
+ currentAccountAvatar.source = ""
+ currentAccountAvatar.source = "image://avatars/currentUser"
+ currentAccountUser.text = userModelBackend.currentUserName();
+ currentAccountServer.text = userModelBackend.currentUserServer();
+ currentAccountStateIndicator.source = ""
+ currentAccountStateIndicator.source = userModelBackend.isUserConnected(userModelBackend.currentUserId()) ? "qrc:///client/theme/colored/state-ok.svg" : "qrc:///client/theme/colored/state-offline.svg"
+ }
+ onNewUserSelected: {
+ accountMenu.close();
+ trayWindowTalkButton.visible = userModelBackend.currentServerHasTalk() ? true : false;
+ }
+ }
+
+ Connections {
+ target: systrayBackend
+ onShowWindow: {
+ accountMenu.close();
+ trayWindow.show();
+ trayWindow.raise();
+ trayWindow.requestActivate();
+ trayWindow.setX( systrayBackend.calcTrayWindowX());
+ trayWindow.setY( systrayBackend.calcTrayWindowY());
+ systrayBackend.setOpened();
+ userModelBackend.fetchCurrentActivityModel();
+ }
+ onHideWindow: {
+ trayWindow.hide();
+ systrayBackend.setClosed();
+ }
+ }
+
+ Rectangle {
+ id: trayWindowBackground
+ anchors.fill: parent
+ radius: 10
+ border.color: "#0082c9"
+
+ Rectangle {
+ id: trayWindowHeaderBackground
+ anchors.left: trayWindowBackground.left
+ anchors.top: trayWindowBackground.top
+ height: 60
+ width: parent.width
+ radius: 9
+ color: "#0082c9"
+
+ Rectangle {
+ anchors.left: trayWindowHeaderBackground.left
+ anchors.bottom: trayWindowHeaderBackground.bottom
+ height: 30
+ width: parent.width
+ color: "#0082c9"
+ }
+
+ RowLayout {
+ id: trayWindowHeaderLayout
+ spacing: 0
+ anchors.fill: parent
+
+ Button {
+ id: currentAccountButton
+ Layout.preferredWidth: 220
+ Layout.preferredHeight: (trayWindowHeaderBackground.height)
+ display: AbstractButton.IconOnly
+ flat: true
+
+ MouseArea {
+ id: accountBtnMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ onContainsMouseChanged: {
+ currentAccountStateIndicatorBackground.color = (containsMouse ? "#009dd9" : "#0082c9")
+ }
+ onClicked:
+ {
+ syncPauseButton.text = systrayBackend.syncIsPaused() ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
+ accountMenu.open()
+ }
+
+ Menu {
+ id: accountMenu
+ x: (currentAccountButton.x + 2)
+ y: (currentAccountButton.y + currentAccountButton.height + 2)
+ width: (currentAccountButton.width - 2)
+ closePolicy: "CloseOnPressOutside"
+
+ background: Rectangle {
+ border.color: "#0082c9"
+ radius: 2
+ }
+
+ onClosed: {
+ userLineInstantiator.active = false;
+ userLineInstantiator.active = true;
+ }
+
+ Instantiator {
+ id: userLineInstantiator
+ model: userModelBackend
+ delegate: UserLine {}
+ onObjectAdded: accountMenu.insertItem(index, object)
+ onObjectRemoved: accountMenu.removeItem(object)
+ }
+
+ MenuItem {
+ id: addAccountButton
+ height: 50
+
+ RowLayout {
+ width: addAccountButton.width
+ height: addAccountButton.height
+ spacing: 0
+
+ Image {
+ Layout.leftMargin: 14
+ verticalAlignment: Qt.AlignCenter
+ source: "qrc:///client/theme/black/add.svg"
+ sourceSize.width: openLocalFolderButton.icon.width
+ sourceSize.height: openLocalFolderButton.icon.height
+ }
+ Label {
+ Layout.leftMargin: 14
+ text: qsTr("Add account")
+ color: "black"
+ font.pixelSize: 12
+ }
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ onClicked: userModelBackend.addAccount()
+ }
+
+ MenuSeparator { id: accountMenuSeparator }
+
+ MenuItem {
+ id: syncPauseButton
+ font.pixelSize: 12
+ onClicked: systrayBackend.pauseResumeSync()
+ }
+
+ MenuItem {
+ text: qsTr("Open settings")
+ font.pixelSize: 12
+ onClicked: systrayBackend.openSettings()
+ }
+
+ MenuItem {
+ text: qsTr("Help")
+ font.pixelSize: 12
+ onClicked: systrayBackend.openHelp()
+ }
+
+ MenuItem {
+ text: qsTr("Quit Nextcloud")
+ font.pixelSize: 12
+ onClicked: systrayBackend.shutdown()
+ }
+ }
+ }
+
+ background:
+ Item {
+ id: leftHoverContainer
+ height: currentAccountButton.height
+ width: currentAccountButton.width
+ Rectangle {
+ width: currentAccountButton.width / 2
+ height: currentAccountButton.height / 2
+ color: "transparent"
+ clip: true
+ Rectangle {
+ width: currentAccountButton.width
+ height: currentAccountButton.height
+ radius: 10
+ color: "white"
+ opacity: 0.2
+ visible: accountBtnMouseArea.containsMouse
+ }
+ }
+ Rectangle {
+ width: currentAccountButton.width / 2
+ height: currentAccountButton.height / 2
+ anchors.bottom: leftHoverContainer.bottom
+ color: "white"
+ opacity: 0.2
+ visible: accountBtnMouseArea.containsMouse
+ }
+ Rectangle {
+ width: currentAccountButton.width / 2
+ height: currentAccountButton.height / 2
+ anchors.right: leftHoverContainer.right
+ color: "white"
+ opacity: 0.2
+ visible: accountBtnMouseArea.containsMouse
+ }
+ Rectangle {
+ width: currentAccountButton.width / 2
+ height: currentAccountButton.height / 2
+ anchors.right: leftHoverContainer.right
+ anchors.bottom: leftHoverContainer.bottom
+ color: "white"
+ opacity: 0.2
+ visible: accountBtnMouseArea.containsMouse
+ }
+ }
+
+ RowLayout {
+ id: accountControlRowLayout
+ height: currentAccountButton.height
+ width: currentAccountButton.width
+ spacing: 0
+ Image {
+ id: currentAccountAvatar
+ Layout.leftMargin: 8
+ verticalAlignment: Qt.AlignCenter
+ cache: false
+ source: "image://avatars/currentUser"
+ Layout.preferredHeight: (trayWindowHeaderBackground.height -16)
+ Layout.preferredWidth: (trayWindowHeaderBackground.height -16)
+ Rectangle {
+ id: currentAccountStateIndicatorBackground
+ width: currentAccountStateIndicator.sourceSize.width + 2
+ height: width
+ anchors.bottom: currentAccountAvatar.bottom
+ anchors.right: currentAccountAvatar.right
+ color: "#0082c9"
+ radius: width*0.5
+ }
+ Image {
+ id: currentAccountStateIndicator
+ source: userModelBackend.isUserConnected(userModelBackend.currentUserId()) ? "qrc:///client/theme/colored/state-ok.svg" : "qrc:///client/theme/colored/state-offline.svg"
+ cache: false
+ x: currentAccountStateIndicatorBackground.x + 1
+ y: currentAccountStateIndicatorBackground.y + 1
+ sourceSize.width: 16
+ sourceSize.height: 16
+ }
+ }
+
+ Column {
+ id: accountLabels
+ spacing: 4
+ Layout.alignment: Qt.AlignLeft
+ Layout.leftMargin: 6
+ Label {
+ id: currentAccountUser
+ width: 128
+ text: userModelBackend.currentUserName()
+ elide: Text.ElideRight
+ color: "white"
+ font.pixelSize: 12
+ font.bold: true
+ }
+ Label {
+ id: currentAccountServer
+ width: 128
+ text: userModelBackend.currentUserServer()
+ elide: Text.ElideRight
+ color: "white"
+ font.pixelSize: 10
+ }
+ }
+
+ Image {
+ Layout.alignment: Qt.AlignRight
+ verticalAlignment: Qt.AlignCenter
+ Layout.margins: 8
+ source: "qrc:///client/theme/white/caret-down.svg"
+ sourceSize.width: 20
+ sourceSize.height: 20
+ }
+ }
+ }
+
+ Item {
+ id: trayWindowHeaderSpacer
+ Layout.fillWidth: true
+ }
+
+ Button {
+ id: openLocalFolderButton
+ Layout.alignment: Qt.AlignRight
+ display: AbstractButton.IconOnly
+ Layout.preferredWidth: (trayWindowHeaderBackground.height)
+ Layout.preferredHeight: (trayWindowHeaderBackground.height)
+ flat: true
+
+ icon.source: "qrc:///client/theme/white/folder.svg"
+ icon.color: "transparent"
+
+ MouseArea {
+ id: folderBtnMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked:
+ {
+ userModelBackend.openCurrentAccountLocalFolder();
+ }
+ }
+
+ background:
+ Rectangle {
+ color: folderBtnMouseArea.containsMouse ? "white" : "transparent"
+ opacity: 0.2
+ }
+ }
+
+ Button {
+ id: trayWindowTalkButton
+ Layout.alignment: Qt.AlignRight
+ display: AbstractButton.IconOnly
+ Layout.preferredWidth: (trayWindowHeaderBackground.height)
+ Layout.preferredHeight: (trayWindowHeaderBackground.height)
+ flat: true
+ visible: userModelBackend.currentServerHasTalk() ? true : false
+
+ icon.source: "qrc:///client/theme/white/talk-app.svg"
+ icon.color: "transparent"
+
+ MouseArea {
+ id: talkBtnMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked:
+ {
+ userModelBackend.openCurrentAccountTalk();
+ }
+ }
+
+ background:
+ Rectangle {
+ color: talkBtnMouseArea.containsMouse ? "white" : "transparent"
+ opacity: 0.2
+ }
+ }
+
+ Button {
+ id: trayWindowAppsButton
+ Layout.alignment: Qt.AlignRight
+ display: AbstractButton.IconOnly
+ Layout.preferredWidth: (trayWindowHeaderBackground.height)
+ Layout.preferredHeight: (trayWindowHeaderBackground.height)
+ flat: true
+
+ icon.source: "qrc:///client/theme/white/more-apps.svg"
+ icon.color: "transparent"
+
+ MouseArea {
+ id: appsBtnMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked:
+ {
+ /*
+ // The count() property was introduced in QtQuick.Controls 2.3 (Qt 5.10)
+ // so we handle this with userModelBackend.openCurrentAccountServer()
+ //
+ // See UserModel::openCurrentAccountServer() to disable this workaround
+ // in the future for Qt >= 5.10
+
+ if(appsMenu.count() > 0) {
+ appsMenu.popup();
+ } else {
+ userModelBackend.openCurrentAccountServer();
+ }
+ */
+
+ appsMenu.open();
+ userModelBackend.openCurrentAccountServer();
+ }
+
+ Menu {
+ id: appsMenu
+ y: (trayWindowAppsButton.y + trayWindowAppsButton.height + 2)
+ width: (trayWindowAppsButton.width * 3)
+ closePolicy: "CloseOnPressOutside"
+
+ background: Rectangle {
+ border.color: "#0082c9"
+ radius: 2
+ }
+
+ Instantiator {
+ id: appsMenuInstantiator
+ model: appsMenuModelBackend
+ onObjectAdded: appsMenu.insertItem(index, object)
+ onObjectRemoved: appsMenu.removeItem(object)
+ delegate: MenuItem {
+ text: appName
+ font.pixelSize: 12
+ icon.source: appIconUrl
+ onTriggered: appsMenuModelBackend.openAppUrl(appUrl)
+ }
+ }
+ }
+ }
+
+ background:
+ Item {
+ id: rightHoverContainer
+ height: trayWindowAppsButton.height
+ width: trayWindowAppsButton.width
+ Rectangle {
+ width: trayWindowAppsButton.width / 2
+ height: trayWindowAppsButton.height / 2
+ color: "white"
+ opacity: 0.2
+ visible: appsBtnMouseArea.containsMouse
+ }
+ Rectangle {
+ width: trayWindowAppsButton.width / 2
+ height: trayWindowAppsButton.height / 2
+ anchors.bottom: rightHoverContainer.bottom
+ color: "white"
+ opacity: 0.2
+ visible: appsBtnMouseArea.containsMouse
+ }
+ Rectangle {
+ width: trayWindowAppsButton.width / 2
+ height: trayWindowAppsButton.height / 2
+ anchors.bottom: rightHoverContainer.bottom
+ anchors.right: rightHoverContainer.right
+ color: "white"
+ opacity: 0.2
+ visible: appsBtnMouseArea.containsMouse
+ }
+ Rectangle {
+ id: rightHoverContainerClipper
+ anchors.right: rightHoverContainer.right
+ width: trayWindowAppsButton.width / 2
+ height: trayWindowAppsButton.height / 2
+ color: "transparent"
+ clip: true
+ Rectangle {
+ width: trayWindowAppsButton.width
+ height: trayWindowAppsButton.height
+ anchors.right: rightHoverContainerClipper.right
+ radius: 10
+ color: "white"
+ opacity: 0.2
+ visible: appsBtnMouseArea.containsMouse
+ }
+ }
+ }
+ }
+ }
+ } // Rectangle trayWindowHeaderBackground
+
+ ListView {
+ id: activityListView
+ anchors.top: trayWindowHeaderBackground.bottom
+ width: trayWindowBackground.width
+ height: trayWindowBackground.height - trayWindowHeaderBackground.height
+ clip: true
+ ScrollBar.vertical: ScrollBar {
+ id: listViewScrollbar
+ }
+
+ model: activityModel
+
+ delegate: RowLayout {
+ id: activityItem
+ width: activityListView.width
+ height: trayWindowHeaderLayout.height
+ spacing: 0
+
+ Image {
+ id: activityIcon
+ Layout.leftMargin: 8
+ Layout.rightMargin: 8
+ Layout.preferredWidth: activityButton1.icon.width
+ Layout.preferredHeight: activityButton1.icon.height
+ verticalAlignment: Qt.AlignCenter
+ cache: true
+ source: icon
+ sourceSize.height: 64
+ sourceSize.width: 64
+ }
+ Column {
+ id: activityTextColumn
+ spacing: 4
+ Layout.alignment: Qt.AlignLeft
+ Text {
+ id: activityTextTitle
+ text: (type === "Activity" || type === "Notification") ? subject : message
+ width: 240 + ((path === "") ? activityItem.height : 0) + ((link === "") ? activityItem.height : 0) - 8
+ elide: Text.ElideRight
+ font.pixelSize: 12
+ color: activityTextTitleColor
+ }
+
+ Text {
+ id: activityTextInfo
+ text: (type === "Activity" || type === "File" || type === "Sync") ? displaypath : message
+ height: (text === "") ? 0 : activityTextTitle.height
+ width: 240 + ((path === "") ? activityItem.height : 0) + ((link === "") ? activityItem.height : 0) - 8
+ elide: Text.ElideRight
+ font.pixelSize: 10
+ }
+
+ Text {
+ id: activityTextDateTime
+ text: dateTime
+ height: (text === "") ? 0 : activityTextTitle.height
+ width: 240 + ((path === "") ? activityItem.height : 0) + ((link === "") ? activityItem.height : 0) - 8
+ elide: Text.ElideRight
+ font.pixelSize: 10
+ color: "#808080"
+ }
+ }
+ Item {
+ id: activityItemFiller
+ Layout.fillWidth: true
+ }
+ Button {
+ id: activityButton1
+ Layout.preferredWidth: (path === "") ? 0 : activityItem.height
+ Layout.preferredHeight: activityItem.height
+ Layout.alignment: Qt.AlignRight
+ flat: true
+ hoverEnabled: false
+ visible: (path === "") ? false : true
+ display: AbstractButton.IconOnly
+ icon.source: "qrc:///client/resources/files.svg"
+ icon.color: "transparent"
+
+ onClicked: {
+ Qt.openUrlExternally(path)
+ }
+ }
+ Button {
+ id: activityButton2
+ Layout.preferredWidth: (link === "") ? 0 : activityItem.height
+ Layout.preferredHeight: activityItem.height
+ Layout.alignment: Qt.AlignRight
+ flat: true
+ hoverEnabled: false
+ visible: (link === "") ? false : true
+ display: AbstractButton.IconOnly
+ icon.source: "qrc:///client/resources/public.svg"
+ icon.color: "transparent"
+
+ onClicked: {
+ Qt.openUrlExternally(link)
+ }
+ }
+ }
+
+ /*add: Transition {
+ NumberAnimation { properties: "y"; from: -60; duration: 100; easing.type: Easing.Linear }
+ }
+
+ remove: Transition {
+ NumberAnimation { property: "opacity"; from: 1.0; to: 0; duration: 100 }
+ }
+
+ removeDisplaced: Transition {
+ SequentialAnimation {
+ PauseAnimation { duration: 100}
+ NumberAnimation { properties: "y"; duration: 100; easing.type: Easing.Linear }
+ }
+ }
+
+ displaced: Transition {
+ NumberAnimation { properties: "y"; duration: 100; easing.type: Easing.Linear }
+ }*/
+ }
+
+ } // Rectangle trayWindowBackground
+}
diff --git a/src/libsync/capabilities.cpp b/src/libsync/capabilities.cpp
index 39df7d1677f8..d0c5f727f7db 100644
--- a/src/libsync/capabilities.cpp
+++ b/src/libsync/capabilities.cpp
@@ -103,7 +103,8 @@ bool Capabilities::isValid() const
return !_capabilities.isEmpty();
}
-bool Capabilities::hasActivities() const {
+bool Capabilities::hasActivities() const
+{
return _capabilities.contains("activity");
}
diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp
index c728172e6a8a..03d1dbf7244a 100644
--- a/src/libsync/owncloudpropagator.cpp
+++ b/src/libsync/owncloudpropagator.cpp
@@ -376,14 +376,14 @@ void OwncloudPropagator::start(const SyncFileItemVector &items,
Q_ASSERT(std::is_sorted(items.begin(), items.end()));
} else if (hasChange) {
Q_ASSERT(std::is_sorted(items.begin(), items.end(),
- [](const SyncFileItemVector::const_reference &a, const SyncFileItemVector::const_reference &b) -> bool {
+ [](SyncFileItemVector::const_reference &a, SyncFileItemVector::const_reference &b) -> bool {
return ((a->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE) && (b->_instruction != CSYNC_INSTRUCTION_TYPE_CHANGE));
}));
Q_ASSERT(std::is_sorted(items.begin(), items.begin() + lastChangeInstruction));
if (hasDelete) {
Q_ASSERT(std::is_sorted(items.begin() + (lastChangeInstruction + 1), items.end(),
- [](const SyncFileItemVector::const_reference &a, const SyncFileItemVector::const_reference &b) -> bool {
+ [](SyncFileItemVector::const_reference &a, SyncFileItemVector::const_reference &b) -> bool {
return ((a->_instruction == CSYNC_INSTRUCTION_REMOVE) && (b->_instruction != CSYNC_INSTRUCTION_REMOVE));
}));
Q_ASSERT(std::is_sorted(items.begin() + (lastChangeInstruction + 1), items.begin() + lastDeleteInstruction));
diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp
index 24732d9753b0..42e45b5c8596 100644
--- a/src/libsync/syncengine.cpp
+++ b/src/libsync/syncengine.cpp
@@ -1078,7 +1078,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
// Get CHANGE instructions to the top first
if (syncItems.count() > 0) {
std::sort(syncItems.begin(), syncItems.end(),
- [](const SyncFileItemVector::const_reference &a, const SyncFileItemVector::const_reference &b) -> bool {
+ [](SyncFileItemVector::const_reference &a, SyncFileItemVector::const_reference &b) -> bool {
return ((a->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE) && (b->_instruction != CSYNC_INSTRUCTION_TYPE_CHANGE));
});
if (syncItems.at(0)->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE) {
@@ -1089,7 +1089,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
std::sort(syncItems.begin(), syncItems.begin() + lastChangeInstruction);
if (syncItems.count() > lastChangeInstruction) {
std::sort(syncItems.begin() + (lastChangeInstruction + 1), syncItems.end(),
- [](const SyncFileItemVector::const_reference &a, const SyncFileItemVector::const_reference &b) -> bool {
+ [](SyncFileItemVector::const_reference &a, SyncFileItemVector::const_reference &b) -> bool {
return ((a->_instruction == CSYNC_INSTRUCTION_REMOVE) && (b->_instruction != CSYNC_INSTRUCTION_REMOVE));
});
if (syncItems.at(lastChangeInstruction + 1)->_instruction == CSYNC_INSTRUCTION_REMOVE) {
diff --git a/src/libsync/theme.cpp b/src/libsync/theme.cpp
index 8b2b15e5c061..08bf1152180f 100644
--- a/src/libsync/theme.cpp
+++ b/src/libsync/theme.cpp
@@ -123,11 +123,11 @@ QIcon Theme::applicationIcon() const
* helper to load a icon from either the icon theme the desktop provides or from
* the apps Qt resources.
*/
-QIcon Theme::themeIcon(const QString &name, bool sysTray, bool sysTrayMenuVisible) const
+QIcon Theme::themeIcon(const QString &name, bool sysTray) const
{
QString flavor;
if (sysTray) {
- flavor = systrayIconFlavor(_mono, sysTrayMenuVisible);
+ flavor = systrayIconFlavor(_mono);
} else {
flavor = QLatin1String("colored");
}
@@ -170,7 +170,7 @@ QIcon Theme::themeIcon(const QString &name, bool sysTray, bool sysTrayMenuVisibl
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
// This defines the icon as a template and enables automatic macOS color handling
// See https://bugreports.qt.io/browse/QTBUG-42109
- cached.setIsMask(_mono && sysTray && !sysTrayMenuVisible);
+ cached.setIsMask(_mono && sysTray);
#endif
#endif
@@ -270,18 +270,11 @@ QString Theme::defaultClientFolder() const
return appName();
}
-QString Theme::systrayIconFlavor(bool mono, bool sysTrayMenuVisible) const
+QString Theme::systrayIconFlavor(bool mono) const
{
- Q_UNUSED(sysTrayMenuVisible)
QString flavor;
if (mono) {
flavor = Utility::hasDarkSystray() ? QLatin1String("white") : QLatin1String("black");
-
-#ifdef Q_OS_MAC
- if (sysTrayMenuVisible) {
- flavor = QLatin1String("white");
- }
-#endif
} else {
flavor = QLatin1String("colored");
}
@@ -395,7 +388,7 @@ QVariant Theme::customMedia(CustomMediaType type)
return re;
}
-QIcon Theme::syncStateIcon(SyncResult::Status status, bool sysTray, bool sysTrayMenuVisible) const
+QIcon Theme::syncStateIcon(SyncResult::Status status, bool sysTray) const
{
// FIXME: Mind the size!
QString statusIcon;
@@ -427,7 +420,7 @@ QIcon Theme::syncStateIcon(SyncResult::Status status, bool sysTray, bool sysTray
statusIcon = QLatin1String("state-error");
}
- return themeIcon(statusIcon, sysTray, sysTrayMenuVisible);
+ return themeIcon(statusIcon, sysTray);
}
QIcon Theme::folderDisabledIcon() const
@@ -435,9 +428,9 @@ QIcon Theme::folderDisabledIcon() const
return themeIcon(QLatin1String("state-pause"));
}
-QIcon Theme::folderOfflineIcon(bool sysTray, bool sysTrayMenuVisible) const
+QIcon Theme::folderOfflineIcon(bool sysTray) const
{
- return themeIcon(QLatin1String("state-offline"), sysTray, sysTrayMenuVisible);
+ return themeIcon(QLatin1String("state-offline"), sysTray);
}
QColor Theme::wizardHeaderTitleColor() const
diff --git a/src/libsync/theme.h b/src/libsync/theme.h
index 966c9b5c9e8f..12a643d0099d 100644
--- a/src/libsync/theme.h
+++ b/src/libsync/theme.h
@@ -93,10 +93,10 @@ class OWNCLOUDSYNC_EXPORT Theme : public QObject
/**
* get an sync state icon
*/
- virtual QIcon syncStateIcon(SyncResult::Status, bool sysTray = false, bool sysTrayMenuVisible = false) const;
+ virtual QIcon syncStateIcon(SyncResult::Status, bool sysTray = false) const;
virtual QIcon folderDisabledIcon() const;
- virtual QIcon folderOfflineIcon(bool sysTray = false, bool sysTrayMenuVisible = false) const;
+ virtual QIcon folderOfflineIcon(bool sysTray = false) const;
virtual QIcon applicationIcon() const;
#endif
@@ -166,7 +166,7 @@ class OWNCLOUDSYNC_EXPORT Theme : public QObject
virtual QString enforcedLocale() const { return QString(); }
/** colored, white or black */
- QString systrayIconFlavor(bool mono, bool sysTrayMenuVisible = false) const;
+ QString systrayIconFlavor(bool mono) const;
#ifndef TOKEN_AUTH_ONLY
/**
@@ -449,7 +449,7 @@ class OWNCLOUDSYNC_EXPORT Theme : public QObject
protected:
#ifndef TOKEN_AUTH_ONLY
- QIcon themeIcon(const QString &name, bool sysTray = false, bool sysTrayMenuVisible = false) const;
+ QIcon themeIcon(const QString &name, bool sysTray = false) const;
#endif
Theme();
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 55e5327e6887..501ec9039af5 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -66,6 +66,8 @@ list(APPEND FolderMan_SRC ../src/gui/guiutility.cpp )
list(APPEND FolderMan_SRC ../src/gui/navigationpanehelper.cpp )
list(APPEND FolderMan_SRC ../src/gui/connectionvalidator.cpp )
list(APPEND FolderMan_SRC ../src/gui/clientproxy.cpp )
+list(APPEND FolderMan_SRC ../src/gui/ocsjob.cpp )
+list(APPEND FolderMan_SRC ../src/gui/ocsnavigationappsjob.cpp )
list(APPEND FolderMan_SRC ../src/gui/accountstate.cpp )
list(APPEND FolderMan_SRC ../src/gui/remotewipe.cpp )
list(APPEND FolderMan_SRC ${FolderWatcher_SRC})
@@ -76,11 +78,13 @@ SET(RemoteWipe_SRC ../src/gui/remotewipe.cpp)
list(APPEND RemoteWipe_SRC ../src/gui/clientproxy.cpp )
list(APPEND RemoteWipe_SRC ../src/gui/guiutility.cpp )
list(APPEND RemoteWipe_SRC ../src/gui/connectionvalidator.cpp )
+list(APPEND RemoteWipe_SRC ../src/gui/ocsjob.cpp )
+list(APPEND RemoteWipe_SRC ../src/gui/ocsnavigationappsjob.cpp )
list(APPEND RemoteWipe_SRC ../src/gui/accountstate.cpp )
list(APPEND RemoteWipe_SRC ../src/gui/socketapi.cpp )
list(APPEND RemoteWipe_SRC ../src/gui/folder.cpp )
list(APPEND RemoteWipe_SRC ../src/gui/syncrunfilelog.cpp )
-list(APPEND RemoteWipe_SRC ../src/gui/folderwatcher_linux.cpp )
+list(APPEND RemoteWipe_SRC ${FolderWatcher_SRC} )
list(APPEND RemoteWipe_SRC ../src/gui/folderwatcher.cpp )
list(APPEND RemoteWipe_SRC ${RemoteWipe_SRC})
list(APPEND RemoteWipe_SRC stubremotewipe.cpp )
diff --git a/theme.qrc b/theme.qrc
index 006056b87bee..2a2881e96302 100644
--- a/theme.qrc
+++ b/theme.qrc
@@ -43,7 +43,7 @@
theme/white/state-sync-64.png
theme/white/state-sync-128.png
theme/white/state-sync-256.png
- theme/black/state-error-32.png
+ theme/black/state-error-32.png
theme/black/state-error-64.png
theme/black/state-error-128.png
theme/black/state-error-256.png
@@ -80,7 +80,7 @@
theme/colored/state-warning-128.png
theme/colored/state-warning-256.png
theme/black/control-next.svg
- theme/black/control-prev.svg
+ theme/black/control-prev.svg
theme/black/state-error.svg
theme/black/state-error-16.png
theme/black/state-offline.svg
@@ -101,8 +101,8 @@
theme/black/state-warning-64.png
theme/black/state-warning-128.png
theme/black/state-warning-256.png
- theme/white/control-next.svg
- theme/white/control-prev.svg
+ theme/white/control-next.svg
+ theme/white/control-prev.svg
theme/white/state-error.svg
theme/white/state-error-16.png
theme/white/state-offline.svg
@@ -131,5 +131,17 @@
theme/colored/wizard-nextcloud@2x.png
theme/colored/wizard-talk.png
theme/colored/wizard-talk@2x.png
+ theme/white/folder.svg
+ theme/white/more-apps.svg
+ theme/white/talk-app.svg
+ theme/white/caret-down.svg
+ theme/black/caret-down.svg
+ theme/white/user.svg
+ theme/black/user.svg
+ theme/white/add.svg
+ theme/black/add.svg
+ theme/black/activity.svg
+ theme/black/bell.svg
+ theme/black/state-info.svg
diff --git a/theme/black/activity.svg b/theme/black/activity.svg
new file mode 100644
index 000000000000..b6282ba77d28
--- /dev/null
+++ b/theme/black/activity.svg
@@ -0,0 +1,2 @@
+
diff --git a/theme/black/add.svg b/theme/black/add.svg
new file mode 100644
index 000000000000..4b2d2133078e
--- /dev/null
+++ b/theme/black/add.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/theme/black/bell.svg b/theme/black/bell.svg
new file mode 100644
index 000000000000..9de878f8d313
--- /dev/null
+++ b/theme/black/bell.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/theme/black/caret-down.svg b/theme/black/caret-down.svg
new file mode 100644
index 000000000000..bc3a4e0c5063
--- /dev/null
+++ b/theme/black/caret-down.svg
@@ -0,0 +1 @@
+
diff --git a/theme/black/state-info.svg b/theme/black/state-info.svg
new file mode 100644
index 000000000000..762de0370ff5
--- /dev/null
+++ b/theme/black/state-info.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/theme/black/user.svg b/theme/black/user.svg
new file mode 100644
index 000000000000..14aef2cd9803
--- /dev/null
+++ b/theme/black/user.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/theme/white/add.svg b/theme/white/add.svg
new file mode 100644
index 000000000000..0604ed0994b2
--- /dev/null
+++ b/theme/white/add.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/theme/white/caret-down.svg b/theme/white/caret-down.svg
new file mode 100644
index 000000000000..c97d6e264074
--- /dev/null
+++ b/theme/white/caret-down.svg
@@ -0,0 +1 @@
+
diff --git a/theme/white/folder.svg b/theme/white/folder.svg
new file mode 100644
index 000000000000..003e8b3fb80d
--- /dev/null
+++ b/theme/white/folder.svg
@@ -0,0 +1 @@
+
diff --git a/theme/white/more-apps.svg b/theme/white/more-apps.svg
new file mode 100644
index 000000000000..ee5b522769f8
--- /dev/null
+++ b/theme/white/more-apps.svg
@@ -0,0 +1 @@
+
diff --git a/theme/white/talk-app.svg b/theme/white/talk-app.svg
new file mode 100644
index 000000000000..867f2ec449b5
--- /dev/null
+++ b/theme/white/talk-app.svg
@@ -0,0 +1 @@
+
diff --git a/theme/white/user.svg b/theme/white/user.svg
new file mode 100644
index 000000000000..991242ce44ba
--- /dev/null
+++ b/theme/white/user.svg
@@ -0,0 +1 @@
+
\ No newline at end of file