From 1cedb1919f29a59f723736106dd6870a298b89de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Sun, 23 Jul 2017 20:58:00 +0200 Subject: [PATCH] Integrate libcloudproviders support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit integrates support for libcloudproviders desktop integration API. If build with the library it will check on startup if the DBus interface is available and then use it instead of the legacy status icon. Signed-off-by: Julius Härtl --- CMakeLists.txt | 3 + cmake/modules/DBusMacros.cmake | 0 cmake/modules/FindGLib2.cmake | 35 ++ cmake/modules/FindGio.cmake | 61 +++ cmake/modules/FindLibcloudproviders.cmake | 22 ++ shell_integration/CMakeLists.txt | 2 + .../libcloudproviders/CMakeLists.txt | 57 +++ .../org.freedesktop.CloudProviders.ini.in | 4 + .../org.freedesktop.CloudProviders.service.in | 4 + src/gui/CMakeLists.txt | 28 ++ src/gui/application.cpp | 3 + .../cloudproviders/cloudproviderconfig.h.in | 21 ++ .../cloudproviders/cloudprovidermanager.cpp | 71 ++++ src/gui/cloudproviders/cloudprovidermanager.h | 41 ++ .../cloudproviders/cloudproviderwrapper.cpp | 349 ++++++++++++++++++ src/gui/cloudproviders/cloudproviderwrapper.h | 66 ++++ src/gui/folderman.cpp | 1 + src/gui/owncloudgui.cpp | 29 ++ src/gui/owncloudgui.h | 7 + 19 files changed, 804 insertions(+) create mode 100644 cmake/modules/DBusMacros.cmake create mode 100644 cmake/modules/FindGLib2.cmake create mode 100644 cmake/modules/FindGio.cmake create mode 100644 cmake/modules/FindLibcloudproviders.cmake create mode 100644 shell_integration/libcloudproviders/CMakeLists.txt create mode 100644 shell_integration/libcloudproviders/org.freedesktop.CloudProviders.ini.in create mode 100644 shell_integration/libcloudproviders/org.freedesktop.CloudProviders.service.in create mode 100644 src/gui/cloudproviders/cloudproviderconfig.h.in create mode 100644 src/gui/cloudproviders/cloudprovidermanager.cpp create mode 100644 src/gui/cloudproviders/cloudprovidermanager.h create mode 100644 src/gui/cloudproviders/cloudproviderwrapper.cpp create mode 100644 src/gui/cloudproviders/cloudproviderwrapper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 67fef50e2..a4bb50cb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,6 +200,9 @@ if(BUILD_CLIENT) endif() find_package(ZLIB REQUIRED) + find_package(GLib2) + find_package(Gio) + find_package(Libcloudproviders) endif() if (NOT DEFINED APPLICATION_ICON_NAME) diff --git a/cmake/modules/DBusMacros.cmake b/cmake/modules/DBusMacros.cmake new file mode 100644 index 000000000..e69de29bb diff --git a/cmake/modules/FindGLib2.cmake b/cmake/modules/FindGLib2.cmake new file mode 100644 index 000000000..3010655ea --- /dev/null +++ b/cmake/modules/FindGLib2.cmake @@ -0,0 +1,35 @@ +# FindGLib2.cmake + +# GLib2_FOUND - System has GLib2 +# GLib2_INCLUDES - The GLib2 include directories +# GLib2_LIBRARIES - The libraries needed to use GLib2 +# GLib2_DEFINITIONS - Compiler switches required for using GLib2 + +find_package(PkgConfig) +pkg_check_modules(GLib2 QUIET glib-2.0) +set(GLib2_DEFINITIONS ${GLib2_CFLAGS_OTHER}) + +find_path(GLib2_INCLUDE_DIR + NAMES glib.h glib-object.h + HINTS ${GLib2_INCLUDEDIR} ${GLib2_INCLUDE_DIRS} + PATH_SUFFIXES glib-2.0) +find_path(GLIBCONFIG_INCLUDE_DIR + NAMES glibconfig.h + HINTS ${LIBDIR} ${LIBRARY_DIRS} ${_GLib2_LIBRARY_DIR} + ${GLib2_INCLUDEDIR} ${GLib2_INCLUDE_DIRS} + ${CMAKE_EXTRA_INCLUDES} ${CMAKE_EXTRA_LIBRARIES} + PATH_SUFFIXES glib-2.0 glib-2.0/include) +list(APPEND GLib2_INCLUDE_DIR ${GLIBCONFIG_INCLUDE_DIR}) + +find_library(GLib2_LIBRARY + NAMES glib-2.0 libglib-2.0 + HINTS ${GLib2_LIBDIR} ${GLib2_LIBRARY_DIRS}) + +set(GLib2_LIBRARIES ${GLib2_LIBRARY}) +set(GLib2_INCLUDE_DIRS ${GLib2_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GLib2 DEFAULT_MSG + GLib2_LIBRARY GLib2_INCLUDE_DIR) + +mark_as_advanced(GLib2_INCLUDE_DIR GLib2_LIBRARY) diff --git a/cmake/modules/FindGio.cmake b/cmake/modules/FindGio.cmake new file mode 100644 index 000000000..f01bbb736 --- /dev/null +++ b/cmake/modules/FindGio.cmake @@ -0,0 +1,61 @@ +# - Try to find Gio +# Once done this will define +# +# GIO_FOUND - system has Gio +# GIO_INCLUDE_DIR - the Gio include directory +# GIO_LIBRARIES - the libraries needed to use Gio +# GIO_DEFINITIONS - Compiler switches required for using Gio + + +IF (GIO_INCLUDE_DIR AND GIO_LIBRARIES) + # in cache already + SET(Gio_FIND_QUIETLY TRUE) +ELSE (GIO_INCLUDE_DIR AND GIO_LIBRARIES) + SET(Gio_FIND_QUIETLY FALSE) +ENDIF (GIO_INCLUDE_DIR AND GIO_LIBRARIES) + +IF (NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + FIND_PACKAGE(PkgConfig) + PKG_CHECK_MODULES(GIO gio-2.0) + #MESSAGE(STATUS "DEBUG: Gio include directory = ${GIO_INCLUDE_DIRS}") + #MESSAGE(STATUS "DEBUG: Gio link directory = ${GIO_LIBRARY_DIRS}") + #MESSAGE(STATUS "DEBUG: Gio CFlags = ${GIO_CFLAGS}") + SET(GIO_DEFINITIONS ${GIO_CFLAGS_OTHER}) +ENDIF (NOT WIN32) + +FIND_PATH(GIO_INCLUDE_DIR gio.h + PATHS + ${GIO_INCLUDEDIR} + ${GIO_INCLUDE_DIRS} + PATH_SUFFIXES glib-2.0/gio/ + ) + +FIND_LIBRARY(_GioLibs NAMES gio-2.0 libgio-2.0 + PATHS + ${GIO_LIBDIR} + ${GIO_LIBRARY_DIRS} + ) + +SET( GIO_LIBRARIES ${_GioLibs} ) +SET( GIO_INCLUDE_DIRS ${GIO_INCLUDE_DIR} ) + +IF (GIO_INCLUDE_DIR AND GIO_LIBRARIES) + SET(GIO_FOUND TRUE) +ELSE (GIO_INCLUDE_DIR AND GIO_LIBRARIES) + SET(GIO_FOUND FALSE) +ENDIF (GIO_INCLUDE_DIR AND GIO_LIBRARIES) + +IF (GIO_FOUND) + IF (NOT Gio_FIND_QUIETLY) + MESSAGE(STATUS "Found Gio libraries: ${GIO_LIBRARIES}") + MESSAGE(STATUS "Found Gio includes : ${GIO_INCLUDE_DIR}") + ENDIF (NOT Gio_FIND_QUIETLY) +ELSE (GIO_FOUND) + IF (Gio_FIND_REQUIRED) + MESSAGE(STATUS "Could NOT find Gio") + ENDIF(Gio_FIND_REQUIRED) +ENDIF (GIO_FOUND) + +MARK_AS_ADVANCED(GIO_INCLUDE_DIR _GioLibs) diff --git a/cmake/modules/FindLibcloudproviders.cmake b/cmake/modules/FindLibcloudproviders.cmake new file mode 100644 index 000000000..6392166e3 --- /dev/null +++ b/cmake/modules/FindLibcloudproviders.cmake @@ -0,0 +1,22 @@ +# FindLibcloudproviders.cmake + +find_path(LIBCLOUDPROVIDERS_INCLUDE_DIR + NAMES cloudprovidersproviderexporter.h cloudprovidersaccountexporter.h + PATH_SUFFIXES cloudproviders +) +find_library(LIBCLOUDPROVIDERS_LIBRARY + NAMES + libcloudproviders + cloudproviders + HINTS + /usr/lib + /usr/lib/${CMAKE_ARCH_TRIPLET} + /usr/local/lib + /opt/local/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib +) + +message("================> ${LIBCLOUDPROVIDERS_LIBRARY}") + +find_package_handle_standard_args(LIBCLOUDPROVIDERS DEFAULT_MSG LIBCLOUDPROVIDERS_INCLUDE_DIR LIBCLOUDPROVIDERS_LIBRARY) diff --git a/shell_integration/CMakeLists.txt b/shell_integration/CMakeLists.txt index 87c453bf2..f428fccb1 100644 --- a/shell_integration/CMakeLists.txt +++ b/shell_integration/CMakeLists.txt @@ -20,4 +20,6 @@ if( UNIX AND NOT APPLE ) message("Dolphin plugin disabled: KDE Frameworks 5.16 not found") endif() endif() + add_subdirectory(libcloudproviders) endif() + diff --git a/shell_integration/libcloudproviders/CMakeLists.txt b/shell_integration/libcloudproviders/CMakeLists.txt new file mode 100644 index 000000000..8a6cab2fa --- /dev/null +++ b/shell_integration/libcloudproviders/CMakeLists.txt @@ -0,0 +1,57 @@ +include(UsePkgConfig) + +MACRO(PKGCONFIG_GETVAR _package _var _output_variable) + SET(${_output_variable}) + + # if pkg-config has been found + IF (PKGCONFIG_EXECUTABLE) + + EXEC_PROGRAM(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --exists RETURN_VALUE _return_VALUE OUTPUT_VARIABLE _pkgconfigDevNull) + + # and if the package of interest also exists for pkg-config, then get the information + IF (NOT _return_VALUE) + + EXEC_PROGRAM(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --variable ${_var} OUTPUT_VARIABLE ${_output_variable}) + + ENDIF (NOT _return_VALUE) + + ENDIF (PKGCONFIG_EXECUTABLE) +ENDMACRO(PKGCONFIG_GETVAR _package _var _output_variable) + +macro(dbus_add_activation_service _sources) + PKGCONFIG_GETVAR(dbus-1 session_bus_services_dir _install_dir) + foreach (_i ${_sources}) + get_filename_component(_service_file ${_i} ABSOLUTE) + string(REGEX REPLACE "\\.service.*$" ".service" _output_file ${_i}) + set(_target ${CMAKE_CURRENT_BINARY_DIR}/${_output_file}) + configure_file(${_service_file} ${_target}) + install(FILES ${_target} DESTINATION ${_install_dir} RENAME "${LIBCLOUDPROVIDERS_DBUS_BUS_NAME}.service") + endforeach (_i ${ARGN}) +endmacro(dbus_add_activation_service _sources) + +macro(libcloudproviders_add_config _sources) + set(_install_dir "${CMAKE_INSTALL_PREFIX}/share/cloud-providers") + foreach (_i ${_sources}) + get_filename_component(_service_file ${_i} ABSOLUTE) + string(REGEX REPLACE "\\.ini.*$" ".ini" _output_file ${_i}) + set(_target ${CMAKE_CURRENT_BINARY_DIR}/${_output_file}) + configure_file(${_service_file} ${_target}) + install(FILES ${_target} DESTINATION ${_install_dir} RENAME "${LIBCLOUDPROVIDERS_DBUS_BUS_NAME}.ini") + endforeach (_i ${ARGN}) +endmacro(libcloudproviders_add_config _sources) + + +IF (UNIX AND WITH_DBUS AND LIBCLOUDPROVIDERS_FOUND) + STRING(TOLOWER "${APPLICATION_VENDOR}" DBUS_VENDOR) + STRING(REGEX REPLACE "[^A-z0-9]" "" DBUS_VENDOR "${DBUS_VENDOR}") + STRING(REGEX REPLACE "[^A-z0-9]" "" DBUS_APPLICATION_NAME "${APPLICATION_SHORTNAME}") + if (NOT DBUS_PREFIX) + set(DBUS_PREFIX "com") + endif () + + set(LIBCLOUDPROVIDERS_DBUS_BUS_NAME "${DBUS_PREFIX}.${DBUS_VENDOR}.${DBUS_APPLICATION_NAME}") + set(LIBCLOUDPROVIDERS_DBUS_OBJECT_PATH "/${DBUS_PREFIX}/${DBUS_VENDOR}/${DBUS_APPLICATION_NAME}") + + dbus_add_activation_service(org.freedesktop.CloudProviders.service.in) + libcloudproviders_add_config(org.freedesktop.CloudProviders.ini.in) +ENDIF () diff --git a/shell_integration/libcloudproviders/org.freedesktop.CloudProviders.ini.in b/shell_integration/libcloudproviders/org.freedesktop.CloudProviders.ini.in new file mode 100644 index 000000000..a7ef95837 --- /dev/null +++ b/shell_integration/libcloudproviders/org.freedesktop.CloudProviders.ini.in @@ -0,0 +1,4 @@ +[Cloud Providers] +BusName=@LIBCLOUDPROVIDERS_DBUS_BUS_NAME@ +ObjectPath=@LIBCLOUDPROVIDERS_DBUS_OBJECT_PATH@ +Version=1 diff --git a/shell_integration/libcloudproviders/org.freedesktop.CloudProviders.service.in b/shell_integration/libcloudproviders/org.freedesktop.CloudProviders.service.in new file mode 100644 index 000000000..90034767e --- /dev/null +++ b/shell_integration/libcloudproviders/org.freedesktop.CloudProviders.service.in @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=@LIBCLOUDPROVIDERS_DBUS_BUS_NAME@ +Exec=@APPLICATION_EXECUTABLE@ + diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 07f72925a..1ce5001b2 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -182,6 +182,20 @@ set(3rdparty_INC include_directories(${3rdparty_INC}) +IF( NOT WIN32 AND NOT APPLE AND WITH_DBUS AND LIBCLOUDPROVIDERS_FOUND) + message("Building with libcloudproviderssupport") + add_definitions(-DWITH_LIBCLOUDPROVIDERS) + set(client_SRCS ${client_SRCS} cloudproviders/cloudprovidermanager.cpp) + set(client_SRCS ${client_SRCS} cloudproviders/cloudproviderwrapper.cpp) + + include_directories(${GLib2_INCLUDE_DIRS}) + include_directories(${GIO_INCLUDE_DIRS}) + include_directories(${LIBCLOUDPROVIDERS_INCLUDE_DIR}) +ENDIF() + + + + # csync is required. include_directories(${CMAKE_SOURCE_DIR}/src/csync ${CMAKE_BINARY_DIR}/src/csync @@ -309,6 +323,20 @@ target_link_libraries( ${APPLICATION_EXECUTABLE} ${QT_LIBRARIES} ) target_link_libraries( ${APPLICATION_EXECUTABLE} ${synclib_NAME} ) target_link_libraries( ${APPLICATION_EXECUTABLE} updater ) target_link_libraries( ${APPLICATION_EXECUTABLE} ${OS_SPECIFIC_LINK_LIBRARIES} ) +IF( LIBCLOUDPROVIDERS_FOUND ) + string(TOLOWER "${APPLICATION_VENDOR}" DBUS_VENDOR) + string(REGEX REPLACE "[^A-z0-9]" "" DBUS_VENDOR "${DBUS_VENDOR}") + string(REGEX REPLACE "[^A-z0-9]" "" DBUS_APPLICATION_NAME "${APPLICATION_SHORTNAME}") + if(NOT DBUS_PREFIX) + set(DBUS_PREFIX "com") + endif(NOT DBUS_PREFIX) + set(LIBCLOUDPROVIDERS_DBUS_BUS_NAME "${DBUS_PREFIX}.${DBUS_VENDOR}.${DBUS_APPLICATION_NAME}") + set(LIBCLOUDPROVIDERS_DBUS_OBJECT_PATH "/${DBUS_PREFIX}/${DBUS_VENDOR}/${DBUS_APPLICATION_NAME}") + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cloudproviders/cloudproviderconfig.h.in ${CMAKE_CURRENT_BINARY_DIR}/cloudproviderconfig.h) + target_link_libraries( ${APPLICATION_EXECUTABLE} ${GLib2_LDFLAGS} ${GIO_LDFLAGS} ${LIBCLOUDPROVIDERS_LIBRARY} ) +ENDIF() + if(WITH_CRASHREPORTER) target_link_libraries( ${APPLICATION_EXECUTABLE} crashreporter-handler) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 8f4b3f1ba..cbdb8514c 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -201,6 +201,9 @@ Application::Application(int &argc, char **argv) if (_showLogWindow) { _gui->slotToggleLogBrowser(); // _showLogWindow is set in parseOptions. } +#if WITH_LIBCLOUDPROVIDERS + _gui->setupCloudProviders(); +#endif // Enable word wrapping of QInputDialog (#4197) setStyleSheet("QInputDialog QLabel { qproperty-wordWrap:1; }"); diff --git a/src/gui/cloudproviders/cloudproviderconfig.h.in b/src/gui/cloudproviders/cloudproviderconfig.h.in new file mode 100644 index 000000000..73384daf2 --- /dev/null +++ b/src/gui/cloudproviders/cloudproviderconfig.h.in @@ -0,0 +1,21 @@ +/* + * Copyright (C) by Julius Härtl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef CLOUDPROVIDERCONFIG_H_IN +#define CLOUDPROVIDERCONFIG_H_IN + +#cmakedefine LIBCLOUDPROVIDERS_DBUS_BUS_NAME "@LIBCLOUDPROVIDERS_DBUS_BUS_NAME@" +#cmakedefine LIBCLOUDPROVIDERS_DBUS_OBJECT_PATH "@LIBCLOUDPROVIDERS_DBUS_OBJECT_PATH@" + +#endif // CLOUDPROVIDERCONFIG_H_IN diff --git a/src/gui/cloudproviders/cloudprovidermanager.cpp b/src/gui/cloudproviders/cloudprovidermanager.cpp new file mode 100644 index 000000000..60d222850 --- /dev/null +++ b/src/gui/cloudproviders/cloudprovidermanager.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) by Julius Härtl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +extern "C" { + #include + #include + #include +} +#include "cloudproviderwrapper.h" +#include "cloudprovidermanager.h" +#include "account.h" +#include "cloudproviderconfig.h" + +CloudProvidersProviderExporter *_providerExporter; + +void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) +{ + Q_UNUSED(name); + CloudProviderManager *self; + self = static_cast(user_data); + _providerExporter = cloud_providers_provider_exporter_new(connection, LIBCLOUDPROVIDERS_DBUS_BUS_NAME, LIBCLOUDPROVIDERS_DBUS_OBJECT_PATH); + cloud_providers_provider_exporter_set_name (_providerExporter, APPLICATION_NAME); + self->registerSignals(); +} + +void CloudProviderManager::registerSignals() +{ + OCC::FolderMan *folderManager = OCC::FolderMan::instance(); + connect(folderManager, SIGNAL(folderListChanged(const Folder::Map &)), SLOT(slotFolderListChanged(const Folder::Map &))); + slotFolderListChanged(folderManager->map()); +} + +CloudProviderManager::CloudProviderManager(QObject *parent) : QObject(parent) +{ + _map = new QMap(); + QString busName = QString(LIBCLOUDPROVIDERS_DBUS_BUS_NAME); + g_bus_own_name (G_BUS_TYPE_SESSION, busName.toAscii().data(), G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, NULL, NULL, this, NULL); +} + +void CloudProviderManager::slotFolderListChanged(const Folder::Map &folderMap) +{ + QMapIterator i(*_map); + while (i.hasNext()) { + i.next(); + if (!folderMap.contains(i.key())) { + cloud_providers_provider_exporter_remove_account(_providerExporter, i.value()->accountExporter()); + delete _map->find(i.key()).value(); + _map->remove(i.key()); + } + } + + Folder::MapIterator j(folderMap); + while (j.hasNext()) { + j.next(); + if (!_map->contains(j.key())) { + auto *cpo = new CloudProviderWrapper(this, j.value(), _providerExporter); + _map->insert(j.key(), cpo); + } + } +} diff --git a/src/gui/cloudproviders/cloudprovidermanager.h b/src/gui/cloudproviders/cloudprovidermanager.h new file mode 100644 index 000000000..f37d1f0b4 --- /dev/null +++ b/src/gui/cloudproviders/cloudprovidermanager.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) by Julius Härtl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef CLOUDPROVIDERMANAGER_H +#define CLOUDPROVIDERMANAGER_H + +#include +#include "folder.h" + +using namespace OCC; + +class CloudProviderWrapper; + +class CloudProviderManager : public QObject +{ + Q_OBJECT +public: + explicit CloudProviderManager(QObject *parent = nullptr); + void registerSignals(); + +signals: + +public slots: + void slotFolderListChanged(const Folder::Map &folderMap); + +private: + QMap *_map; +}; + +#endif // CLOUDPROVIDERMANAGER_H diff --git a/src/gui/cloudproviders/cloudproviderwrapper.cpp b/src/gui/cloudproviders/cloudproviderwrapper.cpp new file mode 100644 index 000000000..63ff806e0 --- /dev/null +++ b/src/gui/cloudproviders/cloudproviderwrapper.cpp @@ -0,0 +1,349 @@ +/* + * Copyright (C) by Klaas Freitag + * Copyright (C) by Julius Härtl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +extern "C" { + #include + #include + #include + #include +} + +#include "cloudproviderwrapper.h" +#include +#include +#include +#include +#include "openfilemanager.h" +#include "owncloudgui.h" +#include "application.h" + +using namespace OCC; + +GSimpleActionGroup *actionGroup = NULL; + +static +gchar* qstring_to_gchar(const QString &string) +{ + QByteArray ba = string.toUtf8(); + char* data = ba.data(); + return g_strdup(data); +} + +CloudProviderWrapper::CloudProviderWrapper(QObject *parent, Folder *folder, CloudProvidersProviderExporter* cloudprovider) : QObject(parent) + , _folder(folder) +{ + _recentlyChanged = new QList>(); + gchar *accountName = g_strdup_printf ("Account%sFolder%s", + qstring_to_gchar(folder->alias()), + qstring_to_gchar(folder->accountState()->account()->id())); + + _cloudProvider = CLOUD_PROVIDERS_PROVIDER_EXPORTER(cloudprovider); + _cloudProviderAccount = cloud_providers_account_exporter_new(_cloudProvider, accountName); + + gchar* folderName = qstring_to_gchar(folder->shortGuiLocalPath()); + gchar* folderPath = qstring_to_gchar(folder->cleanPath()); + cloud_providers_account_exporter_set_name (_cloudProviderAccount, folderName); + cloud_providers_account_exporter_set_icon (_cloudProviderAccount, g_icon_new_for_string(APPLICATION_ICON_NAME, NULL)); + cloud_providers_account_exporter_set_path (_cloudProviderAccount, folderPath); + cloud_providers_account_exporter_set_status (_cloudProviderAccount, CLOUD_PROVIDERS_ACCOUNT_STATUS_IDLE); + cloud_providers_account_exporter_set_menu_model (_cloudProviderAccount, getMenuModel()); + cloud_providers_account_exporter_set_action_group (_cloudProviderAccount, getActionGroup()); + + connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString, ProgressInfo)), this, SLOT(slotUpdateProgress(QString, ProgressInfo))); + connect(_folder, SIGNAL(syncStarted()), this, SLOT(slotSyncStarted())); + connect(_folder, SIGNAL(syncFinished(SyncResult)), this, SLOT(slotSyncFinished(const SyncResult))); + connect(_folder, SIGNAL(syncPausedChanged(Folder*,bool)), this, SLOT(slotSyncPausedChanged(Folder*, bool))); + + _paused = _folder->syncPaused(); + updatePauseStatus(); + + g_free(accountName); + g_free(folderName); + g_free(folderPath); +} + +CloudProviderWrapper::~CloudProviderWrapper() +{ + g_object_unref(_cloudProviderAccount); + g_object_unref(_mainMenu); + g_object_unref(actionGroup); +} + +CloudProvidersAccountExporter* CloudProviderWrapper::accountExporter() +{ + return _cloudProviderAccount; +} + +static bool shouldShowInRecentsMenu(const SyncFileItem &item) +{ + return !Progress::isIgnoredKind(item._status) + && item._instruction != CSYNC_INSTRUCTION_EVAL + && item._instruction != CSYNC_INSTRUCTION_NONE; +} + +void CloudProviderWrapper::slotUpdateProgress(const QString &folder, const ProgressInfo &progress) +{ + // Only update progress for the current folder + Folder *f = FolderMan::instance()->folder(folder); + if (f != _folder) + return; + + // Build recently changed files list + if (!progress._lastCompletedItem.isEmpty() && shouldShowInRecentsMenu(progress._lastCompletedItem)) { + QString kindStr = Progress::asResultString(progress._lastCompletedItem); + QString timeStr = QTime::currentTime().toString("hh:mm"); + QString actionText = tr("%1 (%2, %3)").arg(progress._lastCompletedItem._file, kindStr, timeStr); + if (f) { + QString fullPath = f->path() + '/' + progress._lastCompletedItem._file; + if (QFile(fullPath).exists()) { + if (_recentlyChanged->length() > 5) + _recentlyChanged->removeFirst(); + _recentlyChanged->append(qMakePair(actionText, fullPath)); + } else { + _recentlyChanged->append(qMakePair(actionText, QString(""))); + } + } + + } + + // Build status details text + QString msg; + if (!progress._currentDiscoveredFolder.isEmpty()) { + msg = tr("Checking for changes in '%1'").arg(progress._currentDiscoveredFolder); + } else if (progress.totalSize() == 0) { + quint64 currentFile = progress.currentFile(); + quint64 totalFileCount = qMax(progress.totalFiles(), currentFile); + if (progress.trustEta()) { + msg = tr("Syncing %1 of %2 (%3 left)") + .arg(currentFile) + .arg(totalFileCount) + .arg(Utility::durationToDescriptiveString2(progress.totalProgress().estimatedEta)); + } else { + msg = tr("Syncing %1 of %2") + .arg(currentFile) + .arg(totalFileCount); + } + } else { + QString totalSizeStr = Utility::octetsToString(progress.totalSize()); + if (progress.trustEta()) { + msg = tr("Syncing %1 (%2 left)") + .arg(totalSizeStr, Utility::durationToDescriptiveString2(progress.totalProgress().estimatedEta)); + } else { + msg = tr("Syncing %1") + .arg(totalSizeStr); + } + } + updateStatusText(msg); + + if (!progress._lastCompletedItem.isEmpty() + && shouldShowInRecentsMenu(progress._lastCompletedItem)) { + GMenuItem* item; + g_menu_remove_all (G_MENU(_recentMenu)); + if(!_recentlyChanged->isEmpty()) { + QList>::iterator i; + for (i = _recentlyChanged->begin(); i != _recentlyChanged->end(); i++) { + gchar *file; + QString label = i->first; + QString fullPath = i->second; + file = g_strdup(qstring_to_gchar(label)); + item = g_menu_item_new(file, "cloudprovider.showfile"); + g_menu_item_set_action_and_target_value(item, "cloudprovider.showfile", g_variant_new_string(qstring_to_gchar(fullPath))); + g_menu_append_item(_recentMenu, item); + } + } else { + item = g_menu_item_new("No recently changed files", NULL); + g_menu_append_item(_recentMenu, item); + } + } +} + +void CloudProviderWrapper::updateStatusText(QString statusText) +{ + char* state = qstring_to_gchar(_folder->accountState()->stateString(_folder->accountState()->state())); + char* statusChar = qstring_to_gchar(statusText); + char* status = g_strdup_printf("%s - %s", state, statusChar); + cloud_providers_account_exporter_set_status_details(_cloudProviderAccount, status); + g_free(state); + g_free(statusChar); + g_free(status); +} + +void CloudProviderWrapper::updatePauseStatus() +{ + if (_paused) { + updateStatusText(tr("Sync paused")); + cloud_providers_account_exporter_set_status (_cloudProviderAccount, CLOUD_PROVIDERS_ACCOUNT_STATUS_ERROR); + } else { + updateStatusText(tr("Syncing")); + cloud_providers_account_exporter_set_status (_cloudProviderAccount, CLOUD_PROVIDERS_ACCOUNT_STATUS_SYNCING); + } +} + +Folder* CloudProviderWrapper::folder() +{ + return _folder; +} + +void CloudProviderWrapper::slotSyncStarted() +{ + cloud_providers_account_exporter_set_status(_cloudProviderAccount, CLOUD_PROVIDERS_ACCOUNT_STATUS_SYNCING); +} + +void CloudProviderWrapper::slotSyncFinished(const SyncResult &result) +{ + if (result.status() == result.Success || result.status() == result.Problem) + { + cloud_providers_account_exporter_set_status(_cloudProviderAccount, CLOUD_PROVIDERS_ACCOUNT_STATUS_IDLE); + updateStatusText(result.statusString()); + return; + } + cloud_providers_account_exporter_set_status(_cloudProviderAccount, CLOUD_PROVIDERS_ACCOUNT_STATUS_ERROR); + updateStatusText(result.statusString()); +} + +GMenuModel* CloudProviderWrapper::getMenuModel() { + + GMenu* section; + GMenuItem* item; + + _mainMenu = g_menu_new(); + + section = g_menu_new(); + item = g_menu_item_new("Open website", "cloudprovider.openwebsite"); + g_menu_append_item(section, item); + g_menu_append_section(_mainMenu, NULL, G_MENU_MODEL(section)); + + _recentMenu = g_menu_new(); + item = g_menu_item_new("No recently changed files", NULL); + g_menu_append_item(_recentMenu, item); + section = g_menu_new(); + item = g_menu_item_new_submenu("Recently changed", G_MENU_MODEL(_recentMenu)); + g_menu_append_item(section, item); + g_menu_append_section(_mainMenu, NULL, G_MENU_MODEL(section)); + + section = g_menu_new(); + item = g_menu_item_new("Pause synchronization", "cloudprovider.pause"); + g_menu_append_item(section, item); + g_menu_append_section(_mainMenu, NULL, G_MENU_MODEL(section)); + + section = g_menu_new(); + item = g_menu_item_new("Help", "cloudprovider.openhelp"); + g_menu_append_item(section, item); + item = g_menu_item_new("Settings", "cloudprovider.opensettings"); + g_menu_append_item(section, item); + item = g_menu_item_new("Log out", "cloudprovider.logout"); + g_menu_append_item(section, item); + item = g_menu_item_new("Quit sync client", "cloudprovider.quit"); + g_menu_append_item(section, item); + g_menu_append_section(_mainMenu, NULL, G_MENU_MODEL(section)); + + return G_MENU_MODEL(_mainMenu); +} + +static void +activate_action_open (GSimpleAction *action, GVariant *parameter, gpointer user_data) +{ + Q_UNUSED(parameter); + const gchar *name = g_action_get_name(G_ACTION(action)); + auto *self = static_cast(user_data); + auto *gui = dynamic_cast(self->parent()->parent()); + + if(g_str_equal(name, "openhelp")) { + gui->slotHelp(); + } + + if(g_str_equal(name, "opensettings")) { + gui->slotShowSettings(); + } + + if(g_str_equal(name, "openwebsite")) { + QDesktopServices::openUrl(self->folder()->accountState()->account()->url()); + } + + if(g_str_equal(name, "openfolder")) { + showInFileManager(self->folder()->cleanPath()); + } + + if(g_str_equal(name, "showfile")) { + gchar *path; + g_variant_get (parameter, "s", &path); + g_print("showfile => %s\n", path); + showInFileManager(QString(path)); + } + + if(g_str_equal(name, "logout")) { + self->folder()->accountState()->signOutByUi(); + } + + if(g_str_equal(name, "quit")) { + qApp->quit(); + } +} + +static void +activate_action_openrecentfile (GSimpleAction *action, GVariant *parameter, gpointer user_data) +{ + Q_UNUSED(action); + Q_UNUSED(parameter); + auto *self = static_cast(user_data); + QDesktopServices::openUrl(self->folder()->accountState()->account()->url()); +} + +static void +activate_action_pause (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + Q_UNUSED(parameter); + auto *self = static_cast(user_data); + GVariant *old_state, *new_state; + + old_state = g_action_get_state (G_ACTION (action)); + new_state = g_variant_new_boolean (!(bool)g_variant_get_boolean (old_state)); + self->folder()->setSyncPaused((bool)g_variant_get_boolean(new_state)); + g_simple_action_set_state (action, new_state); + g_variant_unref (old_state); +} + +static GActionEntry actions[] = { + { "openwebsite", activate_action_open, NULL, NULL, NULL, {0,0,0}}, + { "quit", activate_action_open, NULL, NULL, NULL, {0,0,0}}, + { "logout", activate_action_open, NULL, NULL, NULL, {0,0,0}}, + { "openfolder", activate_action_open, NULL, NULL, NULL, {0,0,0}}, + { "showfile", activate_action_open, "s", NULL, NULL, {0,0,0}}, + { "openhelp", activate_action_open, NULL, NULL, NULL, {0,0,0}}, + { "opensettings", activate_action_open, NULL, NULL, NULL, {0,0,0}}, + { "openrecentfile", activate_action_openrecentfile, "s", NULL, NULL, {0,0,0}}, + { "pause", activate_action_pause, NULL, "false", NULL, {0,0,0}} +}; + +GActionGroup* CloudProviderWrapper::getActionGroup() +{ + actionGroup = g_simple_action_group_new (); + g_action_map_add_action_entries (G_ACTION_MAP (actionGroup), actions, G_N_ELEMENTS (actions), this); + bool state = _folder->syncPaused(); + GAction *pause = g_action_map_lookup_action(G_ACTION_MAP(actionGroup), "pause"); + g_simple_action_set_state(G_SIMPLE_ACTION(pause), g_variant_new_boolean(state)); + return G_ACTION_GROUP (actionGroup); +} + +void CloudProviderWrapper::slotSyncPausedChanged(Folder *folder, bool state) +{ + Q_UNUSED(folder); + _paused = state; + GAction *pause = g_action_map_lookup_action(G_ACTION_MAP(actionGroup), "pause"); + g_simple_action_set_state (G_SIMPLE_ACTION(pause), g_variant_new_boolean(state)); + updatePauseStatus(); +} diff --git a/src/gui/cloudproviders/cloudproviderwrapper.h b/src/gui/cloudproviders/cloudproviderwrapper.h new file mode 100644 index 000000000..de2583013 --- /dev/null +++ b/src/gui/cloudproviders/cloudproviderwrapper.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) by Julius Härtl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef CLOUDPROVIDER_H +#define CLOUDPROVIDER_H + +#include +#include "folderman.h" + +/* Forward declaration required since gio header files interfere with QObject headers */ +struct _CloudProvidersProviderExporter; +typedef _CloudProvidersProviderExporter CloudProvidersProviderExporter; +struct _CloudProvidersAccountExporter; +typedef _CloudProvidersAccountExporter CloudProvidersAccountExporter; +struct _GMenuModel; +typedef _GMenuModel GMenuModel; +struct _GMenu; +typedef _GMenu GMenu; +struct _GActionGroup; +typedef _GActionGroup GActionGroup; +typedef char gchar; +typedef void* gpointer; + +using namespace OCC; + +class CloudProviderWrapper : public QObject +{ + Q_OBJECT +public: + explicit CloudProviderWrapper(QObject *parent = nullptr, Folder *folder = nullptr, CloudProvidersProviderExporter* cloudprovider = nullptr); + ~CloudProviderWrapper(); + CloudProvidersAccountExporter* accountExporter(); + Folder* folder(); + GMenuModel* getMenuModel(); + GActionGroup* getActionGroup(); + void updateStatusText(QString statusText); + void updatePauseStatus(); + +public slots: + void slotSyncStarted(); + void slotSyncFinished(const SyncResult &); + void slotUpdateProgress(const QString &folder, const ProgressInfo &progress); + void slotSyncPausedChanged(Folder*, bool); + +private: + Folder *_folder; + CloudProvidersProviderExporter *_cloudProvider; + CloudProvidersAccountExporter *_cloudProviderAccount; + QList> *_recentlyChanged; + bool _paused; + GMenu* _mainMenu = NULL; + GMenu* _recentMenu = NULL; +}; + +#endif // CLOUDPROVIDER_H diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index 7e5174700..ca73ea0ec 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -763,6 +763,7 @@ void FolderMan::slotRemoveFoldersForAccount(AccountState *accountState) foreach (const auto &f, foldersToRemove) { removeFolder(f); } + emit folderListChanged(_folderMap); } void FolderMan::slotForwardFolderSyncStateChange() diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index 0e845e562..5c2bd7c48 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -34,10 +34,16 @@ #include "accountmanager.h" #include "common/syncjournalfilerecord.h" #include "creds/abstractcredentials.h" +#ifdef WITH_LIBCLOUDPROVIDERS +#include "cloudproviders/cloudprovidermanager.h" +#endif #include #include #include +#include +#include +#include #if defined(Q_OS_X11) #include @@ -60,6 +66,7 @@ ownCloudGui::ownCloudGui(Application *parent) #endif _logBrowser(0) , _contextMenuVisibleOsx(false) + , _bus(QDBusConnection::sessionBus()) , _recentActionsMenu(0) , _qdbusmenuWorkaround(false) , _app(parent) @@ -99,6 +106,28 @@ ownCloudGui::ownCloudGui(Application *parent) this, &ownCloudGui::slotShowGuiMessage); } +#ifdef WITH_LIBCLOUDPROVIDERS +void ownCloudGui::setupCloudProviders() +{ + new CloudProviderManager(this); +} + +bool ownCloudGui::cloudProviderApiAvailable() +{ + if (!_bus.isConnected()) { + return false; + } + QDBusInterface dbus_iface("org.freedesktop.CloudProviderManager", "/org/freedesktop/CloudProviderManager", + "org.freedesktop.CloudProvider.Manager1", _bus); + + if (!dbus_iface.isValid()) { + qCInfo(lcApplication) << "DBus interface unavailable"; + return false; + } + return true; +} +#endif + // This should rather be in application.... or rather in ConfigFile? void ownCloudGui::slotOpenSettingsDialog() { diff --git a/src/gui/owncloudgui.h b/src/gui/owncloudgui.h index d1b60ae0e..f1341545f 100644 --- a/src/gui/owncloudgui.h +++ b/src/gui/owncloudgui.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace OCC { @@ -52,6 +53,10 @@ public: static void raiseDialog(QWidget *raiseWidget); static QSize settingsDialogSize() { return QSize(800, 500); } void setupOverlayIcons(); +#ifdef WITH_LIBCLOUDPROVIDERS + void setupCloudProviders(); + bool cloudProviderApiAvailable(); +#endif /// Whether the tray menu is visible bool contextMenuVisible() const; @@ -123,6 +128,8 @@ private: // on OSX because aboutToHide is not reliable everywhere. bool _contextMenuVisibleOsx; + QDBusConnection _bus; + QMenu *_recentActionsMenu; QVector _accountMenus; bool _qdbusmenuWorkaround;