mirror of
https://github.com/nextcloud/desktop.git
synced 2025-10-26 11:17:43 +00:00
Compare commits
144 Commits
v4.0.0-rc1
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
529a85bce3 | ||
|
|
60aa7c76dc | ||
|
|
d73bd2d7e5 | ||
|
|
83c6bc3c4f | ||
|
|
3220e9ec88 | ||
|
|
aecfdf4b48 | ||
|
|
347e7345bd | ||
|
|
d509974bfd | ||
|
|
b240648fd0 | ||
|
|
a313ea6aff | ||
|
|
06b87b679d | ||
|
|
bddb39df16 | ||
|
|
7ac1a0d1e7 | ||
|
|
9d2333c4ed | ||
|
|
467a59e88d | ||
|
|
265385dc62 | ||
|
|
438b0af64b | ||
|
|
4417cf60d4 | ||
|
|
d702a5bd6b | ||
|
|
3de563e54f | ||
|
|
06c80d731b | ||
|
|
e738580d5b | ||
|
|
c301eb2a24 | ||
|
|
33f5c04328 | ||
|
|
5acf0f791f | ||
|
|
b26c20e642 | ||
|
|
aa7ee74781 | ||
|
|
be3a001edc | ||
|
|
8145c1ef7a | ||
|
|
adaa54cced | ||
|
|
4c8debe0ad | ||
|
|
7ab63e9466 | ||
|
|
a5dfc3ae59 | ||
|
|
e2f44b9a80 | ||
|
|
2b0ee2e292 | ||
|
|
2186edeeb7 | ||
|
|
39bf9e4ad8 | ||
|
|
8812fb37f8 | ||
|
|
92c87d460b | ||
|
|
5336d8e909 | ||
|
|
4bf78810ab | ||
|
|
c2442bce71 | ||
|
|
0402f2fced | ||
|
|
b2326a7845 | ||
|
|
62db000adc | ||
|
|
7d932f8a48 | ||
|
|
042a943685 | ||
|
|
8ae74384ea | ||
|
|
7219c3bf20 | ||
|
|
b3e705cbe1 | ||
|
|
700fcafe35 | ||
|
|
cd8601e3be | ||
|
|
01526c34e5 | ||
|
|
0d83bb2bf9 | ||
|
|
51b5ed53ec | ||
|
|
559e9c21aa | ||
|
|
5423d8383b | ||
|
|
54cb247c0d | ||
|
|
979b6cf197 | ||
|
|
ff43776cb7 | ||
|
|
383a971aa0 | ||
|
|
2566309a15 | ||
|
|
86ceba756d | ||
|
|
9a92dac5f5 | ||
|
|
9b8c831696 | ||
|
|
eb0b82b175 | ||
|
|
b46afb791c | ||
|
|
e68be46635 | ||
|
|
1251cdb0a1 | ||
|
|
955a47468a | ||
|
|
32e66991ae | ||
|
|
272ca1dcf5 | ||
|
|
92f13d4be8 | ||
|
|
57c5680d60 | ||
|
|
0cad5b1d61 | ||
|
|
224c9b1f12 | ||
|
|
335015fa2d | ||
|
|
4a0138e952 | ||
|
|
da0836f2e8 | ||
|
|
dc7e1295ab | ||
|
|
cca6805448 | ||
|
|
d354b329f4 | ||
|
|
c0bd552d7e | ||
|
|
506e2d9358 | ||
|
|
031cda0e5c | ||
|
|
7b89dd1f9e | ||
|
|
a04811e2d1 | ||
|
|
4e12a82563 | ||
|
|
bd658a3626 | ||
|
|
53a165f56d | ||
|
|
c69d8edc90 | ||
|
|
3adeb70af0 | ||
|
|
e839b25f67 | ||
|
|
b533819f20 | ||
|
|
80d79e411a | ||
|
|
89d5df6672 | ||
|
|
fb20bb2810 | ||
|
|
da19d87a8f | ||
|
|
97d75fcb6c | ||
|
|
954467383a | ||
|
|
59a9a751b0 | ||
|
|
db6a1daa5b | ||
|
|
f16ff7ff40 | ||
|
|
eaac96db85 | ||
|
|
6ba027c906 | ||
|
|
dd351b73dc | ||
|
|
bb6157e329 | ||
|
|
3d7c3ffe6b | ||
|
|
2e619ec97a | ||
|
|
f02f6c906b | ||
|
|
e9ee0602e7 | ||
|
|
c4b84a9b30 | ||
|
|
9d7f8653bc | ||
|
|
6cdbf8acc5 | ||
|
|
1ec98ee46e | ||
|
|
491e7cd55c | ||
|
|
0c97accb5c | ||
|
|
37b204af24 | ||
|
|
f219ad6c63 | ||
|
|
2adb172fdb | ||
|
|
f6e69b6be8 | ||
|
|
0bbb377c23 | ||
|
|
0d953425ca | ||
|
|
d9fc4e8ead | ||
|
|
92d4dcdb03 | ||
|
|
41d03f61e3 | ||
|
|
4ebf5ae806 | ||
|
|
1f812b54f5 | ||
|
|
5e9b74594b | ||
|
|
f88098ecdb | ||
|
|
3b452d9a72 | ||
|
|
87275ec495 | ||
|
|
6b80f6dd0d | ||
|
|
cc9b32a880 | ||
|
|
a2c2dadcbe | ||
|
|
e4fc075c75 | ||
|
|
4067a0dae5 | ||
|
|
3f08e6ac8b | ||
|
|
c2e909efde | ||
|
|
5e7bcaedee | ||
|
|
966067391a | ||
|
|
90d1e409f6 | ||
|
|
6381e56b1d | ||
|
|
bcde65baa6 |
2
.github/workflows/clang-format.yml
vendored
2
.github/workflows/clang-format.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: cpp-linter/cpp-linter-action@7dacd91f6a008a7c714bba700f4d08468a1eb428 # v2.16.4
|
||||
- uses: cpp-linter/cpp-linter-action@b7fbdde0f6776f478f4d8867c2b746077fa7ea89 # v2.16.5
|
||||
id: linter
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@ -8,7 +8,7 @@ jobs:
|
||||
build:
|
||||
name: Linux Clang compilation and tests
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-trixie-6.8.2-1
|
||||
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-trixie-6.8.2-2
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
|
||||
@ -8,7 +8,7 @@ jobs:
|
||||
build:
|
||||
name: Linux GCC compilation and tests
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-trixie-6.8.2-1
|
||||
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-trixie-6.8.2-2
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
|
||||
4
.github/workflows/macos-build-and-test.yml
vendored
4
.github/workflows/macos-build-and-test.yml
vendored
@ -35,8 +35,8 @@ jobs:
|
||||
- name: List Xcode installations
|
||||
run: sudo ls -1 /Applications | grep "Xcode"
|
||||
|
||||
- name: Select Xcode 16.2
|
||||
run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
|
||||
- name: Select Xcode 16.3
|
||||
run: sudo xcode-select -s /Applications/Xcode_16.3.app/Contents/Developer
|
||||
|
||||
- name: Set up Python ${{ inputs.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
|
||||
4
.github/workflows/macos-craft-ci.yml
vendored
4
.github/workflows/macos-craft-ci.yml
vendored
@ -43,9 +43,9 @@ jobs:
|
||||
if: steps.cache-craft-restore.outputs.cache-hit != 'true'
|
||||
run: sudo ls -1 /Applications | grep "Xcode"
|
||||
|
||||
- name: Select Xcode 16.2
|
||||
- name: Select Xcode 16.3
|
||||
if: steps.cache-craft-restore.outputs.cache-hit != 'true'
|
||||
run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
|
||||
run: sudo xcode-select -s /Applications/Xcode_16.3.app/Contents/Developer
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
if: steps.cache-craft-restore.outputs.cache-hit != 'true'
|
||||
|
||||
2
.github/workflows/reuse.yml
vendored
2
.github/workflows/reuse.yml
vendored
@ -24,4 +24,4 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: REUSE Compliance Check
|
||||
uses: fsfe/reuse-action@bb774aa972c2a89ff34781233d275075cbddf542 # v5.0.0
|
||||
uses: fsfe/reuse-action@676e2d560c9a403aa252096d99fcab3e1132b0f5 # v6.0.0
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
|
||||
- uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
|
||||
with:
|
||||
operations-per-run: 1500
|
||||
days-before-stale: 28
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@ -195,3 +195,9 @@ convert.exe
|
||||
*state-*.png
|
||||
theme.qrc
|
||||
*.AppImage
|
||||
|
||||
# Xcode Build Artifacts
|
||||
DerivedData
|
||||
|
||||
# Swift Package Manager Build Artifacts
|
||||
.swiftpm
|
||||
@ -82,7 +82,20 @@ if (NOT DEFINED PACKAGE)
|
||||
set(PACKAGE "${LINUX_PACKAGE_SHORTNAME}-client")
|
||||
endif()
|
||||
|
||||
set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
|
||||
set(APPLE_SUPPRESS_X11_WARNING ON)
|
||||
|
||||
find_package(ECM 6.0.0 REQUIRED NO_MODULE)
|
||||
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://invent.kde.org/frameworks/extra-cmake-modules")
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH} ${CMAKE_MODULE_PATH})
|
||||
|
||||
include(KDEInstallDirs)
|
||||
include(KDECMakeSettings)
|
||||
include(ECMMarkNonGuiExecutable)
|
||||
include(ECMSetupVersion)
|
||||
|
||||
#include(KDECompilerSettings NO_POLICY_SCOPE)
|
||||
include(ECMEnableSanitizers)
|
||||
|
||||
include(ECMCoverageOption)
|
||||
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
Copyright (c) <year> <owner>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@ -36,6 +36,7 @@ If you find any bugs or have any suggestion for improvement, please
|
||||
> [!TIP]
|
||||
> For building the client on macOS we have a tool called `mac-crafter`.
|
||||
> You will find more information about it in [its dedicated README](admin/osx/mac-crafter/README.md).
|
||||
> Also, please note the [README in the NextcloudIntegration project](shell_integration/MacOSX/NextcloudIntegration/README.md) which provides an even more convenient way to work on and build the desktop client on macOS by using Xcode.
|
||||
|
||||
#### 1. 🚀 Set up your local development environment
|
||||
|
||||
|
||||
22
REUSE.toml
22
REUSE.toml
@ -29,6 +29,16 @@ precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2014 ownCloud GmbH"
|
||||
SPDX-License-Identifier = "GPL-2.0-or-later"
|
||||
|
||||
[[annotations]]
|
||||
path = [
|
||||
"admin/osx/TransifexStringCatalogSanitizer/Package.swift",
|
||||
"admin/osx/TransifexStringCatalogSanitizer/Package.resolved",
|
||||
"admin/osx/TransifexStringCatalogSanitizer/README.md",
|
||||
]
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2025 Nextcloud GmbH and Nextcloud contributors"
|
||||
SPDX-License-Identifier = "GPL-2.0-or-later"
|
||||
|
||||
[[annotations]]
|
||||
path = ["admin/osx/mac-crafter/Package.resolved", "shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved"]
|
||||
precedence = "aggregate"
|
||||
@ -42,13 +52,21 @@ SPDX-FileCopyrightText = "2014 ownCloud GmbH, 2022 Nextcloud GmbH and Nextcloud
|
||||
SPDX-License-Identifier = "GPL-2.0-or-later"
|
||||
|
||||
[[annotations]]
|
||||
path = ["shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj", "shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/contents.xcworkspacedata", "shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist", "shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/xcshareddata/xcschemes/FinderSyncExt.xcscheme"]
|
||||
path = [
|
||||
"shell_integration/MacOSX/NextcloudIntegration/.gitignore",
|
||||
"shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj",
|
||||
"shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
|
||||
"shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
|
||||
"shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/xcshareddata/xcschemes/FinderSyncExt.xcscheme",
|
||||
"shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/xcshareddata/xcschemes/NextcloudDev.xcscheme",
|
||||
"shell_integration/MacOSX/NextcloudIntegration/README.md"
|
||||
]
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2025 Nextcloud GmbH and Nextcloud contributors"
|
||||
SPDX-License-Identifier = "GPL-2.0-or-later"
|
||||
|
||||
[[annotations]]
|
||||
path = ["shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Localizable.xcstrings", "shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Localizable.xcstrings"]
|
||||
path = ["shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Localizable.xcstrings", "shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Localizable.xcstrings", "shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Assets.xcassets/*", "shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Assets.xcassets/FileProviderDomainSymbol.symbolset/*", "shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Assets.xcassets/*", "shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Assets.xcassets/FileProviderDomainSymbol.symbolset/*"]
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2015 ownCloud GmbH, 2022 Nextcloud GmbH and Nextcloud contributors"
|
||||
SPDX-License-Identifier = "GPL-2.0-or-later"
|
||||
|
||||
@ -5,20 +5,20 @@
|
||||
# ------------------------------------
|
||||
# Version information
|
||||
# ------------------------------------
|
||||
set(MIRALL_VERSION_MAJOR 3)
|
||||
set(MIRALL_VERSION_MINOR 17)
|
||||
set(MIRALL_VERSION_MAJOR 4)
|
||||
set(MIRALL_VERSION_MINOR 0)
|
||||
set(MIRALL_VERSION_PATCH 50)
|
||||
set(MIRALL_VERSION_YEAR 2025)
|
||||
set(MIRALL_SOVERSION 0)
|
||||
set(MIRALL_PREVERSION_HUMAN "3.18.0 alpha") # For preversions where PATCH>=50. Use version + alpha, rc1, rc2, etc.
|
||||
set(MIRALL_PREVERSION_HUMAN "4.1.0 alpha") # For preversions where PATCH>=50. Use version + alpha, rc1, rc2, etc.
|
||||
set(NCEXT_BUILD_NUM 47)
|
||||
set(NCEXT_VERSION 3,0,0,${NCEXT_BUILD_NUM})
|
||||
|
||||
# ------------------------------------
|
||||
# Minimum supported server versions
|
||||
# According to: https://docs.nextcloud.com/server/latest/admin_manual/release_schedule.html
|
||||
# According to: https://github.com/nextcloud/server/wiki/Maintenance-and-Release-Schedule
|
||||
# ------------------------------------
|
||||
set(NEXTCLOUD_SERVER_VERSION_MIN_SUPPORTED_MAJOR 18)
|
||||
set(NEXTCLOUD_SERVER_VERSION_MIN_SUPPORTED_MAJOR 20)
|
||||
set(NEXTCLOUD_SERVER_VERSION_MIN_SUPPORTED_MINOR 0)
|
||||
set(NEXTCLOUD_SERVER_VERSION_MIN_SUPPORTED_PATCH 0)
|
||||
|
||||
|
||||
10
admin/osx/TransifexStringCatalogSanitizer/.gitignore
vendored
Normal file
10
admin/osx/TransifexStringCatalogSanitizer/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm
|
||||
.netrc
|
||||
15
admin/osx/TransifexStringCatalogSanitizer/Package.resolved
Normal file
15
admin/osx/TransifexStringCatalogSanitizer/Package.resolved
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"originHash" : "29c76d8c60e24badae4a42909eb97c07dfa3a8dbfe5940de1e06ec95358c8fda",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "swift-argument-parser",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-argument-parser.git",
|
||||
"state" : {
|
||||
"revision" : "309a47b2b1d9b5e991f36961c983ecec72275be3",
|
||||
"version" : "1.6.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
21
admin/osx/TransifexStringCatalogSanitizer/Package.swift
Normal file
21
admin/osx/TransifexStringCatalogSanitizer/Package.swift
Normal file
@ -0,0 +1,21 @@
|
||||
// swift-tools-version: 6.0
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "TransifexStringCatalogSanitizer",
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.0"),
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package, defining a module or a test suite.
|
||||
// Targets can depend on other targets in this package and products from dependencies.
|
||||
.executableTarget(
|
||||
name: "TransifexStringCatalogSanitizer",
|
||||
dependencies: [
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
15
admin/osx/TransifexStringCatalogSanitizer/README.md
Normal file
15
admin/osx/TransifexStringCatalogSanitizer/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Transifex String Catalog Sanitizer
|
||||
|
||||
Sanitize Xcode string catalogs which were pulled from a Transifex online resource.
|
||||
|
||||
## Usage
|
||||
|
||||
See the integrated help for up to date reference.
|
||||
|
||||
```sh
|
||||
swift run TransifexStringCatalogSanitizer --help
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
This Swift command-line utility can be easily run and debugged from Xcode.
|
||||
@ -0,0 +1,83 @@
|
||||
// SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import ArgumentParser
|
||||
import Foundation
|
||||
|
||||
@main
|
||||
struct TransifexStringCatalogSanitizer: ParsableCommand {
|
||||
@Argument(help: "The string catalog file to sanitize.")
|
||||
var input: String
|
||||
|
||||
mutating func run() throws {
|
||||
let url = URL(fileURLWithPath: input)
|
||||
let data = try Data(contentsOf: url)
|
||||
|
||||
guard var root = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
||||
throw TransifexStringCatalogSanitizerError.jsonObject
|
||||
}
|
||||
|
||||
guard var strings = root["strings"] as? [String: Any] else {
|
||||
throw TransifexStringCatalogSanitizerError.missingStrings
|
||||
}
|
||||
|
||||
try sanitizeStrings(&strings)
|
||||
|
||||
// Update the root with modified strings
|
||||
root["strings"] = strings
|
||||
|
||||
// Write the processed data back to the original file
|
||||
let processedData = try JSONSerialization.data(withJSONObject: root, options: [.prettyPrinted, .sortedKeys])
|
||||
try processedData.write(to: url)
|
||||
}
|
||||
|
||||
private func sanitizeStrings(_ strings: inout [String: Any]) throws {
|
||||
for key in strings.keys.sorted() {
|
||||
print("💬 \"\(key)\"")
|
||||
|
||||
guard var string = strings[key] as? [String: Any] else {
|
||||
throw TransifexStringCatalogSanitizerError.missingString
|
||||
}
|
||||
|
||||
guard var localizations = string["localizations"] as? [String: Any] else {
|
||||
throw TransifexStringCatalogSanitizerError.missingLocalizations
|
||||
}
|
||||
|
||||
try sanitizeLocalizations(&localizations)
|
||||
|
||||
// Update the string with modified localizations
|
||||
string["localizations"] = localizations
|
||||
strings[key] = string
|
||||
}
|
||||
}
|
||||
|
||||
private func sanitizeLocalizations(_ localizations: inout [String: Any]) throws {
|
||||
var localizationsToRemove: [String] = []
|
||||
|
||||
for localeCode in localizations.keys.sorted() {
|
||||
guard let localization = localizations[localeCode] as? [String: Any] else {
|
||||
throw TransifexStringCatalogSanitizerError.missingLocalization
|
||||
}
|
||||
|
||||
guard let stringUnit = localization["stringUnit"] as? [String: Any] else {
|
||||
throw TransifexStringCatalogSanitizerError.missingStringUnit
|
||||
}
|
||||
|
||||
guard let value = stringUnit["value"] as? String else {
|
||||
throw TransifexStringCatalogSanitizerError.missingValue
|
||||
}
|
||||
|
||||
if value.isEmpty {
|
||||
print("\t❌ \(localeCode): empty, will be removed")
|
||||
localizationsToRemove.append(localeCode)
|
||||
} else {
|
||||
print("\t✅ \(localeCode): \"\(value)\"")
|
||||
}
|
||||
}
|
||||
|
||||
// Remove empty localizations
|
||||
for localeCode in localizationsToRemove {
|
||||
localizations.removeValue(forKey: localeCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
// SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
enum TransifexStringCatalogSanitizerError: Error {
|
||||
case jsonObject
|
||||
case missingLocalization
|
||||
case missingLocalizations
|
||||
case missingString
|
||||
case missingStrings
|
||||
case missingStringUnit
|
||||
case missingValue
|
||||
}
|
||||
@ -19,6 +19,7 @@ endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW)
|
||||
|
||||
function (ADD_CMOCKA_TEST _testName _testSource)
|
||||
add_executable(${_testName} ${_testSource})
|
||||
set_target_properties(${_testName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY})
|
||||
target_link_libraries(${_testName} ${ARGN})
|
||||
add_test(${_testName} ${CMAKE_CURRENT_BINARY_DIR}/${_testName})
|
||||
add_test(${_testName} ${BIN_OUTPUT_DIRECTORY}/${_testName})
|
||||
endfunction (ADD_CMOCKA_TEST)
|
||||
|
||||
@ -1,422 +0,0 @@
|
||||
#.rst:
|
||||
# ECMAddAppIcon
|
||||
# -------------
|
||||
#
|
||||
# Add icons to executable files and packages.
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_add_app_icon(<sources_var>
|
||||
# ICONS <icon> [<icon> [...]]
|
||||
# [SIDEBAR_ICONS <icon> [<icon> [...]] # Since 5.49
|
||||
# [OUTFILE_BASENAME <name>]) # Since 5.49
|
||||
# )
|
||||
#
|
||||
# The given icons, whose names must match the pattern::
|
||||
#
|
||||
# <size>-<other_text>.png
|
||||
#
|
||||
# will be added to the executable target whose sources are specified by
|
||||
# ``<sources_var>`` on platforms that support it (Windows and Mac OS X).
|
||||
# Other icon files are ignored but on Mac SVG files can be supported and
|
||||
# it is thus possible to mix those with png files in a single macro call.
|
||||
#
|
||||
# ``<size>`` is a numeric pixel size (typically 16, 32, 48, 64, 128 or 256).
|
||||
# ``<other_text>`` can be any other text. See the platform notes below for any
|
||||
# recommendations about icon sizes.
|
||||
#
|
||||
# ``SIDEBAR_ICONS`` can be used to add Mac OS X sidebar
|
||||
# icons to the generated iconset. They are used when a folder monitored by the
|
||||
# application is dragged into Finder's sidebar. Since 5.49.
|
||||
#
|
||||
# ``OUTFILE_BASENAME`` will be used as the basename for the icon file. If
|
||||
# you specify it, the icon file will be called ``<OUTFILE_BASENAME>.icns`` on Mac OS X
|
||||
# and ``<OUTFILE_BASENAME>.ico`` on Windows. If you don't specify it, it defaults
|
||||
# to ``<sources_var>.<ext>``. Since 5.49.
|
||||
#
|
||||
#
|
||||
# Windows notes
|
||||
# * Icons are compiled into the executable using a resource file.
|
||||
# * Icons may not show up in Windows Explorer if the executable
|
||||
# target does not have the ``WIN32_EXECUTABLE`` property set.
|
||||
# * One of the tools png2ico (See :find-module:`FindPng2Ico`) or
|
||||
# icotool (see :find-module:`FindIcoTool`) is required.
|
||||
# * Supported sizes: 16, 20, 24, 32, 40, 48, 64, 128, 256, 512 and 1024.
|
||||
#
|
||||
# Mac OS X notes
|
||||
# * The executable target must have the ``MACOSX_BUNDLE`` property set.
|
||||
# * Icons are added to the bundle.
|
||||
# * If the ksvg2icns tool from KIconThemes is available, .svg and .svgz
|
||||
# files are accepted; the first that is converted successfully to .icns
|
||||
# will provide the application icon. SVG files are ignored otherwise.
|
||||
# * The tool iconutil (provided by Apple) is required for bitmap icons.
|
||||
# * Supported sizes: 16, 32, 64, 128, 256 (and 512, 1024 after OS X 10.9).
|
||||
# * At least a 128x128px (or an SVG) icon is required.
|
||||
# * Larger sizes are automatically used to substitute for smaller sizes on
|
||||
# "Retina" (high-resolution) displays. For example, a 32px icon, if
|
||||
# provided, will be used as a 32px icon on standard-resolution displays,
|
||||
# and as a 16px-equivalent icon (with an "@2x" tag) on high-resolution
|
||||
# displays. That is why you should provide 64px and 1024px icons although
|
||||
# they are not supported anymore directly. Instead they will be used as
|
||||
# 32px@2x and 512px@2x. ksvg2icns handles this internally.
|
||||
# * This function sets the ``MACOSX_BUNDLE_ICON_FILE`` variable to the name
|
||||
# of the generated icns file, so that it will be used as the
|
||||
# ``MACOSX_BUNDLE_ICON_FILE`` target property when you call
|
||||
# ``add_executable``.
|
||||
# * Sidebar icons should typically provided in 16, 32, 64, 128 and 256px.
|
||||
#
|
||||
# Since 1.7.0.
|
||||
|
||||
|
||||
#=============================================================================
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
# SPDX-FileCopyrightText: 2014 Ralf Habacker <ralf.habacker@freenet.de>
|
||||
# SPDX-FileCopyrightText: 2006-2009 Alexander Neundorf, <neundorf@kde.org>
|
||||
# SPDX-FileCopyrightText: 2006, 2007, Laurent Montel, <montel@kde.org>
|
||||
# SPDX-FileCopyrightText: 2007 Matthias Kretz <kretz@kde.org>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
# Copyright 2014 Alex Merry <alex.merry@kde.org>
|
||||
# Copyright 2014 Ralf Habacker <ralf.habacker@freenet.de>
|
||||
# Copyright 2006-2009 Alexander Neundorf, <neundorf@kde.org>
|
||||
# Copyright 2006, 2007, Laurent Montel, <montel@kde.org>
|
||||
# Copyright 2007 Matthias Kretz <kretz@kde.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
function(ecm_add_app_icon appsources)
|
||||
set(options)
|
||||
set(oneValueArgs OUTFILE_BASENAME ICON_INDEX DO_NOT_GENERATE_RC_FILE)
|
||||
set(multiValueArgs ICONS SIDEBAR_ICONS RC_DEPENDENCIES)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
if (ARG_DO_NOT_GENERATE_RC_FILE)
|
||||
set (_do_not_generate_rc_file TRUE)
|
||||
else()
|
||||
set (_do_not_generate_rc_file FALSE)
|
||||
endif()
|
||||
|
||||
if(NOT ARG_ICONS)
|
||||
message(FATAL_ERROR "No ICONS argument given to ecm_add_app_icon")
|
||||
endif()
|
||||
if(ARG_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_add_app_icon: ${ARG_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
find_program(KSVG2ICNS NAMES ksvg2icns)
|
||||
foreach(icon ${ARG_ICONS})
|
||||
get_filename_component(icon_full ${icon} ABSOLUTE)
|
||||
get_filename_component(icon_type ${icon_full} EXT)
|
||||
# do we have ksvg2icns in the path and did we receive an svg (or compressed svg) icon?
|
||||
if(KSVG2ICNS AND (${icon_type} STREQUAL ".svg" OR ${icon_type} STREQUAL ".svgz"))
|
||||
# convert the svg icon to an icon resource
|
||||
execute_process(COMMAND ${KSVG2ICNS} "${icon_full}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE KSVG2ICNS_ERROR)
|
||||
if(${KSVG2ICNS_ERROR})
|
||||
message(AUTHOR_WARNING "ksvg2icns could not generate an OS X application icon from ${icon}")
|
||||
else()
|
||||
# install the icns file we just created
|
||||
get_filename_component(icon_name ${icon_full} NAME_WE)
|
||||
set(MACOSX_BUNDLE_ICON_FILE ${icon_name}.icns PARENT_SCOPE)
|
||||
set(${appsources} "${${appsources}};${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns" PARENT_SCOPE)
|
||||
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
# we're done now
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
_ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;20;24;32;40;48;64;128;256;512;1024")
|
||||
else()
|
||||
_ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;24;32;48;64;128;256;512;1024")
|
||||
endif()
|
||||
if(ARG_SIDEBAR_ICONS)
|
||||
_ecm_add_app_icon_categorize_icons("${ARG_SIDEBAR_ICONS}" "sidebar_icons" "16;32;64;128;256")
|
||||
endif()
|
||||
|
||||
set(mac_icons
|
||||
# Icons: https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
|
||||
${icons_at_16px}
|
||||
${icons_at_32px}
|
||||
${icons_at_64px}
|
||||
${icons_at_128px}
|
||||
${icons_at_256px}
|
||||
${icons_at_512px}
|
||||
${icons_at_1024px})
|
||||
|
||||
set(mac_sidebar_icons
|
||||
# Sidebar Icons: https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
|
||||
${sidebar_icons_at_16px}
|
||||
${sidebar_icons_at_32px}
|
||||
${sidebar_icons_at_64px}
|
||||
${sidebar_icons_at_128px}
|
||||
${sidebar_icons_at_256px})
|
||||
|
||||
if (NOT (mac_icons OR mac_sidebar_icons))
|
||||
message(AUTHOR_WARNING "No icons suitable for use on macOS provided")
|
||||
endif()
|
||||
|
||||
|
||||
set(windows_icons ${icons_at_16px}
|
||||
${icons_at_20px}
|
||||
${icons_at_24px}
|
||||
${icons_at_32px}
|
||||
${icons_at_40px}
|
||||
${icons_at_48px}
|
||||
${icons_at_64px}
|
||||
${icons_at_128px}
|
||||
${icons_at_256px}
|
||||
${icons_at_512px}
|
||||
${icons_at_1024px})
|
||||
|
||||
if (NOT (windows_icons))
|
||||
message(AUTHOR_WARNING "No icons suitable for use on Windows provided")
|
||||
endif()
|
||||
|
||||
if (ARG_OUTFILE_BASENAME)
|
||||
set (_outfilebasename "${ARG_OUTFILE_BASENAME}")
|
||||
else()
|
||||
set (_outfilebasename "${appsources}")
|
||||
endif()
|
||||
set (_outfilename "${CMAKE_CURRENT_BINARY_DIR}/${_outfilebasename}")
|
||||
|
||||
if (WIN32 AND windows_icons)
|
||||
set(saved_CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}")
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_FIND_MODULE_DIR})
|
||||
find_package(Png2Ico)
|
||||
find_package(IcoTool)
|
||||
set(CMAKE_MODULE_PATH "${saved_CMAKE_MODULE_PATH}")
|
||||
|
||||
function(create_windows_icon_and_rc command args deps)
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.ico"
|
||||
COMMAND ${command}
|
||||
ARGS ${args}
|
||||
DEPENDS ${deps}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
if (NOT _do_not_generate_rc_file)
|
||||
# this bit's a little hacky to make the dependency stuff work
|
||||
file(WRITE "${_outfilename}.rc.in" "IDI_ICON${ARG_ICON_INDEX} ICON DISCARDABLE \"${_outfilename}.ico\"\n")
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.rc"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
ARGS -E copy "${_outfilename}.rc.in" "${_outfilename}.rc"
|
||||
DEPENDS ${ARG_RC_DEPENDENCIES} "${_outfilename}.ico"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
if (IcoTool_FOUND)
|
||||
list(APPEND icotool_args "-c" "-o" "${_outfilename}.ico")
|
||||
|
||||
# According to https://stackoverflow.com/a/40851713/2886832
|
||||
# Windows always chooses the first icon above 255px, all other ones will be ignored
|
||||
set(maxSize 0)
|
||||
foreach(size 256 512 1024)
|
||||
if(icons_at_${size}px)
|
||||
set(maxSize "${size}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(size 16 20 24 32 40 48 64 128 ${maxSize})
|
||||
if(NOT icons_at_${size}px)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(icotool_icon_arg "")
|
||||
if(size STREQUAL "${maxSize}")
|
||||
# maxSize icon needs to be included as raw png
|
||||
list(APPEND icotool_args "-r")
|
||||
endif()
|
||||
|
||||
foreach(icon ${icons_at_${size}px})
|
||||
list(APPEND icotool_args "${icons_at_${size}px}")
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
create_windows_icon_and_rc(IcoTool::IcoTool "${icotool_args}" "${windows_icons_modern}")
|
||||
set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
|
||||
|
||||
# standard png2ico has no rcfile argument
|
||||
# NOTE: We generally use https://github.com/hiiamok/png2ImageMagickICO
|
||||
# or similar on windows, which is why we provide resolutions >= 256px here.
|
||||
# Standard png2ico will fail with this.
|
||||
elseif(Png2Ico_FOUND AND NOT Png2Ico_HAS_RCFILE_ARGUMENT AND windows_icons)
|
||||
set(png2ico_args)
|
||||
list(APPEND png2ico_args "${_outfilename}.ico")
|
||||
list(APPEND png2ico_args "${windows_icons}")
|
||||
create_windows_icon_and_rc(Png2Ico::Png2Ico "${png2ico_args}" "${windows_icons}")
|
||||
|
||||
set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
|
||||
|
||||
# png2ico from kdewin provides rcfile argument
|
||||
elseif(Png2Ico_FOUND AND windows_icons)
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.rc" "${_outfilename}.ico"
|
||||
COMMAND Png2Ico::Png2Ico
|
||||
ARGS
|
||||
--rcfile "${_outfilename}.rc"
|
||||
"${_outfilename}.ico"
|
||||
${windows_icons}
|
||||
DEPENDS ${windows_icons}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
|
||||
|
||||
# else none of the supported tools was found
|
||||
else()
|
||||
message(WARNING "Unable to find the png2ico or icotool utilities or icons in matching sizes - application will not have an application icon!")
|
||||
endif()
|
||||
|
||||
elseif (APPLE AND (mac_icons OR mac_sidebar_icons))
|
||||
# first generate .iconset directory structure, then convert to .icns format using the Mac OS X "iconutil" utility,
|
||||
# to create retina compatible icon, you need png source files in pixel resolution 16x16, 32x32, 64x64, 128x128,
|
||||
# 256x256, 512x512, 1024x1024
|
||||
find_program(ICONUTIL_EXECUTABLE NAMES iconutil)
|
||||
if (ICONUTIL_EXECUTABLE)
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.iconset"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
ARGS -E make_directory "${_outfilename}.iconset"
|
||||
)
|
||||
set(iconset_icons)
|
||||
macro(copy_icon filename sizename type)
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.iconset/${type}_${sizename}.png"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
ARGS -E copy
|
||||
"${filename}"
|
||||
"${_outfilename}.iconset/${type}_${sizename}.png"
|
||||
DEPENDS
|
||||
"${_outfilename}.iconset"
|
||||
"${filename}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
list(APPEND iconset_icons
|
||||
"${_outfilename}.iconset/${type}_${sizename}.png")
|
||||
endmacro()
|
||||
|
||||
# List of supported sizes and filenames taken from:
|
||||
# https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
|
||||
foreach(size 16 32 128 256 512)
|
||||
math(EXPR double_size "2 * ${size}")
|
||||
foreach(file ${icons_at_${size}px})
|
||||
copy_icon("${file}" "${size}x${size}" "icon")
|
||||
endforeach()
|
||||
foreach(file ${icons_at_${double_size}px})
|
||||
copy_icon("${file}" "${size}x${size}@2x" "icon")
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# List of supported sizes and filenames taken from:
|
||||
# https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
|
||||
foreach(file ${sidebar_icons_at_16px})
|
||||
copy_icon("${file}" "16x16" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_32px})
|
||||
copy_icon("${file}" "16x16@2x" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_32px})
|
||||
copy_icon("${file}" "18x18" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_64px})
|
||||
copy_icon("${file}" "18x18@2x" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_128px})
|
||||
copy_icon("${file}" "32x32" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_256px})
|
||||
copy_icon("${file}" "32x32@2x" "sidebar")
|
||||
endforeach()
|
||||
|
||||
# generate .icns icon file
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.icns"
|
||||
COMMAND ${ICONUTIL_EXECUTABLE}
|
||||
ARGS
|
||||
--convert icns
|
||||
--output "${_outfilename}.icns"
|
||||
"${_outfilename}.iconset"
|
||||
DEPENDS "${iconset_icons}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
# This will register the icon into the bundle
|
||||
set(MACOSX_BUNDLE_ICON_FILE "${_outfilebasename}.icns" PARENT_SCOPE)
|
||||
|
||||
# Append the icns file to the sources list so it will be a dependency to the
|
||||
# main target
|
||||
set(${appsources} "${${appsources}};${_outfilename}.icns" PARENT_SCOPE)
|
||||
|
||||
# Install the icon into the Resources dir in the bundle
|
||||
set_source_files_properties("${_outfilename}.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
else()
|
||||
message(STATUS "Unable to find the iconutil utility - application will not have an application icon!")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
macro(_ecm_add_app_icon_categorize_icons icons type known_sizes)
|
||||
set(_${type}_known_sizes)
|
||||
foreach(size ${known_sizes})
|
||||
set(${type}_at_${size}px)
|
||||
list(APPEND _${type}_known_sizes ${size})
|
||||
endforeach()
|
||||
|
||||
|
||||
foreach(icon ${icons})
|
||||
get_filename_component(icon_full ${icon} ABSOLUTE)
|
||||
if (NOT EXISTS "${icon_full}")
|
||||
message(AUTHOR_WARNING "${icon_full} does not exist, ignoring")
|
||||
else()
|
||||
get_filename_component(icon_name ${icon} NAME)
|
||||
string(REGEX MATCH "([0-9]+)\\-[^/]+\\.([a-z]+)$"
|
||||
_dummy "${icon_name}")
|
||||
set(size "${CMAKE_MATCH_1}")
|
||||
set(ext "${CMAKE_MATCH_2}")
|
||||
|
||||
if (NOT (ext STREQUAL "svg" OR ext STREQUAL "svgz"))
|
||||
if (NOT size)
|
||||
message(AUTHOR_WARNING "${icon_full} is not named correctly for ecm_add_app_icon - ignoring")
|
||||
elseif (NOT ext STREQUAL "png")
|
||||
message(AUTHOR_WARNING "${icon_full} is not a png file - ignoring")
|
||||
else()
|
||||
list(FIND _${type}_known_sizes ${size} offset)
|
||||
|
||||
if (offset GREATER -1)
|
||||
list(APPEND ${type}_at_${size}px "${icon_full}")
|
||||
else()
|
||||
message(STATUS "not found ${type}_at_${size}px ${icon_full}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endmacro()
|
||||
@ -1,29 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMCoverageOption
|
||||
--------------------
|
||||
|
||||
Allow users to easily enable GCov code coverage support.
|
||||
|
||||
Code coverage allows you to check how much of your codebase is covered by
|
||||
your tests. This module makes it easy to build with support for
|
||||
`GCov <https://gcc.gnu.org/onlinedocs/gcc/Gcov.html>`_.
|
||||
|
||||
When this module is included, a ``BUILD_COVERAGE`` option is added (default
|
||||
OFF). Turning this option on enables GCC's coverage instrumentation, and
|
||||
links against ``libgcov``.
|
||||
|
||||
Note that this will probably break the build if you are not using GCC.
|
||||
|
||||
Since 1.3.0.
|
||||
#]=======================================================================]
|
||||
|
||||
option(BUILD_COVERAGE "Build the project with gcov support" OFF)
|
||||
|
||||
if(BUILD_COVERAGE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov")
|
||||
endif()
|
||||
@ -1,169 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2014 Mathieu Tarral <mathieu.tarral@gmail.com>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMEnableSanitizers
|
||||
-------------------
|
||||
|
||||
Enable compiler sanitizer flags.
|
||||
|
||||
The following sanitizers are supported:
|
||||
|
||||
- Address Sanitizer
|
||||
- Memory Sanitizer
|
||||
- Thread Sanitizer
|
||||
- Leak Sanitizer
|
||||
- Undefined Behaviour Sanitizer
|
||||
|
||||
All of them are implemented in Clang, depending on your version, and
|
||||
there is an work in progress in GCC, where some of them are currently
|
||||
implemented.
|
||||
|
||||
This module will check your current compiler version to see if it
|
||||
supports the sanitizers that you want to enable
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Simply add::
|
||||
|
||||
include(ECMEnableSanitizers)
|
||||
|
||||
to your ``CMakeLists.txt``. Note that this module is included in
|
||||
KDECompilerSettings, so projects using that module do not need to also
|
||||
include this one.
|
||||
|
||||
The sanitizers are not enabled by default. Instead, you must set
|
||||
``ECM_ENABLE_SANITIZERS`` (either in your ``CMakeLists.txt`` or on the
|
||||
command line) to a semicolon-separated list of sanitizers you wish to enable.
|
||||
The options are:
|
||||
|
||||
- address
|
||||
- memory
|
||||
- thread
|
||||
- leak
|
||||
- undefined
|
||||
- fuzzer
|
||||
|
||||
The sanitizers "address", "memory" and "thread" are mutually exclusive. You
|
||||
cannot enable two of them in the same build.
|
||||
|
||||
"leak" requires the "address" sanitizer.
|
||||
|
||||
.. note::
|
||||
|
||||
To reduce the overhead induced by the instrumentation of the sanitizers, it
|
||||
is advised to enable compiler optimizations (``-O1`` or higher).
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
This is an example of usage::
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DECM_ENABLE_SANITIZERS='address;leak;undefined' ..
|
||||
|
||||
.. note::
|
||||
|
||||
Most of the sanitizers will require Clang. To enable it, use::
|
||||
|
||||
-DCMAKE_CXX_COMPILER=clang++
|
||||
|
||||
Since 1.3.0.
|
||||
#]=======================================================================]
|
||||
|
||||
# MACRO check_compiler_version
|
||||
#-----------------------------
|
||||
macro (check_compiler_version gcc_required_version clang_required_version msvc_required_version)
|
||||
if (
|
||||
(
|
||||
CMAKE_CXX_COMPILER_ID MATCHES "GNU"
|
||||
AND
|
||||
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${gcc_required_version}
|
||||
)
|
||||
OR
|
||||
(
|
||||
CMAKE_CXX_COMPILER_ID MATCHES "Clang"
|
||||
AND
|
||||
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${clang_required_version}
|
||||
)
|
||||
OR
|
||||
(
|
||||
CMAKE_CXX_COMPILER_ID MATCHES "MSVC"
|
||||
AND
|
||||
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${msvc_required_version}
|
||||
)
|
||||
)
|
||||
# error !
|
||||
message(FATAL_ERROR "You ask to enable the sanitizer ${CUR_SANITIZER},
|
||||
but your compiler ${CMAKE_CXX_COMPILER_ID} version ${CMAKE_CXX_COMPILER_VERSION}
|
||||
does not support it !
|
||||
You should use at least GCC ${gcc_required_version}, Clang ${clang_required_version}
|
||||
or MSVC ${msvc_required_version}
|
||||
(99.99 means not implemented yet)")
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
# MACRO check_compiler_support
|
||||
#------------------------------
|
||||
macro (enable_sanitizer_flags sanitize_option)
|
||||
if (${sanitize_option} MATCHES "address")
|
||||
check_compiler_version("4.8" "3.1" "19.28")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=address")
|
||||
else()
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls")
|
||||
set(XSAN_LINKER_FLAGS "-fsanitize=address")
|
||||
endif()
|
||||
elseif (${sanitize_option} MATCHES "thread")
|
||||
check_compiler_version("4.8" "3.1" "99.99")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=thread")
|
||||
set(XSAN_LINKER_FLAGS "tsan")
|
||||
elseif (${sanitize_option} MATCHES "memory")
|
||||
check_compiler_version("99.99" "3.1" "99.99")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=memory")
|
||||
elseif (${sanitize_option} MATCHES "leak")
|
||||
check_compiler_version("4.9" "3.4" "99.99")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=leak")
|
||||
set(XSAN_LINKER_FLAGS "lsan")
|
||||
elseif (${sanitize_option} MATCHES "undefined")
|
||||
check_compiler_version("4.9" "3.1" "99.99")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls")
|
||||
elseif (${sanitize_option} MATCHES "fuzzer")
|
||||
check_compiler_version("99.99" "6.0" "99.99")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=fuzzer")
|
||||
else ()
|
||||
message(FATAL_ERROR "Compiler sanitizer option \"${sanitize_option}\" not supported.")
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
if (ECM_ENABLE_SANITIZERS)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
# for each element of the ECM_ENABLE_SANITIZERS list
|
||||
foreach ( CUR_SANITIZER ${ECM_ENABLE_SANITIZERS} )
|
||||
# lowercase filter
|
||||
string(TOLOWER ${CUR_SANITIZER} CUR_SANITIZER)
|
||||
# check option and enable appropriate flags
|
||||
enable_sanitizer_flags ( ${CUR_SANITIZER} )
|
||||
# TODO: GCC will not link pthread library if enabled ASan
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${XSAN_COMPILE_FLAGS}" )
|
||||
link_libraries(${XSAN_LINKER_FLAGS})
|
||||
endif()
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${XSAN_COMPILE_FLAGS}" )
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
link_libraries(${XSAN_LINKER_FLAGS})
|
||||
endif()
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
string(REPLACE "-Wl,--no-undefined" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
string(REPLACE "-Wl,--no-undefined" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}")
|
||||
link_libraries(${XSAN_LINKER_FLAGS})
|
||||
endif ()
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS "Tried to enable sanitizers (-DECM_ENABLE_SANITIZERS=${ECM_ENABLE_SANITIZERS}), \
|
||||
but compiler (${CMAKE_CXX_COMPILER_ID}) does not have sanitizer support")
|
||||
endif()
|
||||
endif()
|
||||
@ -1,300 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
#.rst:
|
||||
# ECMFindModuleHelpers
|
||||
# --------------------
|
||||
#
|
||||
# Helper macros for find modules: ecm_find_package_version_check(),
|
||||
# ecm_find_package_parse_components() and
|
||||
# ecm_find_package_handle_library_components().
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_find_package_version_check(<name>)
|
||||
#
|
||||
# Prints warnings if the CMake version or the project's required CMake version
|
||||
# is older than that required by extra-cmake-modules.
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_find_package_parse_components(<name>
|
||||
# RESULT_VAR <variable>
|
||||
# KNOWN_COMPONENTS <component1> [<component2> [...]]
|
||||
# [SKIP_DEPENDENCY_HANDLING])
|
||||
#
|
||||
# This macro will populate <variable> with a list of components found in
|
||||
# <name>_FIND_COMPONENTS, after checking that all those components are in the
|
||||
# list of KNOWN_COMPONENTS; if there are any unknown components, it will print
|
||||
# an error or warning (depending on the value of <name>_FIND_REQUIRED) and call
|
||||
# return().
|
||||
#
|
||||
# The order of components in <variable> is guaranteed to match the order they
|
||||
# are listed in the KNOWN_COMPONENTS argument.
|
||||
#
|
||||
# If SKIP_DEPENDENCY_HANDLING is not set, for each component the variable
|
||||
# <name>_<component>_component_deps will be checked for dependent components.
|
||||
# If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
|
||||
# dependencies will also be added to <variable>.
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_find_package_handle_library_components(<name>
|
||||
# COMPONENTS <component> [<component> [...]]
|
||||
# [SKIP_DEPENDENCY_HANDLING])
|
||||
# [SKIP_PKG_CONFIG])
|
||||
#
|
||||
# Creates an imported library target for each component. The operation of this
|
||||
# macro depends on the presence of a number of CMake variables.
|
||||
#
|
||||
# The <name>_<component>_lib variable should contain the name of this library,
|
||||
# and <name>_<component>_header variable should contain the name of a header
|
||||
# file associated with it (whatever relative path is normally passed to
|
||||
# '#include'). <name>_<component>_header_subdir variable can be used to specify
|
||||
# which subdirectory of the include path the headers will be found in.
|
||||
# ecm_find_package_components() will then search for the library
|
||||
# and include directory (creating appropriate cache variables) and create an
|
||||
# imported library target named <name>::<component>.
|
||||
#
|
||||
# Additional variables can be used to provide additional information:
|
||||
#
|
||||
# If SKIP_PKG_CONFIG, the <name>_<component>_pkg_config variable is set, and
|
||||
# pkg-config is found, the pkg-config module given by
|
||||
# <name>_<component>_pkg_config will be searched for and used to help locate the
|
||||
# library and header file. It will also be used to set
|
||||
# <name>_<component>_VERSION.
|
||||
#
|
||||
# Note that if version information is found via pkg-config,
|
||||
# <name>_<component>_FIND_VERSION can be set to require a particular version
|
||||
# for each component.
|
||||
#
|
||||
# If SKIP_DEPENDENCY_HANDLING is not set, the INTERFACE_LINK_LIBRARIES property
|
||||
# of the imported target for <component> will be set to contain the imported
|
||||
# targets for the components listed in <name>_<component>_component_deps.
|
||||
# <component>_FOUND will also be set to false if any of the components in
|
||||
# <name>_<component>_component_deps are not found. This requires the components
|
||||
# in <name>_<component>_component_deps to be listed before <component> in the
|
||||
# COMPONENTS argument.
|
||||
#
|
||||
# The following variables will be set:
|
||||
#
|
||||
# ``<name>_TARGETS``
|
||||
# the imported targets
|
||||
# ``<name>_LIBRARIES``
|
||||
# the found libraries
|
||||
# ``<name>_INCLUDE_DIRS``
|
||||
# the combined required include directories for the components
|
||||
# ``<name>_DEFINITIONS``
|
||||
# the "other" CFLAGS provided by pkg-config, if any
|
||||
# ``<name>_VERSION``
|
||||
# the value of ``<name>_<component>_VERSION`` for the first component that
|
||||
# has this variable set (note that components are searched for in the order
|
||||
# they are passed to the macro), although if it is already set, it will not
|
||||
# be altered
|
||||
#
|
||||
# Note that these variables are never cleared, so if
|
||||
# ecm_find_package_handle_library_components() is called multiple times with
|
||||
# different components (typically because of multiple find_package() calls) then
|
||||
# ``<name>_TARGETS``, for example, will contain all the targets found in any
|
||||
# call (although no duplicates).
|
||||
#
|
||||
# Since pre-1.0.0.
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2014 Alex Merry <alex.merry@kde.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
macro(ecm_find_package_version_check module_name)
|
||||
if(CMAKE_VERSION VERSION_LESS 2.8.12)
|
||||
message(FATAL_ERROR "CMake 2.8.12 is required by Find${module_name}.cmake")
|
||||
endif()
|
||||
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
|
||||
message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Find${module_name}.cmake")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(ecm_find_package_parse_components module_name)
|
||||
set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING)
|
||||
set(ecm_fppc_oneValueArgs RESULT_VAR)
|
||||
set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS)
|
||||
cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ECM_FPPC_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_RESULT_VAR)
|
||||
message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_KNOWN_COMPONENTS)
|
||||
message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_DEFAULT_COMPONENTS)
|
||||
set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||
endif()
|
||||
|
||||
if(${module_name}_FIND_COMPONENTS)
|
||||
set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS})
|
||||
|
||||
if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING)
|
||||
# Make sure deps are included
|
||||
foreach(ecm_fppc_comp ${ecm_fppc_requestedComps})
|
||||
foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps})
|
||||
list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index)
|
||||
if("${ecm_fppc_index}" STREQUAL "-1")
|
||||
if(NOT ${module_name}_FIND_QUIETLY)
|
||||
message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}")
|
||||
endif()
|
||||
list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS "Skipping dependency handling for ${module_name}")
|
||||
endif()
|
||||
list(REMOVE_DUPLICATES ecm_fppc_requestedComps)
|
||||
|
||||
# This makes sure components are listed in the same order as
|
||||
# KNOWN_COMPONENTS (potentially important for inter-dependencies)
|
||||
set(${ECM_FPPC_RESULT_VAR})
|
||||
foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||
list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index)
|
||||
if(NOT "${ecm_fppc_index}" STREQUAL "-1")
|
||||
list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}")
|
||||
list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index})
|
||||
endif()
|
||||
endforeach()
|
||||
# if there are any left, they are unknown components
|
||||
if(ecm_fppc_requestedComps)
|
||||
set(ecm_fppc_msgType STATUS)
|
||||
if(${module_name}_FIND_REQUIRED)
|
||||
set(ecm_fppc_msgType FATAL_ERROR)
|
||||
endif()
|
||||
if(NOT ${module_name}_FIND_QUIETLY)
|
||||
message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(ecm_find_package_handle_library_components module_name)
|
||||
set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING)
|
||||
set(ecm_fpwc_oneValueArgs)
|
||||
set(ecm_fpwc_multiValueArgs COMPONENTS)
|
||||
cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ECM_FPWC_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ECM_FPWC_COMPONENTS)
|
||||
message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components")
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package(PkgConfig)
|
||||
foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS})
|
||||
set(ecm_fpwc_dep_vars)
|
||||
set(ecm_fpwc_dep_targets)
|
||||
if(NOT SKIP_DEPENDENCY_HANDLING)
|
||||
foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps})
|
||||
list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND")
|
||||
list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config)
|
||||
pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET
|
||||
${${module_name}_${ecm_fpwc_comp}_pkg_config})
|
||||
endif()
|
||||
|
||||
find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
NAMES ${${module_name}_${ecm_fpwc_comp}_header}
|
||||
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir}
|
||||
)
|
||||
find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
NAMES ${${module_name}_${ecm_fpwc_comp}_lib}
|
||||
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}")
|
||||
if(NOT ${module_name}_VERSION)
|
||||
set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION})
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp}
|
||||
FOUND_VAR
|
||||
${module_name}_${ecm_fpwc_comp}_FOUND
|
||||
REQUIRED_VARS
|
||||
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
${ecm_fpwc_dep_vars}
|
||||
VERSION_VAR
|
||||
${module_name}_${ecm_fpwc_comp}_VERSION
|
||||
)
|
||||
|
||||
mark_as_advanced(
|
||||
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(${module_name}_${ecm_fpwc_comp}_FOUND)
|
||||
list(APPEND ${module_name}_LIBRARIES
|
||||
"${${module_name}_${ecm_fpwc_comp}_LIBRARY}")
|
||||
list(APPEND ${module_name}_INCLUDE_DIRS
|
||||
"${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}")
|
||||
set(${module_name}_DEFINITIONS
|
||||
${${module_name}_DEFINITIONS}
|
||||
${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS})
|
||||
if(NOT TARGET ${module_name}::${ecm_fpwc_comp})
|
||||
add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED)
|
||||
set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES
|
||||
IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}"
|
||||
)
|
||||
endif()
|
||||
list(APPEND ${module_name}_TARGETS
|
||||
"${module_name}::${ecm_fpwc_comp}")
|
||||
endif()
|
||||
endforeach()
|
||||
if(${module_name}_LIBRARIES)
|
||||
list(REMOVE_DUPLICATES ${module_name}_LIBRARIES)
|
||||
endif()
|
||||
if(${module_name}_INCLUDE_DIRS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS)
|
||||
endif()
|
||||
if(${module_name}_DEFINITIONS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS)
|
||||
endif()
|
||||
if(${module_name}_TARGETS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_TARGETS)
|
||||
endif()
|
||||
endmacro()
|
||||
@ -1,4 +0,0 @@
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../modules/ECMFindModuleHelpers.cmake)
|
||||
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
@ -1,62 +0,0 @@
|
||||
#.rst:
|
||||
# FindInotify
|
||||
# --------------
|
||||
#
|
||||
# Try to find inotify on this system. This finds:
|
||||
# - libinotify on Unix like systems, or
|
||||
# - the kernel's inotify on Linux systems.
|
||||
#
|
||||
# This will define the following variables:
|
||||
#
|
||||
# ``Inotify_FOUND``
|
||||
# True if inotify is available
|
||||
# ``Inotify_LIBRARIES``
|
||||
# This has to be passed to target_link_libraries()
|
||||
# ``Inotify_INCLUDE_DIRS``
|
||||
# This has to be passed to target_include_directories()
|
||||
#
|
||||
# On Linux, the libraries and include directories are empty,
|
||||
# even though ``Inotify_FOUND`` may be set to TRUE. This is because
|
||||
# no special includes or libraries are needed. On other systems
|
||||
# these may be needed to use inotify.
|
||||
#
|
||||
# Since 5.32.0.
|
||||
|
||||
#=============================================================================
|
||||
# SPDX-FileCopyrightText: 2016 Tobias C. Berner <tcberner@FreeBSD.org>
|
||||
# SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#=============================================================================
|
||||
|
||||
find_path(Inotify_INCLUDE_DIRS sys/inotify.h)
|
||||
|
||||
if(Inotify_INCLUDE_DIRS)
|
||||
# On Linux there is no library to link against, on the BSDs there is.
|
||||
# On the BSD's, inotify is implemented through a library, libinotify.
|
||||
if( CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
set(Inotify_FOUND TRUE)
|
||||
set(Inotify_LIBRARIES "")
|
||||
set(Inotify_INCLUDE_DIRS "")
|
||||
else()
|
||||
find_library(Inotify_LIBRARIES NAMES inotify)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Inotify
|
||||
FOUND_VAR
|
||||
Inotify_FOUND
|
||||
REQUIRED_VARS
|
||||
Inotify_LIBRARIES
|
||||
Inotify_INCLUDE_DIRS
|
||||
)
|
||||
mark_as_advanced(Inotify_LIBRARIES Inotify_INCLUDE_DIRS)
|
||||
include(FeatureSummary)
|
||||
set_package_properties(Inotify PROPERTIES
|
||||
URL "https://github.com/libinotify-kqueue/"
|
||||
DESCRIPTION "inotify API on the *BSD family of operating systems."
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
set(Inotify_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(Inotify_LIBRARIES Inotify_INCLUDE_DIRS)
|
||||
@ -1,93 +0,0 @@
|
||||
#.rst:
|
||||
# FindSharedMimeInfo
|
||||
# ------------------
|
||||
#
|
||||
# Try to find the shared-mime-info package.
|
||||
#
|
||||
# This will define the following variables:
|
||||
#
|
||||
# ``SharedMimeInfo_FOUND``
|
||||
# True if system has the shared-mime-info package
|
||||
# ``UPDATE_MIME_DATABASE_EXECUTABLE``
|
||||
# The update-mime-database executable
|
||||
#
|
||||
# and the following imported targets:
|
||||
#
|
||||
# ``SharedMimeInfo::UpdateMimeDatabase``
|
||||
# The update-mime-database executable
|
||||
#
|
||||
# The follow macro is available::
|
||||
#
|
||||
# update_xdg_mimetypes(<path>)
|
||||
#
|
||||
# Updates the XDG mime database at install time (unless the ``$DESTDIR``
|
||||
# environment variable is set, in which case it is up to package managers to
|
||||
# perform this task).
|
||||
#
|
||||
# Since pre-1.0.0.
|
||||
|
||||
#=============================================================================
|
||||
# SPDX-FileCopyrightText: 2013-2014 Alex Merry <alex.merry@kdemail.net>
|
||||
# SPDX-FileCopyrightText: 2007 Pino Toscano <toscano.pino@tiscali.it>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#=============================================================================
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake)
|
||||
|
||||
ecm_find_package_version_check(SharedMimeInfo)
|
||||
|
||||
find_program (UPDATE_MIME_DATABASE_EXECUTABLE NAMES update-mime-database)
|
||||
|
||||
if (UPDATE_MIME_DATABASE_EXECUTABLE)
|
||||
execute_process(
|
||||
COMMAND "${UPDATE_MIME_DATABASE_EXECUTABLE}" -v
|
||||
OUTPUT_VARIABLE _smiVersionRaw
|
||||
ERROR_VARIABLE _smiVersionRaw)
|
||||
|
||||
string(REGEX REPLACE "update-mime-database \\([a-zA-Z\\-]+\\) ([0-9]\\.[0-9]+).*"
|
||||
"\\1" SharedMimeInfo_VERSION_STRING "${_smiVersionRaw}")
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SharedMimeInfo
|
||||
FOUND_VAR
|
||||
SharedMimeInfo_FOUND
|
||||
REQUIRED_VARS
|
||||
UPDATE_MIME_DATABASE_EXECUTABLE
|
||||
VERSION_VAR
|
||||
SharedMimeInfo_VERSION_STRING)
|
||||
|
||||
if(SharedMimeInfo_FOUND AND NOT TARGET SharedMimeInfo::UpdateMimeDatabase)
|
||||
add_executable(SharedMimeInfo::UpdateMimeDatabase IMPORTED)
|
||||
set_target_properties(SharedMimeInfo::UpdateMimeDatabase PROPERTIES
|
||||
IMPORTED_LOCATION "${UPDATE_MIME_DATABASE_EXECUTABLE}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(UPDATE_MIME_DATABASE_EXECUTABLE)
|
||||
|
||||
function(UPDATE_XDG_MIMETYPES _path)
|
||||
get_filename_component(_xdgmimeDir "${_path}" NAME)
|
||||
if("${_xdgmimeDir}" STREQUAL packages )
|
||||
get_filename_component(_xdgmimeDir "${_path}" PATH)
|
||||
else()
|
||||
set(_xdgmimeDir "${_path}")
|
||||
endif()
|
||||
|
||||
# Note that targets and most variables are not available to install code
|
||||
install(CODE "
|
||||
set(DESTDIR_VALUE \"\$ENV{DESTDIR}\")
|
||||
if (NOT DESTDIR_VALUE)
|
||||
# under Windows relative paths are used, that's why it runs from CMAKE_INSTALL_PREFIX
|
||||
message(STATUS \"Updating MIME database at \${CMAKE_INSTALL_PREFIX}/${_xdgmimeDir}\")
|
||||
execute_process(COMMAND \"${UPDATE_MIME_DATABASE_EXECUTABLE}\" -n \"${_xdgmimeDir}\"
|
||||
WORKING_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}\")
|
||||
endif (NOT DESTDIR_VALUE)
|
||||
")
|
||||
endfunction()
|
||||
|
||||
include(FeatureSummary)
|
||||
set_package_properties(SharedMimeInfo PROPERTIES
|
||||
URL https://freedesktop.org/wiki/Software/shared-mime-info/
|
||||
DESCRIPTION "A database of common MIME types")
|
||||
@ -49,7 +49,6 @@
|
||||
<file>src/gui/tray/EncryptionTokenDiscoveryDialog.qml</file>
|
||||
<file>src/gui/tray/NCBusyIndicator.qml</file>
|
||||
<file>src/gui/tray/NCIconWithBackgroundImage.qml</file>
|
||||
<file>src/gui/tray/NCToolTip.qml</file>
|
||||
<file>src/gui/tray/NCProgressBar.qml</file>
|
||||
<file>src/gui/tray/EnforcedPlainTextLabel.qml</file>
|
||||
<file>theme/Style/Style.qml</file>
|
||||
@ -62,8 +61,5 @@
|
||||
<file>src/gui/ConflictItemFileInfo.qml</file>
|
||||
<file>src/gui/macOS/ui/FileProviderSettings.qml</file>
|
||||
<file>src/gui/macOS/ui/FileProviderFileDelegate.qml</file>
|
||||
<file>src/gui/macOS/ui/FileProviderEvictionDialog.qml</file>
|
||||
<file>src/gui/macOS/ui/FileProviderSyncStatus.qml</file>
|
||||
<file>src/gui/macOS/ui/FileProviderStorageInfo.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
if(APPLE)
|
||||
set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/${APPLICATION_ICON_NAME}.icns")
|
||||
|
||||
if (CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
|
||||
if (NEXTCLOUD_DEV)
|
||||
set(XCODE_TARGET_CONFIGURATION "Debug")
|
||||
else()
|
||||
set(XCODE_TARGET_CONFIGURATION "Release")
|
||||
|
||||
7
shell_integration/MacOSX/NextcloudIntegration/.gitignore
vendored
Normal file
7
shell_integration/MacOSX/NextcloudIntegration/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
DerivedData
|
||||
|
||||
# exception
|
||||
!NextcloudDev/Build.xcconfig.template
|
||||
19
shell_integration/MacOSX/NextcloudIntegration/.tx/config
Executable file
19
shell_integration/MacOSX/NextcloudIntegration/.tx/config
Executable file
@ -0,0 +1,19 @@
|
||||
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
[main]
|
||||
host = https://app.transifex.com
|
||||
# Complete mapping from language codes on Transifex to the ones configured in the Xcode project for those where the codes diverge.
|
||||
lang_map = bg_BG: bg, bn_BD: bn, cs_CZ: cs, cy_GB: cy, es_AR: es-AR, es_DO: es-DO, es_EC: es-EC, es_SV: es-SV, es_GT: es-GT, es_HN: es-HN, es_419: es-419, es_MX: es-MX, es_NI: es-NI, es_PA: es-PA, es_PY: es-PY, es_PE: es-PE, es_PR: es-PR, es_UY: es-UY, et_EE: et, fi_FI: fi, hi_IN: hi, hu_HU: hu, ja_JP: ja, ka_GE: ka, lt_LT: lt, ms_MY: ms, nb_NO: nb-NO, nn_NO: nn-NO, pt_BR: pt-BR, pt_PT: pt-PT, sk_SK: sk, th_TH: th, ur_PK: ur, zh_CN: zh-Hans, zh_HK: zh-HK, zh_TW: zh-Hant-TW, zu_ZA: zu
|
||||
|
||||
[o:nextcloud:p:nextcloud:r:client-fileprovider]
|
||||
file_filter = FileProviderExt/Localizable.xcstrings
|
||||
source_file = FileProviderExt/Localizable.xcstrings
|
||||
source_lang = en
|
||||
type = XCSTRINGS
|
||||
|
||||
[o:nextcloud:p:nextcloud:r:client-fileproviderui]
|
||||
file_filter = FileProviderUIExt/Localizable.xcstrings
|
||||
source_file = FileProviderUIExt/Localizable.xcstrings
|
||||
source_lang = en
|
||||
type = XCSTRINGS
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"symbol-rendering-intent" : "template"
|
||||
},
|
||||
"symbols" : [
|
||||
{
|
||||
"filename" : "FileProviderDomainSymbol.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 341-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 3300 2200">
|
||||
<!--glyph: "", point size: 100.0, font version: "20.0d10e1", template writer version: "138.0.0"-->
|
||||
<style>.SFSymbolsPreviewWireframe {fill:none;opacity:1.0;stroke:black;stroke-width:0.5}
|
||||
</style>
|
||||
<g id="Notes">
|
||||
<rect height="2200" id="artboard" style="fill:white;opacity:1" width="3300" x="0" y="0"/>
|
||||
<line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="292" y2="292"/>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 322)">Weight/Scale Variations</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 559.711 322)">Ultralight</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 856.422 322)">Thin</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1153.13 322)">Light</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1449.84 322)">Regular</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1746.56 322)">Medium</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2043.27 322)">Semibold</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2339.98 322)">Bold</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2636.69 322)">Heavy</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2933.4 322)">Black</text>
|
||||
<line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1903" y2="1903"/>
|
||||
<g transform="matrix(0.2 0 0 0.2 263 1933)">
|
||||
<path d="m46.2402 4.15039c21.7773 0 39.4531-17.627 39.4531-39.4043s-17.6758-39.4043-39.4531-39.4043c-21.7285 0-39.4043 17.627-39.4043 39.4043s17.6758 39.4043 39.4043 39.4043Zm0-7.42188c-17.6758 0-31.9336-14.3066-31.9336-31.9824s14.2578-31.9824 31.9336-31.9824 31.9824 14.3066 31.9824 31.9824-14.3066 31.9824-31.9824 31.9824Zm-17.9688-31.9824c0 2.14844 1.51367 3.61328 3.75977 3.61328h10.498v10.5957c0 2.19727 1.46484 3.71094 3.61328 3.71094 2.24609 0 3.71094-1.51367 3.71094-3.71094v-10.5957h10.5957c2.19727 0 3.71094-1.46484 3.71094-3.61328 0-2.19727-1.51367-3.71094-3.71094-3.71094h-10.5957v-10.5469c0-2.24609-1.46484-3.75977-3.71094-3.75977-2.14844 0-3.61328 1.51367-3.61328 3.75977v10.5469h-10.498c-2.24609 0-3.75977 1.51367-3.75977 3.71094Z"/>
|
||||
</g>
|
||||
<g transform="matrix(0.2 0 0 0.2 281.506 1933)">
|
||||
<path d="m58.5449 14.5508c27.4902 0 49.8047-22.3145 49.8047-49.8047s-22.3145-49.8047-49.8047-49.8047-49.8047 22.3145-49.8047 49.8047 22.3145 49.8047 49.8047 49.8047Zm0-8.30078c-22.9492 0-41.5039-18.5547-41.5039-41.5039s18.5547-41.5039 41.5039-41.5039 41.5039 18.5547 41.5039 41.5039-18.5547 41.5039-41.5039 41.5039Zm-22.6562-41.5039c0 2.39258 1.66016 4.00391 4.15039 4.00391h14.3555v14.4043c0 2.44141 1.66016 4.15039 4.05273 4.15039 2.44141 0 4.15039-1.66016 4.15039-4.15039v-14.4043h14.4043c2.44141 0 4.15039-1.61133 4.15039-4.00391 0-2.44141-1.70898-4.15039-4.15039-4.15039h-14.4043v-14.3555c0-2.49023-1.70898-4.19922-4.15039-4.19922-2.39258 0-4.05273 1.70898-4.05273 4.19922v14.3555h-14.3555c-2.49023 0-4.15039 1.70898-4.15039 4.15039Z"/>
|
||||
</g>
|
||||
<g transform="matrix(0.2 0 0 0.2 304.924 1933)">
|
||||
<path d="m74.8535 28.3203c35.1074 0 63.623-28.4668 63.623-63.5742s-28.5156-63.623-63.623-63.623-63.5742 28.5156-63.5742 63.623 28.4668 63.5742 63.5742 63.5742Zm0-9.08203c-30.127 0-54.4922-24.3652-54.4922-54.4922s24.3652-54.4922 54.4922-54.4922 54.4922 24.3652 54.4922 54.4922-24.3652 54.4922-54.4922 54.4922Zm-28.8574-54.4922c0 2.58789 1.85547 4.39453 4.58984 4.39453h19.7266v19.7754c0 2.68555 1.85547 4.58984 4.44336 4.58984 2.68555 0 4.54102-1.85547 4.54102-4.58984v-19.7754h19.7754c2.68555 0 4.58984-1.80664 4.58984-4.39453 0-2.73438-1.85547-4.58984-4.58984-4.58984h-19.7754v-19.7266c0-2.73438-1.85547-4.63867-4.54102-4.63867-2.58789 0-4.44336 1.9043-4.44336 4.63867v19.7266h-19.7266c-2.73438 0-4.58984 1.85547-4.58984 4.58984Z"/>
|
||||
</g>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 1953)">Design Variations</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1971)">Symbols are supported in up to nine weights and three scales.</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1989)">For optimal layout with text and other symbols, vertically align</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 2007)">symbols with the adjacent text.</text>
|
||||
<line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="776" x2="776" y1="1919" y2="1933"/>
|
||||
<g transform="matrix(0.2 0 0 0.2 776 1933)">
|
||||
<path d="m16.5527 0.78125c2.58789 0 3.85742-0.976562 4.78516-3.71094l6.29883-17.2363h28.8086l6.29883 17.2363c0.927734 2.73438 2.19727 3.71094 4.73633 3.71094 2.58789 0 4.24805-1.5625 4.24805-4.00391 0-0.830078-0.146484-1.61133-0.537109-2.63672l-22.9004-60.9863c-1.12305-2.97852-3.125-4.49219-6.25-4.49219-3.02734 0-5.07812 1.46484-6.15234 4.44336l-22.9004 61.084c-0.390625 1.02539-0.537109 1.80664-0.537109 2.63672 0 2.44141 1.5625 3.95508 4.10156 3.95508Zm13.4766-28.3691 11.8652-32.8613h0.244141l11.8652 32.8613Z"/>
|
||||
</g>
|
||||
<line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="792.836" x2="792.836" y1="1919" y2="1933"/>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 776 1953)">Margins</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1971)">Leading and trailing margins on the left and right side of each symbol</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1989)">can be adjusted by modifying the x-location of the margin guidelines.</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2007)">Modifications are automatically applied proportionally to all</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2025)">scales and weights.</text>
|
||||
<g transform="matrix(0.2 0 0 0.2 1289 1933)">
|
||||
<path d="m14.209 9.32617 8.49609 8.54492c4.29688 4.3457 9.22852 4.05273 13.8672-1.07422l53.4668-58.9355-4.83398-4.88281-53.0762 58.3984c-1.75781 2.00195-3.41797 2.49023-5.76172 0.146484l-5.85938-5.81055c-2.34375-2.29492-1.80664-4.00391 0.195312-5.81055l57.373-54.0039-4.88281-4.83398-57.959 54.4434c-4.93164 4.58984-5.32227 9.47266-1.02539 13.8184Zm32.0801-90.9668c-2.09961 2.05078-2.24609 4.93164-1.07422 6.88477 1.17188 1.80664 3.4668 2.97852 6.68945 2.14844 7.32422-1.70898 14.9414-2.00195 22.0703 2.68555l-2.92969 7.27539c-1.70898 4.15039-0.830078 7.08008 1.85547 9.81445l11.4746 11.5723c2.44141 2.44141 4.49219 2.53906 7.32422 2.05078l5.32227-0.976562 3.32031 3.36914-0.195312 2.7832c-0.195312 2.49023 0.439453 4.39453 2.88086 6.78711l3.80859 3.71094c2.39258 2.39258 5.46875 2.53906 7.8125 0.195312l14.5508-14.5996c2.34375-2.34375 2.24609-5.32227-0.146484-7.71484l-3.85742-3.80859c-2.39258-2.39258-4.24805-3.17383-6.64062-2.97852l-2.88086 0.244141-3.22266-3.17383 1.2207-5.61523c0.634766-2.83203-0.146484-5.0293-3.07617-7.95898l-10.9863-10.9375c-16.6992-16.6016-38.8672-16.2109-53.3203-1.75781Zm7.4707 1.85547c12.1582-8.88672 28.6133-7.37305 39.7461 3.75977l12.1582 12.0605c1.17188 1.17188 1.36719 2.09961 1.02539 3.80859l-1.61133 7.42188 7.51953 7.42188 4.93164-0.292969c1.26953-0.0488281 1.66016 0.0488281 2.63672 1.02539l2.88086 2.88086-12.207 12.207-2.88086-2.88086c-0.976562-0.976562-1.12305-1.36719-1.07422-2.68555l0.341797-4.88281-7.4707-7.42188-7.61719 1.26953c-1.61133 0.341797-2.34375 0.195312-3.56445-0.976562l-10.0098-10.0098c-1.26953-1.17188-1.41602-2.00195-0.634766-3.85742l4.39453-10.4492c-7.8125-7.27539-17.9688-10.4004-28.125-7.42188-0.78125 0.195312-1.07422-0.439453-0.439453-0.976562Z"/>
|
||||
</g>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 1289 1953)">Exporting</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1971)">Symbols should be outlined when exporting to ensure the</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1989)">design is preserved when submitting to Xcode.</text>
|
||||
<text id="template-version" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1933)">Template v.6.0</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1951)">Requires Xcode 16 or greater</text>
|
||||
<text id="descriptive-name" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1969)">Generated from nextcloud</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1987)">Typeset at 100.0 points</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 726)">Small</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1156)">Medium</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1586)">Large</text>
|
||||
</g>
|
||||
<g id="Guides">
|
||||
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 696)">
|
||||
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
|
||||
</g>
|
||||
<line id="Baseline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="696" y2="696"/>
|
||||
<line id="Capline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="625.541" y2="625.541"/>
|
||||
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1126)">
|
||||
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
|
||||
</g>
|
||||
<line id="Baseline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1126" y2="1126"/>
|
||||
<line id="Capline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1055.54" y2="1055.54"/>
|
||||
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1556)">
|
||||
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
|
||||
</g>
|
||||
<line id="Baseline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1556" y2="1556"/>
|
||||
<line id="Capline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1485.54" y2="1485.54"/>
|
||||
<line id="right-margin-Black-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="2989.55" x2="2989.55" y1="600.785" y2="720.121"/>
|
||||
<line id="left-margin-Black-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="2877.25" x2="2877.25" y1="600.785" y2="720.121"/>
|
||||
<line id="right-margin-Regular-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1501.6" x2="1501.6" y1="600.785" y2="720.121"/>
|
||||
<line id="left-margin-Regular-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1398.09" x2="1398.09" y1="600.785" y2="720.121"/>
|
||||
<line id="right-margin-Ultralight-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="610.493" x2="610.493" y1="600.785" y2="720.121"/>
|
||||
<line id="left-margin-Ultralight-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="508.93" x2="508.93" y1="600.785" y2="720.121"/>
|
||||
</g>
|
||||
<g id="Symbols">
|
||||
<g id="Black-S" transform="matrix(1 0 0 1 2877.25 696)">
|
||||
<path class="SFSymbolsPreviewWireframe" d="M56.15-58.0091C45.7881-58.0091 36.9888-51.019 34.275-41.4795C31.9724-46.4959 26.8737-50.0321 20.9526-50.0321C12.8112-50.0321 6.15002-43.3709 6.15002-35.2295C6.15002-27.088 12.8112-20.4269 20.9526-20.4269C26.8737-20.4269 31.9724-23.963 34.3572-28.9795C37.071-19.44 45.8704-12.4499 56.2322-12.4499C66.5118-12.4499 75.2289-19.3578 78.025-28.7328C80.4099-23.7986 85.5085-20.3446 91.3474-20.3446C99.4888-20.3446 106.15-27.0058 106.15-35.1473C106.15-43.2887 99.4888-49.9499 91.3474-49.9499C85.5085-49.9499 80.4921-46.4959 78.025-41.5617C75.2289-51.1012 66.4296-58.0091 56.15-58.0091ZM56.15-49.292C63.9625-49.292 70.2125-43.042 70.2125-35.2295C70.2125-27.417 63.9625-21.167 56.15-21.167C48.3375-21.167 42.0875-27.417 42.0875-35.2295C42.0875-43.1242 48.3375-49.292 56.15-49.292ZM20.9526-41.3973C24.4066-41.3973 27.0381-38.6834 27.0381-35.3117C27.0381-31.8578 24.3243-29.2262 20.9526-29.2262C17.4987-29.2262 14.8671-31.94 14.8671-35.3117C14.8671-38.6834 17.4987-41.3973 20.9526-41.3973ZM91.2651-41.3973C94.7191-41.3973 97.3506-38.6834 97.3506-35.3117C97.3506-31.8578 94.6368-29.2262 91.2651-29.2262C87.8112-29.2262 85.1796-31.94 85.1796-35.3117C85.1796-38.6834 87.8112-41.3973 91.2651-41.3973Z"/>
|
||||
</g>
|
||||
<g id="Regular-S" transform="matrix(1 0 0 1 1398.09 696)">
|
||||
<path class="SFSymbolsPreviewWireframe" d="M51.755-58.0091C41.3931-58.0091 32.5938-51.019 29.88-41.4795C27.5774-46.4959 22.4787-50.0321 16.5576-50.0321C8.41617-50.0321 1.75497-43.3709 1.75497-35.2295C1.75497-27.088 8.41617-20.4269 16.5576-20.4269C22.4787-20.4269 27.5774-23.963 29.9622-28.9795C32.676-19.44 41.4754-12.4499 51.8372-12.4499C62.1168-12.4499 70.8339-19.3578 73.63-28.7328C76.0149-23.7986 81.1135-20.3446 86.9524-20.3446C95.0938-20.3446 101.755-27.0058 101.755-35.1473C101.755-43.2887 95.0938-49.9499 86.9524-49.9499C81.1135-49.9499 76.0971-46.4959 73.63-41.5617C70.8339-51.1012 62.0346-58.0091 51.755-58.0091ZM51.755-49.292C59.5675-49.292 65.8175-43.042 65.8175-35.2295C65.8175-27.417 59.5675-21.167 51.755-21.167C43.9425-21.167 37.6925-27.417 37.6925-35.2295C37.6925-43.1242 43.9425-49.292 51.755-49.292ZM16.5576-41.3973C20.0116-41.3973 22.6431-38.6834 22.6431-35.3117C22.6431-31.8578 19.9293-29.2262 16.5576-29.2262C13.1037-29.2262 10.4721-31.94 10.4721-35.3117C10.4721-38.6834 13.1037-41.3973 16.5576-41.3973ZM86.8701-41.3973C90.3241-41.3973 92.9556-38.6834 92.9556-35.3117C92.9556-31.8578 90.2418-29.2262 86.8701-29.2262C83.4162-29.2262 80.7846-31.94 80.7846-35.3117C80.7846-38.6834 83.4162-41.3973 86.8701-41.3973Z"/>
|
||||
</g>
|
||||
<g id="Ultralight-S" transform="matrix(1 0 0 1 508.93 696)">
|
||||
<path class="SFSymbolsPreviewWireframe" d="M50.7815-58.3266C40.4197-58.3266 31.6203-51.3365 28.9065-41.797C26.6039-46.8135 21.5052-50.3497 15.5841-50.3497C7.44268-50.3497 0.781495-43.6885 0.781495-35.547C0.781495-27.4056 7.44268-20.7444 15.5841-20.7444C21.5052-20.7444 26.6039-24.2806 28.9887-29.297C31.7025-19.7576 40.5019-12.7674 50.8637-12.7674C61.1433-12.7674 69.8604-19.6753 72.6565-29.0503C75.0414-24.1161 80.14-20.6622 85.9789-20.6622C94.1203-20.6622 100.781-27.3233 100.781-35.4648C100.781-43.6062 94.1203-50.2674 85.9789-50.2674C80.14-50.2674 75.1236-46.8135 72.6565-41.8793C69.8604-51.4187 61.0611-58.3266 50.7815-58.3266ZM50.7815-49.6095C58.594-49.6095 64.844-43.3595 64.844-35.547C64.844-27.7345 58.594-21.4845 50.7815-21.4845C42.969-21.4845 36.719-27.7345 36.719-35.547C36.719-43.4418 42.969-49.6095 50.7815-49.6095ZM15.5841-41.7148C19.0381-41.7148 21.6697-39.001 21.6697-35.6293C21.6697-32.1753 18.9558-29.5437 15.5841-29.5437C12.1302-29.5437 9.49861-32.2576 9.49861-35.6293C9.49861-39.001 12.1302-41.7148 15.5841-41.7148ZM85.8966-41.7148C89.3506-41.7148 91.9822-39.001 91.9822-35.6293C91.9822-32.1753 89.2683-29.5437 85.8966-29.5437C82.4427-29.5437 79.8111-32.2576 79.8111-35.6293C79.8111-39.001 82.4427-41.7148 85.8966-41.7148Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 17 KiB |
@ -117,11 +117,9 @@ import OSLog
|
||||
|
||||
// MARK: - NSFileProviderReplicatedExtension protocol methods
|
||||
|
||||
func item(
|
||||
for identifier: NSFileProviderItemIdentifier,
|
||||
request _: NSFileProviderRequest,
|
||||
completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void
|
||||
) -> Progress {
|
||||
func item(for identifier: NSFileProviderItemIdentifier, request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) -> Progress {
|
||||
logger.debug("Received request for item.", [.item: identifier, .request: request])
|
||||
|
||||
guard let ncAccount else {
|
||||
logger.error("Not fetching item because account not set up yet.", [.item: identifier])
|
||||
completionHandler(nil, NSFileProviderError(.notAuthenticated))
|
||||
@ -130,7 +128,7 @@ import OSLog
|
||||
|
||||
guard let dbManager else {
|
||||
logger.error("Not fetching item because database is unavailable.", [.item: identifier])
|
||||
completionHandler(nil, NSFileProviderError(.cannotSynchronize))
|
||||
completionHandler(nil, NSFileProviderError(.notAuthenticated))
|
||||
return Progress()
|
||||
}
|
||||
|
||||
@ -163,8 +161,7 @@ import OSLog
|
||||
) -> Progress {
|
||||
let actionId = UUID()
|
||||
insertSyncAction(actionId)
|
||||
|
||||
logger.info("Received request to fetch contents of item.", [.item: itemIdentifier])
|
||||
logger.debug("Received request to fetch contents of item.", [.item: itemIdentifier, .request: request])
|
||||
|
||||
guard requestedVersion == nil else {
|
||||
// TODO: Add proper support for file versioning
|
||||
@ -233,7 +230,7 @@ import OSLog
|
||||
) -> Progress {
|
||||
let actionId = UUID()
|
||||
insertSyncAction(actionId)
|
||||
logger.debug("Received request to create item.", [.item: itemTemplate, .name: itemTemplate.filename])
|
||||
logger.debug("Received request to create item.", [.item: itemTemplate, .name: itemTemplate.filename, .request: request])
|
||||
|
||||
guard let ncAccount else {
|
||||
logger.error(
|
||||
@ -313,18 +310,17 @@ import OSLog
|
||||
insertSyncAction(actionId)
|
||||
|
||||
let identifier = item.itemIdentifier
|
||||
let ocId = identifier.rawValue
|
||||
logger.debug("Received request to modify item.", [.item: item])
|
||||
logger.debug("Received request to modify item.", [.item: item, .request: request])
|
||||
|
||||
guard let ncAccount else {
|
||||
logger.error("Not modifying item: \(ocId) as account not set up yet.")
|
||||
logger.error("Not modifying item because account not set up yet.", [.item: identifier])
|
||||
insertErrorAction(actionId)
|
||||
completionHandler(item, [], false, NSFileProviderError(.notAuthenticated))
|
||||
return Progress()
|
||||
}
|
||||
|
||||
guard let ignoredFiles else {
|
||||
logger.error("Not modifying item: \(ocId) as ignore list not set up yet.")
|
||||
logger.error("Not modifying item because ignore list not set up yet.", [.item: identifier])
|
||||
insertErrorAction(actionId)
|
||||
completionHandler(item, [], false, NSFileProviderError(.notAuthenticated))
|
||||
return Progress()
|
||||
@ -332,13 +328,14 @@ import OSLog
|
||||
|
||||
|
||||
guard let dbManager else {
|
||||
logger.error("Not modifying item because database is unavailable.")
|
||||
logger.error("Not modifying item because the database is unavailable.")
|
||||
insertErrorAction(actionId)
|
||||
completionHandler(item, [], false, NSFileProviderError(.cannotSynchronize))
|
||||
return Progress()
|
||||
}
|
||||
|
||||
let progress = Progress()
|
||||
|
||||
Task {
|
||||
guard let existingItem = await Item.storedItem(
|
||||
identifier: identifier,
|
||||
@ -347,16 +344,19 @@ import OSLog
|
||||
dbManager: dbManager,
|
||||
log: log
|
||||
) else {
|
||||
logger.error("Not modifying item: \(ocId) as item not found.")
|
||||
logger.error("Not modifying item because it was not found.", [.item: identifier])
|
||||
insertErrorAction(actionId)
|
||||
|
||||
completionHandler(
|
||||
item,
|
||||
[],
|
||||
false,
|
||||
NSError.fileProviderErrorForNonExistentItem(withIdentifier: item.itemIdentifier)
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let (modifiedItem, error) = await existingItem.modify(
|
||||
itemTarget: item,
|
||||
baseVersion: baseVersion,
|
||||
@ -380,6 +380,7 @@ import OSLog
|
||||
logger.debug("Calling item modification completion handler.", [.item: item.itemIdentifier, .name: item.filename, .error: error])
|
||||
completionHandler(modifiedItem ?? item, [], false, error)
|
||||
}
|
||||
|
||||
return progress
|
||||
}
|
||||
|
||||
@ -387,13 +388,13 @@ import OSLog
|
||||
identifier: NSFileProviderItemIdentifier,
|
||||
baseVersion _: NSFileProviderItemVersion,
|
||||
options _: NSFileProviderDeleteItemOptions = [],
|
||||
request _: NSFileProviderRequest,
|
||||
request: NSFileProviderRequest,
|
||||
completionHandler: @escaping (Error?) -> Void
|
||||
) -> Progress {
|
||||
let actionId = UUID()
|
||||
insertSyncAction(actionId)
|
||||
|
||||
logger.debug("Received request to delete item.", [.item: identifier])
|
||||
logger.debug("Received request to delete item.", [.item: identifier, .request: request])
|
||||
|
||||
guard let ncAccount else {
|
||||
logger.error("Not deleting item \(identifier.rawValue), account not set up yet")
|
||||
@ -456,9 +457,9 @@ import OSLog
|
||||
return progress
|
||||
}
|
||||
|
||||
func enumerator(
|
||||
for containerItemIdentifier: NSFileProviderItemIdentifier, request _: NSFileProviderRequest
|
||||
) throws -> NSFileProviderEnumerator {
|
||||
func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier, request: NSFileProviderRequest) throws -> NSFileProviderEnumerator {
|
||||
logger.debug("System requested enumerator.", [.item: containerItemIdentifier])
|
||||
|
||||
guard let ncAccount else {
|
||||
logger.error("Not providing enumerator for container with identifier \(containerItemIdentifier.rawValue) yet as account not set up")
|
||||
throw NSFileProviderError(.notAuthenticated)
|
||||
|
||||
@ -2,6 +2,14 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIcons</key>
|
||||
<dict>
|
||||
<key>CFBundlePrimaryIcon</key>
|
||||
<dict>
|
||||
<key>CFBundleSymbolName</key>
|
||||
<string>cloud</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
|
||||
@ -1,12 +1,124 @@
|
||||
{
|
||||
"sourceLanguage": "en",
|
||||
"strings": {
|
||||
"Allow automatic freeing up space": {
|
||||
"extractionState": "manual"
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"Allow automatic freeing up space" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Automatisches Freigeben von Speicherplatz zulassen"
|
||||
}
|
||||
},
|
||||
"de_DE" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Automatisches Freigeben von Speicherplatz zulassen"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Allow automatic freeing up space"
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Permitir la liberación de espacio automática"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Autoriser la libération automatique de l'espace"
|
||||
}
|
||||
},
|
||||
"gl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Permitir a liberación automática de espazo"
|
||||
}
|
||||
},
|
||||
"pt_BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Permitir liberação automática de espaço"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Yer açmak için dosyalar otomatik olarak silinsin"
|
||||
}
|
||||
},
|
||||
"zh_HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "允許自動釋放空間"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Always keep downloaded": {
|
||||
"extractionState": "manual"
|
||||
"Always keep downloaded" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Immer heruntergeladen halten"
|
||||
}
|
||||
},
|
||||
"de_DE" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Immer heruntergeladen halten"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Always keep downloaded"
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Siempre mantener descargado"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Toujours conserver en local"
|
||||
}
|
||||
},
|
||||
"gl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Manter sempre descargado"
|
||||
}
|
||||
},
|
||||
"pt_BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Sempre manter baixados"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Her zaman indirilmiş tutulsun"
|
||||
}
|
||||
},
|
||||
"zh_HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "一律保留已下載的下載"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": "1.0"
|
||||
}
|
||||
"version" : "1.0"
|
||||
}
|
||||
@ -38,7 +38,7 @@ class ClientCommunicationService: NSObject, NSFileProviderServiceSource, NSXPCLi
|
||||
|
||||
func getFileProviderDomainIdentifier(completionHandler: @escaping (String?, Error?) -> Void) {
|
||||
let identifier = self.fpExtension.domain.identifier.rawValue
|
||||
logger.info("Returning file provider domain identifier \(identifier)")
|
||||
logger.debug("Returning file provider domain identifier.", [.domain: identifier])
|
||||
completionHandler(identifier, nil)
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"symbol-rendering-intent" : "template"
|
||||
},
|
||||
"symbols" : [
|
||||
{
|
||||
"filename" : "FileProviderDomainSymbol.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 341-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 3300 2200">
|
||||
<!--glyph: "", point size: 100.0, font version: "20.0d10e1", template writer version: "138.0.0"-->
|
||||
<style>.SFSymbolsPreviewWireframe {fill:none;opacity:1.0;stroke:black;stroke-width:0.5}
|
||||
</style>
|
||||
<g id="Notes">
|
||||
<rect height="2200" id="artboard" style="fill:white;opacity:1" width="3300" x="0" y="0"/>
|
||||
<line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="292" y2="292"/>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 322)">Weight/Scale Variations</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 559.711 322)">Ultralight</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 856.422 322)">Thin</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1153.13 322)">Light</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1449.84 322)">Regular</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1746.56 322)">Medium</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2043.27 322)">Semibold</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2339.98 322)">Bold</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2636.69 322)">Heavy</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2933.4 322)">Black</text>
|
||||
<line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1903" y2="1903"/>
|
||||
<g transform="matrix(0.2 0 0 0.2 263 1933)">
|
||||
<path d="m46.2402 4.15039c21.7773 0 39.4531-17.627 39.4531-39.4043s-17.6758-39.4043-39.4531-39.4043c-21.7285 0-39.4043 17.627-39.4043 39.4043s17.6758 39.4043 39.4043 39.4043Zm0-7.42188c-17.6758 0-31.9336-14.3066-31.9336-31.9824s14.2578-31.9824 31.9336-31.9824 31.9824 14.3066 31.9824 31.9824-14.3066 31.9824-31.9824 31.9824Zm-17.9688-31.9824c0 2.14844 1.51367 3.61328 3.75977 3.61328h10.498v10.5957c0 2.19727 1.46484 3.71094 3.61328 3.71094 2.24609 0 3.71094-1.51367 3.71094-3.71094v-10.5957h10.5957c2.19727 0 3.71094-1.46484 3.71094-3.61328 0-2.19727-1.51367-3.71094-3.71094-3.71094h-10.5957v-10.5469c0-2.24609-1.46484-3.75977-3.71094-3.75977-2.14844 0-3.61328 1.51367-3.61328 3.75977v10.5469h-10.498c-2.24609 0-3.75977 1.51367-3.75977 3.71094Z"/>
|
||||
</g>
|
||||
<g transform="matrix(0.2 0 0 0.2 281.506 1933)">
|
||||
<path d="m58.5449 14.5508c27.4902 0 49.8047-22.3145 49.8047-49.8047s-22.3145-49.8047-49.8047-49.8047-49.8047 22.3145-49.8047 49.8047 22.3145 49.8047 49.8047 49.8047Zm0-8.30078c-22.9492 0-41.5039-18.5547-41.5039-41.5039s18.5547-41.5039 41.5039-41.5039 41.5039 18.5547 41.5039 41.5039-18.5547 41.5039-41.5039 41.5039Zm-22.6562-41.5039c0 2.39258 1.66016 4.00391 4.15039 4.00391h14.3555v14.4043c0 2.44141 1.66016 4.15039 4.05273 4.15039 2.44141 0 4.15039-1.66016 4.15039-4.15039v-14.4043h14.4043c2.44141 0 4.15039-1.61133 4.15039-4.00391 0-2.44141-1.70898-4.15039-4.15039-4.15039h-14.4043v-14.3555c0-2.49023-1.70898-4.19922-4.15039-4.19922-2.39258 0-4.05273 1.70898-4.05273 4.19922v14.3555h-14.3555c-2.49023 0-4.15039 1.70898-4.15039 4.15039Z"/>
|
||||
</g>
|
||||
<g transform="matrix(0.2 0 0 0.2 304.924 1933)">
|
||||
<path d="m74.8535 28.3203c35.1074 0 63.623-28.4668 63.623-63.5742s-28.5156-63.623-63.623-63.623-63.5742 28.5156-63.5742 63.623 28.4668 63.5742 63.5742 63.5742Zm0-9.08203c-30.127 0-54.4922-24.3652-54.4922-54.4922s24.3652-54.4922 54.4922-54.4922 54.4922 24.3652 54.4922 54.4922-24.3652 54.4922-54.4922 54.4922Zm-28.8574-54.4922c0 2.58789 1.85547 4.39453 4.58984 4.39453h19.7266v19.7754c0 2.68555 1.85547 4.58984 4.44336 4.58984 2.68555 0 4.54102-1.85547 4.54102-4.58984v-19.7754h19.7754c2.68555 0 4.58984-1.80664 4.58984-4.39453 0-2.73438-1.85547-4.58984-4.58984-4.58984h-19.7754v-19.7266c0-2.73438-1.85547-4.63867-4.54102-4.63867-2.58789 0-4.44336 1.9043-4.44336 4.63867v19.7266h-19.7266c-2.73438 0-4.58984 1.85547-4.58984 4.58984Z"/>
|
||||
</g>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 1953)">Design Variations</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1971)">Symbols are supported in up to nine weights and three scales.</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1989)">For optimal layout with text and other symbols, vertically align</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 2007)">symbols with the adjacent text.</text>
|
||||
<line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="776" x2="776" y1="1919" y2="1933"/>
|
||||
<g transform="matrix(0.2 0 0 0.2 776 1933)">
|
||||
<path d="m16.5527 0.78125c2.58789 0 3.85742-0.976562 4.78516-3.71094l6.29883-17.2363h28.8086l6.29883 17.2363c0.927734 2.73438 2.19727 3.71094 4.73633 3.71094 2.58789 0 4.24805-1.5625 4.24805-4.00391 0-0.830078-0.146484-1.61133-0.537109-2.63672l-22.9004-60.9863c-1.12305-2.97852-3.125-4.49219-6.25-4.49219-3.02734 0-5.07812 1.46484-6.15234 4.44336l-22.9004 61.084c-0.390625 1.02539-0.537109 1.80664-0.537109 2.63672 0 2.44141 1.5625 3.95508 4.10156 3.95508Zm13.4766-28.3691 11.8652-32.8613h0.244141l11.8652 32.8613Z"/>
|
||||
</g>
|
||||
<line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="792.836" x2="792.836" y1="1919" y2="1933"/>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 776 1953)">Margins</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1971)">Leading and trailing margins on the left and right side of each symbol</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1989)">can be adjusted by modifying the x-location of the margin guidelines.</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2007)">Modifications are automatically applied proportionally to all</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2025)">scales and weights.</text>
|
||||
<g transform="matrix(0.2 0 0 0.2 1289 1933)">
|
||||
<path d="m14.209 9.32617 8.49609 8.54492c4.29688 4.3457 9.22852 4.05273 13.8672-1.07422l53.4668-58.9355-4.83398-4.88281-53.0762 58.3984c-1.75781 2.00195-3.41797 2.49023-5.76172 0.146484l-5.85938-5.81055c-2.34375-2.29492-1.80664-4.00391 0.195312-5.81055l57.373-54.0039-4.88281-4.83398-57.959 54.4434c-4.93164 4.58984-5.32227 9.47266-1.02539 13.8184Zm32.0801-90.9668c-2.09961 2.05078-2.24609 4.93164-1.07422 6.88477 1.17188 1.80664 3.4668 2.97852 6.68945 2.14844 7.32422-1.70898 14.9414-2.00195 22.0703 2.68555l-2.92969 7.27539c-1.70898 4.15039-0.830078 7.08008 1.85547 9.81445l11.4746 11.5723c2.44141 2.44141 4.49219 2.53906 7.32422 2.05078l5.32227-0.976562 3.32031 3.36914-0.195312 2.7832c-0.195312 2.49023 0.439453 4.39453 2.88086 6.78711l3.80859 3.71094c2.39258 2.39258 5.46875 2.53906 7.8125 0.195312l14.5508-14.5996c2.34375-2.34375 2.24609-5.32227-0.146484-7.71484l-3.85742-3.80859c-2.39258-2.39258-4.24805-3.17383-6.64062-2.97852l-2.88086 0.244141-3.22266-3.17383 1.2207-5.61523c0.634766-2.83203-0.146484-5.0293-3.07617-7.95898l-10.9863-10.9375c-16.6992-16.6016-38.8672-16.2109-53.3203-1.75781Zm7.4707 1.85547c12.1582-8.88672 28.6133-7.37305 39.7461 3.75977l12.1582 12.0605c1.17188 1.17188 1.36719 2.09961 1.02539 3.80859l-1.61133 7.42188 7.51953 7.42188 4.93164-0.292969c1.26953-0.0488281 1.66016 0.0488281 2.63672 1.02539l2.88086 2.88086-12.207 12.207-2.88086-2.88086c-0.976562-0.976562-1.12305-1.36719-1.07422-2.68555l0.341797-4.88281-7.4707-7.42188-7.61719 1.26953c-1.61133 0.341797-2.34375 0.195312-3.56445-0.976562l-10.0098-10.0098c-1.26953-1.17188-1.41602-2.00195-0.634766-3.85742l4.39453-10.4492c-7.8125-7.27539-17.9688-10.4004-28.125-7.42188-0.78125 0.195312-1.07422-0.439453-0.439453-0.976562Z"/>
|
||||
</g>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 1289 1953)">Exporting</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1971)">Symbols should be outlined when exporting to ensure the</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1989)">design is preserved when submitting to Xcode.</text>
|
||||
<text id="template-version" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1933)">Template v.6.0</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1951)">Requires Xcode 16 or greater</text>
|
||||
<text id="descriptive-name" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1969)">Generated from nextcloud</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1987)">Typeset at 100.0 points</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 726)">Small</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1156)">Medium</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1586)">Large</text>
|
||||
</g>
|
||||
<g id="Guides">
|
||||
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 696)">
|
||||
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
|
||||
</g>
|
||||
<line id="Baseline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="696" y2="696"/>
|
||||
<line id="Capline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="625.541" y2="625.541"/>
|
||||
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1126)">
|
||||
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
|
||||
</g>
|
||||
<line id="Baseline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1126" y2="1126"/>
|
||||
<line id="Capline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1055.54" y2="1055.54"/>
|
||||
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1556)">
|
||||
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
|
||||
</g>
|
||||
<line id="Baseline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1556" y2="1556"/>
|
||||
<line id="Capline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1485.54" y2="1485.54"/>
|
||||
<line id="right-margin-Black-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="2989.55" x2="2989.55" y1="600.785" y2="720.121"/>
|
||||
<line id="left-margin-Black-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="2877.25" x2="2877.25" y1="600.785" y2="720.121"/>
|
||||
<line id="right-margin-Regular-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1501.6" x2="1501.6" y1="600.785" y2="720.121"/>
|
||||
<line id="left-margin-Regular-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1398.09" x2="1398.09" y1="600.785" y2="720.121"/>
|
||||
<line id="right-margin-Ultralight-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="610.493" x2="610.493" y1="600.785" y2="720.121"/>
|
||||
<line id="left-margin-Ultralight-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="508.93" x2="508.93" y1="600.785" y2="720.121"/>
|
||||
</g>
|
||||
<g id="Symbols">
|
||||
<g id="Black-S" transform="matrix(1 0 0 1 2877.25 696)">
|
||||
<path class="SFSymbolsPreviewWireframe" d="M56.15-58.0091C45.7881-58.0091 36.9888-51.019 34.275-41.4795C31.9724-46.4959 26.8737-50.0321 20.9526-50.0321C12.8112-50.0321 6.15002-43.3709 6.15002-35.2295C6.15002-27.088 12.8112-20.4269 20.9526-20.4269C26.8737-20.4269 31.9724-23.963 34.3572-28.9795C37.071-19.44 45.8704-12.4499 56.2322-12.4499C66.5118-12.4499 75.2289-19.3578 78.025-28.7328C80.4099-23.7986 85.5085-20.3446 91.3474-20.3446C99.4888-20.3446 106.15-27.0058 106.15-35.1473C106.15-43.2887 99.4888-49.9499 91.3474-49.9499C85.5085-49.9499 80.4921-46.4959 78.025-41.5617C75.2289-51.1012 66.4296-58.0091 56.15-58.0091ZM56.15-49.292C63.9625-49.292 70.2125-43.042 70.2125-35.2295C70.2125-27.417 63.9625-21.167 56.15-21.167C48.3375-21.167 42.0875-27.417 42.0875-35.2295C42.0875-43.1242 48.3375-49.292 56.15-49.292ZM20.9526-41.3973C24.4066-41.3973 27.0381-38.6834 27.0381-35.3117C27.0381-31.8578 24.3243-29.2262 20.9526-29.2262C17.4987-29.2262 14.8671-31.94 14.8671-35.3117C14.8671-38.6834 17.4987-41.3973 20.9526-41.3973ZM91.2651-41.3973C94.7191-41.3973 97.3506-38.6834 97.3506-35.3117C97.3506-31.8578 94.6368-29.2262 91.2651-29.2262C87.8112-29.2262 85.1796-31.94 85.1796-35.3117C85.1796-38.6834 87.8112-41.3973 91.2651-41.3973Z"/>
|
||||
</g>
|
||||
<g id="Regular-S" transform="matrix(1 0 0 1 1398.09 696)">
|
||||
<path class="SFSymbolsPreviewWireframe" d="M51.755-58.0091C41.3931-58.0091 32.5938-51.019 29.88-41.4795C27.5774-46.4959 22.4787-50.0321 16.5576-50.0321C8.41617-50.0321 1.75497-43.3709 1.75497-35.2295C1.75497-27.088 8.41617-20.4269 16.5576-20.4269C22.4787-20.4269 27.5774-23.963 29.9622-28.9795C32.676-19.44 41.4754-12.4499 51.8372-12.4499C62.1168-12.4499 70.8339-19.3578 73.63-28.7328C76.0149-23.7986 81.1135-20.3446 86.9524-20.3446C95.0938-20.3446 101.755-27.0058 101.755-35.1473C101.755-43.2887 95.0938-49.9499 86.9524-49.9499C81.1135-49.9499 76.0971-46.4959 73.63-41.5617C70.8339-51.1012 62.0346-58.0091 51.755-58.0091ZM51.755-49.292C59.5675-49.292 65.8175-43.042 65.8175-35.2295C65.8175-27.417 59.5675-21.167 51.755-21.167C43.9425-21.167 37.6925-27.417 37.6925-35.2295C37.6925-43.1242 43.9425-49.292 51.755-49.292ZM16.5576-41.3973C20.0116-41.3973 22.6431-38.6834 22.6431-35.3117C22.6431-31.8578 19.9293-29.2262 16.5576-29.2262C13.1037-29.2262 10.4721-31.94 10.4721-35.3117C10.4721-38.6834 13.1037-41.3973 16.5576-41.3973ZM86.8701-41.3973C90.3241-41.3973 92.9556-38.6834 92.9556-35.3117C92.9556-31.8578 90.2418-29.2262 86.8701-29.2262C83.4162-29.2262 80.7846-31.94 80.7846-35.3117C80.7846-38.6834 83.4162-41.3973 86.8701-41.3973Z"/>
|
||||
</g>
|
||||
<g id="Ultralight-S" transform="matrix(1 0 0 1 508.93 696)">
|
||||
<path class="SFSymbolsPreviewWireframe" d="M50.7815-58.3266C40.4197-58.3266 31.6203-51.3365 28.9065-41.797C26.6039-46.8135 21.5052-50.3497 15.5841-50.3497C7.44268-50.3497 0.781495-43.6885 0.781495-35.547C0.781495-27.4056 7.44268-20.7444 15.5841-20.7444C21.5052-20.7444 26.6039-24.2806 28.9887-29.297C31.7025-19.7576 40.5019-12.7674 50.8637-12.7674C61.1433-12.7674 69.8604-19.6753 72.6565-29.0503C75.0414-24.1161 80.14-20.6622 85.9789-20.6622C94.1203-20.6622 100.781-27.3233 100.781-35.4648C100.781-43.6062 94.1203-50.2674 85.9789-50.2674C80.14-50.2674 75.1236-46.8135 72.6565-41.8793C69.8604-51.4187 61.0611-58.3266 50.7815-58.3266ZM50.7815-49.6095C58.594-49.6095 64.844-43.3595 64.844-35.547C64.844-27.7345 58.594-21.4845 50.7815-21.4845C42.969-21.4845 36.719-27.7345 36.719-35.547C36.719-43.4418 42.969-49.6095 50.7815-49.6095ZM15.5841-41.7148C19.0381-41.7148 21.6697-39.001 21.6697-35.6293C21.6697-32.1753 18.9558-29.5437 15.5841-29.5437C12.1302-29.5437 9.49861-32.2576 9.49861-35.6293C9.49861-39.001 12.1302-41.7148 15.5841-41.7148ZM85.8966-41.7148C89.3506-41.7148 91.9822-39.001 91.9822-35.6293C91.9822-32.1753 89.2683-29.5437 85.8966-29.5437C82.4427-29.5437 79.8111-32.2576 79.8111-35.6293C79.8111-39.001 82.4427-41.7148 85.8966-41.7148Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 17 KiB |
@ -2,6 +2,14 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIcons</key>
|
||||
<dict>
|
||||
<key>CFBundlePrimaryIcon</key>
|
||||
<dict>
|
||||
<key>CFBundleSymbolName</key>
|
||||
<string>cloud</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
//
|
||||
// The common name of the signing certificate to use for code signing.
|
||||
// It must be present in your keychain and a development certificate.
|
||||
//
|
||||
CODE_SIGN_IDENTITY=
|
||||
@ -0,0 +1,40 @@
|
||||
#!/bin/env zsh
|
||||
|
||||
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Read the available environment paths which include (for example) Homebrew.
|
||||
for f in /etc/paths.d/*; do
|
||||
while read -r line; do
|
||||
export PATH="$PATH:$line"
|
||||
done < "$f"
|
||||
done
|
||||
|
||||
if [ -f "$HOME/.zprofile" ]; then
|
||||
echo "Sourcing $HOME/.zprofile to include possible PATH definitions..."
|
||||
source "$HOME/.zprofile"
|
||||
fi
|
||||
|
||||
if [ -z "${CODE_SIGN_IDENTITY}" ]; then
|
||||
echo "Error: CODE_SIGN_IDENTITY is not defined or is empty!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DESKTOP_CLIENT_PROJECT_ROOT="$SOURCE_ROOT/../../.."
|
||||
|
||||
if [ -d "$DESKTOP_CLIENT_PROJECT_ROOT/admin/osx/mac-crafter" ]; then
|
||||
cd "$DESKTOP_CLIENT_PROJECT_ROOT/admin/osx/mac-crafter"
|
||||
else
|
||||
echo "Error: Directory '$DESKTOP_CLIENT_PROJECT_ROOT/admin/osx/mac-crafter' does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
swift run mac-crafter \
|
||||
--build-path="$SOURCE_ROOT/DerivedData" \
|
||||
--product-path="/Applications" \
|
||||
--build-type="Debug" \
|
||||
--dev \
|
||||
--disable-auto-updater \
|
||||
--build-file-provider-module \
|
||||
--code-sign-identity="$CODE_SIGN_IDENTITY" \
|
||||
"$DESKTOP_CLIENT_PROJECT_ROOT"
|
||||
@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objectVersion = 70;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@ -11,7 +11,6 @@
|
||||
531522822B8E01C6002E31BE /* ShareTableItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 531522812B8E01C6002E31BE /* ShareTableItemView.xib */; };
|
||||
5350E4E92B0C534A00F276CB /* ClientCommunicationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5350E4E82B0C534A00F276CB /* ClientCommunicationService.swift */; };
|
||||
5352B36C29DC44B50011CE03 /* FileProviderExtension+Thumbnailing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5352B36B29DC44B50011CE03 /* FileProviderExtension+Thumbnailing.swift */; };
|
||||
5358F2B92BAA0F5300E3C729 /* NextcloudCapabilitiesKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5358F2B82BAA0F5300E3C729 /* NextcloudCapabilitiesKit */; };
|
||||
53651E442BBC0CA300ECAC29 /* SuggestionsTextFieldKit in Frameworks */ = {isa = PBXBuildFile; productRef = 53651E432BBC0CA300ECAC29 /* SuggestionsTextFieldKit */; };
|
||||
53651E462BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53651E452BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift */; };
|
||||
536EFBF7295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536EFBF6295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift */; };
|
||||
@ -56,6 +55,8 @@
|
||||
AA9987862E72B6EF00B2C428 /* NextcloudKit+clearAccountErrorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA9987852E72B6DB00B2C428 /* NextcloudKit+clearAccountErrorState.swift */; };
|
||||
AAA69D932E3BB09900BBD44D /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = AAA69D922E3BB09900BBD44D /* Localizable.xcstrings */; };
|
||||
AAC00D2A2E37B29D006010FE /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = AAC00D292E37B29D006010FE /* Localizable.xcstrings */; };
|
||||
AAF19A682E8D5B4E005FE5B0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AAF19A672E8D5B4E005FE5B0 /* Assets.xcassets */; };
|
||||
AAF19A7A2E8D5B63005FE5B0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AAF19A792E8D5B63005FE5B0 /* Assets.xcassets */; };
|
||||
C2B573BA1B1CD91E00303B36 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573B91B1CD91E00303B36 /* main.m */; };
|
||||
C2B573D21B1CD94B00303B36 /* main.m in Resources */ = {isa = PBXBuildFile; fileRef = C2B573B91B1CD91E00303B36 /* main.m */; };
|
||||
C2B573DE1B1CD9CE00303B36 /* FinderSync.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573DD1B1CD9CE00303B36 /* FinderSync.m */; };
|
||||
@ -196,12 +197,18 @@
|
||||
53FE14642B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewDataSourceUIDelegate.swift; sourceTree = "<group>"; };
|
||||
53FE14662B8F78B6006C4193 /* ShareOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareOptionsView.swift; sourceTree = "<group>"; };
|
||||
AA02B2AA2E7048C600C72B34 /* Keychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = "<group>"; };
|
||||
AA0A6B1D2E8EA94F007F4A7A /* Craft.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = Craft.sh; sourceTree = "<group>"; };
|
||||
AA1191B52E8EAFF900E21C7B /* Build.xcconfig.template */ = {isa = PBXFileReference; lastKnownFileType = text; path = Build.xcconfig.template; sourceTree = "<group>"; };
|
||||
AA27A4E32E93C0D700665051 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
AA7F17E02E7017230000E928 /* Authentication.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Authentication.storyboard; sourceTree = "<group>"; };
|
||||
AA7F17E22E70173E0000E928 /* AuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewController.swift; sourceTree = "<group>"; };
|
||||
AA7F17E62E7038340000E928 /* NSError+FileProviderErrorCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSError+FileProviderErrorCode.swift"; sourceTree = "<group>"; };
|
||||
AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Build.xcconfig; sourceTree = "<group>"; };
|
||||
AA9987852E72B6DB00B2C428 /* NextcloudKit+clearAccountErrorState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NextcloudKit+clearAccountErrorState.swift"; sourceTree = "<group>"; };
|
||||
AAA69D922E3BB09900BBD44D /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||
AAC00D292E37B29D006010FE /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||
AAF19A672E8D5B4E005FE5B0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
AAF19A792E8D5B63005FE5B0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
C2B573B11B1CD91E00303B36 /* desktopclient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = desktopclient.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C2B573B51B1CD91E00303B36 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
C2B573B91B1CD91E00303B36 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
@ -217,6 +224,10 @@
|
||||
C2B573F11B1DAD6400303B36 /* warning.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = warning.iconset; path = ../../icons/nopadding/warning.iconset; sourceTree = SOURCE_ROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
AAD7F6032EAA11670071D385 /* gui */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); name = gui; path = /Users/iva/Projekte/nextcloud/desktop/src/gui; sourceTree = "<absolute>"; };
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
538E396427F4765000FA63D5 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
@ -239,7 +250,6 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5358F2B92BAA0F5300E3C729 /* NextcloudCapabilitiesKit in Frameworks */,
|
||||
53C331B62BCD3AFF0093D38B /* NextcloudFileProviderKit in Frameworks */,
|
||||
53651E442BBC0CA300ECAC29 /* SuggestionsTextFieldKit in Frameworks */,
|
||||
);
|
||||
@ -341,6 +351,7 @@
|
||||
538E397227F4765000FA63D5 /* Info.plist */,
|
||||
5350E4EA2B0C9CE100F276CB /* FileProviderExt-Bridging-Header.h */,
|
||||
AAA69D922E3BB09900BBD44D /* Localizable.xcstrings */,
|
||||
AAF19A672E8D5B4E005FE5B0 /* Assets.xcassets */,
|
||||
);
|
||||
path = FileProviderExt;
|
||||
sourceTree = "<group>";
|
||||
@ -369,10 +380,21 @@
|
||||
53FE14572B8E3A7C006C4193 /* FileProviderUIExt.entitlements */,
|
||||
53B979852B84C81F002DA742 /* Info.plist */,
|
||||
AAC00D292E37B29D006010FE /* Localizable.xcstrings */,
|
||||
AAF19A792E8D5B63005FE5B0 /* Assets.xcassets */,
|
||||
);
|
||||
path = FileProviderUIExt;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AA0A6B1C2E8EA948007F4A7A /* NextcloudDev */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */,
|
||||
AA1191B52E8EAFF900E21C7B /* Build.xcconfig.template */,
|
||||
AA0A6B1D2E8EA94F007F4A7A /* Craft.sh */,
|
||||
);
|
||||
path = NextcloudDev;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AA7F17DF2E70171A0000E928 /* Authentication */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -385,7 +407,10 @@
|
||||
C2B573941B1CD88000303B36 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AAD7F6032EAA11670071D385 /* gui */,
|
||||
AA27A4E32E93C0D700665051 /* README.md */,
|
||||
C2B573B31B1CD91E00303B36 /* desktopclient */,
|
||||
AA0A6B1C2E8EA948007F4A7A /* NextcloudDev */,
|
||||
C2B573D81B1CD9CE00303B36 /* FinderSyncExt */,
|
||||
538E396B27F4765000FA63D5 /* FileProviderExt */,
|
||||
53B9797F2B84C81F002DA742 /* FileProviderUIExt */,
|
||||
@ -466,6 +491,25 @@
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXLegacyTarget section */
|
||||
AA0A62F22E8EA929007F4A7A /* NextcloudDev */ = {
|
||||
isa = PBXLegacyTarget;
|
||||
buildArgumentsString = "\"$(SOURCE_ROOT)/NextcloudDev/Craft.sh\"";
|
||||
buildConfigurationList = AA0A62F52E8EA929007F4A7A /* Build configuration list for PBXLegacyTarget "NextcloudDev" */;
|
||||
buildPhases = (
|
||||
);
|
||||
buildToolPath = /bin/zsh;
|
||||
buildWorkingDirectory = "";
|
||||
dependencies = (
|
||||
);
|
||||
name = NextcloudDev;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
passBuildSettingsInEnvironment = 1;
|
||||
productName = NextcloudDev;
|
||||
};
|
||||
/* End PBXLegacyTarget section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
538E396627F4765000FA63D5 /* FileProviderExt */ = {
|
||||
isa = PBXNativeTarget;
|
||||
@ -521,7 +565,6 @@
|
||||
);
|
||||
name = FileProviderUIExt;
|
||||
packageProductDependencies = (
|
||||
5358F2B82BAA0F5300E3C729 /* NextcloudCapabilitiesKit */,
|
||||
53651E432BBC0CA300ECAC29 /* SuggestionsTextFieldKit */,
|
||||
53C331B52BCD3AFF0093D38B /* NextcloudFileProviderKit */,
|
||||
);
|
||||
@ -593,6 +636,9 @@
|
||||
53B9797D2B84C81F002DA742 = {
|
||||
CreatedOnToolsVersion = 15.2;
|
||||
};
|
||||
AA0A62F22E8EA929007F4A7A = {
|
||||
CreatedOnToolsVersion = 26.0.1;
|
||||
};
|
||||
C2B573B01B1CD91E00303B36 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
DevelopmentTeam = 9B5WD74GWJ;
|
||||
@ -726,7 +772,6 @@
|
||||
);
|
||||
mainGroup = C2B573941B1CD88000303B36;
|
||||
packageReferences = (
|
||||
5358F2B72BAA045E00E3C729 /* XCRemoteSwiftPackageReference "NextcloudCapabilitiesKit" */,
|
||||
53651E422BBC0C7F00ECAC29 /* XCRemoteSwiftPackageReference "SuggestionsTextFieldKit" */,
|
||||
53C331B02BCD28C30093D38B /* XCRemoteSwiftPackageReference "NextcloudFileProviderKit" */,
|
||||
);
|
||||
@ -739,6 +784,7 @@
|
||||
538E396627F4765000FA63D5 /* FileProviderExt */,
|
||||
53B9797D2B84C81F002DA742 /* FileProviderUIExt */,
|
||||
53903D0B2956164F00D0B308 /* NCDesktopClientSocketKit */,
|
||||
AA0A62F22E8EA929007F4A7A /* NextcloudDev */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@ -748,6 +794,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AAF19A682E8D5B4E005FE5B0 /* Assets.xcassets in Resources */,
|
||||
AAA69D932E3BB09900BBD44D /* Localizable.xcstrings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -767,6 +814,7 @@
|
||||
AAC00D2A2E37B29D006010FE /* Localizable.xcstrings in Resources */,
|
||||
531522822B8E01C6002E31BE /* ShareTableItemView.xib in Resources */,
|
||||
537630912B85F4980026BFAB /* ShareViewController.xib in Resources */,
|
||||
AAF19A7A2E8D5B63005FE5B0 /* Assets.xcassets in Resources */,
|
||||
AA7F17E12E7017230000E928 /* Authentication.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -928,7 +976,6 @@
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
@ -985,7 +1032,6 @@
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
@ -1162,7 +1208,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
@ -1227,7 +1272,6 @@
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
@ -1267,6 +1311,80 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
AA0A62F32E8EA929007F4A7A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUGGING_SYMBOLS = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
AA0A62F42E8EA929007F4A7A /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
C2B573991B1CD88000303B36 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -1288,6 +1406,7 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
@ -1323,6 +1442,7 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
@ -1359,7 +1479,6 @@
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
@ -1416,7 +1535,6 @@
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
@ -1464,7 +1582,6 @@
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
@ -1524,7 +1641,6 @@
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
@ -1584,6 +1700,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
AA0A62F52E8EA929007F4A7A /* Build configuration list for PBXLegacyTarget "NextcloudDev" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
AA0A62F32E8EA929007F4A7A /* Debug */,
|
||||
AA0A62F42E8EA929007F4A7A /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
C2B573981B1CD88000303B36 /* Build configuration list for PBXProject "NextcloudIntegration" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
@ -1614,14 +1739,6 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
5358F2B72BAA045E00E3C729 /* XCRemoteSwiftPackageReference "NextcloudCapabilitiesKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/claucambra/NextcloudCapabilitiesKit.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 2.3.0;
|
||||
};
|
||||
};
|
||||
53651E422BBC0C7F00ECAC29 /* XCRemoteSwiftPackageReference "SuggestionsTextFieldKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/claucambra/SuggestionsTextFieldKit.git";
|
||||
@ -1634,18 +1751,13 @@
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/nextcloud/NextcloudFileProviderKit.git";
|
||||
requirement = {
|
||||
branch = main;
|
||||
kind = branch;
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 3.0.0;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
5358F2B82BAA0F5300E3C729 /* NextcloudCapabilitiesKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 5358F2B72BAA045E00E3C729 /* XCRemoteSwiftPackageReference "NextcloudCapabilitiesKit" */;
|
||||
productName = NextcloudCapabilitiesKit;
|
||||
};
|
||||
53651E432BBC0CA300ECAC29 /* SuggestionsTextFieldKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 53651E422BBC0C7F00ECAC29 /* XCRemoteSwiftPackageReference "SuggestionsTextFieldKit" */;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "af7b30e0399513f38616bd75821c834208fc3c22e3f5ec7d1765b1dbb03a46cd",
|
||||
"originHash" : "74a3274bff88648671702b7d1dfa0c8ea5acb44280987019f6c5bb7055b848b2",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "alamofire",
|
||||
@ -13,10 +13,10 @@
|
||||
{
|
||||
"identity" : "nextcloudcapabilitieskit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/claucambra/NextcloudCapabilitiesKit.git",
|
||||
"location" : "https://github.com/nextcloud/NextcloudCapabilitiesKit.git",
|
||||
"state" : {
|
||||
"revision" : "a680e947d6cc2a1c101335797213e4b7d3b22102",
|
||||
"version" : "2.3.0"
|
||||
"revision" : "07a481be18943cfa3ce17c91b83d6017cdbb5846",
|
||||
"version" : "2.4.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -24,8 +24,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/nextcloud/NextcloudFileProviderKit.git",
|
||||
"state" : {
|
||||
"branch" : "main",
|
||||
"revision" : "b87926142d8bc474c95acc29f66fce102ed27942"
|
||||
"revision" : "aef90f069659f240380e05f9301e080e92e094aa",
|
||||
"version" : "3.2.8"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -33,8 +33,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/nextcloud/NextcloudKit",
|
||||
"state" : {
|
||||
"revision" : "96c2b0e6a8bbf84c69412128f703812aec5982de",
|
||||
"version" : "7.1.5"
|
||||
"revision" : "71c06c6b1ea0a8f042133fdd9843901930b827f8",
|
||||
"version" : "7.1.8"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2600"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "AA0A62F22E8EA929007F4A7A"
|
||||
BuildableName = "NextcloudDev"
|
||||
BlueprintName = "NextcloudDev"
|
||||
ReferencedContainer = "container:NextcloudIntegration.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "NO"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<PathRunnable
|
||||
runnableDebuggingMode = "0"
|
||||
BundleIdentifier = "com.nextcloud.desktopclient"
|
||||
FilePath = "/Applications/NextcloudDev.app">
|
||||
</PathRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "AA0A62F22E8EA929007F4A7A"
|
||||
BuildableName = "NextcloudDev"
|
||||
BlueprintName = "NextcloudDev"
|
||||
ReferencedContainer = "container:NextcloudIntegration.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
105
shell_integration/MacOSX/NextcloudIntegration/README.md
Normal file
105
shell_integration/MacOSX/NextcloudIntegration/README.md
Normal file
@ -0,0 +1,105 @@
|
||||
# Nextcloud macOS Integration Project
|
||||
|
||||
This is an Xcode project to build platform-specific components for the Nextcloud desktop client.
|
||||
As an example, this includes the file provider extension.
|
||||
|
||||
## Localization
|
||||
|
||||
Transifex is used for localization.
|
||||
Currently, [the file provider extension](https://app.transifex.com/nextcloud/nextcloud/client-fileprovider/) and [file provider UI extension](https://app.transifex.com/nextcloud/nextcloud/client-fileproviderui/) both have a resource there.
|
||||
These localizations are excluded from our usual and automated translation flow due to how Transifex synchronizes Xcode string catalogs and the danger of data loss.
|
||||
To pull updated localizations from Transifex into the Xcode project manually, follow the steps below.
|
||||
|
||||
### Configuration
|
||||
|
||||
The dedicated [`.tx/config`](.tx/config) file is used.
|
||||
|
||||
## Pull Translations
|
||||
|
||||
Run this in the "NextcloudIntegration" project folder of your repository clone:
|
||||
|
||||
```sh
|
||||
tx pull --force --all --mode=reviewed --minimum-perc=50
|
||||
```
|
||||
|
||||
### Sanitize Translations
|
||||
|
||||
Transifex returns empty strings for keys with untranslated localizations.
|
||||
To remove them, we use the Swift command-line utility [TransifexStringCatalogSanitizer](../../../admin/osx/TransifexStringCatalogSanitizer/).
|
||||
See its dedicated README for usage instructions.
|
||||
Use it for all updated Xcode string catalogs.
|
||||
|
||||
### Cleanup
|
||||
|
||||
1. Revert your changes to the Transifex configuration.
|
||||
2. Review the updated Xcode string catalogs.
|
||||
3. Commit the updated Xcode string catalogs.
|
||||
|
||||
## Nextcloud Developer Build
|
||||
|
||||
There is a special target in the Xcode project which integrates the `mac-crafter` command-line tool as an external build system in form of a scheme.
|
||||
In other words: The "NextcloudDev" scheme enables you to build, run and debug the Nextcloud desktop client with the simple run action in Xcode.
|
||||
|
||||
### Requirements
|
||||
|
||||
- You must have an Apple Development certificate for signing in your keychain.
|
||||
|
||||
### Usage
|
||||
|
||||
1. Copy [`Build.xcconfig.template`](NextcloudDev/Build.xcconfig.template) in the "NextcloudDev" source code folder to `Build.xcconfig` in the same location and adjust the values in it to your local setup.
|
||||
2. Build or run the "NextcloudDev scheme".
|
||||
|
||||
### Known Issues
|
||||
|
||||
- Right now, the project does not support signing a different app identity than the default one (`com.nextcloud.desktopclient`) which is owned by the Nextcloud GmbH development team registered with Apple.
|
||||
This means that you have to be signed in with a developer account in Xcode which is part of that development team when building.
|
||||
- Even when building successfully, Xcode may conclude that the build failed or at least some errors occurred during the build.
|
||||
During the build, some command outputs messages with an "Error: " prefix.
|
||||
Since this is the same way the compiler usually reports errors to Xcode, the latter assumes something might have gone wrong.
|
||||
But no invocation exits with an error code.
|
||||
Hence, the build can still complete successfully while Xcode might just misinterpret the console output.
|
||||
You will see at the end of the build output log in Xcode.
|
||||
|
||||
### How It Works
|
||||
|
||||
- The "NextcloudDev" target runs the [Craft.sh](NextcloudDev/Craft.sh) shell script which is part of this Xcode project.
|
||||
- `Craft.sh` prepares the execution of and finally runs [`mac-crafter`](https://github.com/nextcloud/desktop/tree/master/admin/osx/mac-crafter) which is part of the Nextcloud desktop client repository to simplify builds on macOS.
|
||||
- By running `mac-crafter` with the right arguments and options, Xcode can attach to the built app with its debugger and stop at breakpoints.
|
||||
One of the key factors is the `Debug` build type which flips certain switches in the CMake build scripts ([in example: app hardening or get-task-allow entitlement](https://github.com/nextcloud/desktop/pull/8474/files)).
|
||||
- The built Nextcloud desktop client app bundle is not placed into a derived data directory of Xcode but `/Applications`.
|
||||
The standard behavior of placing the product into Xcode's derived data directory would result in absolute reference paths within the scheme file which are not portable across devices and users due to varying user names.
|
||||
|
||||
### Hints
|
||||
|
||||
Just for reference, a few helpful snippets for inspecting state on breakpoints with the Xcode debugger.
|
||||
|
||||
#### Print a `QString`
|
||||
|
||||
```lldb
|
||||
call someString.toStdString()
|
||||
```
|
||||
|
||||
#### Print a `QStringList`
|
||||
|
||||
```lldb
|
||||
call someStrings.join("\n").toStdString()
|
||||
```
|
||||
|
||||
#### Attach to File Provider Extension Process
|
||||
|
||||
You can debug the file provider extension process(es) in Xcode by attaching to them by their binary name.
|
||||
|
||||
1. Select this menu item in Xcode: _Debug_ → _Attach to Process by PID or Name..._
|
||||
2. Enter `FileProviderExt`.
|
||||
If you would also like to debug the file provider UI extension, then you can additionally specify `FileProviderUIExt`.
|
||||
3. Confirm.
|
||||
4. If no process is already running, then Xcode will wait for it to be launched to attach automatically.
|
||||
This usually happens when you launch the main app or set up a new account with file provider enabled.
|
||||
|
||||
#### Work on NextcloudFileProviderKit and NextcloudKit
|
||||
|
||||
You can directly debug changes to these dependencies edited from this project.
|
||||
You have to have local repository clones of the packages somewhere locally, though.
|
||||
Drag and drop the package folders into the project navigator of the NextcloudIntegration project.
|
||||
This will cause Xcode to resolve to the local and editable package instead of a cached read-only clone from GitHub.
|
||||
When you then run the build action of this root project, the local dependency is built as well.
|
||||
@ -31,8 +31,8 @@ if(NOT BUILD_LIBRARIES_ONLY)
|
||||
add_executable(nextcloudcmd
|
||||
cmd.h
|
||||
cmd.cpp)
|
||||
set_target_properties(nextcloudcmd PROPERTIES
|
||||
RUNTIME_OUTPUT_NAME "${APPLICATION_EXECUTABLE}cmd")
|
||||
ecm_mark_nongui_executable(nextcloudcmd)
|
||||
set_target_properties(nextcloudcmd PROPERTIES RUNTIME_OUTPUT_NAME "${APPLICATION_EXECUTABLE}cmd")
|
||||
|
||||
target_link_libraries(nextcloudcmd cmdCore)
|
||||
|
||||
|
||||
@ -779,14 +779,14 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
|
||||
if (!GetFileSecurityW(path.toStdWString().c_str(), info, nullptr, 0, &neededLength)) {
|
||||
const auto lastError = GetLastError();
|
||||
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
|
||||
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << lastError;
|
||||
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << Utility::formatWinError(lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
securityDescriptor.reset(new char[neededLength]);
|
||||
|
||||
if (!GetFileSecurityW(path.toStdWString().c_str(), info, securityDescriptor.get(), neededLength, &neededLength)) {
|
||||
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << GetLastError();
|
||||
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << Utility::formatWinError(GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -794,7 +794,7 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
|
||||
int daclPresent = false, daclDefault = false;
|
||||
PACL resultDacl = nullptr;
|
||||
if (!GetSecurityDescriptorDacl(securityDescriptor.get(), &daclPresent, &resultDacl, &daclDefault)) {
|
||||
qCWarning(lcFileSystem) << "error when calling GetSecurityDescriptorDacl" << path << GetLastError();
|
||||
qCWarning(lcFileSystem) << "error when calling GetSecurityDescriptorDacl" << path << Utility::formatWinError(GetLastError());
|
||||
return false;
|
||||
}
|
||||
if (!daclPresent || !resultDacl) {
|
||||
@ -805,13 +805,13 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
|
||||
PSID sid = nullptr;
|
||||
if (!ConvertStringSidToSidW(L"S-1-5-32-545", &sid))
|
||||
{
|
||||
qCWarning(lcFileSystem) << "error when calling ConvertStringSidToSidA" << path << GetLastError();
|
||||
qCWarning(lcFileSystem) << "error when calling ConvertStringSidToSidA" << path << Utility::formatWinError(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
ACL_SIZE_INFORMATION aclSize;
|
||||
if (!GetAclInformation(resultDacl, &aclSize, sizeof(aclSize), AclSizeInformation)) {
|
||||
qCWarning(lcFileSystem) << "error when calling GetAclInformation" << path << GetLastError();
|
||||
qCWarning(lcFileSystem) << "error when calling GetAclInformation" << path << Utility::formatWinError(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -821,19 +821,19 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
|
||||
std::unique_ptr<ACL> newDacl{reinterpret_cast<PACL>(new char[newAclSize])};
|
||||
if (!InitializeAcl(newDacl.get(), newAclSize, ACL_REVISION)) {
|
||||
const auto lastError = GetLastError();
|
||||
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
|
||||
if (lastError == ERROR_INSUFFICIENT_BUFFER) {
|
||||
qCWarning(lcFileSystem) << "insufficient memory error when calling InitializeAcl" << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
qCWarning(lcFileSystem) << "error when calling InitializeAcl" << path << lastError;
|
||||
qCWarning(lcFileSystem) << "error when calling InitializeAcl" << path << Utility::formatWinError(lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (permissions == FileSystem::FolderPermissions::ReadOnly) {
|
||||
if (!AddAccessDeniedAceEx(newDacl.get(), ACL_REVISION, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE,
|
||||
FILE_DELETE_CHILD | DELETE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA, sid)) {
|
||||
qCWarning(lcFileSystem) << "error when calling AddAccessDeniedAce << path" << GetLastError();
|
||||
qCWarning(lcFileSystem) << "error when calling AddAccessDeniedAce" << path << Utility::formatWinError(GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -841,7 +841,7 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
|
||||
for (int i = 0; i < aclSize.AceCount; ++i) {
|
||||
void *currentAce = nullptr;
|
||||
if (!GetAce(resultDacl, i, ¤tAce)) {
|
||||
qCWarning(lcFileSystem) << "error when calling GetAce" << path << GetLastError();
|
||||
qCWarning(lcFileSystem) << "error when calling GetAce" << path << Utility::formatWinError(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -854,29 +854,29 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
|
||||
|
||||
if (!AddAce(newDacl.get(), ACL_REVISION, i + 1, currentAce, currentAceHeader->AceSize)) {
|
||||
const auto lastError = GetLastError();
|
||||
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
|
||||
if (lastError == ERROR_INSUFFICIENT_BUFFER) {
|
||||
qCWarning(lcFileSystem) << "insufficient memory error when calling AddAce" << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lastError != ERROR_INVALID_PARAMETER) {
|
||||
if (lastError == ERROR_INVALID_PARAMETER) {
|
||||
qCWarning(lcFileSystem) << "invalid parameter error when calling AddAce" << path << "ACL size" << newAclSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
qCWarning(lcFileSystem) << "error when calling AddAce" << path << lastError << "acl index" << (i + 1);
|
||||
qCWarning(lcFileSystem) << "error when calling AddAce" << path << Utility::formatWinError(lastError) << "acl index" << (i + 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SECURITY_DESCRIPTOR newSecurityDescriptor;
|
||||
if (!InitializeSecurityDescriptor(&newSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)) {
|
||||
qCWarning(lcFileSystem) << "error when calling InitializeSecurityDescriptor" << path << GetLastError();
|
||||
qCWarning(lcFileSystem) << "error when calling InitializeSecurityDescriptor" << path << Utility::formatWinError(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetSecurityDescriptorDacl(&newSecurityDescriptor, true, newDacl.get(), false)) {
|
||||
qCWarning(lcFileSystem) << "error when calling SetSecurityDescriptorDacl" << path << GetLastError();
|
||||
qCWarning(lcFileSystem) << "error when calling SetSecurityDescriptorDacl" << path << Utility::formatWinError(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -896,14 +896,14 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
|
||||
}
|
||||
|
||||
if (!SetFileSecurityW(childFileStdWString.c_str(), info, &newSecurityDescriptor)) {
|
||||
qCWarning(lcFileSystem) << "error when calling SetFileSecurityW" << childFile << GetLastError();
|
||||
qCWarning(lcFileSystem) << "error when calling SetFileSecurityW" << childFile << Utility::formatWinError(GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!SetFileSecurityW(QDir::toNativeSeparators(path).toStdWString().c_str(), info, &newSecurityDescriptor)) {
|
||||
qCWarning(lcFileSystem) << "error when calling SetFileSecurityW" << QDir::toNativeSeparators(path) << GetLastError();
|
||||
qCWarning(lcFileSystem) << "error when calling SetFileSecurityW" << QDir::toNativeSeparators(path) << Utility::formatWinError(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ namespace OCC {
|
||||
/**
|
||||
* Represent the quota for each folder retrieved from the server
|
||||
* bytesUsed: space used in bytes
|
||||
* bytesAvailale: free space available in bytes or
|
||||
* bytesAvailable: free space available in bytes or
|
||||
* -1: Uncomputed free space - new folder (externally created) not yet scanned by the server
|
||||
* -2: Unknown free space
|
||||
* -3: Unlimited free space.
|
||||
|
||||
@ -733,7 +733,8 @@ void ExcludedFiles::prepare(const BasePathString & basePath)
|
||||
pattern.append(appendMe);
|
||||
};
|
||||
|
||||
for (auto exclude : _allExcludes.value(basePath)) {
|
||||
const auto &allValues = _allExcludes.value(basePath);
|
||||
for (auto exclude : allValues) {
|
||||
if (exclude[0] == QLatin1Char('\n'))
|
||||
continue; // empty line
|
||||
if (exclude[0] == QLatin1Char('\r'))
|
||||
|
||||
@ -302,17 +302,12 @@ IF( APPLE )
|
||||
macOS/fileprovider_mac.mm
|
||||
macOS/fileproviderdomainmanager.h
|
||||
macOS/fileproviderdomainmanager_mac.mm
|
||||
macOS/fileproviderdomainsyncstatus.h
|
||||
macOS/fileproviderdomainsyncstatus_mac.mm
|
||||
macOS/fileprovidereditlocallyjob.h
|
||||
macOS/fileprovidereditlocallyjob.cpp
|
||||
macOS/fileprovidereditlocallyjob_mac.mm
|
||||
macOS/fileprovideritemmetadata.h
|
||||
macOS/fileprovideritemmetadata.cpp
|
||||
macOS/fileprovideritemmetadata_mac.mm
|
||||
macOS/fileprovidermaterialiseditemsmodel.h
|
||||
macOS/fileprovidermaterialiseditemsmodel.cpp
|
||||
macOS/fileprovidermaterialiseditemsmodel_mac.mm
|
||||
macOS/fileprovidersettingscontroller.h
|
||||
macOS/fileprovidersettingscontroller_mac.mm
|
||||
macOS/fileprovidersocketcontroller.h
|
||||
@ -320,8 +315,6 @@ IF( APPLE )
|
||||
macOS/fileprovidersocketserver.h
|
||||
macOS/fileprovidersocketserver.cpp
|
||||
macOS/fileprovidersocketserver_mac.mm
|
||||
macOS/fileproviderstorageuseenumerationobserver.h
|
||||
macOS/fileproviderstorageuseenumerationobserver.m
|
||||
macOS/fileproviderutils.h
|
||||
macOS/fileproviderutils_mac.mm
|
||||
macOS/fileproviderxpc.h
|
||||
@ -484,27 +477,6 @@ set(APP_ICON_WIN_FOLDER_SVG "${APP_SECONDARY_ICONS}/${APPLICATION_ICON_NAME}-ico
|
||||
|
||||
set(RC_DEPENDENCIES "")
|
||||
|
||||
if(WIN32)
|
||||
if (EXISTS ${APP_ICON_WIN_FOLDER_SVG})
|
||||
get_filename_component(output_icon_name_win ${APP_ICON_WIN_FOLDER_SVG} NAME_WLE)
|
||||
# Product icon (for smallest size)
|
||||
foreach(size IN ITEMS 16;20)
|
||||
generate_sized_png_from_svg(${APP_ICON_SVG} ${size} OUTPUT_ICON_NAME ${output_icon_name_win} OUTPUT_ICON_PATH "${APP_SECONDARY_ICONS}/")
|
||||
endforeach()
|
||||
|
||||
# Product icon with Windows folder (for sizes larger than 20)
|
||||
foreach(size IN ITEMS 24;32;40;48;64;128;256;512;1024)
|
||||
generate_sized_png_from_svg(${APP_ICON_WIN_FOLDER_SVG} ${size} OUTPUT_ICON_NAME ${output_icon_name_win} OUTPUT_ICON_PATH "${APP_SECONDARY_ICONS}/")
|
||||
endforeach()
|
||||
|
||||
file(GLOB_RECURSE OWNCLOUD_ICONS_WIN_FOLDER "${APP_SECONDARY_ICONS}/*-${APPLICATION_ICON_NAME}-icon*")
|
||||
set(APP_ICON_WIN_FOLDER_ICO_NAME "${APPLICATION_ICON_NAME}-win-folder")
|
||||
set(RC_DEPENDENCIES "${RC_DEPENDENCIES} ${APP_ICON_WIN_FOLDER_ICO_NAME}.ico")
|
||||
ecm_add_app_icon(APP_ICON_WIN_FOLDER ICONS "${OWNCLOUD_ICONS_WIN_FOLDER}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASENAME "${APP_ICON_WIN_FOLDER_ICO_NAME}" ICON_INDEX 2)
|
||||
endif()
|
||||
endif()
|
||||
# --------------------------------------
|
||||
|
||||
if (NOT ${RC_DEPENDENCIES} STREQUAL "")
|
||||
string(STRIP ${RC_DEPENDENCIES} RC_DEPENDENCIES)
|
||||
endif()
|
||||
@ -527,35 +499,26 @@ if(APPLE)
|
||||
MESSAGE(STATUS "OWNCLOUD_SIDEBAR_ICONS: ${APPLICATION_ICON_NAME}: ${OWNCLOUD_SIDEBAR_ICONS}")
|
||||
endif()
|
||||
|
||||
ecm_add_app_icon(APP_ICON RC_DEPENDENCIES ${RC_DEPENDENCIES} ICONS "${OWNCLOUD_ICONS}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASENAME "${APPLICATION_ICON_NAME}" ICON_INDEX 1)
|
||||
# --------------------------------------
|
||||
if(WIN32 AND EXISTS ${APP_ICON_WIN_FOLDER_SVG})
|
||||
get_filename_component(output_icon_name_win ${APP_ICON_WIN_FOLDER_SVG} NAME_WLE)
|
||||
# Product icon (for smallest size)
|
||||
foreach(size IN ITEMS 16;20)
|
||||
generate_sized_png_from_svg(${APP_ICON_SVG} ${size} OUTPUT_ICON_NAME ${output_icon_name_win} OUTPUT_ICON_PATH "${APP_SECONDARY_ICONS}/")
|
||||
endforeach()
|
||||
|
||||
if(WIN32)
|
||||
# merge *.rc.in files for Windows (multiple ICON resources must be placed in a single file, otherwise, this won't work de to a bug in Windows compiler https://developercommunity.visualstudio.com/t/visual-studio-2017-prof-1557-cvt1100-duplicate-res/363156)
|
||||
function(merge_files IN_FILE OUT_FILE)
|
||||
file(READ ${IN_FILE} CONTENTS)
|
||||
message("Merging ${IN_FILE} into ${OUT_FILE}")
|
||||
file(APPEND ${OUT_FILE} "${CONTENTS}")
|
||||
endfunction()
|
||||
message("APP_ICON is: ${APP_ICON}")
|
||||
if(APP_ICON)
|
||||
get_filename_component(RC_IN_FOLDER ${APP_ICON}} DIRECTORY)
|
||||
# Product icon with Windows folder (for sizes larger than 20)
|
||||
foreach(size IN ITEMS 24;32;40;48;64;128;256;512;1024)
|
||||
generate_sized_png_from_svg(${APP_ICON_WIN_FOLDER_SVG} ${size} OUTPUT_ICON_NAME ${output_icon_name_win} OUTPUT_ICON_PATH "${APP_SECONDARY_ICONS}/")
|
||||
endforeach()
|
||||
|
||||
file(GLOB_RECURSE RC_IN_FILES "${RC_IN_FOLDER}/*rc.in")
|
||||
|
||||
foreach(rc_in_file IN ITEMS ${RC_IN_FILES})
|
||||
get_filename_component(rc_in_file_name ${rc_in_file} NAME)
|
||||
get_filename_component(app_icon_name "${APP_ICON}.in" NAME)
|
||||
if(NOT "${rc_in_file_name}" STREQUAL "${app_icon_name}")
|
||||
merge_files(${rc_in_file} "${APP_ICON}.in")
|
||||
if (DEFINED APPLICATION_FOLDER_ICON_INDEX)
|
||||
MATH(EXPR APPLICATION_FOLDER_ICON_INDEX "${APPLICATION_FOLDER_ICON_INDEX}+1")
|
||||
message("APPLICATION_FOLDER_ICON_INDEX is now set to: ${APPLICATION_FOLDER_ICON_INDEX}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
file(GLOB_RECURSE OWNCLOUD_ICONS_WIN_FOLDER "${APP_SECONDARY_ICONS}/*-${APPLICATION_ICON_NAME}-icon*")
|
||||
set(APP_ICON_WIN_FOLDER_ICO_NAME "${APPLICATION_ICON_NAME}-win-folder")
|
||||
set(RC_DEPENDENCIES "${RC_DEPENDENCIES} ${APP_ICON_WIN_FOLDER_ICO_NAME}.ico")
|
||||
ecm_add_app_icon(APP_ICON ICONS ${OWNCLOUD_ICONS_WIN_FOLDER} ${OWNCLOUD_ICONS} SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASENAME "${APPLICATION_ICON_NAME}")
|
||||
else ()
|
||||
ecm_add_app_icon(APP_ICON ICONS "${OWNCLOUD_ICONS}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASENAME "${APPLICATION_ICON_NAME}")
|
||||
endif()
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
|
||||
@ -116,6 +116,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
|
||||
text: modelData === undefined ? "" : modelData.shortname
|
||||
visible: emojiDelegate.hovered
|
||||
delay: Qt.styleHints.mousePressAndHoldInterval
|
||||
|
||||
@ -217,7 +217,7 @@ bool AccountManager::restoreFromLegacySettings()
|
||||
legacyLocations.append({legacyCfgFileParentFolder + unbrandedCfgFileNamePath, legacyCfgFileGrandParentFolder + unbrandedCfgFileRelativePath});
|
||||
}
|
||||
|
||||
for (const auto &configFile : legacyLocations) {
|
||||
for (const auto &configFile : std::as_const(legacyLocations)) {
|
||||
auto oCSettings = std::make_unique<QSettings>(configFile, QSettings::IniFormat);
|
||||
if (oCSettings->status() != QSettings::Status::NoError) {
|
||||
qCInfo(lcAccountManager) << "Error reading legacy configuration file" << oCSettings->status();
|
||||
@ -699,7 +699,7 @@ void AccountManager::deleteAccount(OCC::AccountState *account)
|
||||
void AccountManager::updateServerHasValidSubscriptionConfig()
|
||||
{
|
||||
auto serverHasValidSubscription = false;
|
||||
for (const auto &account : _accounts) {
|
||||
for (const auto &account : std::as_const(_accounts)) {
|
||||
if (!account->account()->serverHasValidSubscription()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1143,7 +1143,8 @@ void AccountSettings::forgetEncryptionOnDeviceForAccount(const AccountPtr &accou
|
||||
|
||||
void AccountSettings::migrateCertificateForAccount(const AccountPtr &account)
|
||||
{
|
||||
for (const auto action : _ui->encryptionMessage->actions()) {
|
||||
const auto &allActions = _ui->encryptionMessage->actions();
|
||||
for (const auto action : allActions) {
|
||||
_ui->encryptionMessage->removeAction(action);
|
||||
}
|
||||
|
||||
@ -1703,7 +1704,8 @@ void AccountSettings::setupE2eEncryption()
|
||||
|
||||
void AccountSettings::forgetE2eEncryption()
|
||||
{
|
||||
for (const auto action : _ui->encryptionMessage->actions()) {
|
||||
const auto &allActions = _ui->encryptionMessage->actions();
|
||||
for (const auto action : allActions) {
|
||||
_ui->encryptionMessage->removeAction(action);
|
||||
}
|
||||
_ui->encryptionMessage->setText({});
|
||||
|
||||
@ -293,7 +293,8 @@ Application::Application(int &argc, char **argv)
|
||||
|
||||
if (_theme->doNotUseProxy()) {
|
||||
ConfigFile().setProxyType(QNetworkProxy::NoProxy);
|
||||
for (const auto &accountState : AccountManager::instance()->accounts()) {
|
||||
const auto &allAccounts = AccountManager::instance()->accounts();
|
||||
for (const auto &accountState : allAccounts) {
|
||||
if (accountState && accountState->account()) {
|
||||
accountState->account()->setProxyType(QNetworkProxy::NoProxy);
|
||||
}
|
||||
@ -401,7 +402,8 @@ Application::Application(int &argc, char **argv)
|
||||
|
||||
if (_theme->doNotUseProxy()) {
|
||||
ConfigFile().setProxyType(QNetworkProxy::NoProxy);
|
||||
for (const auto &accountState : AccountManager::instance()->accounts()) {
|
||||
const auto &allAccounts = AccountManager::instance()->accounts();
|
||||
for (const auto &accountState : allAccounts) {
|
||||
if (accountState && accountState->account()) {
|
||||
accountState->account()->setProxyType(QNetworkProxy::NoProxy);
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include "account.h"
|
||||
#include "folder.h"
|
||||
#include "common/filesystembase.h"
|
||||
#include "common/utility.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QDir>
|
||||
@ -202,7 +203,7 @@ void CaseClashFilenameDialog::updateFileWidgetGroup(const QString &filePath,
|
||||
const auto lastModifiedString = filePathFileInfo.lastModified().toString();
|
||||
const auto fileSizeString = locale().formattedDataSize(filePathFileInfo.size());
|
||||
const auto fileUrl = QUrl::fromLocalFile(filePath).toString();
|
||||
const auto linkString = QStringLiteral("<a href='%1'>%2</a>").arg(fileUrl, linkText);
|
||||
const auto linkString = QStringLiteral("<a href=\"%1\">%2</a>").arg(Utility::escape(fileUrl), linkText);
|
||||
const auto mime = QMimeDatabase().mimeTypeForFile(_filePath, QMimeDatabase::MatchExtension);
|
||||
QIcon fileTypeIcon;
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "ui_conflictdialog.h"
|
||||
|
||||
#include "conflictsolver.h"
|
||||
#include "common/utility.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
@ -121,7 +122,7 @@ void ConflictDialog::updateWidgets()
|
||||
|
||||
const auto updateGroup = [this, &mimeDb](const QString &filename, QLabel *linkLabel, const QString &linkText, QLabel *mtimeLabel, QLabel *sizeLabel, QToolButton *button) {
|
||||
const auto fileUrl = QUrl::fromLocalFile(filename).toString();
|
||||
linkLabel->setText(QStringLiteral("<a href='%1'>%2</a>").arg(fileUrl).arg(linkText));
|
||||
linkLabel->setText(QStringLiteral("<a href=\"%1\">%2</a>").arg(Utility::escape(fileUrl)).arg(linkText));
|
||||
|
||||
const auto info = QFileInfo(filename);
|
||||
mtimeLabel->setText(info.lastModified().toString());
|
||||
|
||||
@ -196,6 +196,7 @@ Page {
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
|
||||
visible: hoverHandler.hovered
|
||||
text: tagRepeater.fileTagModel.overflowTagsString
|
||||
}
|
||||
|
||||
@ -385,7 +385,8 @@ void FolderMan::backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringLi
|
||||
const auto foldersVersion = settings->value(QLatin1String(settingsVersionC), 1).toInt();
|
||||
qCInfo(lcFolderMan) << "FolderDefinition::maxSettingsVersion:" << FolderDefinition::maxSettingsVersion();
|
||||
if (foldersVersion <= maxFoldersVersion) {
|
||||
for (const auto &folderAlias : settings->childGroups()) {
|
||||
const auto &childGroups = settings->childGroups();
|
||||
for (const auto &folderAlias : childGroups) {
|
||||
settings->beginGroup(folderAlias);
|
||||
const auto folderVersion = settings->value(QLatin1String(settingsVersionC), 1).toInt();
|
||||
if (folderVersion > FolderDefinition::maxSettingsVersion()) {
|
||||
@ -591,7 +592,7 @@ void FolderMan::setupLegacyFolder(const QString &fileNamePath, AccountState *acc
|
||||
legacyBlacklist << settings.value(QLatin1String("blackList")).toStringList();
|
||||
if (!legacyBlacklist.isEmpty()) {
|
||||
qCInfo(lcFolderMan) << "Legacy selective sync list found:" << legacyBlacklist;
|
||||
for (const auto &legacyFolder : legacyBlacklist) {
|
||||
for (const auto &legacyFolder : std::as_const(legacyBlacklist)) {
|
||||
folder->migrateBlackListPath(legacyFolder);
|
||||
}
|
||||
settings.remove(QLatin1String("blackList"));
|
||||
|
||||
@ -333,7 +333,7 @@ bool FolderStatusModel::setData(const QModelIndex &index, const QVariant &value,
|
||||
const auto parentInfo = infoForIndex(parent);
|
||||
if (parentInfo && parentInfo->_checked != Qt::Checked) {
|
||||
auto hasUnchecked = false;
|
||||
for (const auto &sub : parentInfo->_subs) {
|
||||
for (const auto &sub : std::as_const(parentInfo->_subs)) {
|
||||
if (sub._checked != Qt::Checked) {
|
||||
hasUnchecked = true;
|
||||
break;
|
||||
@ -714,7 +714,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
|
||||
}
|
||||
|
||||
std::set<QString> selectiveSyncUndecidedSet; // not QSet because it's not sorted
|
||||
for (const auto &str : selectiveSyncUndecidedList) {
|
||||
for (const auto &str : std::as_const(selectiveSyncUndecidedList)) {
|
||||
if (str.startsWith(parentInfo->_path) || parentInfo->_path == QLatin1String("/")) {
|
||||
selectiveSyncUndecidedSet.insert(str);
|
||||
}
|
||||
@ -732,7 +732,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
|
||||
|
||||
QVector<SubFolderInfo> newSubs;
|
||||
newSubs.reserve(sortedSubfolders.size());
|
||||
for (const auto &path : sortedSubfolders) {
|
||||
for (const auto &path : std::as_const(sortedSubfolders)) {
|
||||
auto relativePath = path.mid(pathToRemove.size());
|
||||
if (parentInfo->_folder->isFileExcludedRelative(relativePath)) {
|
||||
continue;
|
||||
@ -781,7 +781,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
|
||||
} else if (parentInfo->_checked == Qt::Checked) {
|
||||
newInfo._checked = Qt::Checked;
|
||||
} else {
|
||||
for (const auto &str : selectiveSyncBlackList) {
|
||||
for (const auto &str : std::as_const(selectiveSyncBlackList)) {
|
||||
if (str == relativePath || str == QLatin1String("/")) {
|
||||
newInfo._checked = Qt::Unchecked;
|
||||
break;
|
||||
@ -940,7 +940,7 @@ void FolderStatusModel::slotApplySelectiveSync()
|
||||
}
|
||||
//The part that changed should not be read from the DB on next sync because there might be new folders
|
||||
// (the ones that are no longer in the blacklist)
|
||||
for (const auto &it : changes) {
|
||||
for (const auto &it : std::as_const(changes)) {
|
||||
folder->journalDb()->schedulePathForRemoteDiscovery(it);
|
||||
folder->schedulePathForLocalDiscovery(it);
|
||||
}
|
||||
@ -1227,7 +1227,7 @@ void FolderStatusModel::slotSyncAllPendingBigFolders()
|
||||
qCWarning(lcFolderStatus) << "Could not read selective sync list from db.";
|
||||
return;
|
||||
}
|
||||
for (const auto &undecidedFolder : undecidedList) {
|
||||
for (const auto &undecidedFolder : std::as_const(undecidedList)) {
|
||||
blackList.removeAll(undecidedFolder);
|
||||
}
|
||||
folder->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blackList);
|
||||
@ -1250,7 +1250,7 @@ void FolderStatusModel::slotSyncAllPendingBigFolders()
|
||||
}
|
||||
// The part that changed should not be read from the DB on next sync because there might be new folders
|
||||
// (the ones that are no longer in the blacklist)
|
||||
for (const auto &it : undecidedList) {
|
||||
for (const auto &it : std::as_const(undecidedList)) {
|
||||
folder->journalDb()->schedulePathForRemoteDiscovery(it);
|
||||
folder->schedulePathForLocalDiscovery(it);
|
||||
}
|
||||
|
||||
@ -360,7 +360,7 @@ void FolderWizardRemotePath::slotUpdateDirectories(const QStringList &list)
|
||||
}
|
||||
QStringList sortedList = list;
|
||||
Utility::sortFilenames(sortedList);
|
||||
for (auto path : sortedList) {
|
||||
for (auto path : std::as_const(sortedList)) {
|
||||
path.remove(webdavFolder);
|
||||
|
||||
// Don't allow to select subfolders of encrypted subfolders
|
||||
|
||||
@ -166,12 +166,12 @@ bool createDebugArchive(const QString &filename)
|
||||
#endif
|
||||
|
||||
const auto clientParameters = QCoreApplication::arguments().join(' ').toUtf8();
|
||||
zip.prepareWriting("__nextcloud_client_parameters.txt", {}, {}, clientParameters.size());
|
||||
zip.prepareWriting("_client_parameters.txt", {}, {}, clientParameters.size());
|
||||
zip.writeData(clientParameters, clientParameters.size());
|
||||
zip.finishWriting(clientParameters.size());
|
||||
|
||||
const auto buildInfo = QString(OCC::Theme::instance()->aboutInfo() + "\n\n" + OCC::Theme::instance()->aboutDetails()).toUtf8();
|
||||
zip.prepareWriting("__nextcloud_client_buildinfo.txt", {}, {}, buildInfo.size());
|
||||
zip.prepareWriting("_client_buildinfo.txt", {}, {}, buildInfo.size());
|
||||
zip.writeData(buildInfo, buildInfo.size());
|
||||
zip.finishWriting(buildInfo.size());
|
||||
return true;
|
||||
|
||||
@ -591,7 +591,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
QStringList configuredDomainIds() const
|
||||
QStringList getAccountIdsOfFoundFileProviderDomains() const
|
||||
{
|
||||
return _registeredDomains.keys();
|
||||
}
|
||||
@ -652,34 +652,36 @@ void FileProviderDomainManager::updateFileProviderDomains()
|
||||
return;
|
||||
}
|
||||
|
||||
const auto vfsEnabledAccounts = FileProviderSettingsController::instance()->vfsEnabledAccounts();
|
||||
auto configuredDomains = d->configuredDomainIds();
|
||||
const auto fileProviderEnabledAccountIds = FileProviderSettingsController::instance()->vfsEnabledAccounts();
|
||||
auto accountIdsOfFoundFileProviderDomains = d->getAccountIdsOfFoundFileProviderDomains();
|
||||
|
||||
for (const auto &accountUserIdAtHost : vfsEnabledAccounts) {
|
||||
// If the domain has already been set up for this account, then don't set it up again
|
||||
if (configuredDomains.contains(accountUserIdAtHost)) {
|
||||
configuredDomains.removeAll(accountUserIdAtHost);
|
||||
for (const auto &fileProviderEnabledAccountId : fileProviderEnabledAccountIds) {
|
||||
// If the domain has already been set up for this account, then don't set it up again.
|
||||
if (accountIdsOfFoundFileProviderDomains.contains(fileProviderEnabledAccountId)) {
|
||||
accountIdsOfFoundFileProviderDomains.removeAll(fileProviderEnabledAccountId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto accountState = AccountManager::instance()->accountFromUserId(accountUserIdAtHost)) {
|
||||
if (const auto accountState = AccountManager::instance()->accountFromUserId(fileProviderEnabledAccountId)) {
|
||||
qCDebug(lcMacFileProviderDomainManager) << "Succeed in fetching account state by account id"
|
||||
<< accountUserIdAtHost
|
||||
<< fileProviderEnabledAccountId
|
||||
<< ", adding file provider domain for account.";
|
||||
|
||||
addFileProviderDomainForAccount(accountState.data());
|
||||
} else {
|
||||
qCWarning(lcMacFileProviderDomainManager) << "Could not fetch account state by account id"
|
||||
<< accountUserIdAtHost
|
||||
<< fileProviderEnabledAccountId
|
||||
<< ", removing account from list of VFS-enabled accounts.";
|
||||
|
||||
FileProviderSettingsController::instance()->setVfsEnabledForAccount(accountUserIdAtHost, false);
|
||||
FileProviderSettingsController::instance()->setVfsEnabledForAccount(fileProviderEnabledAccountId, false);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &remainingDomainUserId : configuredDomains) {
|
||||
const auto accountId = accountIdFromDomainId(remainingDomainUserId);
|
||||
const auto accountState = AccountManager::instance()->accountFromUserId(accountId);
|
||||
for (const auto &remainingAccountId : accountIdsOfFoundFileProviderDomains) {
|
||||
qCDebug(lcMacFileProviderDomainManager) << "Orphaned file provider domain to remove found for account id"
|
||||
<< remainingAccountId;
|
||||
|
||||
const auto accountState = AccountManager::instance()->accountFromUserId(remainingAccountId);
|
||||
removeFileProviderDomainForAccount(accountState.data());
|
||||
}
|
||||
|
||||
@ -721,9 +723,6 @@ void FileProviderDomainManager::removeFileProviderDomainForAccount(const Account
|
||||
}
|
||||
|
||||
Q_ASSERT(accountState);
|
||||
const auto account = accountState->account();
|
||||
Q_ASSERT(account);
|
||||
|
||||
d->removeFileProviderDomain(accountState);
|
||||
}
|
||||
|
||||
|
||||
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace OCC::Mac
|
||||
{
|
||||
|
||||
class FileProviderDomainSyncStatus : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("FileProviderDomainSyncStatus cannot be instantiated from QML")
|
||||
Q_PROPERTY(bool syncing READ syncing NOTIFY syncingChanged)
|
||||
Q_PROPERTY(bool downloading READ downloading NOTIFY downloadingChanged)
|
||||
Q_PROPERTY(bool uploading READ uploading NOTIFY uploadingChanged)
|
||||
Q_PROPERTY(double fractionCompleted READ fractionCompleted NOTIFY fractionCompletedChanged)
|
||||
Q_PROPERTY(double downloadFractionCompleted READ downloadFractionCompleted NOTIFY downloadFractionCompletedChanged)
|
||||
Q_PROPERTY(double uploadFractionCompleted READ uploadFractionCompleted NOTIFY uploadFractionCompletedChanged)
|
||||
Q_PROPERTY(int downloadFileTotalCount READ downloadFileTotalCount NOTIFY downloadFileTotalCountChanged)
|
||||
Q_PROPERTY(int downloadFileCompletedCount READ downloadFileCompletedCount NOTIFY downloadFileCompletedCountChanged)
|
||||
Q_PROPERTY(int uploadFileTotalCount READ uploadFileTotalCount NOTIFY uploadFileTotalCountChanged)
|
||||
Q_PROPERTY(int uploadFileCompletedCount READ uploadFileCompletedCount NOTIFY uploadFileCompletedCountChanged)
|
||||
// TODO: more detailed reporting (time remaining, megabytes, etc.)
|
||||
Q_PROPERTY(QUrl icon READ icon NOTIFY iconChanged)
|
||||
|
||||
public:
|
||||
explicit FileProviderDomainSyncStatus(const QString &domainIdentifier, QObject *parent = nullptr);
|
||||
~FileProviderDomainSyncStatus() override;
|
||||
|
||||
[[nodiscard]] bool syncing() const;
|
||||
[[nodiscard]] bool downloading() const;
|
||||
[[nodiscard]] bool uploading() const;
|
||||
[[nodiscard]] double fractionCompleted() const;
|
||||
[[nodiscard]] double downloadFractionCompleted() const;
|
||||
[[nodiscard]] double uploadFractionCompleted() const;
|
||||
[[nodiscard]] int downloadFileTotalCount() const;
|
||||
[[nodiscard]] int downloadFileCompletedCount() const;
|
||||
[[nodiscard]] int uploadFileTotalCount() const;
|
||||
[[nodiscard]] int uploadFileCompletedCount() const;
|
||||
[[nodiscard]] QUrl icon() const;
|
||||
|
||||
signals:
|
||||
void syncingChanged(bool syncing);
|
||||
void downloadingChanged(bool downloading);
|
||||
void uploadingChanged(bool uploading);
|
||||
void fractionCompletedChanged(double fractionCompleted);
|
||||
void downloadFractionCompletedChanged(double downloadFractionCompleted);
|
||||
void uploadFractionCompletedChanged(double uploadFractionCompleted);
|
||||
void downloadFileTotalCountChanged(int downloadFileTotalCount);
|
||||
void downloadFileCompletedCountChanged(int downloadFileCompletedCount);
|
||||
void uploadFileTotalCountChanged(int uploadFileTotalCount);
|
||||
void uploadFileCompletedCountChanged(int uploadFileCompletedCount);
|
||||
void iconChanged(const QUrl &icon);
|
||||
|
||||
private:
|
||||
void setDownloading(const bool syncing);
|
||||
void setUploading(const bool syncing);
|
||||
void setDownloadFractionCompleted(const double fractionCompleted);
|
||||
void setUploadFractionCompleted(const double fractionCompleted);
|
||||
void setDownloadFileTotalCount(const int fileTotalCount);
|
||||
void setDownloadFileCompletedCount(const int fileCompletedCount);
|
||||
void setUploadFileTotalCount(const int fileTotalCount);
|
||||
void setUploadFileCompletedCount(const int fileCompletedCount);
|
||||
void setIcon(const QUrl &icon);
|
||||
void updateIcon();
|
||||
|
||||
bool _downloading = false;
|
||||
bool _uploading = false;
|
||||
double _downloadFractionCompleted = 0.0;
|
||||
double _uploadFractionCompleted = 0.0;
|
||||
int _downloadFileTotalCount = 0;
|
||||
int _downloadFileCompletedCount = 0;
|
||||
int _uploadFileTotalCount = 0;
|
||||
int _uploadFileCompletedCount = 0;
|
||||
QUrl _icon;
|
||||
|
||||
class MacImplementation;
|
||||
std::unique_ptr<MacImplementation> d;
|
||||
};
|
||||
|
||||
} // OCC::Mac
|
||||
|
||||
Q_DECLARE_METATYPE(OCC::Mac::FileProviderDomainSyncStatus*)
|
||||
@ -1,261 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "fileproviderdomainsyncstatus.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "gui/macOS/fileproviderutils.h"
|
||||
#include "libsync/theme.h"
|
||||
|
||||
#import <FileProvider/FileProvider.h>
|
||||
|
||||
#import "gui/macOS/progressobserver.h"
|
||||
|
||||
namespace OCC::Mac
|
||||
{
|
||||
|
||||
Q_LOGGING_CATEGORY(lcMacFileProviderDomainSyncStatus, "nextcloud.gui.macfileproviderdomainsyncstatus", QtInfoMsg)
|
||||
|
||||
class FileProviderDomainSyncStatus::MacImplementation
|
||||
{
|
||||
public:
|
||||
explicit MacImplementation(const QString &domainIdentifier, FileProviderDomainSyncStatus *parent)
|
||||
: q(parent)
|
||||
{
|
||||
_domain = FileProviderUtils::domainForIdentifier(domainIdentifier);
|
||||
_manager = [NSFileProviderManager managerForDomain:_domain];
|
||||
|
||||
if (_manager == nil) {
|
||||
qCWarning(lcMacFileProviderDomainSyncStatus) << "Could not get manager for domain" << domainIdentifier;
|
||||
return;
|
||||
}
|
||||
[_manager retain];
|
||||
|
||||
if (@available(macOS 11.3, *)) {
|
||||
NSProgress *const downloadProgress = [_manager globalProgressForKind:NSProgressFileOperationKindDownloading];
|
||||
NSProgress *const uploadProgress = [_manager globalProgressForKind:NSProgressFileOperationKindUploading];
|
||||
_downloadProgressObserver = [[ProgressObserver alloc] initWithProgress:downloadProgress];
|
||||
_uploadProgressObserver = [[ProgressObserver alloc] initWithProgress:uploadProgress];
|
||||
|
||||
_downloadProgressObserver.progressKVOChangeHandler = ^(NSProgress *const progress){
|
||||
updateDownload(progress);
|
||||
};
|
||||
_uploadProgressObserver.progressKVOChangeHandler = ^(NSProgress *const progress){
|
||||
updateUpload(progress);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
~MacImplementation()
|
||||
{
|
||||
[_downloadProgressObserver release];
|
||||
[_uploadProgressObserver release];
|
||||
[_domain release];
|
||||
[_manager release];
|
||||
}
|
||||
|
||||
void updateDownload(NSProgress *const progress) const
|
||||
{
|
||||
qCInfo(lcMacFileProviderDomainSyncStatus) << "Download progress changed" << progress.localizedDescription;
|
||||
if (progress == nil || q == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
q->setDownloading(!progress.paused && !progress.cancelled && !progress.finished);
|
||||
q->setDownloadFractionCompleted(progress.fractionCompleted);
|
||||
q->setDownloadFileTotalCount(progress.fileTotalCount.intValue);
|
||||
q->setDownloadFileCompletedCount(progress.fileCompletedCount.intValue);
|
||||
q->updateIcon();
|
||||
}
|
||||
|
||||
void updateUpload(NSProgress *const progress) const
|
||||
{
|
||||
qCInfo(lcMacFileProviderDomainSyncStatus) << "Upload progress changed" << progress.localizedDescription;
|
||||
if (progress == nil || q == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
q->setUploading(!progress.paused && !progress.cancelled && !progress.finished);
|
||||
q->setUploadFractionCompleted(progress.fractionCompleted);
|
||||
q->setUploadFileTotalCount(progress.fileTotalCount.intValue);
|
||||
q->setUploadFileCompletedCount(progress.fileCompletedCount.intValue);
|
||||
q->updateIcon();
|
||||
}
|
||||
|
||||
private:
|
||||
NSFileProviderDomain *_domain = nil;
|
||||
NSFileProviderManager *_manager = nil;
|
||||
ProgressObserver *_downloadProgressObserver = nullptr;
|
||||
ProgressObserver *_uploadProgressObserver = nullptr;
|
||||
FileProviderDomainSyncStatus *q = nullptr;
|
||||
};
|
||||
|
||||
FileProviderDomainSyncStatus::FileProviderDomainSyncStatus(const QString &domainIdentifier, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(std::make_unique<MacImplementation>(domainIdentifier, this))
|
||||
{
|
||||
qRegisterMetaType<FileProviderDomainSyncStatus*>("FileProviderDomainSyncStatus*");
|
||||
updateIcon();
|
||||
}
|
||||
|
||||
FileProviderDomainSyncStatus::~FileProviderDomainSyncStatus() = default;
|
||||
|
||||
bool FileProviderDomainSyncStatus::syncing() const
|
||||
{
|
||||
return downloading() || uploading();
|
||||
}
|
||||
|
||||
bool FileProviderDomainSyncStatus::downloading() const
|
||||
{
|
||||
return _downloading;
|
||||
}
|
||||
|
||||
bool FileProviderDomainSyncStatus::uploading() const
|
||||
{
|
||||
return _uploading;
|
||||
}
|
||||
|
||||
double FileProviderDomainSyncStatus::fractionCompleted() const
|
||||
{
|
||||
return (downloadFractionCompleted() + uploadFractionCompleted()) / 2;
|
||||
}
|
||||
|
||||
double FileProviderDomainSyncStatus::downloadFractionCompleted() const
|
||||
{
|
||||
return _downloadFractionCompleted;
|
||||
}
|
||||
|
||||
double FileProviderDomainSyncStatus::uploadFractionCompleted() const
|
||||
{
|
||||
return _uploadFractionCompleted;
|
||||
}
|
||||
|
||||
int FileProviderDomainSyncStatus::downloadFileTotalCount() const
|
||||
{
|
||||
return _downloadFileTotalCount;
|
||||
}
|
||||
|
||||
int FileProviderDomainSyncStatus::downloadFileCompletedCount() const
|
||||
{
|
||||
return _downloadFileCompletedCount;
|
||||
}
|
||||
|
||||
int FileProviderDomainSyncStatus::uploadFileTotalCount() const
|
||||
{
|
||||
return _uploadFileTotalCount;
|
||||
}
|
||||
|
||||
int FileProviderDomainSyncStatus::uploadFileCompletedCount() const
|
||||
{
|
||||
return _uploadFileCompletedCount;
|
||||
}
|
||||
|
||||
QUrl FileProviderDomainSyncStatus::icon() const
|
||||
{
|
||||
return _icon;
|
||||
}
|
||||
|
||||
void FileProviderDomainSyncStatus::setDownloading(const bool downloading)
|
||||
{
|
||||
if (_downloading == downloading) {
|
||||
return;
|
||||
}
|
||||
|
||||
_downloading = downloading;
|
||||
emit downloadingChanged(_downloading);
|
||||
emit syncingChanged(syncing());
|
||||
}
|
||||
|
||||
void FileProviderDomainSyncStatus::setUploading(const bool uploading)
|
||||
{
|
||||
if (_uploading == uploading) {
|
||||
return;
|
||||
}
|
||||
|
||||
_uploading = uploading;
|
||||
emit uploadingChanged(_uploading);
|
||||
emit syncingChanged(syncing());
|
||||
}
|
||||
|
||||
void FileProviderDomainSyncStatus::setDownloadFractionCompleted(const double downloadFractionCompleted)
|
||||
{
|
||||
if (_downloadFractionCompleted == downloadFractionCompleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
_downloadFractionCompleted = downloadFractionCompleted;
|
||||
emit downloadFractionCompletedChanged(_downloadFractionCompleted);
|
||||
emit fractionCompletedChanged(fractionCompleted());
|
||||
}
|
||||
|
||||
void FileProviderDomainSyncStatus::setUploadFractionCompleted(const double uploadFractionCompleted)
|
||||
{
|
||||
if (_uploadFractionCompleted == uploadFractionCompleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
_uploadFractionCompleted = uploadFractionCompleted;
|
||||
emit uploadFractionCompletedChanged(_uploadFractionCompleted);
|
||||
emit fractionCompletedChanged(fractionCompleted());
|
||||
}
|
||||
|
||||
void FileProviderDomainSyncStatus::setDownloadFileTotalCount(const int fileTotalCount)
|
||||
{
|
||||
if (_downloadFileTotalCount == fileTotalCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
_downloadFileTotalCount = fileTotalCount;
|
||||
emit downloadFileTotalCountChanged(_downloadFileTotalCount);
|
||||
}
|
||||
|
||||
void FileProviderDomainSyncStatus::setDownloadFileCompletedCount(const int fileCompletedCount)
|
||||
{
|
||||
if (_downloadFileCompletedCount == fileCompletedCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
_downloadFileCompletedCount = fileCompletedCount;
|
||||
emit downloadFileCompletedCountChanged(_downloadFileCompletedCount);
|
||||
}
|
||||
|
||||
void FileProviderDomainSyncStatus::setUploadFileTotalCount(const int fileTotalCount)
|
||||
{
|
||||
if (_uploadFileTotalCount == fileTotalCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
_uploadFileTotalCount = fileTotalCount;
|
||||
emit uploadFileTotalCountChanged(_uploadFileTotalCount);
|
||||
}
|
||||
|
||||
void FileProviderDomainSyncStatus::setUploadFileCompletedCount(const int fileCompletedCount)
|
||||
{
|
||||
if (_uploadFileCompletedCount == fileCompletedCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
_uploadFileCompletedCount = fileCompletedCount;
|
||||
emit uploadFileCompletedCountChanged(_uploadFileCompletedCount);
|
||||
}
|
||||
|
||||
void FileProviderDomainSyncStatus::setIcon(const QUrl &icon)
|
||||
{
|
||||
if (_icon == icon) {
|
||||
return;
|
||||
}
|
||||
|
||||
_icon = icon;
|
||||
emit iconChanged(_icon);
|
||||
}
|
||||
|
||||
void FileProviderDomainSyncStatus::updateIcon()
|
||||
{
|
||||
const auto iconUrl = syncing() ? Theme::instance()->syncStatusRunning() : Theme::instance()->syncStatusOk();
|
||||
setIcon(iconUrl);
|
||||
}
|
||||
|
||||
} // OCC::Mac
|
||||
@ -1,166 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "fileprovidermaterialiseditemsmodel.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
namespace Mac {
|
||||
|
||||
FileProviderMaterialisedItemsModel::FileProviderMaterialisedItemsModel(QObject * const parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
int FileProviderMaterialisedItemsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _items.count();
|
||||
}
|
||||
|
||||
QVariant FileProviderMaterialisedItemsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
const auto item = _items.at(index.row());
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case FilenameRole:
|
||||
return item.filename();
|
||||
case IdentifierRole:
|
||||
return item.identifier();
|
||||
case ParentItemIdentifierRole:
|
||||
return item.parentItemIdentifier();
|
||||
case DomainIdentifierRole:
|
||||
return item.domainIdentifier();
|
||||
case TypeIdentifierRole:
|
||||
return item.typeIdentifier();
|
||||
case SymlinkTargetPathRole:
|
||||
return item.symlinkTargetPath();
|
||||
case UploadingErrorRole:
|
||||
return item.uploadingError();
|
||||
case DownloadingErrorRole:
|
||||
return item.downloadingError();
|
||||
case MostRecentEditorNameRole:
|
||||
return item.mostRecentEditorName();
|
||||
case OwnerNameRole:
|
||||
return item.ownerName();
|
||||
case ContentModificationDateRole:
|
||||
return item.contentModificationDate();
|
||||
case CreationDateRole:
|
||||
return item.creationDate();
|
||||
case LastUsedDateRole:
|
||||
return item.lastUsedDate();
|
||||
case ContentVersionRole:
|
||||
return item.contentVersion();
|
||||
case MetadataVersionRole:
|
||||
return item.metadataVersion();
|
||||
case TagDataRole:
|
||||
return item.tagData();
|
||||
case CapabilitiesRole:
|
||||
return item.capabilities();
|
||||
case FileSystemFlagsRole:
|
||||
return item.fileSystemFlags();
|
||||
case ChildItemCountRole:
|
||||
return item.childItemCount();
|
||||
case TypeOsCodeRole:
|
||||
return item.typeOsCode();
|
||||
case CreatorOsCodeRole:
|
||||
return item.creatorOsCode();
|
||||
case DocumentSizeRole:
|
||||
return item.documentSize();
|
||||
case MostRecentVersionDownloadedRole:
|
||||
return item.mostRecentVersionDownloaded();
|
||||
case UploadingRole:
|
||||
return item.uploading();
|
||||
case UploadedRole:
|
||||
return item.uploaded();
|
||||
case DownloadingRole:
|
||||
return item.downloading();
|
||||
case DownloadedRole:
|
||||
return item.downloaded();
|
||||
case SharedRole:
|
||||
return item.shared();
|
||||
case SharedByCurrentUserRole:
|
||||
return item.sharedByCurrentUser();
|
||||
case UserVisiblePathRole:
|
||||
return item.userVisiblePath();
|
||||
case FileTypeStringRole:
|
||||
return item.fileTypeString();
|
||||
case FileSizeStringRole:
|
||||
return _locale.formattedDataSize(item.documentSize());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> FileProviderMaterialisedItemsModel::roleNames() const
|
||||
{
|
||||
auto roleNames = QAbstractListModel::roleNames();
|
||||
roleNames.insert({
|
||||
{ IdentifierRole, "identifier" },
|
||||
{ ParentItemIdentifierRole, "parentItemIdentifier" },
|
||||
{ DomainIdentifierRole, "domainIdentifier" },
|
||||
{ FilenameRole, "fileName" },
|
||||
{ TypeIdentifierRole, "typeIdentifier" },
|
||||
{ SymlinkTargetPathRole, "symlinkTargetPath" },
|
||||
{ UploadingErrorRole, "uploadingError" },
|
||||
{ DownloadingErrorRole, "downloadingError" },
|
||||
{ MostRecentEditorNameRole, "mostRecentEditorName" },
|
||||
{ OwnerNameRole, "ownerName" },
|
||||
{ ContentModificationDateRole, "contentModificationDate" },
|
||||
{ CreationDateRole, "creationDate" },
|
||||
{ LastUsedDateRole, "lastUsedDate" },
|
||||
{ ContentVersionRole, "contentVersion" },
|
||||
{ MetadataVersionRole, "metadataVersion" },
|
||||
{ TagDataRole, "tagData" },
|
||||
{ CapabilitiesRole, "capabilities" },
|
||||
{ FileSystemFlagsRole, "fileSystemFlags" },
|
||||
{ ChildItemCountRole, "childItemCount" },
|
||||
{ TypeOsCodeRole, "typeOsCode" },
|
||||
{ CreatorOsCodeRole, "creatorOsCode" },
|
||||
{ DocumentSizeRole, "documentSize" },
|
||||
{ MostRecentVersionDownloadedRole, "mostRecentVersionDownloaded" },
|
||||
{ UploadingRole, "uploading" },
|
||||
{ UploadedRole, "uploaded" },
|
||||
{ DownloadingRole, "downloading" },
|
||||
{ DownloadedRole, "downloaded" },
|
||||
{ SharedRole, "shared" },
|
||||
{ SharedByCurrentUserRole, "sharedByCurrentUser" },
|
||||
{ UserVisiblePathRole, "userVisiblePath" },
|
||||
{ FileTypeStringRole, "fileTypeString" },
|
||||
{ FileSizeStringRole, "fileSizeString" },
|
||||
});
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
QVector<FileProviderItemMetadata> FileProviderMaterialisedItemsModel::items() const
|
||||
{
|
||||
return _items;
|
||||
}
|
||||
|
||||
void FileProviderMaterialisedItemsModel::setItems(const QVector<FileProviderItemMetadata> &items)
|
||||
{
|
||||
if (items == _items) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
// We are using this in the "Free up space" dialog so, sort by size.
|
||||
_items = items;
|
||||
std::ranges::sort(_items, [](const auto &a, const auto &b) {
|
||||
return a.documentSize() > b.documentSize();
|
||||
});
|
||||
endResetModel();
|
||||
|
||||
Q_EMIT itemsChanged();
|
||||
}
|
||||
|
||||
} // namespace Mac
|
||||
|
||||
} // namespace OCC
|
||||
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QLocale>
|
||||
|
||||
#include "gui/macOS/fileprovideritemmetadata.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
namespace Mac {
|
||||
|
||||
class FileProviderMaterialisedItemsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QVector<FileProviderItemMetadata> items READ items WRITE setItems NOTIFY itemsChanged)
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
IdentifierRole = Qt::UserRole + 1,
|
||||
ParentItemIdentifierRole,
|
||||
DomainIdentifierRole,
|
||||
FilenameRole,
|
||||
TypeIdentifierRole,
|
||||
SymlinkTargetPathRole,
|
||||
UploadingErrorRole,
|
||||
DownloadingErrorRole,
|
||||
MostRecentEditorNameRole,
|
||||
OwnerNameRole,
|
||||
ContentModificationDateRole,
|
||||
CreationDateRole,
|
||||
LastUsedDateRole,
|
||||
ContentVersionRole,
|
||||
MetadataVersionRole,
|
||||
TagDataRole,
|
||||
CapabilitiesRole,
|
||||
FileSystemFlagsRole,
|
||||
ChildItemCountRole,
|
||||
TypeOsCodeRole,
|
||||
CreatorOsCodeRole,
|
||||
DocumentSizeRole,
|
||||
MostRecentVersionDownloadedRole,
|
||||
UploadingRole,
|
||||
UploadedRole,
|
||||
DownloadingRole,
|
||||
DownloadedRole,
|
||||
SharedRole,
|
||||
SharedByCurrentUserRole,
|
||||
UserVisiblePathRole,
|
||||
FileTypeStringRole,
|
||||
FileSizeStringRole,
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit FileProviderMaterialisedItemsModel(QObject *parent = nullptr);
|
||||
[[nodiscard]] int rowCount(const QModelIndex &parent = {}) const override;
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
[[nodiscard]] QVector<FileProviderItemMetadata> items() const;
|
||||
|
||||
signals:
|
||||
void itemsChanged();
|
||||
|
||||
public slots:
|
||||
void setItems(const QVector<FileProviderItemMetadata> &items);
|
||||
void evictItem(const QString &identifier, const QString &domainIdentifier);
|
||||
|
||||
private:
|
||||
QVector<FileProviderItemMetadata> _items;
|
||||
QLocale _locale;
|
||||
};
|
||||
|
||||
} // namespace Mac
|
||||
|
||||
} // namespace OCC
|
||||
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "fileprovidermaterialiseditemsmodel.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#import <FileProvider/FileProvider.h>
|
||||
|
||||
#include "fileproviderutils.h"
|
||||
|
||||
#include "gui/systray.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
namespace Mac {
|
||||
|
||||
Q_LOGGING_CATEGORY(lcMacImplFileProviderMaterialisedItemsModelMac, "nextcloud.gui.macfileprovidermaterialiseditemsmodelmac", QtInfoMsg)
|
||||
|
||||
void FileProviderMaterialisedItemsModel::evictItem(const QString &identifier, const QString &domainIdentifier)
|
||||
{
|
||||
NSFileProviderManager *const manager = FileProviderUtils::managerForDomainIdentifier(domainIdentifier);
|
||||
if (manager == nil) {
|
||||
qCWarning(lcMacImplFileProviderMaterialisedItemsModelMac) << "Received null manager for domain"
|
||||
<< domainIdentifier
|
||||
<< "cannot evict item"
|
||||
<< identifier;
|
||||
Systray::instance()->showMessage(tr("Error"),
|
||||
tr("An internal error occurred. Please try again later."),
|
||||
QSystemTrayIcon::Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
__block BOOL successfullyDeleted = NO;
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
|
||||
[manager evictItemWithIdentifier:identifier.toNSString() completionHandler:^(NSError *error) {
|
||||
if (error != nil) {
|
||||
const auto errorDesc = QString::fromNSString(error.localizedDescription);
|
||||
qCWarning(lcMacImplFileProviderMaterialisedItemsModelMac) << "Error evicting item:" << errorDesc;
|
||||
Systray::instance()->showMessage(tr("Error"),
|
||||
tr("An error occurred while trying to delete the local copy of this item: %1").arg(errorDesc),
|
||||
QSystemTrayIcon::Warning);
|
||||
} else {
|
||||
successfullyDeleted = YES;
|
||||
}
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC));
|
||||
[manager release];
|
||||
|
||||
if (successfullyDeleted == NO) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto deletedItemIt = std::find_if(_items.cbegin(),
|
||||
_items.cend(),
|
||||
[identifier, domainIdentifier](const FileProviderItemMetadata &item) {
|
||||
return item.identifier() == identifier && item.domainIdentifier() == domainIdentifier;
|
||||
});
|
||||
|
||||
if (deletedItemIt == _items.cend()) {
|
||||
qCWarning(lcMacImplFileProviderMaterialisedItemsModelMac) << "Could not find item"
|
||||
<< identifier
|
||||
<< "in model items.";
|
||||
return;
|
||||
}
|
||||
|
||||
const auto deletedItemRow = std::distance(_items.cbegin(), deletedItemIt);
|
||||
beginRemoveRows({}, deletedItemRow, deletedItemRow);
|
||||
_items.remove(deletedItemRow);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
} // namespace Mac
|
||||
@ -8,8 +8,6 @@
|
||||
#include <QObject>
|
||||
#include <QtQuickWidgets/QtQuickWidgets>
|
||||
|
||||
#include "gui/macOS/fileproviderdomainsyncstatus.h"
|
||||
|
||||
class QAbstractListModel;
|
||||
|
||||
namespace OCC {
|
||||
@ -31,30 +29,15 @@ public:
|
||||
|
||||
[[nodiscard]] QStringList vfsEnabledAccounts() const;
|
||||
[[nodiscard]] Q_INVOKABLE bool vfsEnabledForAccount(const QString &userIdAtHost) const;
|
||||
[[nodiscard]] unsigned long long localStorageUsageForAccount(const QString &userIdAtHost) const;
|
||||
[[nodiscard]] Q_INVOKABLE float localStorageUsageGbForAccount(const QString &userIdAtHost) const;
|
||||
[[nodiscard]] unsigned long long remoteStorageUsageForAccount(const QString &userIdAtHost) const;
|
||||
[[nodiscard]] Q_INVOKABLE float remoteStorageUsageGbForAccount(const QString &userIdAtHost) const;
|
||||
[[nodiscard]] Q_INVOKABLE bool trashDeletionEnabledForAccount(const QString &userIdAtHost) const;
|
||||
[[nodiscard]] Q_INVOKABLE bool trashDeletionSetForAccount(const QString &userIdAtHost) const;
|
||||
|
||||
[[nodiscard]] Q_INVOKABLE QAbstractListModel *materialisedItemsModelForAccount(const QString &userIdAtHost);
|
||||
[[nodiscard]] Q_INVOKABLE FileProviderDomainSyncStatus *domainSyncStatusForAccount(const QString &userIdAtHost) const;
|
||||
|
||||
public slots:
|
||||
void setVfsEnabledForAccount(const QString &userIdAtHost, const bool setEnabled, const bool showInformationDialog = true);
|
||||
void setTrashDeletionEnabledForAccount(const QString &userIdAtHost, const bool setEnabled);
|
||||
void resetVfsForAccount(const QString &userIdAtHost);
|
||||
|
||||
void createEvictionWindowForAccount(const QString &userIdAtHost);
|
||||
void refreshMaterialisedItemsForAccount(const QString &userIdAtHost);
|
||||
void signalFileProviderDomain(const QString &userIdAtHost);
|
||||
|
||||
signals:
|
||||
void vfsEnabledAccountsChanged();
|
||||
void localStorageUsageForAccountChanged(const QString &userIdAtHost);
|
||||
void remoteStorageUsageForAccountChanged(const QString &userIdAtHost);
|
||||
void materialisedItemsForAccountChanged(const QString &userIdAtHost);
|
||||
void trashDeletionEnabledForAccountChanged(const QString &userIdAtHost);
|
||||
void trashDeletionSetForAccountChanged(const QString &userIdAtHost);
|
||||
|
||||
|
||||
@ -12,31 +12,21 @@
|
||||
#include "gui/userinfo.h"
|
||||
#include "gui/macOS/fileprovider.h"
|
||||
#include "gui/macOS/fileprovideritemmetadata.h"
|
||||
#include "gui/macOS/fileprovidermaterialiseditemsmodel.h"
|
||||
#include "gui/macOS/fileproviderutils.h"
|
||||
|
||||
// Objective-C imports
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "fileproviderstorageuseenumerationobserver.h"
|
||||
// End of Objective-C imports
|
||||
|
||||
namespace {
|
||||
constexpr auto fpSettingsQmlPath = "qrc:/qml/src/gui/macOS/ui/FileProviderSettings.qml";
|
||||
constexpr auto fpEvictionDialogQmlPath = "qrc:/qml/src/gui/macOS/ui/FileProviderEvictionDialog.qml";
|
||||
|
||||
// QML properties -- make sure they match up in QML file!
|
||||
constexpr auto fpAccountUserIdAtHostProp = "accountUserIdAtHost";
|
||||
constexpr auto fpMaterialisedItemsModelProp = "materialisedItemsModel";
|
||||
|
||||
// NSUserDefaults entries
|
||||
constexpr auto enabledAccountsSettingsKey = "enabledAccounts";
|
||||
|
||||
float gbFromBytesWithOneDecimal(const unsigned long long bytes)
|
||||
{
|
||||
constexpr auto bytesIn100Mb = 1ULL * 1000ULL * 1000ULL * 100ULL;
|
||||
return ((bytes * 1.0) / bytesIn100Mb) / 10.0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace OCC {
|
||||
@ -57,7 +47,6 @@ public:
|
||||
{
|
||||
q = parent;
|
||||
initialCheck();
|
||||
fetchMaterialisedFilesStorageUsage();
|
||||
};
|
||||
|
||||
~MacImplementation() override = default;
|
||||
@ -146,161 +135,18 @@ public:
|
||||
return overallActResult;
|
||||
}
|
||||
|
||||
[[nodiscard]] unsigned long long localStorageUsageForAccount(const QString &userIdAtHost) const
|
||||
{
|
||||
// Return cached value as we fetch asynchronously on initialisation of this class.
|
||||
// We will then emit a signal when the new value is found.
|
||||
return _storageUsage.value(userIdAtHost);
|
||||
}
|
||||
|
||||
[[nodiscard]] QVector<FileProviderItemMetadata> materialisedItemsForAccount(const QString &userIdAtHost) const
|
||||
{
|
||||
return _materialisedFiles.value(userIdAtHost);
|
||||
}
|
||||
|
||||
void signalFileProviderDomain(const QString &userIdAtHost) const
|
||||
{
|
||||
qCInfo(lcFileProviderSettingsController) << "Signalling file provider domain" << userIdAtHost;
|
||||
NSFileProviderDomain * const domain = FileProviderUtils::domainForIdentifier(userIdAtHost);
|
||||
NSFileProviderManager * const manager = [NSFileProviderManager managerForDomain:domain];
|
||||
[domain release];
|
||||
[manager signalEnumeratorForContainerItemIdentifier:NSFileProviderRootContainerItemIdentifier
|
||||
completionHandler:^(NSError *const error) {
|
||||
if (error != nil) {
|
||||
qCWarning(lcFileProviderSettingsController) << "Could not signal file provider domain, error"
|
||||
<< error.localizedDescription;
|
||||
return;
|
||||
}
|
||||
|
||||
qCInfo(lcFileProviderSettingsController) << "Successfully signalled file provider domain";
|
||||
// TODO: Provide some feedback in the UI
|
||||
}];
|
||||
}
|
||||
|
||||
[[nodiscard]] FileProviderDomainSyncStatus *domainSyncStatusForAccount(const QString &userIdAtHost) const
|
||||
{
|
||||
return _fileProviderDomainSyncStatuses.value(userIdAtHost);
|
||||
}
|
||||
|
||||
public slots:
|
||||
// NOTE: This method will release the provided args so make sure to retain them beforehand
|
||||
void enumerateMaterialisedFilesForDomainManager(NSFileProviderManager * const managerForDomain,
|
||||
NSFileProviderDomain * const domain)
|
||||
{
|
||||
const id<NSFileProviderEnumerator> enumerator = [managerForDomain enumeratorForMaterializedItems];
|
||||
Q_ASSERT(enumerator != nil);
|
||||
[enumerator retain];
|
||||
|
||||
FileProviderStorageUseEnumerationObserver *const storageUseObserver = [[FileProviderStorageUseEnumerationObserver alloc] init];
|
||||
storageUseObserver.enumerationFinishedHandler = ^(NSError *const error) {
|
||||
qCInfo(lcFileProviderSettingsController) << "Enumeration finished for" << domain.identifier;
|
||||
if (error != nil) {
|
||||
qCWarning(lcFileProviderSettingsController) << "Error while enumerating storage use" << error.localizedDescription;
|
||||
[storageUseObserver release];
|
||||
[enumerator release];
|
||||
return;
|
||||
}
|
||||
|
||||
const auto items = storageUseObserver.materialisedItems;
|
||||
Q_ASSERT(items != nil);
|
||||
|
||||
// Remember that OCC::Account::userIdAtHost == domain.identifier for us
|
||||
const auto qDomainIdentifier = QString::fromNSString(domain.identifier);
|
||||
QVector<FileProviderItemMetadata> qMaterialisedItems;
|
||||
qMaterialisedItems.reserve(items.count);
|
||||
unsigned long long storageUsage = 0;
|
||||
for (const id<NSFileProviderItem> item in items) {
|
||||
const auto itemMetadata = FileProviderItemMetadata::fromNSFileProviderItem(item, qDomainIdentifier);
|
||||
storageUsage += itemMetadata.documentSize();
|
||||
qCDebug(lcFileProviderSettingsController) << "Adding item" << itemMetadata.identifier()
|
||||
<< "with size" << itemMetadata.documentSize()
|
||||
<< "to storage usage for account" << qDomainIdentifier
|
||||
<< "with total size" << storageUsage;
|
||||
qMaterialisedItems.append(itemMetadata);
|
||||
}
|
||||
_storageUsage.insert(qDomainIdentifier, storageUsage);
|
||||
_materialisedFiles.insert(qDomainIdentifier, qMaterialisedItems);
|
||||
|
||||
emit q->localStorageUsageForAccountChanged(qDomainIdentifier);
|
||||
emit q->materialisedItemsForAccountChanged(qDomainIdentifier);
|
||||
|
||||
[storageUseObserver release];
|
||||
[enumerator release];
|
||||
|
||||
[managerForDomain release];
|
||||
[domain release];
|
||||
};
|
||||
[enumerator enumerateItemsForObserver:storageUseObserver startingAtPage:NSFileProviderInitialPageSortedByName];
|
||||
}
|
||||
|
||||
private slots:
|
||||
void updateDomainSyncStatuses()
|
||||
{
|
||||
qCInfo(lcFileProviderSettingsController) << "Updating file provider domain sync statuses.";
|
||||
_fileProviderDomainSyncStatuses.clear();
|
||||
const auto enabledAccounts = nsEnabledAccounts();
|
||||
|
||||
for (NSString *const accountIdentifier in enabledAccounts) {
|
||||
const auto qAccountIdentifier = QString::fromNSString(accountIdentifier);
|
||||
const auto domainIdentifier = FileProviderUtils::domainIdentifierForAccountIdentifier(accountIdentifier);
|
||||
const auto syncStatus = new FileProviderDomainSyncStatus(domainIdentifier, q);
|
||||
_fileProviderDomainSyncStatuses.insert(qAccountIdentifier, syncStatus);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] NSArray<NSString *> *nsEnabledAccounts() const
|
||||
{
|
||||
return (NSArray<NSString *> *)[_userDefaults objectForKey:_accountsKey];
|
||||
}
|
||||
|
||||
void fetchMaterialisedFilesStorageUsage()
|
||||
{
|
||||
qCInfo(lcFileProviderSettingsController) << "Fetching used storage space of materialized items.";
|
||||
|
||||
[NSFileProviderManager getDomainsWithCompletionHandler: ^(NSArray<NSFileProviderDomain *> *const domains, NSError *const error) {
|
||||
if (error != nil) {
|
||||
qCWarning(lcFileProviderSettingsController) << "Could not get file provider domains:"
|
||||
<< error.localizedDescription
|
||||
<< "Will try again in 2 secs";
|
||||
|
||||
// HACK: Sometimes the system is not in a state where it wants to give us access to
|
||||
// the file provider domains. We will try again in 2 seconds and hope it works
|
||||
const auto thisQobject = (QObject*)this;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[NSTimer scheduledTimerWithTimeInterval:2 repeats:NO block:^(NSTimer *const timer) {
|
||||
Q_UNUSED(timer)
|
||||
QMetaObject::invokeMethod(thisQobject, [this] { fetchMaterialisedFilesStorageUsage(); });
|
||||
}];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
for (NSFileProviderDomain *const domain in domains) {
|
||||
qCInfo(lcFileProviderSettingsController) << "Checking storage use for domain:" << domain.identifier;
|
||||
|
||||
NSFileProviderManager *const managerForDomain = [NSFileProviderManager managerForDomain:domain];
|
||||
if (managerForDomain == nil) {
|
||||
qCWarning(lcFileProviderSettingsController) << "Got a nil file provider manager for domain"
|
||||
<< domain.identifier
|
||||
<< ", returning early.";
|
||||
return;
|
||||
}
|
||||
[managerForDomain retain];
|
||||
[domain retain];
|
||||
enumerateMaterialisedFilesForDomainManager(managerForDomain, domain);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
void initialCheck()
|
||||
{
|
||||
qCInfo(lcFileProviderSettingsController) << "Running initial checks for file provider settings controller.";
|
||||
NSArray<NSString *> *const vfsEnabledAccounts = nsEnabledAccounts();
|
||||
|
||||
if (vfsEnabledAccounts != nil) {
|
||||
updateDomainSyncStatuses();
|
||||
connect(q, &FileProviderSettingsController::vfsEnabledAccountsChanged, this, &MacImplementation::updateDomainSyncStatuses);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -313,9 +159,7 @@ private:
|
||||
FileProviderSettingsController *q = nullptr;
|
||||
NSUserDefaults *_userDefaults = NSUserDefaults.standardUserDefaults;
|
||||
NSString *_accountsKey = [NSString stringWithUTF8String:enabledAccountsSettingsKey];
|
||||
QHash<QString, QVector<FileProviderItemMetadata>> _materialisedFiles;
|
||||
QHash<QString, unsigned long long> _storageUsage;
|
||||
QHash<QString, FileProviderDomainSyncStatus*> _fileProviderDomainSyncStatuses;
|
||||
};
|
||||
|
||||
FileProviderSettingsController *FileProviderSettingsController::instance()
|
||||
@ -337,9 +181,6 @@ FileProviderSettingsController::FileProviderSettingsController(QObject *parent)
|
||||
const auto accountUserIdAtHost = account->userIdAtHostWithPort();
|
||||
|
||||
_userInfos.insert(accountUserIdAtHost, userInfo);
|
||||
connect(userInfo, &UserInfo::fetchedLastInfo, this, [this, accountUserIdAtHost] {
|
||||
emit remoteStorageUsageForAccountChanged(accountUserIdAtHost);
|
||||
});
|
||||
userInfo->setActive(true);
|
||||
}
|
||||
}
|
||||
@ -383,6 +224,10 @@ void FileProviderSettingsController::setVfsEnabledForAccount(const QString &user
|
||||
|
||||
bool FileProviderSettingsController::trashDeletionEnabledForAccount(const QString &userIdAtHost) const
|
||||
{
|
||||
if (userIdAtHost.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto xpc = FileProvider::instance()->xpc();
|
||||
|
||||
if (!xpc) {
|
||||
@ -434,140 +279,6 @@ void FileProviderSettingsController::setTrashDeletionEnabledForAccount(const QSt
|
||||
emit trashDeletionSetForAccountChanged(userIdAtHost);
|
||||
}
|
||||
|
||||
unsigned long long FileProviderSettingsController::localStorageUsageForAccount(const QString &userIdAtHost) const
|
||||
{
|
||||
return d->localStorageUsageForAccount(userIdAtHost);
|
||||
}
|
||||
|
||||
float FileProviderSettingsController::localStorageUsageGbForAccount(const QString &userIdAtHost) const
|
||||
{
|
||||
return gbFromBytesWithOneDecimal(localStorageUsageForAccount(userIdAtHost));
|
||||
}
|
||||
|
||||
unsigned long long FileProviderSettingsController::remoteStorageUsageForAccount(const QString &userIdAtHost) const
|
||||
{
|
||||
const auto userInfoForAccount = _userInfos.value(userIdAtHost);
|
||||
if (!userInfoForAccount) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return userInfoForAccount->lastQuotaUsedBytes();
|
||||
}
|
||||
|
||||
float FileProviderSettingsController::remoteStorageUsageGbForAccount(const QString &userIdAtHost) const
|
||||
{
|
||||
return gbFromBytesWithOneDecimal(remoteStorageUsageForAccount(userIdAtHost));
|
||||
}
|
||||
|
||||
QAbstractListModel *FileProviderSettingsController::materialisedItemsModelForAccount(const QString &userIdAtHost)
|
||||
{
|
||||
const auto items = d->materialisedItemsForAccount(userIdAtHost);
|
||||
if (items.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto model = new FileProviderMaterialisedItemsModel(this);
|
||||
model->setItems(items);
|
||||
|
||||
connect(this, &FileProviderSettingsController::materialisedItemsForAccountChanged,
|
||||
model, [this, model, userIdAtHost](const QString &accountUserIdAtHost) {
|
||||
if (accountUserIdAtHost != userIdAtHost) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto items = d->materialisedItemsForAccount(userIdAtHost);
|
||||
model->setItems(items);
|
||||
});
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
void FileProviderSettingsController::createEvictionWindowForAccount(const QString &userIdAtHost)
|
||||
{
|
||||
const auto engine = Systray::instance()->trayEngine();
|
||||
QQmlComponent component(engine, QUrl(fpEvictionDialogQmlPath));
|
||||
const auto model = materialisedItemsModelForAccount(userIdAtHost);
|
||||
const auto genericDialog = component.createWithInitialProperties({
|
||||
{fpAccountUserIdAtHostProp, userIdAtHost},
|
||||
{fpMaterialisedItemsModelProp, QVariant::fromValue(model)},
|
||||
});
|
||||
const auto dialog = qobject_cast<QQuickWindow *>(genericDialog);
|
||||
QObject::connect(dialog, SIGNAL(reloadMaterialisedItems(QString)),
|
||||
this, SLOT(refreshMaterialisedItemsForAccount(QString)));
|
||||
Q_ASSERT(dialog);
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
void FileProviderSettingsController::refreshMaterialisedItemsForAccount(const QString &userIdAtHost)
|
||||
{
|
||||
d->enumerateMaterialisedFilesForDomainManager(FileProviderUtils::managerForDomainIdentifier(userIdAtHost),
|
||||
FileProviderUtils::domainForIdentifier(userIdAtHost));
|
||||
}
|
||||
|
||||
void FileProviderSettingsController::signalFileProviderDomain(const QString &userIdAtHost)
|
||||
{
|
||||
d->signalFileProviderDomain(userIdAtHost);
|
||||
}
|
||||
|
||||
FileProviderDomainSyncStatus *FileProviderSettingsController::domainSyncStatusForAccount(const QString &userIdAtHost) const
|
||||
{
|
||||
return d->domainSyncStatusForAccount(userIdAtHost);
|
||||
}
|
||||
|
||||
void FileProviderSettingsController::resetVfsForAccount(const QString &userIdAtHost)
|
||||
{
|
||||
qCInfo(lcFileProviderSettingsController) << "Resetting virtual files environment for account" << userIdAtHost;
|
||||
setVfsEnabledForAccount(userIdAtHost, false);
|
||||
|
||||
const auto accountState = AccountManager::instance()->accountFromUserId(userIdAtHost);
|
||||
if (!accountState) {
|
||||
qCWarning(lcFileProviderSettingsController) << "Could not find account for userIdAtHost" << userIdAtHost
|
||||
<< "to reset VFS environment.";
|
||||
return;
|
||||
}
|
||||
const auto splitUserId = userIdAtHost.split('@');
|
||||
if (splitUserId.size() != 2) {
|
||||
qCWarning(lcFileProviderSettingsController) << "Invalid userIdAtHost format" << userIdAtHost
|
||||
<< "Expected format: userId@host";
|
||||
return;
|
||||
}
|
||||
const auto accUserId = splitUserId.at(0);
|
||||
const auto accHost = splitUserId.at(1);
|
||||
|
||||
// Delete the database in the group container
|
||||
const auto groupContainerPath = FileProviderUtils::groupContainerPath();
|
||||
if (groupContainerPath.isEmpty()) {
|
||||
qCWarning(lcFileProviderSettingsController) << "Could not determine group container path, cannot reset VFS.";
|
||||
return;
|
||||
}
|
||||
const auto dbsPath = QDir::cleanPath(groupContainerPath + "/FileProviderExt/Database");
|
||||
qCInfo(lcFileProviderSettingsController) << "Resetting VFS for account" << userIdAtHost
|
||||
<< "by deleting database files in" << dbsPath;
|
||||
const auto databases = QDir(dbsPath).entryList(QDir::Files);
|
||||
for (const auto &dbFile : databases) {
|
||||
// Format of db file names is "userId_cloud_nc_com-fileproviderextdatabase.realm"
|
||||
const auto splitDbName = dbFile.split('-');
|
||||
const auto address = splitDbName.at(0).split('_').mid(1).join('.');
|
||||
const auto userId = splitDbName.at(0).split('_').first();
|
||||
|
||||
if (userId != accUserId || address != accHost) {
|
||||
qCInfo(lcFileProviderSettingsController) << "Skipping database file" << dbFile
|
||||
<< "for userId" << userId
|
||||
<< "and host" << address
|
||||
<< "as it does not match the account we are resetting.";
|
||||
continue; // Not the database we are looking for
|
||||
}
|
||||
|
||||
const auto dbPath = QDir(dbsPath).filePath(dbFile);
|
||||
qCInfo(lcFileProviderSettingsController) << "Deleting database file" << dbPath;
|
||||
if (QFile::exists(dbPath)) {
|
||||
QFile::remove(dbPath);
|
||||
}
|
||||
}
|
||||
|
||||
setVfsEnabledForAccount(userIdAtHost, true);
|
||||
}
|
||||
|
||||
} // namespace Mac
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <FileProvider/FileProvider.h>
|
||||
|
||||
typedef void(^UsageEnumerationFinishedHandler)(NSError *const error);
|
||||
|
||||
@interface FileProviderStorageUseEnumerationObserver : NSObject<NSFileProviderEnumerationObserver>
|
||||
|
||||
@property (readwrite, strong) UsageEnumerationFinishedHandler enumerationFinishedHandler;
|
||||
@property (readonly) NSSet<id<NSFileProviderItem>> *materialisedItems;
|
||||
|
||||
@end
|
||||
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#import "fileproviderstorageuseenumerationobserver.h"
|
||||
|
||||
@implementation FileProviderStorageUseEnumerationObserver
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_materialisedItems = [NSSet set];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// NSFileProviderEnumerationObserver protocol methods
|
||||
- (void)didEnumerateItems:(NSArray<id<NSFileProviderItem>> *)updatedItems
|
||||
{
|
||||
NSMutableSet<id<NSFileProviderItem>> * const existingItems = self.materialisedItems.mutableCopy;
|
||||
|
||||
for (const id<NSFileProviderItem> item in updatedItems) {
|
||||
NSLog(@"StorageUseEnumerationObserver: Enumerating %@ with size %llu",
|
||||
item.filename, item.documentSize.unsignedLongLongValue);
|
||||
[existingItems addObject:item];
|
||||
}
|
||||
|
||||
_materialisedItems = existingItems.copy;
|
||||
}
|
||||
|
||||
- (void)finishEnumeratingWithError:(NSError *)error
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.enumerationFinishedHandler(error);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)finishEnumeratingUpToPage:(NSFileProviderPage)nextPage
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.enumerationFinishedHandler(nil);
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import Style 1.0
|
||||
import "../../filedetails"
|
||||
import "../../tray"
|
||||
|
||||
import com.nextcloud.desktopclient 1.0
|
||||
|
||||
ApplicationWindow {
|
||||
id: root
|
||||
|
||||
signal reloadMaterialisedItems(string accountUserIdAtHost)
|
||||
|
||||
property var materialisedItemsModel: null
|
||||
property string accountUserIdAtHost: ""
|
||||
|
||||
LayoutMirroring.enabled: Application.layoutDirection === Qt.RightToLeft
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
title: qsTr("Remove local copies")
|
||||
color: palette.base
|
||||
flags: Qt.Dialog | Qt.WindowStaysOnTopHint
|
||||
width: 640
|
||||
height: 480
|
||||
|
||||
Component.onCompleted: reloadMaterialisedItems(accountUserIdAtHost)
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: Style.standardSpacing
|
||||
|
||||
EnforcedPlainTextLabel {
|
||||
text: qsTr("Local copies")
|
||||
font.bold: true
|
||||
font.pointSize: Style.headerFontPtSize
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
padding: Style.smallSpacing
|
||||
text: qsTr("Reload")
|
||||
onClicked: reloadMaterialisedItems(accountUserIdAtHost)
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
Layout.leftMargin: Style.standardSpacing
|
||||
Layout.rightMargin: Style.standardSpacing
|
||||
|
||||
clip: true
|
||||
model: root.materialisedItemsModel
|
||||
delegate: FileProviderFileDelegate {
|
||||
width: parent.width
|
||||
height: 60
|
||||
onEvictItem: root.materialisedItemsModel.evictItem(identifier, domainIdentifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,63 +53,11 @@ Page {
|
||||
checked: root.controller.vfsEnabledForAccount(root.accountUserIdAtHost)
|
||||
onClicked: root.controller.setVfsEnabledForAccount(root.accountUserIdAtHost, checked)
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: vfsSettingsLoader
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
active: vfsEnabledCheckBox.checked
|
||||
sourceComponent: ColumnLayout {
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: Style.normalBorderWidth
|
||||
color: palette.dark
|
||||
}
|
||||
|
||||
FileProviderSyncStatus {
|
||||
syncStatus: root.controller.domainSyncStatusForAccount(root.accountUserIdAtHost)
|
||||
onDomainSignalRequested: root.controller.signalFileProviderDomain(root.accountUserIdAtHost)
|
||||
}
|
||||
|
||||
FileProviderStorageInfo {
|
||||
id: storageInfo
|
||||
localUsedStorage: root.controller.localStorageUsageGbForAccount(root.accountUserIdAtHost)
|
||||
remoteUsedStorage: root.controller.remoteStorageUsageGbForAccount(root.accountUserIdAtHost)
|
||||
|
||||
onEvictDialogRequested: root.controller.createEvictionWindowForAccount(root.accountUserIdAtHost)
|
||||
|
||||
Connections {
|
||||
target: root.controller
|
||||
|
||||
function onLocalStorageUsageForAccountChanged(accountUserIdAtHost) {
|
||||
if (root.accountUserIdAtHost !== accountUserIdAtHost) {
|
||||
return;
|
||||
}
|
||||
storageInfo.localUsedStorage = root.controller.localStorageUsageGbForAccount(root.accountUserIdAtHost);
|
||||
}
|
||||
|
||||
function onRemoteStorageUsageForAccountChanged(accountUserIdAtHost) {
|
||||
if (root.accountUserIdAtHost !== accountUserIdAtHost) {
|
||||
return;
|
||||
}
|
||||
storageInfo.remoteUsedStorage = root.controller.remoteStorageUsageGbForAccount(root.accountUserIdAtHost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
text: qsTr("Allow deletion of items in Trash")
|
||||
checked: root.controller.trashDeletionEnabledForAccount(root.accountUserIdAtHost)
|
||||
onClicked: root.controller.setTrashDeletionEnabledForAccount(root.accountUserIdAtHost, checked)
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Reset virtual files environment")
|
||||
onPressed: root.controller.resetVfsForAccount(root.accountUserIdAtHost);
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
text: qsTr("Allow deletion of items in Trash")
|
||||
checked: root.controller.trashDeletionEnabledForAccount(root.accountUserIdAtHost)
|
||||
onClicked: root.controller.setTrashDeletionEnabledForAccount(root.accountUserIdAtHost, checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import Style 1.0
|
||||
import "../../filedetails"
|
||||
import "../../tray"
|
||||
|
||||
import com.nextcloud.desktopclient 1.0
|
||||
|
||||
GridLayout {
|
||||
id: root
|
||||
|
||||
signal evictDialogRequested()
|
||||
|
||||
required property real localUsedStorage
|
||||
required property real remoteUsedStorage
|
||||
|
||||
Layout.fillWidth: true
|
||||
columns: 3
|
||||
|
||||
EnforcedPlainTextLabel {
|
||||
Layout.row: 0
|
||||
Layout.column: 0
|
||||
Layout.alignment: Layout.AlignLeft | Layout.AlignVCenter
|
||||
text: qsTr("Local storage use")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
EnforcedPlainTextLabel {
|
||||
Layout.row: 0
|
||||
Layout.column: 1
|
||||
Layout.alignment: Layout.AlignRight | Layout.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("%1 GB of %2 GB remote files synced").arg(root.localUsedStorage.toFixed(2)).arg(root.remoteUsedStorage.toFixed(2));
|
||||
elide: Text.ElideRight
|
||||
color: palette.dark
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.row: 0
|
||||
Layout.column: 2
|
||||
Layout.alignment: Layout.AlignRight | Layout.AlignVCenter
|
||||
text: qsTr("Free up space …")
|
||||
onPressed: root.evictDialogRequested()
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
Layout.row: 1
|
||||
Layout.columnSpan: root.columns
|
||||
Layout.fillWidth: true
|
||||
value: root.localUsedStorage / root.remoteUsedStorage
|
||||
}
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import Style 1.0
|
||||
import "../../filedetails"
|
||||
import "../../tray"
|
||||
|
||||
import com.nextcloud.desktopclient 1.0
|
||||
|
||||
GridLayout {
|
||||
id: root
|
||||
|
||||
signal domainSignalRequested
|
||||
required property var syncStatus
|
||||
|
||||
rows: syncStatus.syncing ? 2 : 1
|
||||
|
||||
NCBusyIndicator {
|
||||
id: syncIcon
|
||||
|
||||
property int size: Style.trayListItemIconSize * 0.8
|
||||
|
||||
Layout.row: 0
|
||||
Layout.rowSpan: root.syncStatus.syncing ? 2 : 1
|
||||
Layout.column: 0
|
||||
Layout.preferredWidth: size
|
||||
Layout.preferredHeight: size
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
|
||||
padding: 0
|
||||
spacing: 0
|
||||
imageSource: root.syncStatus.icon
|
||||
running: false // avoid rotating the icon when syncing
|
||||
}
|
||||
|
||||
EnforcedPlainTextLabel {
|
||||
Layout.row: 0
|
||||
Layout.column: 1
|
||||
Layout.columnSpan: root.syncStatus.syncing ? 2 : 1
|
||||
Layout.fillWidth: true
|
||||
font.bold: true
|
||||
font.pointSize: Style.headerFontPtSize
|
||||
text: root.syncStatus.syncing ? qsTr("Syncing") : qsTr("All synced!")
|
||||
}
|
||||
|
||||
NCProgressBar {
|
||||
Layout.row: 1
|
||||
Layout.column: 1
|
||||
Layout.fillWidth: true
|
||||
value: root.syncStatus.fractionCompleted
|
||||
visible: root.syncStatus.syncing
|
||||
}
|
||||
|
||||
Button {
|
||||
id: requestSyncButton
|
||||
text: qsTr("Request sync")
|
||||
visible: !root.syncStatus.syncing
|
||||
hoverEnabled: true
|
||||
onClicked: root.domainSignalRequested()
|
||||
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
|
||||
ToolTip.text: qsTr("Request a sync of changes for the VFS environment.\nmacOS may ignore or delay this request.")
|
||||
}
|
||||
}
|
||||
@ -287,7 +287,8 @@ void ownCloudGui::slotComputeOverallSyncStatus()
|
||||
bool allPaused = true;
|
||||
QVector<AccountStatePtr> problemAccounts;
|
||||
|
||||
for (const auto &account : AccountManager::instance()->accounts()) {
|
||||
const auto &allAccounts = AccountManager::instance()->accounts();
|
||||
for (const auto &account : allAccounts) {
|
||||
if (!account->isSignedOut()) {
|
||||
allSignedOut = false;
|
||||
}
|
||||
@ -308,7 +309,8 @@ void ownCloudGui::slotComputeOverallSyncStatus()
|
||||
QList<QString> idleFileProviderAccounts;
|
||||
|
||||
if (Mac::FileProvider::fileProviderAvailable()) {
|
||||
for (const auto &accountState : AccountManager::instance()->accounts()) {
|
||||
const auto &allAccounts = AccountManager::instance()->accounts();
|
||||
for (const auto &accountState : allAccounts) {
|
||||
const auto account = accountState->account();
|
||||
const auto userIdAtHostWithPort = account->userIdAtHostWithPort();
|
||||
if (!Mac::FileProviderSettingsController::instance()->vfsEnabledForAccount(userIdAtHostWithPort)) {
|
||||
|
||||
@ -806,7 +806,7 @@ AccountState *OwncloudSetupWizard::applyAccountChanges()
|
||||
auto newState = manager->addAccount(newAccount);
|
||||
|
||||
if (newAccount->isPublicShareLink()) {
|
||||
qCInfo(lcWizard()) << "seeting up public share link account";
|
||||
qCInfo(lcWizard()) << "setting up public share link account";
|
||||
}
|
||||
|
||||
manager->saveAccount(newAccount);
|
||||
|
||||
@ -525,7 +525,7 @@ void SelectiveSyncDialog::accept()
|
||||
//The part that changed should not be read from the DB on next sync because there might be new folders
|
||||
// (the ones that are no longer in the blacklist)
|
||||
auto blackListSet = QSet<QString>{blackList.begin(), blackList.end()};
|
||||
auto changes = (oldBlackListSet - blackListSet) + (blackListSet - oldBlackListSet);
|
||||
const auto changes = (oldBlackListSet - blackListSet) + (blackListSet - oldBlackListSet);
|
||||
for (const auto &it : changes) {
|
||||
_folder->journalDb()->schedulePathForRemoteDiscovery(it);
|
||||
_folder->schedulePathForLocalDiscovery(it);
|
||||
|
||||
@ -149,7 +149,7 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent)
|
||||
|
||||
customizeStyle();
|
||||
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & Qt::Window);
|
||||
cfg.restoreGeometry(this);
|
||||
}
|
||||
|
||||
@ -335,7 +335,8 @@ void SettingsDialog::customizeStyle()
|
||||
QString background(palette().base().color().name());
|
||||
_toolBar->setStyleSheet(TOOLBAR_CSS().arg(background, dark, highlightColor, highlightTextColor));
|
||||
|
||||
for (const auto a : _actionGroup->actions()) {
|
||||
const auto &allActions = _actionGroup->actions();
|
||||
for (const auto a : allActions) {
|
||||
QIcon icon = Theme::createColorAwareIcon(a->property("iconPath").toString(), palette());
|
||||
a->setIcon(icon);
|
||||
auto *btn = qobject_cast<QToolButton *>(_toolBar->widgetForAction(a));
|
||||
|
||||
@ -465,7 +465,10 @@ void ShareManager::createShare(const QString &path,
|
||||
[=, this](const QJsonDocument &reply) {
|
||||
// Find existing share permissions (if this was shared with us)
|
||||
Share::Permissions existingPermissions = SharePermissionAll;
|
||||
for (const auto &element : reply.object()["ocs"].toObject()["data"].toArray()) {
|
||||
const auto &replyObject = reply.object();
|
||||
const auto &ocsObject = replyObject["ocs"].toObject();
|
||||
const auto &dataArray = ocsObject["data"].toArray();
|
||||
for (const auto &element : dataArray) {
|
||||
auto map = element.toObject();
|
||||
if (map["file_target"] == path)
|
||||
existingPermissions = Share::Permissions(map["permissions"].toInt());
|
||||
@ -554,7 +557,7 @@ void ShareManager::fetchSharedWithMe(const QString &path)
|
||||
const QList<SharePtr> ShareManager::parseShares(const QJsonDocument &reply) const
|
||||
{
|
||||
qDebug() << reply;
|
||||
auto tmpShares = reply.object().value("ocs").toObject().value("data").toArray();
|
||||
const auto tmpShares = reply.object().value("ocs").toObject().value("data").toArray();
|
||||
const QString versionString = _account->serverVersion();
|
||||
qCDebug(lcSharing) << versionString << "Fetched" << tmpShares.count() << "shares";
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ bool SslDialogErrorHandler::handleErrors(QList<QSslError> errors, const QSslConf
|
||||
|
||||
// Check if this host has an active HSTS policy
|
||||
auto hstsPolicies = qnam->strictTransportSecurityHosts();
|
||||
for (const auto &policy : hstsPolicies) {
|
||||
for (const auto &policy : std::as_const(hstsPolicies)) {
|
||||
if (policy.host() == host && !policy.isExpired()) {
|
||||
// HSTS is active for this host, don't show the dialog
|
||||
qCInfo(lcSslErrorDialog) << "SSL certificate error, but HSTS is active. Rejecting connection.";
|
||||
@ -152,7 +152,7 @@ bool SslErrorDialog::checkFailingCertsKnown(const QList<QSslError> &errors)
|
||||
msg += QL("<h3>") + tr("Cannot connect securely to <i>%1</i>:").arg(host) + QL("</h3>");
|
||||
// loop over the unknown certs and line up their errors.
|
||||
msg += QL("<div id=\"ca_errors\">");
|
||||
for (const auto &cert : _unknownCerts) {
|
||||
for (const auto &cert : std::as_const(_unknownCerts)) {
|
||||
msg += QL("<div id=\"ca_error\">");
|
||||
// add the errors for this cert
|
||||
for (const auto &err : errors) {
|
||||
|
||||
@ -30,6 +30,7 @@ ItemDelegate {
|
||||
Accessible.onPressAction: root.clicked()
|
||||
|
||||
ToolTip {
|
||||
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
|
||||
visible: root.hovered && !activityContent.childHovered && model.displayLocation !== ""
|
||||
text: qsTr("In %1").arg(model.displayLocation)
|
||||
}
|
||||
|
||||
@ -178,6 +178,7 @@ RowLayout {
|
||||
icon.height: Style.activityListButtonIconSize
|
||||
|
||||
ToolTip {
|
||||
popupType: Qt.platform.os === "windows" ? Popup.Item : Qt.platform.os === "windows" ? Popup.Item : Popup.Native
|
||||
text: qsTr("Open file details")
|
||||
visible: parent.hovered
|
||||
}
|
||||
@ -200,6 +201,7 @@ RowLayout {
|
||||
display: Button.IconOnly
|
||||
|
||||
ToolTip {
|
||||
popupType: Qt.platform.os === "windows" ? Popup.Item : Qt.platform.os === "windows" ? Popup.Item : Popup.Native
|
||||
text: qsTr("Dismiss")
|
||||
visible: parent.hovered
|
||||
}
|
||||
|
||||
@ -53,12 +53,14 @@ Button {
|
||||
x: (root.x + 2)
|
||||
y: (root.y + Style.trayWindowHeaderHeight + 2)
|
||||
|
||||
width: (Style.rootWidth - 2)
|
||||
property real widestMenuItemWidth: 0
|
||||
property real maximumWidthAllowed: trayWindowHeader.width - (root.x + 4)
|
||||
width: Math.min( widestMenuItemWidth + leftPadding + rightPadding, maximumWidthAllowed )
|
||||
height: Math.min(implicitHeight, maxMenuHeight)
|
||||
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape
|
||||
|
||||
onClosed: {
|
||||
// HACK: reload account Instantiator immediately by restting it - could be done better I guess
|
||||
// HACK: reload account Instantiator immediately by resetting it - could be done better I guess
|
||||
// see also onVisibleChanged above
|
||||
userLineInstantiator.active = false;
|
||||
userLineInstantiator.active = true;
|
||||
@ -68,10 +70,30 @@ Button {
|
||||
id: userLineInstantiator
|
||||
model: UserModel
|
||||
delegate: MenuItem {
|
||||
implicitHeight: instantiatedUserLine.height + Style.standardSpacing
|
||||
implicitHeight: instantiatedUserLine.height
|
||||
UserLine {
|
||||
id: instantiatedUserLine
|
||||
width: parent.width
|
||||
width: Math.min(accountMenu.widestMenuItemWidth, accountMenu.maximumWidthAllowed)
|
||||
|
||||
Component.onCompleted: {
|
||||
instantiatedUserLine.updateMenuWidth()
|
||||
}
|
||||
|
||||
onImplicitWidthChanged: {
|
||||
instantiatedUserLine.updateMenuWidth()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: model
|
||||
function onNameChanged() {
|
||||
instantiatedUserLine.updateMenuWidth()
|
||||
}
|
||||
|
||||
function onStatusChanged() {
|
||||
instantiatedUserLine.updateMenuWidth()
|
||||
}
|
||||
}
|
||||
|
||||
onShowUserStatusSelector: {
|
||||
userStatusDrawer.openUserStatusDrawer(model.index);
|
||||
accountMenu.close();
|
||||
@ -81,6 +103,11 @@ Button {
|
||||
accountMenu.close();
|
||||
}
|
||||
onClicked: UserModel.currentUserId = model.index;
|
||||
|
||||
function updateMenuWidth()
|
||||
{
|
||||
accountMenu.widestMenuItemWidth = Math.max( instantiatedUserLine.implicitWidth, accountMenu.widestMenuItemWidth )
|
||||
}
|
||||
}
|
||||
}
|
||||
onObjectAdded: function(index, object) {
|
||||
@ -95,10 +122,22 @@ Button {
|
||||
id: addAccountButton
|
||||
hoverEnabled: true
|
||||
visible: Systray.enableAddAccount
|
||||
implicitHeight: Style.trayWindowHeaderHeight
|
||||
|
||||
icon.source: "image://svgimage-custom-color/add.svg/" + palette.windowText
|
||||
icon.width: Style.accountAvatarSize
|
||||
text: qsTr("Add account")
|
||||
readonly property real addAccountIconSize: Style.accountAvatarSize * Style.smallIconScaleFactor
|
||||
readonly property real addAccountHorizontalOffset: ( (Style.accountAvatarSize - addAccountIconSize) / 2 ) + Style.accountIconsMenuMargin
|
||||
property var iconColor: !addAccountButton.enabled
|
||||
? addAccountButton.palette.mid
|
||||
: ((addAccountButton.highlighted || addAccountButton.down) && Qt.platform.os !== "windows"
|
||||
? addAccountButton.palette.highlightedText
|
||||
: addAccountButton.palette.text)
|
||||
|
||||
icon.source: "image://svgimage-custom-color/add.svg/" + iconColor
|
||||
icon.width: addAccountIconSize
|
||||
icon.height: addAccountIconSize
|
||||
leftPadding: addAccountHorizontalOffset
|
||||
spacing: Style.userLineSpacing
|
||||
text: qsTr("Add account")
|
||||
onClicked: UserModel.addAccount()
|
||||
|
||||
Accessible.role: Accessible.MenuItem
|
||||
@ -119,6 +158,19 @@ Button {
|
||||
Accessible.role: Accessible.MenuItem
|
||||
Accessible.name: Systray.syncIsPaused ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
|
||||
Accessible.onPressAction: syncPauseButton.clicked()
|
||||
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
leftPadding: Style.userLineSpacing
|
||||
elide: Text.ElideRight
|
||||
color: !parent.enabled
|
||||
? parent.palette.mid
|
||||
: ((parent.highlighted || parent.down) && Qt.platform.os !== "windows"
|
||||
? parent.palette.highlightedText
|
||||
: parent.palette.text)
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
@ -130,6 +182,19 @@ Button {
|
||||
Accessible.role: Accessible.MenuItem
|
||||
Accessible.name: text
|
||||
Accessible.onPressAction: settingsButton.clicked()
|
||||
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
leftPadding: Style.userLineSpacing
|
||||
elide: Text.ElideRight
|
||||
color: !parent.enabled
|
||||
? parent.palette.mid
|
||||
: ((parent.highlighted || parent.down) && Qt.platform.os !== "windows"
|
||||
? parent.palette.highlightedText
|
||||
: parent.palette.text)
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
@ -140,7 +205,20 @@ Button {
|
||||
onClicked: Systray.shutdown()
|
||||
Accessible.role: Accessible.MenuItem
|
||||
Accessible.name: text
|
||||
Accessible.onPressAction: exitButton.clicked()
|
||||
Accessible.onPressAction: exitButton.clicked()
|
||||
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
leftPadding: Style.userLineSpacing
|
||||
elide: Text.ElideRight
|
||||
color: !parent.enabled
|
||||
? parent.palette.mid
|
||||
: ((parent.highlighted || parent.down) && Qt.platform.os !== "windows"
|
||||
? parent.palette.highlightedText
|
||||
: parent.palette.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQml
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import Style
|
||||
|
||||
ToolTip {
|
||||
id: toolTip
|
||||
clip: true
|
||||
delay: Qt.styleHints.mousePressAndHoldInterval
|
||||
contentItem: EnforcedPlainTextLabel {
|
||||
text: toolTip.text
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
@ -52,6 +52,7 @@ TextField {
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
|
||||
visible: sendReplyMessageButton.hovered
|
||||
text: qsTr("Send reply to chat message")
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ MenuItem {
|
||||
property string toolTipText: root.text
|
||||
|
||||
ToolTip {
|
||||
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
|
||||
visible: root.hovered && root.toolTipText !== ""
|
||||
text: root.toolTipText
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ HeaderButton {
|
||||
|
||||
ToolTip {
|
||||
id: tooltip
|
||||
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
|
||||
visible: root.hovered && !foldersMenuLoader.isMenuVisible
|
||||
text: root.userHasGroupFolders ? qsTr("Open local or group folders") : qsTr("Open local folder")
|
||||
}
|
||||
|
||||
@ -30,6 +30,7 @@ MouseArea {
|
||||
height: Style.unifiedSearchItemHeight
|
||||
|
||||
ToolTip {
|
||||
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
|
||||
visible: unifiedSearchResultMouseArea.containsMouse
|
||||
text: isFetchMoreTrigger ? qsTr("Load more results") : model.resultTitle + "\n\n" + model.subline
|
||||
}
|
||||
|
||||
@ -78,6 +78,12 @@ AbstractButton {
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Style.topLinePixelSize
|
||||
font.bold: true
|
||||
|
||||
color: !userLine.parent.enabled
|
||||
? userLine.parent.palette.mid
|
||||
: ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows"
|
||||
? userLine.parent.palette.highlightedText
|
||||
: userLine.parent.palette.text)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@ -92,6 +98,12 @@ AbstractButton {
|
||||
id: emoji
|
||||
visible: model.statusEmoji !== ""
|
||||
text: statusEmoji
|
||||
|
||||
color: !userLine.parent.enabled
|
||||
? userLine.parent.palette.mid
|
||||
: ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows"
|
||||
? userLine.parent.palette.highlightedText
|
||||
: userLine.parent.palette.text)
|
||||
}
|
||||
|
||||
EnforcedPlainTextLabel {
|
||||
@ -101,6 +113,12 @@ AbstractButton {
|
||||
text: statusMessage
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Style.subLinePixelSize
|
||||
|
||||
color: !userLine.parent.enabled
|
||||
? userLine.parent.palette.mid
|
||||
: ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows"
|
||||
? userLine.parent.palette.highlightedText
|
||||
: userLine.parent.palette.text)
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,9 +130,19 @@ AbstractButton {
|
||||
text: server
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Style.subLinePixelSize
|
||||
|
||||
color: !userLine.parent.enabled
|
||||
? userLine.parent.palette.mid
|
||||
: ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows"
|
||||
? userLine.parent.palette.highlightedText
|
||||
: userLine.parent.palette.text)
|
||||
}
|
||||
}
|
||||
|
||||
Item { // Spacer
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
id: userMoreButton
|
||||
Layout.preferredWidth: Style.headerButtonIconSize
|
||||
@ -127,46 +155,55 @@ AbstractButton {
|
||||
|
||||
onClicked: userMoreButtonMenu.visible ? userMoreButtonMenu.close() : userMoreButtonMenu.popup()
|
||||
|
||||
icon.source: "image://svgimage-custom-color/more.svg/" + palette.windowText
|
||||
property var iconColor: !userLine.parent.enabled
|
||||
? userLine.parent.palette.mid
|
||||
: (!hovered && ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows")
|
||||
? userLine.parent.palette.highlightedText
|
||||
: userLine.parent.palette.text)
|
||||
icon.source: "image://svgimage-custom-color/more.svg/" + iconColor
|
||||
|
||||
AutoSizingMenu {
|
||||
id: userMoreButtonMenu
|
||||
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape
|
||||
height: implicitHeight
|
||||
|
||||
MenuItem {
|
||||
visible: model.isConnected && model.serverHasUserStatus
|
||||
height: visible ? implicitHeight : 0
|
||||
id: setStatusButton
|
||||
enabled: model.isConnected && model.serverHasUserStatus
|
||||
text: qsTr("Set status")
|
||||
font.pixelSize: Style.topLinePixelSize
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: showUserStatusSelector(index)
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: text
|
||||
Accessible.onPressAction: setStatusButton.clicked()
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
visible: model.isConnected && model.serverHasUserStatus
|
||||
height: visible ? implicitHeight : 0
|
||||
id: statusMessageButton
|
||||
enabled: model.isConnected && model.serverHasUserStatus
|
||||
text: qsTr("Status message")
|
||||
font.pixelSize: Style.topLinePixelSize
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: showUserStatusMessageSelector(index)
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: text
|
||||
Accessible.onPressAction: statusMessageButton.clicked()
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
id: logInOutButton
|
||||
enabled: model.canLogout
|
||||
text: model.isConnected ? qsTr("Log out") : qsTr("Log in")
|
||||
visible: model.canLogout
|
||||
height: visible ? implicitHeight : 0
|
||||
width: parent.width
|
||||
font.pixelSize: Style.topLinePixelSize
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
model.isConnected ? UserModel.logout(index) : UserModel.login(index)
|
||||
accountMenu.close()
|
||||
}
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: model.isConnected ? qsTr("Log out") : qsTr("Log in")
|
||||
|
||||
onPressed: {
|
||||
if (model.isConnected) {
|
||||
UserModel.logout(index)
|
||||
} else {
|
||||
@ -174,6 +211,10 @@ AbstractButton {
|
||||
}
|
||||
accountMenu.close()
|
||||
}
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: text
|
||||
Accessible.onPressAction: logInOutButton.clicked()
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
|
||||
@ -165,7 +165,7 @@ OCC::Activity Activity::fromActivityJson(const QJsonObject &json, const AccountP
|
||||
}
|
||||
|
||||
auto actions = json.value("actions").toArray();
|
||||
for (const auto &action : actions) {
|
||||
for (const auto &action : std::as_const(actions)) {
|
||||
activity._links.append(ActivityLink::createFomJsonObject(action.toObject()));
|
||||
}
|
||||
|
||||
|
||||
@ -664,7 +664,7 @@ void ActivityListModel::removeActivityFromActivityList(const Activity &activity)
|
||||
void ActivityListModel::removeOutdatedNotifications(const OCC::ActivityList &receivedNotifications)
|
||||
{
|
||||
ActivityList activitiesToRemove;
|
||||
for (const auto &activity : _finalList) {
|
||||
for (const auto &activity : std::as_const(_finalList)) {
|
||||
if (activity._type != Activity::NotificationType || receivedNotifications.contains(activity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -93,8 +93,8 @@ public:
|
||||
|
||||
[[nodiscard]] bool canFetchMore(const QModelIndex &) const override;
|
||||
|
||||
ActivityList activityList() { return _finalList; }
|
||||
ActivityList errorsList() { return _notificationErrorsLists; }
|
||||
[[nodiscard]] const ActivityList& activityList() const { return _finalList; }
|
||||
[[nodiscard]] const ActivityList& errorsList() const { return _notificationErrorsLists; }
|
||||
|
||||
[[nodiscard]] AccountState *accountState() const;
|
||||
|
||||
|
||||
@ -66,21 +66,31 @@ void AsyncImageResponse::processNextImage()
|
||||
return;
|
||||
}
|
||||
|
||||
OCC::AccountPtr accountInRequestedServer;
|
||||
OCC::AccountPtr accountInRequestedServer = nullptr;
|
||||
|
||||
const auto accountsList = OCC::AccountManager::instance()->accounts();
|
||||
for (const auto &account : accountsList) {
|
||||
if (account && account->account() && imagePath.startsWith(account->account()->url().toString())) {
|
||||
accountInRequestedServer = account->account();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (accountInRequestedServer) {
|
||||
const QUrl iconUrl(_imagePaths.at(_index));
|
||||
if (iconUrl.isValid() && !iconUrl.scheme().isEmpty()) {
|
||||
// fetch the remote resource
|
||||
const auto reply = accountInRequestedServer->sendRawRequest(QByteArrayLiteral("GET"), iconUrl);
|
||||
connect(reply, &QNetworkReply::finished, this, &AsyncImageResponse::slotProcessNetworkReply);
|
||||
// fetch the remote resource in the thread of the account (or rather its QNAM)
|
||||
// for some reason trying to use `accountInRequestedServer` causes clang 21 to crash for me :(
|
||||
const auto accountQnam = accountInRequestedServer->networkAccessManager();
|
||||
QMetaObject::invokeMethod(accountQnam, [this, accountInRequestedServer, iconUrl]() -> void {
|
||||
const auto reply = accountInRequestedServer->sendRawRequest(QByteArrayLiteral("GET"), iconUrl);
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() -> void {
|
||||
QMetaObject::invokeMethod(this, [this, reply]() -> void {
|
||||
processNetworkReply(reply);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
++_index;
|
||||
return;
|
||||
}
|
||||
@ -89,9 +99,8 @@ void AsyncImageResponse::processNextImage()
|
||||
setImageAndEmitFinished();
|
||||
}
|
||||
|
||||
void AsyncImageResponse::slotProcessNetworkReply()
|
||||
void AsyncImageResponse::processNetworkReply(QNetworkReply *reply)
|
||||
{
|
||||
const auto reply = qobject_cast<QNetworkReply *>(sender());
|
||||
if (!reply) {
|
||||
setImageAndEmitFinished();
|
||||
return;
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <QImage>
|
||||
#include <QQuickImageProvider>
|
||||
#include <QFileIconProvider>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class AsyncImageResponse : public QQuickImageResponse
|
||||
{
|
||||
@ -18,9 +19,7 @@ public:
|
||||
|
||||
private:
|
||||
void processNextImage();
|
||||
|
||||
private slots:
|
||||
void slotProcessNetworkReply();
|
||||
void processNetworkReply(QNetworkReply *reply);
|
||||
|
||||
QImage _image;
|
||||
QStringList _imagePaths;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user