Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/owncloud/client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--src/common/utility.h13
-rw-r--r--src/common/utility_win.cpp167
-rw-r--r--src/gui/CMakeLists.txt1
-rw-r--r--src/gui/accountsettings.cpp3
-rw-r--r--src/gui/folder.cpp7
-rw-r--r--src/gui/folder.h6
-rw-r--r--src/gui/folderman.cpp6
-rw-r--r--src/gui/folderman.h3
-rw-r--r--src/gui/navigationpanehelper.cpp133
-rw-r--r--src/gui/navigationpanehelper.h42
-rw-r--r--src/gui/owncloudsetupwizard.cpp2
-rw-r--r--test/CMakeLists.txt1
13 files changed, 385 insertions, 2 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c2540f689..b61aa0ff8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -213,6 +213,9 @@ add_definitions(-D_UNICODE)
if( WIN32 )
add_definitions( -D__USE_MINGW_ANSI_STDIO=1 )
add_definitions( -DNOMINMAX )
+# Get APIs from from Vista onwards.
+add_definitions( -D_WIN32_WINNT=0x0600)
+add_definitions( -DWINVER=0x0600)
endif( WIN32 )
include(QtVersionAbstraction)
diff --git a/src/common/utility.h b/src/common/utility.h
index 9fc0a72d7..1f63302b7 100644
--- a/src/common/utility.h
+++ b/src/common/utility.h
@@ -28,8 +28,13 @@
#include <QLoggingCategory>
#include <QMap>
#include <QUrl>
+#include <functional>
#include <memory>
+#ifdef Q_OS_WIN
+#include <windows.h>
+#endif
+
class QSettings;
namespace OCC {
@@ -187,6 +192,14 @@ namespace Utility {
* Experimental! Real feature planned for 2.5.
*/
OCSYNC_EXPORT bool shouldUploadConflictFiles();
+
+#ifdef Q_OS_WIN
+ OCSYNC_EXPORT QVariant registryGetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName);
+ OCSYNC_EXPORT bool registrySetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName, DWORD type, const QVariant &value);
+ OCSYNC_EXPORT bool registryDeleteKeyTree(HKEY hRootKey, const QString &subKey);
+ OCSYNC_EXPORT bool registryDeleteKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName);
+ OCSYNC_EXPORT bool registryWalkSubKeys(HKEY hRootKey, const QString &subKey, const std::function<void(HKEY, const QString &)> &callback);
+#endif
}
/** @} */ // \addtogroup
diff --git a/src/common/utility_win.cpp b/src/common/utility_win.cpp
index 5374b0986..e3e4234e8 100644
--- a/src/common/utility_win.cpp
+++ b/src/common/utility_win.cpp
@@ -16,8 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#define _WIN32_WINNT 0x0600
-#define WINVER 0x0600
+#include "asserts.h"
#include <shlobj.h>
#include <winbase.h>
#include <windows.h>
@@ -93,4 +92,168 @@ static inline bool hasDarkSystray_private()
return true;
}
+QVariant Utility::registryGetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName)
+{
+ QVariant value;
+
+ HKEY hKey;
+
+ REGSAM sam = KEY_READ | KEY_WOW64_64KEY;
+ LONG result = RegOpenKeyEx(hRootKey, reinterpret_cast<LPCWSTR>(subKey.utf16()), 0, sam, &hKey);
+ ASSERT(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+ if (result != ERROR_SUCCESS)
+ return value;
+
+ DWORD type = 0, sizeInBytes = 0;
+ result = RegQueryValueEx(hKey, reinterpret_cast<LPCWSTR>(valueName.utf16()), 0, &type, nullptr, &sizeInBytes);
+ ASSERT(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+ if (result == ERROR_SUCCESS) {
+ switch (type) {
+ case REG_DWORD:
+ DWORD dword;
+ Q_ASSERT(sizeInBytes == sizeof(dword));
+ if (RegQueryValueEx(hKey, reinterpret_cast<LPCWSTR>(valueName.utf16()), 0, &type, reinterpret_cast<LPBYTE>(&dword), &sizeInBytes) == ERROR_SUCCESS) {
+ value = int(dword);
+ }
+ break;
+ case REG_EXPAND_SZ:
+ case REG_SZ: {
+ QString string;
+ string.resize(sizeInBytes / sizeof(QChar));
+ result = RegQueryValueEx(hKey, reinterpret_cast<LPCWSTR>(valueName.utf16()), 0, &type, reinterpret_cast<LPBYTE>(string.data()), &sizeInBytes);
+
+ if (result == ERROR_SUCCESS) {
+ int newCharSize = sizeInBytes / sizeof(QChar);
+ // From the doc:
+ // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, the string may not have been stored with
+ // the proper terminating null characters. Therefore, even if the function returns ERROR_SUCCESS,
+ // the application should ensure that the string is properly terminated before using it; otherwise, it may overwrite a buffer.
+ if (string.at(newCharSize - 1) == QChar('\0'))
+ string.resize(newCharSize - 1);
+ value = string;
+ }
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ }
+ }
+ ASSERT(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ RegCloseKey(hKey);
+ return value;
+}
+
+bool Utility::registrySetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName, DWORD type, const QVariant &value)
+{
+ HKEY hKey;
+ // KEY_WOW64_64KEY is necessary because CLSIDs are "Redirected and reflected only for CLSIDs that do not specify InprocServer32 or InprocHandler32."
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384253%28v=vs.85%29.aspx#redirected__shared__and_reflected_keys_under_wow64
+ // This shouldn't be an issue in our case since we use shell32.dll as InprocServer32, so we could write those registry keys for both 32 and 64bit.
+ // FIXME: Not doing so at the moment means that explorer will show the cloud provider, but 32bit processes' open dialogs (like the ownCloud client itself) won't show it.
+ REGSAM sam = KEY_WRITE | KEY_WOW64_64KEY;
+ LONG result = RegCreateKeyEx(hRootKey, reinterpret_cast<LPCWSTR>(subKey.utf16()), 0, nullptr, 0, sam, nullptr, &hKey, nullptr);
+ ASSERT(result == ERROR_SUCCESS);
+ if (result != ERROR_SUCCESS)
+ return false;
+
+ result = -1;
+ switch (type) {
+ case REG_DWORD: {
+ DWORD dword = value.toInt();
+ result = RegSetValueEx(hKey, reinterpret_cast<LPCWSTR>(valueName.utf16()), 0, type, reinterpret_cast<const BYTE *>(&dword), sizeof(dword));
+ break;
+ }
+ case REG_EXPAND_SZ:
+ case REG_SZ: {
+ QString string = value.toString();
+ result = RegSetValueEx(hKey, reinterpret_cast<LPCWSTR>(valueName.utf16()), 0, type, reinterpret_cast<const BYTE *>(string.constData()), (string.size() + 1) * sizeof(QChar));
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ }
+ ASSERT(result == ERROR_SUCCESS);
+
+ RegCloseKey(hKey);
+ return result == ERROR_SUCCESS;
+}
+
+bool Utility::registryDeleteKeyTree(HKEY hRootKey, const QString &subKey)
+{
+ HKEY hKey;
+ REGSAM sam = DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_64KEY;
+ LONG result = RegOpenKeyEx(hRootKey, reinterpret_cast<LPCWSTR>(subKey.utf16()), 0, sam, &hKey);
+ ASSERT(result == ERROR_SUCCESS);
+ if (result != ERROR_SUCCESS)
+ return false;
+
+ result = RegDeleteTree(hKey, nullptr);
+ RegCloseKey(hKey);
+ ASSERT(result == ERROR_SUCCESS);
+
+ result |= RegDeleteKeyEx(hRootKey, reinterpret_cast<LPCWSTR>(subKey.utf16()), sam, 0);
+ ASSERT(result == ERROR_SUCCESS);
+
+ return result == ERROR_SUCCESS;
+}
+
+bool Utility::registryDeleteKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName)
+{
+ HKEY hKey;
+ REGSAM sam = KEY_WRITE | KEY_WOW64_64KEY;
+ LONG result = RegOpenKeyEx(hRootKey, reinterpret_cast<LPCWSTR>(subKey.utf16()), 0, sam, &hKey);
+ ASSERT(result == ERROR_SUCCESS);
+ if (result != ERROR_SUCCESS)
+ return false;
+
+ result = RegDeleteValue(hKey, reinterpret_cast<LPCWSTR>(valueName.utf16()));
+ ASSERT(result == ERROR_SUCCESS);
+
+ RegCloseKey(hKey);
+ return result == ERROR_SUCCESS;
+}
+
+bool Utility::registryWalkSubKeys(HKEY hRootKey, const QString &subKey, const std::function<void(HKEY, const QString &)> &callback)
+{
+ HKEY hKey;
+ REGSAM sam = KEY_READ | KEY_WOW64_64KEY;
+ LONG result = RegOpenKeyEx(hRootKey, reinterpret_cast<LPCWSTR>(subKey.utf16()), 0, sam, &hKey);
+ ASSERT(result == ERROR_SUCCESS);
+ if (result != ERROR_SUCCESS)
+ return false;
+
+ DWORD maxSubKeyNameSize;
+ // Get the largest keyname size once instead of relying each call on ERROR_MORE_DATA.
+ result = RegQueryInfoKey(hKey, nullptr, nullptr, nullptr, nullptr, &maxSubKeyNameSize, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+ ASSERT(result == ERROR_SUCCESS);
+ if (result != ERROR_SUCCESS) {
+ RegCloseKey(hKey);
+ return false;
+ }
+
+ QString subKeyName;
+ subKeyName.reserve(maxSubKeyNameSize + 1);
+
+ DWORD retCode = ERROR_SUCCESS;
+ for (DWORD i = 0; retCode == ERROR_SUCCESS; ++i) {
+ Q_ASSERT(unsigned(subKeyName.capacity()) > maxSubKeyNameSize);
+ // Make the previously reserved capacity official again.
+ subKeyName.resize(subKeyName.capacity());
+ DWORD subKeyNameSize = subKeyName.size();
+ retCode = RegEnumKeyEx(hKey, i, reinterpret_cast<LPWSTR>(subKeyName.data()), &subKeyNameSize, nullptr, nullptr, nullptr, nullptr);
+
+ ASSERT(result == ERROR_SUCCESS || retCode == ERROR_NO_MORE_ITEMS);
+ if (retCode == ERROR_SUCCESS) {
+ // subKeyNameSize excludes the trailing \0
+ subKeyName.resize(subKeyNameSize);
+ // Pass only the sub keyname, not the full path.
+ callback(hKey, subKeyName);
+ }
+ }
+
+ RegCloseKey(hKey);
+ return retCode != ERROR_NO_MORE_ITEMS;
+}
+
} // namespace OCC
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 2ae957355..cee4cb457 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -57,6 +57,7 @@ set(client_SRCS
ignorelisteditor.cpp
lockwatcher.cpp
logbrowser.cpp
+ navigationpanehelper.cpp
networksettings.cpp
ocsjob.cpp
ocssharejob.cpp
diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp
index 23b287244..fd5a67148 100644
--- a/src/gui/accountsettings.cpp
+++ b/src/gui/accountsettings.cpp
@@ -401,6 +401,9 @@ void AccountSettings::slotFolderWizardAccepted()
*/
definition.ignoreHiddenFiles = folderMan->ignoreHiddenFiles();
+ // FIXME: Make this depend on a checkbox in settings.
+ definition.navigationPaneClsid = QUuid::createUuid();
+
auto selectiveSyncBlackList = folderWizard->property("selectiveSyncBlackList").toStringList();
folderMan->setSyncEnabled(true);
diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp
index 176a49323..544da3bf0 100644
--- a/src/gui/folder.cpp
+++ b/src/gui/folder.cpp
@@ -1075,6 +1075,12 @@ void FolderDefinition::save(QSettings &settings, const FolderDefinition &folder)
settings.setValue(QLatin1String("targetPath"), folder.targetPath);
settings.setValue(QLatin1String("paused"), folder.paused);
settings.setValue(QLatin1String("ignoreHiddenFiles"), folder.ignoreHiddenFiles);
+
+ // Happens only on Windows when the explorer integration is enabled.
+ if (!folder.navigationPaneClsid.isNull())
+ settings.setValue(QLatin1String("navigationPaneClsid"), folder.navigationPaneClsid);
+ else
+ settings.remove(QLatin1String("navigationPaneClsid"));
settings.endGroup();
}
@@ -1088,6 +1094,7 @@ bool FolderDefinition::load(QSettings &settings, const QString &alias,
folder->targetPath = settings.value(QLatin1String("targetPath")).toString();
folder->paused = settings.value(QLatin1String("paused")).toBool();
folder->ignoreHiddenFiles = settings.value(QLatin1String("ignoreHiddenFiles"), QVariant(true)).toBool();
+ folder->navigationPaneClsid = settings.value(QLatin1String("navigationPaneClsid")).toUuid();
settings.endGroup();
// Old settings can contain paths with native separators. In the rest of the
diff --git a/src/gui/folder.h b/src/gui/folder.h
index 2e7f2a7c0..ac4954a06 100644
--- a/src/gui/folder.h
+++ b/src/gui/folder.h
@@ -27,6 +27,7 @@
#include <QObject>
#include <QStringList>
+#include <QUuid>
#include <set>
class QThread;
@@ -64,6 +65,8 @@ public:
bool paused;
/// whether the folder syncs hidden files
bool ignoreHiddenFiles;
+ /// The CLSID where this folder appears in registry for the Explorer navigation pane entry.
+ QUuid navigationPaneClsid;
/// Saves the folder definition, creating a new settings group.
static void save(QSettings &settings, const FolderDefinition &folder);
@@ -135,6 +138,9 @@ public:
*/
QString remotePath() const;
+ void setNavigationPaneClsid(const QUuid &clsid) { _definition.navigationPaneClsid = clsid; }
+ QUuid navigationPaneClsid() const { return _definition.navigationPaneClsid; }
+
/**
* remote folder path with server url
*/
diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp
index 517d5dc99..d4dd22035 100644
--- a/src/gui/folderman.cpp
+++ b/src/gui/folderman.cpp
@@ -49,6 +49,7 @@ FolderMan::FolderMan(QObject *parent)
, _currentSyncFolder(0)
, _syncEnabled(true)
, _lockWatcher(new LockWatcher)
+ , _navigationPaneHelper(this)
, _appRestartRequired(false)
{
ASSERT(!_instance);
@@ -894,6 +895,9 @@ Folder *FolderMan::addFolder(AccountState *accountState, const FolderDefinition
emit folderSyncStateChange(folder);
emit folderListChanged(_folderMap);
}
+
+ _navigationPaneHelper.scheduleUpdateCloudStorageRegistry();
+
return folder;
}
@@ -1003,6 +1007,8 @@ void FolderMan::removeFolder(Folder *f)
delete f;
}
+ _navigationPaneHelper.scheduleUpdateCloudStorageRegistry();
+
emit folderListChanged(_folderMap);
}
diff --git a/src/gui/folderman.h b/src/gui/folderman.h
index 002fb9b45..415118689 100644
--- a/src/gui/folderman.h
+++ b/src/gui/folderman.h
@@ -22,6 +22,7 @@
#include "folder.h"
#include "folderwatcher.h"
+#include "navigationpanehelper.h"
#include "syncfileitem.h"
class TestFolderMan;
@@ -115,6 +116,7 @@ public:
static QString unescapeAlias(const QString &);
SocketApi *socketApi();
+ NavigationPaneHelper &navigationPaneHelper() { return _navigationPaneHelper; }
/**
* Check if @a path is a valid path for a new folder considering the already sync'ed items.
@@ -315,6 +317,7 @@ private:
QTimer _startScheduledSyncTimer;
QScopedPointer<SocketApi> _socketApi;
+ NavigationPaneHelper _navigationPaneHelper;
bool _appRestartRequired;
diff --git a/src/gui/navigationpanehelper.cpp b/src/gui/navigationpanehelper.cpp
new file mode 100644
index 000000000..c717727e9
--- /dev/null
+++ b/src/gui/navigationpanehelper.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) by Jocelyn Turcotte <jturcotte@woboq.com>
+ *
+ * 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.
+ */
+
+#include "navigationpanehelper.h"
+#include "accountmanager.h"
+#include "folderman.h"
+
+#include <QDir>
+#include <QCoreApplication>
+
+namespace OCC {
+
+Q_LOGGING_CATEGORY(lcNavPane, "gui.folder.navigationpane", QtInfoMsg)
+
+NavigationPaneHelper::NavigationPaneHelper(FolderMan *folderMan)
+ : _folderMan(folderMan)
+{
+ _updateCloudStorageRegistryTimer.setSingleShot(true);
+ connect(&_updateCloudStorageRegistryTimer, &QTimer::timeout, this, &NavigationPaneHelper::updateCloudStorageRegistry);
+}
+
+void NavigationPaneHelper::scheduleUpdateCloudStorageRegistry()
+{
+ // Schedule the update to happen a bit later to avoid doing the update multiple times in a row.
+ if (!_updateCloudStorageRegistryTimer.isActive())
+ _updateCloudStorageRegistryTimer.start(500);
+}
+
+void NavigationPaneHelper::updateCloudStorageRegistry()
+{
+ // Start by looking at every registered namespace extension for the sidebar, and look for an "ApplicationName" value
+ // that matches ours when we saved.
+ QVector<QUuid> entriesToRemove;
+#ifdef Q_OS_WIN
+ Utility::registryWalkSubKeys(
+ HKEY_CURRENT_USER,
+ QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace"),
+ [&entriesToRemove](HKEY key, const QString &subKey) {
+ QVariant appName = Utility::registryGetKeyValue(key, subKey, QStringLiteral("ApplicationName"));
+ if (appName.toString() == QLatin1String(APPLICATION_NAME)) {
+ QUuid clsid{ subKey };
+ Q_ASSERT(!clsid.isNull());
+ entriesToRemove.append(clsid);
+ }
+ });
+#endif
+
+ // Then re-save every folder that has a valid navigationPaneClsid to the registry.
+ // We currently don't distinguish between new and existing CLSIDs, if it's there we just
+ // save over it. We at least need to update the tile in case we are suddently using multiple accounts.
+ foreach (Folder *folder, _folderMan->map()) {
+ if (!folder->navigationPaneClsid().isNull()) {
+ // If it already exists, unmark it for removal, this is a valid sync root.
+ entriesToRemove.removeOne(folder->navigationPaneClsid());
+
+ QString clsidStr = folder->navigationPaneClsid().toString();
+ QString clsidPath = QString() % "Software\\Classes\\CLSID\\" % clsidStr;
+ QString namespacePath = QString() % "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" % clsidStr;
+
+ QString title = folder->shortGuiRemotePathOrAppName();
+ // Write the account name in the sidebar only when using more than one account.
+ if (AccountManager::instance()->accounts().size() > 1)
+ title = title % " - " % folder->accountState()->account()->displayName();
+ QString iconPath = QDir::toNativeSeparators(qApp->applicationFilePath());
+ QString targetFolderPath = QDir::toNativeSeparators(folder->cleanPath());
+
+ qCInfo(lcNavPane) << "Explorer Cloud storage provider: saving path" << targetFolderPath << "to CLSID" << clsidStr;
+#ifdef Q_OS_WIN
+ // Steps taken from: https://msdn.microsoft.com/en-us/library/windows/desktop/dn889934%28v=vs.85%29.aspx
+ // Step 1: Add your CLSID and name your extension
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QString(), REG_SZ, title);
+ // Step 2: Set the image for your icon
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\DefaultIcon"), QString(), REG_SZ, iconPath);
+ // Step 3: Add your extension to the Navigation Pane and make it visible
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QStringLiteral("System.IsPinnedToNameSpaceTree"), REG_DWORD, 0x1);
+ // Step 4: Set the location for your extension in the Navigation Pane
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QStringLiteral("SortOrderIndex"), REG_DWORD, 0x41);
+ // Step 5: Provide the dll that hosts your extension.
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\InProcServer32"), QString(), REG_EXPAND_SZ, QStringLiteral("%systemroot%\\system32\\shell32.dll"));
+ // Step 6: Define the instance object
+ // Indicate that your namespace extension should function like other file folder structures in File Explorer.
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance"), QStringLiteral("CLSID"), REG_SZ, QStringLiteral("{0E5AAE11-A475-4c5b-AB00-C66DE400274E}"));
+ // Step 7: Provide the file system attributes of the target folder
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("Attributes"), REG_DWORD, 0x11);
+ // Step 8: Set the path for the sync root
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("TargetFolderPath"), REG_SZ, targetFolderPath);
+ // Step 9: Set appropriate shell flags
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\ShellFolder"), QStringLiteral("FolderValueFlags"), REG_DWORD, 0x28);
+ // Step 10: Set the appropriate flags to control your shell behavior
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\ShellFolder"), QStringLiteral("Attributes"), REG_DWORD, 0xF080004D);
+ // Step 11: Register your extension in the namespace root
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, namespacePath, QString(), REG_SZ, title);
+ // Step 12: Hide your extension from the Desktop
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel"), clsidStr, REG_DWORD, 0x1);
+
+ // For us, to later be able to iterate and find our own namespace entries and associated CLSID.
+ // Use the macro instead of the theme to make sure it matches with the uninstaller.
+ Utility::registrySetKeyValue(HKEY_CURRENT_USER, namespacePath, QStringLiteral("ApplicationName"), REG_SZ, QLatin1String(APPLICATION_NAME));
+#else
+ // This code path should only occur on Windows (the config will be false, and the checkbox invisible on other platforms).
+ // Add runtime checks rather than #ifdefing out the whole code to help catch breakages when developing on other platforms.
+ Q_ASSERT(false);
+#endif
+ }
+ }
+
+ // Then remove anything that isn't in our folder list anymore.
+ foreach (auto &clsid, entriesToRemove) {
+ QString clsidStr = clsid.toString();
+ QString clsidPath = QString() % "Software\\Classes\\CLSID\\" % clsidStr;
+ QString namespacePath = QString() % "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" % clsidStr;
+
+ qCInfo(lcNavPane) << "Explorer Cloud storage provider: now unused, removing own CLSID" << clsidStr;
+#ifdef Q_OS_WIN
+ Utility::registryDeleteKeyTree(HKEY_CURRENT_USER, clsidPath);
+ Utility::registryDeleteKeyTree(HKEY_CURRENT_USER, namespacePath);
+ Utility::registryDeleteKeyValue(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel"), clsidStr);
+#endif
+ }
+}
+
+} // namespace OCC
diff --git a/src/gui/navigationpanehelper.h b/src/gui/navigationpanehelper.h
new file mode 100644
index 000000000..62e75dd1e
--- /dev/null
+++ b/src/gui/navigationpanehelper.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) by Jocelyn Turcotte <jturcotte@woboq.com>
+ *
+ * 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 NAVIGATIONPANEHELPER_H
+#define NAVIGATIONPANEHELPER_H
+
+#include <QObject>
+#include <QTimer>
+
+namespace OCC {
+
+class FolderMan;
+
+class NavigationPaneHelper : public QObject
+{
+ Q_OBJECT
+public:
+ NavigationPaneHelper(FolderMan *folderMan);
+
+ void scheduleUpdateCloudStorageRegistry();
+
+private:
+ void updateCloudStorageRegistry();
+
+ FolderMan *_folderMan;
+ QTimer _updateCloudStorageRegistryTimer;
+};
+
+} // namespace OCC
+#endif // NAVIGATIONPANEHELPER_H
diff --git a/src/gui/owncloudsetupwizard.cpp b/src/gui/owncloudsetupwizard.cpp
index 86a0c19ad..1e189d976 100644
--- a/src/gui/owncloudsetupwizard.cpp
+++ b/src/gui/owncloudsetupwizard.cpp
@@ -579,6 +579,8 @@ void OwncloudSetupWizard::slotAssistantFinished(int result)
folderDefinition.localPath = localFolder;
folderDefinition.targetPath = FolderDefinition::prepareTargetPath(_remoteFolder);
folderDefinition.ignoreHiddenFiles = folderMan->ignoreHiddenFiles();
+ // FIXME: Make this depend on a checkbox in settings.
+ folderDefinition.navigationPaneClsid = QUuid::createUuid();
auto f = folderMan->addFolder(account, folderDefinition);
if (f) {
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 14235a49d..7327212ae 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -65,6 +65,7 @@ list(APPEND FolderMan_SRC ../src/gui/accountstate.cpp )
list(APPEND FolderMan_SRC ../src/gui/syncrunfilelog.cpp )
list(APPEND FolderMan_SRC ../src/gui/lockwatcher.cpp )
list(APPEND FolderMan_SRC ../src/gui/guiutility.cpp )
+list(APPEND FolderMan_SRC ../src/gui/navigationpanehelper.cpp )
list(APPEND FolderMan_SRC ${FolderWatcher_SRC})
list(APPEND FolderMan_SRC stub.cpp )
owncloud_add_test(FolderMan "${FolderMan_SRC}")