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 #include #include +#include #include +#ifdef Q_OS_WIN +#include +#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 &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 #include #include @@ -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(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(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(valueName.utf16()), 0, &type, reinterpret_cast(&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(valueName.utf16()), 0, &type, reinterpret_cast(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(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(valueName.utf16()), 0, type, reinterpret_cast(&dword), sizeof(dword)); + break; + } + case REG_EXPAND_SZ: + case REG_SZ: { + QString string = value.toString(); + result = RegSetValueEx(hKey, reinterpret_cast(valueName.utf16()), 0, type, reinterpret_cast(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(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(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(subKey.utf16()), 0, sam, &hKey); + ASSERT(result == ERROR_SUCCESS); + if (result != ERROR_SUCCESS) + return false; + + result = RegDeleteValue(hKey, reinterpret_cast(valueName.utf16())); + ASSERT(result == ERROR_SUCCESS); + + RegCloseKey(hKey); + return result == ERROR_SUCCESS; +} + +bool Utility::registryWalkSubKeys(HKEY hRootKey, const QString &subKey, const std::function &callback) +{ + HKEY hKey; + REGSAM sam = KEY_READ | KEY_WOW64_64KEY; + LONG result = RegOpenKeyEx(hRootKey, reinterpret_cast(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(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 #include +#include #include 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; + 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 + * + * 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 +#include + +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 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 + * + * 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 +#include + +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}")