mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-21 12:35:52 +03:00
Integrate libcloudproviders support
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 <jus@bitgrid.net>
This commit is contained in:
parent
09fa5966da
commit
1cedb1919f
19 changed files with 804 additions and 0 deletions
|
@ -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)
|
||||
|
|
0
cmake/modules/DBusMacros.cmake
Normal file
0
cmake/modules/DBusMacros.cmake
Normal file
35
cmake/modules/FindGLib2.cmake
Normal file
35
cmake/modules/FindGLib2.cmake
Normal file
|
@ -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)
|
61
cmake/modules/FindGio.cmake
Normal file
61
cmake/modules/FindGio.cmake
Normal file
|
@ -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)
|
22
cmake/modules/FindLibcloudproviders.cmake
Normal file
22
cmake/modules/FindLibcloudproviders.cmake
Normal file
|
@ -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)
|
|
@ -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()
|
||||
|
||||
|
|
57
shell_integration/libcloudproviders/CMakeLists.txt
Normal file
57
shell_integration/libcloudproviders/CMakeLists.txt
Normal file
|
@ -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 ()
|
|
@ -0,0 +1,4 @@
|
|||
[Cloud Providers]
|
||||
BusName=@LIBCLOUDPROVIDERS_DBUS_BUS_NAME@
|
||||
ObjectPath=@LIBCLOUDPROVIDERS_DBUS_OBJECT_PATH@
|
||||
Version=1
|
|
@ -0,0 +1,4 @@
|
|||
[D-BUS Service]
|
||||
Name=@LIBCLOUDPROVIDERS_DBUS_BUS_NAME@
|
||||
Exec=@APPLICATION_EXECUTABLE@
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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; }");
|
||||
|
|
21
src/gui/cloudproviders/cloudproviderconfig.h.in
Normal file
21
src/gui/cloudproviders/cloudproviderconfig.h.in
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) by Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* 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
|
71
src/gui/cloudproviders/cloudprovidermanager.cpp
Normal file
71
src/gui/cloudproviders/cloudprovidermanager.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (C) by Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* 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 <glib.h>
|
||||
#include <gio.h>
|
||||
#include <cloudprovidersproviderexporter.h>
|
||||
}
|
||||
#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<CloudProviderManager*>(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, CloudProviderWrapper*>();
|
||||
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<QString, CloudProviderWrapper*> 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);
|
||||
}
|
||||
}
|
||||
}
|
41
src/gui/cloudproviders/cloudprovidermanager.h
Normal file
41
src/gui/cloudproviders/cloudprovidermanager.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) by Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* 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 <QObject>
|
||||
#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<QString, CloudProviderWrapper*> *_map;
|
||||
};
|
||||
|
||||
#endif // CLOUDPROVIDERMANAGER_H
|
349
src/gui/cloudproviders/cloudproviderwrapper.cpp
Normal file
349
src/gui/cloudproviders/cloudproviderwrapper.cpp
Normal file
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
* Copyright (C) by Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* 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 <glib.h>
|
||||
#include <gio.h>
|
||||
#include <cloudprovidersaccountexporter.h>
|
||||
#include <cloudprovidersproviderexporter.h>
|
||||
}
|
||||
|
||||
#include "cloudproviderwrapper.h"
|
||||
#include <account.h>
|
||||
#include <folder.h>
|
||||
#include <accountstate.h>
|
||||
#include <QDesktopServices>
|
||||
#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<QPair<QString, QString>>();
|
||||
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<QPair<QString, QString>>::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<CloudProviderWrapper*>(user_data);
|
||||
auto *gui = dynamic_cast<ownCloudGui*>(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<CloudProviderWrapper*>(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<CloudProviderWrapper*>(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();
|
||||
}
|
66
src/gui/cloudproviders/cloudproviderwrapper.h
Normal file
66
src/gui/cloudproviders/cloudproviderwrapper.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (C) by Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* 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 <QObject>
|
||||
#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<QPair<QString, QString>> *_recentlyChanged;
|
||||
bool _paused;
|
||||
GMenu* _mainMenu = NULL;
|
||||
GMenu* _recentMenu = NULL;
|
||||
};
|
||||
|
||||
#endif // CLOUDPROVIDER_H
|
|
@ -763,6 +763,7 @@ void FolderMan::slotRemoveFoldersForAccount(AccountState *accountState)
|
|||
foreach (const auto &f, foldersToRemove) {
|
||||
removeFolder(f);
|
||||
}
|
||||
emit folderListChanged(_folderMap);
|
||||
}
|
||||
|
||||
void FolderMan::slotForwardFolderSyncStateChange()
|
||||
|
|
|
@ -34,10 +34,16 @@
|
|||
#include "accountmanager.h"
|
||||
#include "common/syncjournalfilerecord.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
#ifdef WITH_LIBCLOUDPROVIDERS
|
||||
#include "cloudproviders/cloudprovidermanager.h"
|
||||
#endif
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QSignalMapper>
|
||||
#include <QtDBus/QDBusConnection>
|
||||
#include <QtDBus/QDBusInterface>
|
||||
|
||||
#if defined(Q_OS_X11)
|
||||
#include <QX11Info>
|
||||
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <QMenu>
|
||||
#include <QSize>
|
||||
#include <QTimer>
|
||||
#include <QDBusConnection>
|
||||
|
||||
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<QMenu *> _accountMenus;
|
||||
bool _qdbusmenuWorkaround;
|
||||
|
|
Loading…
Reference in a new issue