mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-21 20:45:51 +03:00
Windows: Add sync folders to Explorer's navigation pane
This is only the navigation pane, the SyncRootManager entries aren't handled yet. This follows the instructions from: https://msdn.microsoft.com/en-us/library/windows/desktop/dn889934%28v=vs.85%29.aspx Issue #5295
This commit is contained in:
parent
e85a339d94
commit
56e38e6f80
13 changed files with 385 additions and 2 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -57,6 +57,7 @@ set(client_SRCS
|
|||
ignorelisteditor.cpp
|
||||
lockwatcher.cpp
|
||||
logbrowser.cpp
|
||||
navigationpanehelper.cpp
|
||||
networksettings.cpp
|
||||
ocsjob.cpp
|
||||
ocssharejob.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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
133
src/gui/navigationpanehelper.cpp
Normal file
133
src/gui/navigationpanehelper.cpp
Normal file
|
@ -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
|
42
src/gui/navigationpanehelper.h
Normal file
42
src/gui/navigationpanehelper.h
Normal file
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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}")
|
||||
|
|
Loading…
Reference in a new issue