1
0
mirror of https://github.com/chylex/Nextcloud-Desktop.git synced 2026-04-04 03:11:32 +02:00

Compare commits

..

257 Commits

Author SHA1 Message Date
Nextcloud bot
6b174d6182 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-06 03:52:32 +00:00
Nextcloud bot
bfed1f952c [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-05 03:52:49 +00:00
allexzander
e5cae81f25 Merge pull request #4186 from nextcloud/feature/improve-activity-buttons
Feature/improve activity buttons
2022-02-04 19:11:59 +02:00
alex-z
ae44dd5978 Adjust icons for activity entries in main dialog. Refactor the dialog by splitting it to separate components.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-02-04 17:52:37 +02:00
Nextcloud bot
5ce8c9bf50 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-04 03:54:01 +00:00
Nextcloud bot
0df0d47c5e [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-03 03:53:38 +00:00
allexzander
c21f02bead Merge pull request #4217 from nextcloud/feature/always-build-with-updater
Always build with updater. Use 'beta/stable' channel selector in 'General Settins' dialog with default 'stable'.
2022-02-02 13:39:15 +02:00
alex-z
0776df65c3 Always build with updater. Use 'beta/stable' channel seletor in 'General Settins' dialog with default 'stable'.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-02-02 10:46:11 +00:00
Matthieu Gallien
375c7b585c Merge pull request #4218 from nextcloud/feature/cmake-option-to-disable-proxy
Cmake option to disable proxy
2022-02-02 11:45:02 +01:00
alex-z
0ea7e8d3a6 Cmake option to disable proxy
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-02-02 10:02:34 +00:00
Nextcloud bot
ceb13aa53c [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-02 03:54:14 +00:00
Claudio Cambra
bb8ca05162 Merge pull request #4216 from nextcloud/bugfix/account-menu-scroll
Allow account menu to scroll when content height is larger than menu height
2022-02-01 17:41:23 +01:00
Claudio Cambra
a2524763c8 Merge branch 'master' into bugfix/account-menu-scroll 2022-02-01 17:01:51 +01:00
Claudio Cambra
fddd9dd884 Merge pull request #4135 from nextcloud/feature/testactivitylistmodel
Add testing for ActivityListModel
2022-02-01 16:51:34 +01:00
Claudio Cambra
ce5494b4ec Add testing for ActivityListModel
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
2022-02-01 13:57:08 +00:00
István Váradi
a66bed5d0f Merge pull request #4212 from ivaradi/hirsute_jammy
Remove Hirsute, add Jammy
2022-02-01 14:35:52 +01:00
István Váradi
566a5e5465 Remove Hirsute, add Jammy
Signed-off-by: István Váradi <ivaradi@varadiistvan.hu>
2022-02-01 12:26:00 +00:00
Nextcloud bot
c78855b4c2 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-01 03:54:14 +00:00
Claudio Cambra
7a1eae45d3 Account menu can now scroll
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
2022-01-31 15:06:55 +00:00
Nextcloud bot
e94802a338 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-31 03:54:11 +00:00
Nextcloud bot
c69cecceda [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-30 03:52:35 +00:00
Nextcloud bot
7e43f29cd1 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-29 03:54:57 +00:00
Nextcloud bot
368d226357 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-28 03:53:29 +00:00
Nextcloud bot
9c98846c23 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-27 03:54:17 +00:00
Nextcloud bot
fe799e983b [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-26 03:54:14 +00:00
Nextcloud bot
004e5e58a3 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-25 03:54:37 +00:00
Matthieu Gallien
29d823571c Merge pull request #4154 from nextch/bug/4153-cloudproviders-dbus-build
Check for dbus-1 when building with cloudproviders
2022-01-24 18:44:41 +01:00
nextch
308dd8e790 Merge branch 'master' into bug/4153-cloudproviders-dbus-build 2022-01-24 18:58:12 +02:00
allexzander
fbc89a3149 Merge pull request #4191 from nextcloud/bugfix/cfapiDehydrateProperly
use proper API to dehydrate a placeholder file
2022-01-24 16:50:57 +02:00
Matthieu Gallien
20a4ed6e7e use proper API to dehydrate a placeholder file
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2022-01-24 14:16:40 +00:00
nextch
b4d6c90c62 Merge branch 'master' into bug/4153-cloudproviders-dbus-build 2022-01-24 15:21:39 +02:00
Matthieu Gallien
41a194952e Merge pull request #4162 from nextcloud/feature/internallink
Add ability to copy internal link from share dialog
2022-01-24 11:39:50 +01:00
Claudio Cambra
624213956e Add ability to copy internal link from share dialog
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
2022-01-24 09:40:28 +00:00
Nextcloud bot
d0364e697a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-24 03:51:33 +00:00
Nextcloud bot
1a3e42451c [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-23 03:51:38 +00:00
Nextcloud bot
40021e6943 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-21 03:50:49 +00:00
Nextcloud bot
580148335f [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-20 03:53:08 +00:00
Nextcloud bot
4e8a5d7aee [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-19 03:51:48 +00:00
Nextcloud bot
45b2647cc9 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-18 04:10:30 +00:00
allexzander
aad028097b Merge pull request #3907 from nextcloud/bugfix/main-dialog-use-system-font-size
Main dialog use system font size
2022-01-17 15:31:47 +02:00
Felix Weilbach
12610baeba Replace Text with Label
Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
2022-01-17 11:11:57 +00:00
Felix Weilbach
f4bf4da44d Remove commented out code
Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
2022-01-17 11:11:57 +00:00
Felix Weilbach
99457e8c38 Use the font size that Qt suggest
Fixes: https://github.com/nextcloud/desktop/issues/3797

Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
2022-01-17 11:11:57 +00:00
Nextcloud bot
f21d064b4f [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-17 03:48:38 +00:00
Nextcloud bot
94ec5396da [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-16 03:52:20 +00:00
Nextcloud bot
610c2f9084 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-15 03:50:57 +00:00
Matthieu Gallien
075f33a272 Merge pull request #4102 from nextcloud/feature/file-names-in-activity
Show only filenames in tray activity items, with full path in tooltip
2022-01-14 11:02:07 +01:00
Claudio Cambra
2a27882307 Show only filenames in tray activity items, with full path in tooltip
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
2022-01-14 08:02:08 +00:00
Nextcloud bot
8b26251199 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-14 03:53:45 +00:00
Matthieu Gallien
1e6d3d2c6b Merge pull request #4038 from nextcloud/bugfix/share-dialog-scroll
Add scroll area for share links.
2022-01-13 15:25:38 +01:00
Camila
fd8186ff0e ShareDialog: group links and users in one scrollbar.
- Remove the scrollbar used only for user shares
- Resize share dialog when removing share links
- Fix widget margins and max height of the share dialog
- slotAdjustScrollWidgetSize => adjustScrollWidgetSize

Signed-off-by: Camila <hello@camila.codes>
2022-01-13 10:39:11 +00:00
Camila
82aed71d09 Just trying to make the sahre dialog look nicer.
Signed-off-by: Camila <hello@camila.codes>
2022-01-13 10:39:11 +00:00
Camila
c08a291742 Remove unused slots.
Signed-off-by: Camila <hello@camila.codes>
2022-01-13 10:39:11 +00:00
Camila
a10f84b5c1 Add scroll area for share links.
Signed-off-by: Camila <hello@camila.codes>
2022-01-13 10:39:11 +00:00
Matthieu Gallien
40d8434c8e Merge pull request #4087 from nextcloud/rakekniven-patch-2
l10n: Fixed grammar
2022-01-13 11:37:58 +01:00
rakekniven
5cec3c3ca1 l10n: Fixed grammar
Reported at Transifex.

Signed-off-by: rakekniven <2069590+rakekniven@users.noreply.github.com>
2022-01-13 09:39:13 +00:00
allexzander
907282cf89 Merge pull request #4171 from nextcloud/bugfix/link-share-ui-not-visible
Bugfix. Re-init sharing manager to enable link sharing UI when receivng sharing permissions.
2022-01-13 11:37:12 +02:00
alex-z
3aaaaaf2d0 Bugfix. Re-init sharing manager to enable link sharing UI when receiving sharing permissions.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-13 10:42:48 +02:00
Nextcloud bot
5ea53cdf80 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-13 03:52:08 +00:00
Matthieu Gallien
5e651878ff Merge pull request #4067 from nextcloud/rakekniven-patch-1
l10n: Change "modified time" to "modification time"
2022-01-12 18:47:24 +01:00
rakekniven
961cea92ca l10n: Fixed grammar
Reported at Transifex.

Signed-off-by: rakekniven <2069590+rakekniven@users.noreply.github.com>
2022-01-12 15:55:56 +01:00
Matthieu Gallien
040e740375 Merge pull request #4168 from nextcloud/bugfix/doNotUseQuickCompilerByDefault
do not activate quick compiler by default
2022-01-12 15:52:13 +01:00
Matthieu Gallien
18be0e88dc do not activate quick compiler by default
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2022-01-12 11:37:13 +00:00
Matthieu Gallien
c2af167933 Merge pull request #4137 from nextcloud/feature/link-share-compromised-password-display-error
Display error message when creating a link share with compromised password.
2022-01-12 12:35:55 +01:00
alex-z
39d1ca3a3c Fix review comments.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-12 10:36:25 +00:00
alex-z
39e2292acd Fix review comments.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-12 10:36:25 +00:00
alex-z
3d7fc166b4 Fix review comments.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-12 10:36:25 +00:00
alex-z
4a8433d297 Display error message when creating a link share with compromised password.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-12 10:36:25 +00:00
Matthieu Gallien
f2d075f3ca Merge pull request #4099 from Dimon4eg/write_logs_to_Output_window_of_Visual_Studio
write logs to Output window of Visual Studio
2022-01-12 11:35:22 +01:00
Dmytro Korchynskyi
c872514571 write logs to Output window of Visual Studio
Signed-off-by: Dmytro Korchynskyi <kdl.dima@gmail.com>
2022-01-12 11:59:34 +02:00
Matthieu Gallien
66ba89a37d Merge branch 'rakekniven-patch-2' 2022-01-12 10:57:35 +01:00
rakekniven
dbe8dc303f Removed server versions as they are mostly outdated.
Addionally added link to server wiki page.

Signed-off-by: rakekniven <2069590+rakekniven@users.noreply.github.com>
2022-01-12 10:56:29 +01:00
Nextcloud bot
2967b3acca [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-12 03:51:38 +00:00
Claudio Cambra
1d8478bc06 Merge pull request #4167 from nextcloud/bugfix/textClipping
Add macOS *.textClipping files to ignore list
2022-01-11 23:22:41 +01:00
Claudio Cambra
27cf2592de Add macOS *.textClipping files to ignore list
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
2022-01-11 15:50:47 +00:00
allexzander
2e0ba8208b Merge pull request #4100 from nextcloud/feature/ask-server-to-recalc-checksum
Ask server to recalculate checksum(hash)
2022-01-11 15:40:19 +02:00
alex-z
225753a8c0 Refactor DeleteJob and DeleteApiJob to use SimpleFileJob.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-11 13:37:09 +00:00
alex-z
190d278fd4 Checksum validation PropagateDownload unit tests.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-11 13:37:09 +00:00
alex-z
5b0e2d8ed0 Checksum validator unit tests.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-11 13:37:09 +00:00
alex-z
6f5f3e769f Fix typo in usernamePrefillServerVersionMinSupportedMajor.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-11 13:37:09 +00:00
alex-z
b7be10f712 Ask server to recalculate checksum on validatin failure.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-11 13:37:09 +00:00
Matthieu Gallien
fbe35538b6 Merge pull request #4015 from nextcloud/update-docs-with-new-features
Add screenshots of the latest features.
2022-01-11 14:24:09 +01:00
Camila
39faeffe09 Add screenshots of the latest features.
- Add a few sentences about the new features
- Reorder the menu to make more sense with the new content.
- Move 'User status' from Architecture to 'Visual Tour'.

Signed-off-by: Camila <hello@camila.codes>
2022-01-11 11:40:15 +00:00
Matthieu Gallien
e89dfe493e Merge pull request #4061 from nextcloud/bugfix/testModTime
check modified time sent by upload jobs in automated tests
2022-01-11 12:39:07 +01:00
Matthieu Gallien
c88f57b42d check modified time sent by upload jobs in automated tests
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2022-01-11 08:46:11 +00:00
Nextcloud bot
9d7568aa4a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-11 03:52:21 +00:00
Nextcloud bot
ab66711513 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-10 03:54:40 +00:00
nextch
e48879555b Check for dbus-1 when building with cloudproviders
Resolves: 4153
Signed-off-by: nextch <97217716+nextch@users.noreply.github.com>
2022-01-08 16:56:40 +02:00
Nextcloud bot
05f4a331bf [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-08 03:49:26 +00:00
Matthieu Gallien
a7c1f58f99 Merge pull request #4120 from nextcloud/feature/qtquick-compile
Add option of enabling QtQuick compiler
2022-01-07 23:32:46 +01:00
Claudio Cambra
f23c7007a5 Fix space
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
2022-01-07 17:48:31 +00:00
Claudio Cambra
044cf7ee13 Add QUICK_COMPILER option to drone builds, set quick compiler on by default
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
2022-01-07 17:48:31 +00:00
Claudio Cambra
a1d42b4177 Make compiler required when option is on, removed unused def
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
2022-01-07 17:48:31 +00:00
Claudio Cambra
8ad04f5a5e Remove AUTORCC
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
2022-01-07 17:48:31 +00:00
Claudio Cambra
e25140d72e Add option of enabling QtQuick compiler
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
2022-01-07 17:48:31 +00:00
Matthieu Gallien
9688ba24ca Merge pull request #4115 from nextcloud/bugfix/vbs-unregister-vfs
Windows. MSI. Unregister Nextcloud folders in SyncRootManager on uninstall.
2022-01-07 18:45:59 +01:00
alex-z
237e5a942f Windows. MSI. Unregister Nextcloud folders in SyncRootManager on uninstall.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-07 17:02:32 +00:00
Matthieu Gallien
888cd27540 Merge pull request #4111 from nextcloud/bugfix/do-not-display-gui-error-on-vfs-folder-conflict
Do not display 'Conflict when uploading some files to a folder
2022-01-07 18:01:26 +01:00
alex-z
77d7a0b7ed Do not display 'Conflict when uploading some files to a folder
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-07 08:46:22 +00:00
Nextcloud bot
263a10cb3a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-07 03:52:19 +00:00
Nextcloud bot
3cf67bb60e [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-06 03:52:33 +00:00
allexzander
ef938a5d54 Merge pull request #4116 from nextcloud/bugfix/test-ssl-errors-relogin
Bugfix/force re-login on SSL Handshake error
2022-01-05 12:14:55 +02:00
alex-z
62b0a9b9f1 Try to sign-in after being signed-out due to SslHandshakeFailedError
Signed-off-by: alex-z <blackslayer4@gmail.com>
2022-01-05 10:01:13 +02:00
Nextcloud bot
d790bafcad [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-04 03:51:31 +00:00
Nextcloud bot
85020c1c95 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-03 03:51:22 +00:00
Nextcloud bot
5c7cce6dfe [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-02 03:51:13 +00:00
Nextcloud bot
72f32fc4f4 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-31 03:52:13 +00:00
Nextcloud bot
a8f8b6d36c [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-30 03:52:09 +00:00
allexzander
fdba105a0d Merge pull request #4022 from antonio-rojas/patch-1
Unbreak loading translations
2021-12-28 11:34:04 +02:00
Antonio Rojas
9d4d11df61 Unbreak loading translations
Commit 18ddb9df4a changed SHAREDIR to point to CMAKE_INSTALL_DATADIR, which is a relative path by default. This prevents the C++ code from finding the translations at runtime.

Signed-off-by: Antonio Rojas <arojas@archlinux.org>
2021-12-28 08:07:04 +00:00
Nextcloud bot
493da5f964 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-28 03:52:02 +00:00
Nextcloud bot
9b5a16e57e [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-27 03:50:47 +00:00
Nextcloud bot
f1e17c6672 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-25 03:51:24 +00:00
Nextcloud bot
3909f62fa9 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-24 03:54:07 +00:00
Nextcloud bot
df40bee75e [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-23 03:52:20 +00:00
Nextcloud bot
55e6301fa6 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-22 03:54:00 +00:00
Nextcloud bot
d549a16ee8 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-21 03:52:58 +00:00
Claudio Cambra
2ba0df3d3c Merge pull request #4090 from nextcloud/feature/hide-share-for-deleted
Hide share button for deleted files and ignored files in tray activity
2021-12-20 17:39:16 +01:00
Claudio Cambra
8c091a2daa Hide share button for deleted and ignored files in tray activity
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
2021-12-20 16:07:11 +00:00
Nextcloud bot
fc366933e4 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-20 03:55:12 +00:00
Nextcloud bot
e62ce9e6e3 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-19 03:58:02 +00:00
Nextcloud bot
728f5fe2cd [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-18 03:52:32 +00:00
Matthieu Gallien
49a81a7a2c Merge pull request #4096 from nextcloud/Valdnet-patch-2
l10n: Remove parenthesis
2021-12-17 09:44:44 +01:00
Valdnet
cedf079762 Remove parenthesis
Signed-off-by: Valdnet <47037905+Valdnet@users.noreply.github.com>
2021-12-17 08:53:35 +01:00
Nextcloud bot
d61983643a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-17 03:56:40 +00:00
Matthieu Gallien
a5275e37d1 Merge pull request #4092 from nextcloud/bugfix/protectLastGetModTimeCalls
ensure any errors after calling FileSystem::getModTime are handled
2021-12-16 16:00:29 +01:00
Matthieu Gallien
3e82466d5e ensure any errors after calling FileSystem::getModTime are handled
be sure that even in release mode no errors when calling getModTime
could be ignored

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-16 15:28:46 +01:00
Nextcloud bot
384c4bb3f8 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-16 03:52:20 +00:00
allexzander
4c447b06a2 Merge pull request #4058 from nextcloud/bugfix/do-not-crash-on-cancele-delete-jobs
Do not crash on findAndCancelDeletedJob
2021-12-15 16:33:46 +02:00
alex-z
b66ba8ba0a Added sync stop when failed to cancel delete jobs.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-12-15 13:41:30 +00:00
alex-z
6381acf85b Do not crash on findAndCancelDeletedJob.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-12-15 13:41:29 +00:00
Nextcloud bot
3758cdf818 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-15 03:51:41 +00:00
allexzander
3b4b2c7a50 Merge pull request #4079 from nextcloud/bugfix/ecm-add-app-icon-fix-mac
Fix CMake error in ECMAddAppIcon for mac
2021-12-14 20:16:48 +02:00
alex-z
2482ca7ea7 Fix CMake error in ECMAddAppIcon for mac.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-12-14 17:31:21 +00:00
Matthieu Gallien
e092909b81 Merge pull request #4076 from nextcloud/bugfix/avoidSyncGettingStuck
Bugfix/avoid sync getting stuck
2021-12-14 17:24:54 +01:00
Matthieu Gallien
24f1d2ce89 fix review comment
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-14 15:28:42 +00:00
Matthieu Gallien
f581f71058 ensure bulk upload jobs finished after an error
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-14 15:28:42 +00:00
allexzander
958a9a3aed Merge pull request #4073 from nextcloud/feature/force-vfs
Enforce VFS. Disable 'Make always available locally'.
2021-12-14 17:18:42 +02:00
alex-z
3f3b752e44 Save folder settings to config when force-switching VFS.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-12-14 16:33:35 +02:00
alex-z
1244e96681 Enforce VFS. Disable 'Make always available locally'.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-12-14 15:03:57 +02:00
Matthieu Gallien
b021460290 Merge pull request #4074 from nextcloud/bugfix/forceDownloadLocalInvalidFiles
Bugfix/force download local invalid files
2021-12-14 11:15:10 +01:00
Matthieu Gallien
9a201a8963 force download from server for local files that have invalid dates
will trigger if local state is incoherent
like the file itself haveing 0 or negative modtime and the database not

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-14 10:00:59 +01:00
Nextcloud bot
5b696e5cf3 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-14 03:51:16 +00:00
Nextcloud bot
3c62c9ddb8 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-13 03:51:32 +00:00
Nextcloud bot
284a5eff89 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-12 03:53:39 +00:00
Nextcloud bot
a0b7ba6d9b [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-11 03:52:23 +00:00
Matthieu Gallien
e1d5a2a4c1 Merge pull request #4064 from nextcloud/bugfix/syncStuckOnError
Bugfix/sync stuck on error
2021-12-10 13:37:20 +01:00
Matthieu Gallien
8ead035016 do not get stuck forever in sync in case of errors
when a local file has invalid date and we try to upload it, properly
handle the error such that we are not stuck forever in sync state

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-10 11:28:35 +01:00
Nextcloud bot
7e19462312 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-10 03:55:50 +00:00
Matthieu Gallien
881697d3d7 Merge pull request #4055 from nextcloud/bugfix/3.4.1RC1
Bugfix/3.4.1 rc1
2021-12-09 10:08:26 +01:00
Matthieu Gallien
66d981aa64 remove files which gets downloaded with an invalid modified time
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-09 10:00:36 +01:00
Nextcloud bot
f2219ee717 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-09 03:55:42 +00:00
Matthieu Gallien
833e3bbcf6 Merge pull request #4045 from nextcloud/bugfix/assertInvalidModtime
Bugfix/assert invalid modtime
2021-12-08 09:00:25 +01:00
Matthieu Gallien
8696670909 add log statements for each new assert about invalid modified time
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-08 07:22:24 +00:00
Matthieu Gallien
791073bd28 recover from local invalid modifie time: force download from server
force file download if local modified time is invalid and server has
valid modified time

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-08 07:22:24 +00:00
Matthieu Gallien
41c86d6459 prevent cases where desktop client would store invalid modified time
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-08 07:22:24 +00:00
Matthieu Gallien
b1977dfb18 prevent injecting invalid modified time through CfApi calls
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-08 07:22:24 +00:00
Matthieu Gallien
76c8d7287e do not consider that a file has changed if its mtime is invalid
a mtime should never be 0 or negative

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-08 07:22:24 +00:00
Matthieu Gallien
389424c54b avoid downloading a file from server when modified time is invalid
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-08 07:22:24 +00:00
Matthieu Gallien
c9f63b5744 prevent invalid modified time from being propagated
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-08 07:22:24 +00:00
Matthieu Gallien
9523c036d6 assert on invalid modtime
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-08 07:22:24 +00:00
Nextcloud bot
2223c6e498 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-08 04:04:09 +00:00
allexzander
6b5a4bbb13 Merge pull request #4014 from nextcloud/feature/folder-logo-variations
Feature/folder logo variations
2021-12-07 19:19:50 +02:00
alex-z
77bf892809 Use different icon for a sync folder on Windows depending on zoom level.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-12-07 15:48:16 +00:00
Matthieu Gallien
7e41cf07d4 Merge pull request #4033 from nextcloud/bugfix/renameBadFileNames
do not forget the path when renaming files with invalid names
2021-12-07 13:41:39 +01:00
Matthieu Gallien
b053cf9e2a do not forget the path when renaming files with invalid names
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-07 11:35:38 +01:00
Matthieu Gallien
6ceb45bac4 test files that should be renamed in sub-directory
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-12-07 11:35:38 +01:00
Nextcloud bot
450af67698 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-07 05:08:46 +00:00
Nextcloud bot
312f5960df [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-06 04:00:07 +00:00
Nextcloud bot
064f137f26 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-05 04:07:16 +00:00
Nextcloud bot
ec431ffd95 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-04 04:02:11 +00:00
allexzander
ec28618413 Merge pull request #4031 from nextcloud/feature/always-prefill-username-for-server24-and-up
Always prefill username from Windows login name based on server version
2021-12-03 12:32:30 +02:00
alex-z
25785841a3 Always prefill username from Windows login name based on server version
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-12-03 11:08:35 +02:00
Nextcloud bot
14374bc1be [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-03 03:57:51 +00:00
Nextcloud bot
aa9c4eec8a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-02 03:57:44 +00:00
Nextcloud bot
f1a7de70e2 [tx-robot] updated from transifex 2021-12-01 18:47:06 +00:00
Nextcloud bot
25f868971b [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-01 04:00:08 +00:00
Matthieu Gallien
17a5cf572d Merge pull request #4009 from nextcloud/rakekniven-patch-1
l10n: Changed case of word
2021-11-30 14:38:17 +01:00
rakekniven
7dfd8181fa l10n: Changed case of word
Reported at Transifex.

Signed-off-by: rakekniven <2069590+rakekniven@users.noreply.github.com>
2021-11-30 11:52:46 +00:00
Nextcloud bot
da56bb8d9f [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-30 03:57:45 +00:00
Camila
7ef4deb76c Merge pull request #4012 from nextcloud/bugfix/fixCfApiUpdateMetadata
fix random error when updating CfApi metadata
2021-11-29 18:22:21 +01:00
Matthieu Gallien
c53159cb0c fix random error when updating CfApi metadata
initialiazing all fields in a structure is required to not have random
behavior

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-29 18:04:00 +01:00
Camila
c12704d726 Merge pull request #4011 from nextcloud/bump-version
Bump version to 3.4.50.
2021-11-29 15:10:47 +01:00
Camila
cbed4786be Bump version to 3.4.50.
Signed-off-by: Camila <hello@camila.codes>
2021-11-29 14:57:18 +01:00
Camila
fd60e60541 Merge pull request #4010 from nextcloud/bump-version
Bump VERSION.cmake to 3.4.0.
2021-11-29 14:46:29 +01:00
Camila
fb833ed311 Bump VERSION.cmake to 3.4.0.
Signed-off-by: Camila <hello@camila.codes>
2021-11-29 14:33:29 +01:00
Nextcloud bot
1440c53ed6 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-29 03:52:09 +00:00
Nextcloud bot
bd42c35e80 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-28 03:50:40 +00:00
Nextcloud bot
a5c82670c9 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-27 03:53:51 +00:00
Matthieu Gallien
3c966a77df Merge pull request #4006 from nextcloud/bugfix/speedUpBulkUpload
Bugfix/speed up bulk upload
2021-11-26 17:50:01 +01:00
Matthieu Gallien
1a9aade28e use the error message sent by the server for bulk upload
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-26 16:32:08 +01:00
Matthieu Gallien
34c4c28879 allow sending parallel batch of files: curretly disabled
can allow to send a new batch before the reply to a previous one is
received

due to concerns with the reliability on the server side this is disabled

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-26 16:32:08 +01:00
Matthieu Gallien
a272b34809 really check that this is a valid answer when receiving batch upload
we could somehow miss that the reply is missing proper fields

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-26 15:49:25 +01:00
Matthieu Gallien
05b8d1e40d batch upload: only handle file that are in the reply
do not handle all files sent but only received ones

should allow to submit more than one request in parallel

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-26 15:49:25 +01:00
Matthieu Gallien
18ef471332 let auto tests of batch upload return proper file paths
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-26 15:49:25 +01:00
Matthieu Gallien
e14502606c make sure we do not start a new batch when the previous one is not sent
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-26 15:49:25 +01:00
Matthieu Gallien
59953d857b use a proper constant for the size of batch
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-26 15:49:25 +01:00
Matthieu Gallien
436eced9fb Merge pull request #4003 from nextcloud/bugfix/errorBulkUpload
if BulkPropagatorJob abort after an error emit finished signal
2021-11-26 15:31:15 +01:00
Matthieu Gallien
f56985938d if BulkPropagatorJob abort after an error emit finished signal
prevent sync engine being stuck because of an error when preparing bulk
upload

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-26 12:48:38 +01:00
Nextcloud bot
56f4198b28 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-26 04:14:39 +00:00
Nextcloud bot
6b22081f61 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-25 03:52:56 +00:00
Matthieu Gallien
a5fa53c460 Merge pull request #4001 from nextcloud/bumpVersion
release 3.4.0 RC2
2021-11-24 18:04:18 +01:00
Matthieu Gallien
426e0af8cd release 3.4.0 RC2
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-24 17:46:10 +01:00
Matthieu Gallien
e2f1854b1e Merge pull request #3887 from nextcloud/feature/bulkUpload
Feature/bulk upload
2021-11-24 17:42:25 +01:00
Matthieu Gallien
c194605c35 implement bulk upload
add PutMultiFileJob to send many files at once

use it in BulkPropagatorJob to implement bulk upload feature

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-24 16:17:47 +01:00
Matthieu Gallien
112be18635 read capabilities for bulk upload from server
use it in dedicated tests

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-24 16:17:47 +01:00
Matthieu Gallien
802c7ac906 make AbstractNetworkJob::errorString virtual: it is already overriden
in practice AbstractNetworkJob::errorString is already overriden but the
overrided code is probably never called while the intention looked like
the opposite

fix that by making the method virtual in base class

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-24 16:17:47 +01:00
allexzander
f575cc1860 Merge pull request #3930 from nextcloud/feature/additional-features-for-initial-setup-config
Pass username from Windows to login page.
2021-11-24 13:26:29 +02:00
alex-z
b03bf1c1f0 Pass username from Windows to login page.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-11-24 10:04:22 +00:00
Matthieu Gallien
9bebda057a Merge pull request #3993 from nextcloud/bugfix/variousVfsFixes
Bugfix/various vfs fixes
2021-11-23 17:11:07 +01:00
Matthieu Gallien
83a8058b51 improve logging for CfApi
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-23 13:41:29 +00:00
Matthieu Gallien
072e9d44bd gracefully handle one case of invalid handles
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-23 13:41:29 +00:00
Matthieu Gallien
a3013de6ea fix OCC::CfApiWrapper::handleForPath when path does not exist
sometime it can be called with a path that is already deleted

ensure we always go to the correct code path

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-23 13:41:29 +00:00
Matthieu Gallien
9eed62a854 remove too noisy log print
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-23 13:41:29 +00:00
Camila
79282a8df9 Merge pull request #3983 from nextcloud/bugfix/only-build-app-bundle-if-needed
Only build app bundle if requested
2021-11-23 14:13:45 +01:00
Felix Weilbach
ec64246dc7 Only build app bundle if requested
For development a app bundle is not needed. The app bundle is only
needed for distribution and macdeployqt takes a lot of time.

Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
2021-11-23 11:41:29 +00:00
Matthieu Gallien
c89d2abf5a Merge pull request #3994 from nextcloud/bugfix/reset-syncfolder-icon
Cleanup system bindings from Windows when removing a local sync folder
2021-11-23 10:58:06 +01:00
alex-z
b3914f627d Cleanup system bindings from Windows when removing a local sync folder
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-11-23 08:32:55 +00:00
Nextcloud bot
998236dcc5 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-23 03:55:50 +00:00
Matthieu Gallien
d9626bf311 Merge pull request #3989 from nextcloud/bugfix/forceVFS
fix button that should be disabled when force VFS
2021-11-22 15:52:19 +01:00
Matthieu Gallien
684d70985e fix button that should be disabled when force VFS
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-22 14:22:59 +01:00
Matthieu Gallien
e92842d837 Merge pull request #3982 from nextcloud/addUserAgentQml
add a network access factory to qml engine
2021-11-22 13:51:55 +01:00
Matthieu Gallien
12c6d6e3bd add a network access factory to qml engine
ensure network access made via qml are using our user agent

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-22 12:21:58 +00:00
Matthieu Gallien
1d704d9352 Merge pull request #3971 from nextcloud/fix/scrolling-activiy-list
Make scrolling with a touchpad in activity list and search result more natural
2021-11-22 13:20:27 +01:00
Carl Schwan
38ac585e7c Add WheelHandler to the Search result list too
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2021-11-22 11:18:18 +00:00
Carl Schwan
892d289f38 [tray] Makes scrolling with a touchpad in activiy list more natural
This basically use the same method that is used in Kirigami and Plasma
Components3.

Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2021-11-22 11:18:18 +00:00
allexzander
5294c5135c Merge pull request #3987 from nextcloud/bugfix/upload-new-folder-with-vfs
Quick fix! Disable VFS folders removal for non-Windows VFS.
2021-11-22 12:39:01 +02:00
alex-z
8e6896ba03 Quick fix! Disable VFS folders removal for non-Windows VFS.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-11-22 12:37:18 +02:00
Nextcloud bot
b2e86c2ea3 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-21 03:52:24 +00:00
Nextcloud bot
8cc58dd8b0 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-20 04:12:29 +00:00
Matthieu Gallien
ca1620ef42 Merge pull request #3988 from nextcloud/bugfix/runVfsFixOnlyOnce
properly query sync journal DB to know when to run fix for VFS
2021-11-19 14:38:03 +01:00
Matthieu Gallien
f1d834df8e properly query sync journal DB to know when to run fix for VFS
the new method added to query the db is not working and so the fix for
vfs is executed at each sync run

the new method for bool was not really needed so let's just remove it
(and that will make the usage of SqlQuery be correct

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-19 14:19:04 +01:00
allexzander
502ffc62ef Merge pull request #3984 from nextcloud/bugfix/vfs-folder-upload-conflict
Added more logs to 'postProcessLocalNew'.
2021-11-19 11:48:05 +02:00
alex-z
73db636361 Added more logs to 'postProcessLocalNew'.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-11-19 09:10:20 +00:00
Nextcloud bot
b222785dc2 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-19 04:35:47 +00:00
Nextcloud bot
5454004ef9 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-18 13:37:40 +00:00
Matthieu Gallien
ed9671c2a6 Merge pull request #3985 from nextcloud/bugfix/unified-search-open-local-file
Use QUrl::fromLocalFile to open local files in Unified Search results
2021-11-18 10:58:50 +01:00
alex-z
911e35bc50 Use QUrl::fromLocalFile to open local files in Unified Search results.
Signed-off-by: alex-z <blackslayer4@gmail.com>
2021-11-18 09:34:54 +00:00
Nextcloud bot
898949d1bc [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-18 09:08:38 +00:00
Matthieu Gallien
ef8fe58245 Merge pull request #3978 from nextcloud/bugfix/correct-placeholder-files
Correct virtual files placeholder files if needed
2021-11-17 11:31:29 +01:00
Felix Weilbach
9e792369b2 Refactor key-value store query code
Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
2021-11-17 09:53:26 +00:00
Felix Weilbach
c76a77e431 Correct virtual files placeholder files if needed
In the past not all files were converted to placeholder files when
converting an existing sync folder to a virtual files folder. Because
some files were not converted to placeholder files, the status would
be wrong on the files. This code makes sure that every file in a
virtual files folder is a placeholder file. It could be removed in the
future.

Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
2021-11-17 09:53:26 +00:00
Felix Weilbach
2308c9da49 Merge pull request #3979 from nextcloud/bugfix/avoidUselessIconDownloads
avoid adding icon data in a cache we never use
2021-11-17 10:17:27 +01:00
Matthieu Gallien
c59f88ca82 avoid adding icon data in a cache we never use
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-16 16:11:44 +01:00
Matthieu Gallien
3edfcff1a0 Merge pull request #3972 from nextcloud/fix/accessibility-keyboard
Fix focus indicator
2021-11-16 14:53:35 +01:00
Carl Schwan
d84673376d More fixes to the menu implementation
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2021-11-16 09:27:10 +00:00
Carl Schwan
69def04ec2 Fix focus indicator
This improve considerably the keyboard navigation in the SystemTray.
But this is still not as good as the golden standard that is recommended
by this article: https://www.sarasoueidan.com/blog/focus-indicators/

Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2021-11-16 09:27:10 +00:00
Matthieu Gallien
3e1a46f2de Merge pull request #3969 from nextcloud/bugfix/finder-arch
Compile Finder extensions for arm and x86
2021-11-16 10:25:58 +01:00
Felix Weilbach
113ba716e6 Compile finder extensions for arm and x86
Fix: https://github.com/nextcloud/desktop/issues/3967

Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
2021-11-16 08:54:48 +00:00
Matthieu Gallien
703037cbfb Merge pull request #3968 from nextcloud/bugfix/user-status-disabled
Check if the server has user status app enabled
2021-11-16 09:53:37 +01:00
Felix Weilbach
07a8e8c91d Check if the server has user status app enabled
According to
https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-status-api.html#user-status-retrieve-statuses
we should check the user status capability, not the the end points.

Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
2021-11-15 16:20:58 +00:00
Matthieu Gallien
df745ef39c Merge pull request #3970 from nextcloud/updateDoc
we run on Windows 8.1+
2021-11-15 17:19:24 +01:00
Matthieu Gallien
2665c8fc16 we run on Windows 8.1+
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2021-11-15 13:40:51 +00:00
Matthieu Gallien
cb34fec596 Merge pull request #3959 from Minoru/bugifx/detect-missing-guiprivate
CMake: fail if `Qt5::GuiPrivate` is not found
2021-11-15 13:18:30 +01:00
Alexander Batischev
d8560dcb19 CMake: fail if Qt5::GuiPrivate is not found
`nextcloud` and `nextcloudCore` depend on three Qt5 components which
aren't mentioned in `find_library`: `Xml`, `Network`, and `GuiPrivate`.
The first two are omitted by mistake, apparently, so this commit just
adds them.

`GuiPrivate` is a special case: it doesn't have its own CMake config, so
adding it to "required components" in `find_package` will always fail
the build. Thus, we implement our own check instead.

Signed-off-by: Alexander Batischev <eual.jp@gmail.com>
2021-11-15 13:57:18 +03:00
Felix Weilbach
0e5f1d9a30 Merge pull request #3966 from nextcloud/bugfix/macos-installer-universal
Let the macOS installer know that the application can run on Arm64
2021-11-15 09:43:55 +01:00
Felix Weilbach
ad814f175e Let the macOS installer know that the application can run on Arm64
Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
2021-11-12 01:42:37 +01:00
215 changed files with 50818 additions and 33383 deletions

View File

@@ -9,7 +9,7 @@ steps:
path: /drone/build
commands:
- cd /drone/build
- cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DCMAKE_BUILD_TYPE=Debug -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DECM_ENABLE_SANITIZERS=address -DCMAKE_CXX_FLAGS=-Werror ../src
- cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DCMAKE_BUILD_TYPE=Debug -DQUICK_COMPILER=ON -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DECM_ENABLE_SANITIZERS=address -DCMAKE_CXX_FLAGS=-Werror ../src
- name: compile
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-4
volumes:
@@ -53,7 +53,7 @@ steps:
path: /drone/build
commands:
- cd /drone/build
- cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_C_COMPILER=clang-10 -DCMAKE_CXX_COMPILER=clang++-10 -DCMAKE_BUILD_TYPE=Debug -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DECM_ENABLE_SANITIZERS=address -DCMAKE_CXX_FLAGS=-Werror ../src
- cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_C_COMPILER=clang-10 -DCMAKE_CXX_COMPILER=clang++-10 -DCMAKE_BUILD_TYPE=Debug -DQUICK_COMPILER=ON -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DECM_ENABLE_SANITIZERS=address -DCMAKE_CXX_FLAGS=-Werror ../src
- name: compile
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-4
volumes:

1
.gitignore vendored
View File

@@ -184,6 +184,7 @@ compile_commands.json
convert.exe
.dir-locals.el
*-icon.png
*-icon-win-folder.png
*-sidebar.png
*-w10startmenu.png
theme.qrc

View File

@@ -22,5 +22,6 @@ Icon=@APPLICATION_EXECUTABLE@
# Translations
Icon[cy_GB]=@APPLICATION_ICON_NAME@
Name[cy_GB]=@APPLICATION_NAME@ cleient cydweddu bwrdd gwaith
Comment[cy_GB]=@APPLICATION_NAME@ cleient cydweddu bwrdd gwaith
GenericName[cy_GB]=Cydweddu Ffolder

View File

@@ -21,7 +21,7 @@ Icon=@APPLICATION_EXECUTABLE@
# Translations
Icon[de_DE]=@APPLICATION_ICON_NAME@
Name[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisierung
Comment[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisierung
GenericName[de_DE]=Ordnersynchronisierung
Icon[de]=@APPLICATION_ICON_NAME@
Name[de]=@APPLICATION_NAME@ Desktop
Comment[de]=@APPLICATION_NAME@ Client zur Desktop-Synchronisierung
GenericName[de]=Ordner-Synchronisation

View File

@@ -0,0 +1,24 @@
[Desktop Entry]
Categories=Utility;X-SuSE-SyncUtility;
Type=Application
Exec=@APPLICATION_EXECUTABLE@
Name=@APPLICATION_NAME@ Desktop
Comment=@APPLICATION_NAME@ desktop synchronization client
GenericName=Folder Sync
Icon=@APPLICATION_ICON_NAME@
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
X-GNOME-Autostart-Delay=3
MimeType=application/vnd.@APPLICATION_EXECUTABLE@;
Actions=Quit;
# Translations
[Desktop Action Quit]
Exec=@APPLICATION_EXECUTABLE@ --quit
Name=Quit @APPLICATION_NAME@
Icon=@APPLICATION_EXECUTABLE@
# Translations
GenericName[id]=Sinkronisasi Folder

View File

@@ -22,5 +22,6 @@ Icon=@APPLICATION_EXECUTABLE@
# Translations
Icon[ko]=@APPLICATION_ICON_NAME@
Name[ko]=@APPLICATION_NAME@ 데스크탑
Comment[ko]=@APPLICATION_NAME@ 데스크톱 동기화 클라이언트
GenericName[ko]=폴더 동기화

View File

@@ -22,5 +22,6 @@ Icon=@APPLICATION_EXECUTABLE@
# Translations
Icon[nb_NO]=@APPLICATION_ICON_NAME@
Name[nb_NO]=@APPLICATION_NAME@ skrivebord
Comment[nb_NO]=@APPLICATION_NAME@ klient for synkroinisering
GenericName[nb_NO]=Mappe synkroinisering

View File

@@ -22,5 +22,6 @@ Icon=@APPLICATION_EXECUTABLE@
# Translations
Icon[oc]=@APPLICATION_ICON_NAME@
Name[oc]=@APPLICATION_NAME@ Burèu
Comment[oc]=@APPLICATION_NAME@ client de sincronizacion
GenericName[oc]=Sincro. dossièr

View File

@@ -22,5 +22,6 @@ Icon=@APPLICATION_EXECUTABLE@
# Translations
Icon[sv]=@APPLICATION_ICON_NAME@
Name[sv]=@APPLICATION_NAME@ Skrivbord
Comment[sv]=@APPLICATION_NAME@ desktopssynkroniseringsklient
GenericName[sv]=Mappsynkronisering

View File

@@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.6)
set(CMAKE_CXX_STANDARD 14)
cmake_policy(SET CMP0071 NEW) # Enable use of QtQuick compiler/generated code
project(client)
@@ -96,26 +97,18 @@ endif()
message(STATUS "GIT_SHA1 ${GIT_SHA1}")
set(SYSCONFDIR ${SYSCONF_INSTALL_DIR})
set(SHAREDIR ${CMAKE_INSTALL_DATADIR})
set(SHAREDIR ${CMAKE_INSTALL_FULL_DATADIR})
#####
## handle BUILD_OWNCLOUD_OSX_BUNDLE
# BUILD_OWNCLOUD_OSX_BUNDLE was not initialized OR set to true on OSX
if(APPLE AND (NOT DEFINED BUILD_OWNCLOUD_OSX_BUNDLE OR BUILD_OWNCLOUD_OSX_BUNDLE))
set(BUILD_OWNCLOUD_OSX_BUNDLE ON)
# Build MacOS app bundle if wished
if(APPLE AND BUILD_OWNCLOUD_OSX_BUNDLE)
message(STATUS "Build MacOS app bundle")
set(OWNCLOUD_OSX_BUNDLE "${APPLICATION_NAME}.app")
set(LIB_INSTALL_DIR "${APPLICATION_NAME}.app/Contents/MacOS")
set(BIN_INSTALL_DIR "${APPLICATION_NAME}.app/Contents/MacOS")
# BUILD_OWNCLOUD_OSX_BUNDLE was disabled on OSX
elseif(APPLE AND NOT BUILD_OWNCLOUD_OSX_BUNDLE)
message(FATAL_ERROR "Building in non-bundle mode on OSX is currently not supported. Comment this error out if you want to work on/test it.")
# any other platform
else()
set(BUILD_OWNCLOUD_OSX_BUNDLE OFF)
endif()
#####
option(QUICK_COMPILER "Use QtQuick compiler to improve performance" OFF)
# this option removes Http authentication, keychain, shibboleth etc and is intended for
# external authentication mechanisms
@@ -130,11 +123,8 @@ if(NO_MSG_HANDLER)
add_definitions(-DNO_MSG_HANDLER=1)
endif()
# this option builds the updater
option(BUILD_UPDATER "BUILD_UPDATER" OFF)
if(BUILD_UPDATER)
message("Compiling with updater")
add_definitions(-DBUILD_UPDATER=1)
else()
message("Compiling without updater")
endif()
@@ -193,6 +183,7 @@ if(BUILD_CLIENT)
pkg_check_modules(CLOUDPROVIDERS cloudproviders IMPORTED_TARGET)
if(CLOUDPROVIDERS_FOUND)
pkg_check_modules(DBUS-1 REQUIRED dbus-1 IMPORTED_TARGET)
pkg_check_modules(GIO REQUIRED gio-2.0 IMPORTED_TARGET)
pkg_check_modules(GLIB2 REQUIRED glib-2.0 IMPORTED_TARGET)
endif()

View File

@@ -30,12 +30,14 @@ option( WITH_CRASHREPORTER "Build crashreporter" OFF )
#set( CRASHREPORTER_ICON ":/owncloud-icon.png" )
## Updater options
option( BUILD_UPDATER "Build updater" OFF )
option( BUILD_UPDATER "Build updater" ON )
option( WITH_PROVIDERS "Build with providers list" ON )
option( ENFORCE_VIRTUAL_FILES_SYNC_FOLDER "Enforce use of virtual files sync folder when available" OFF )
option( DO_NOT_USE_PROXY "Do not use system wide proxy, instead always do a direct connection to server" OFF )
## Theming options
set(NEXTCLOUD_BACKGROUND_COLOR "#0082c9" CACHE STRING "Default Nextcloud background color")
set( APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR ${NEXTCLOUD_BACKGROUND_COLOR} CACHE STRING "Hex color of the wizard header background")

View File

@@ -1,6 +1,6 @@
set( MIRALL_VERSION_MAJOR 3 )
set( MIRALL_VERSION_MINOR 3 )
set( MIRALL_VERSION_PATCH 81 )
set( MIRALL_VERSION_MINOR 4 )
set( MIRALL_VERSION_PATCH 50 )
set( MIRALL_VERSION_YEAR 2021 )
set( MIRALL_SOVERSION 0 )

View File

@@ -15,10 +15,10 @@ OBS_PROJECT_BETA=home:ivaradi:beta
OBS_PACKAGE=nextcloud-desktop
if test "${DRONE_TARGET_BRANCH}" = "stable-2.6"; then
UBUNTU_DISTRIBUTIONS="bionic focal hirsute impish"
UBUNTU_DISTRIBUTIONS="bionic focal impish jammy"
DEBIAN_DISTRIBUTIONS="buster stretch testing"
else
UBUNTU_DISTRIBUTIONS="focal hirsute impish"
UBUNTU_DISTRIBUTIONS="focal impish jammy"
DEBIAN_DISTRIBUTIONS="testing"
fi

View File

@@ -695,7 +695,12 @@
<key>PROJECT_SETTINGS</key>
<dict>
<key>ADVANCED_OPTIONS</key>
<dict/>
<dict>
<key>installer-script.options:hostArchitectures</key>
<array>
<string>x86_64,arm64</string>
</array>
</dict>
<key>BUILD_FORMAT</key>
<integer>0</integer>
<key>BUILD_PATH</key>

View File

@@ -26,6 +26,8 @@ install(FILES
${CMAKE_CURRENT_BINARY_DIR}/make-msi.bat
Platform.wxi
Nextcloud.wxs
RegistryCleanup.vbs
RegistryCleanupCustomAction.wxs
gui/banner.bmp
gui/dialog.bmp
DESTINATION msi/)

View File

@@ -76,12 +76,16 @@
<!-- Uninstall: Remove sync folders from Explorer's Navigation Pane, only effective for the current user (home users) -->
<Custom Action="RemoveNavigationPaneEntries" After="RemoveFiles">(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")</Custom>
<!-- Uninstall: Cleanup the Registry -->
<Custom Action="RegistryCleanupCustomAction" After="RemoveFiles">(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")</Custom>
<!-- Schedule Reboot for the Shell Extensions (in silent installation mode only, or if SCHEDULE_REBOOT argument is set-->
<ScheduleReboot After="InstallFinalize">(SCHEDULE_REBOOT=1) OR NOT (UILevel=2)</ScheduleReboot>
</InstallExecuteSequence>
<!-- "Add or Remove" Programs Entries -->
<Property Id="APPNAME">$(var.AppName)</Property>
<Property Id="ARPPRODUCTICON">$(var.AppIcon)</Property>
<Property Id="ARPHELPLINK">$(var.AppHelpLink)</Property>
<Property Id="ARPURLINFOABOUT">$(var.AppInfoLink)</Property>

View File

@@ -0,0 +1,54 @@
On Error goto 0
Const HKEY_LOCAL_MACHINE = &H80000002
Const strObjRegistry = "winmgmts:\\.\root\default:StdRegProv"
Function RegistryDeleteKeyRecursive(regRoot, strKeyPath)
Set objRegistry = GetObject(strObjRegistry)
objRegistry.EnumKey regRoot, strKeyPath, arrSubkeys
If IsArray(arrSubkeys) Then
For Each strSubkey In arrSubkeys
RegistryDeleteKeyRecursive regRoot, strKeyPath & "\" & strSubkey
Next
End If
objRegistry.DeleteKey regRoot, strKeyPath
End Function
Function RegistryListSubkeys(regRoot, strKeyPath)
Set objRegistry = GetObject(strObjRegistry)
objRegistry.EnumKey regRoot, strKeyPath, arrSubkeys
RegistryListSubkeys = arrSubkeys
End Function
Function GetUserSID()
Dim objWshNetwork, objUserAccount
Set objWshNetwork = CreateObject("WScript.Network")
Set objUserAccount = GetObject("winmgmts://" & objWshNetwork.UserDomain & "/root/cimv2").Get("Win32_UserAccount.Domain='" & objWshNetwork.ComputerName & "',Name='" & objWshNetwork.UserName & "'")
GetUserSID = objUserAccount.SID
End Function
Function RegistryCleanupSyncRootManager()
strSyncRootManagerKeyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SyncRootManager"
arrSubKeys = RegistryListSubkeys(HKEY_LOCAL_MACHINE, strSyncRootManagerKeyPath)
If IsArray(arrSubkeys) Then
arrSubkeys=Filter(arrSubkeys, Session.Property("APPNAME"))
End If
If IsArray(arrSubkeys) Then
arrSubkeys=Filter(arrSubkeys, GetUserSID())
End If
If IsArray(arrSubkeys) Then
For Each strSubkey In arrSubkeys
RegistryDeleteKeyRecursive HKEY_LOCAL_MACHINE, strSyncRootManagerKeyPath & "\" & strSubkey
Next
End If
End Function
Function RegistryCleanup()
RegistryCleanupSyncRootManager()
End Function

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<Binary Id="RegistryCleanup" SourceFile="RegistryCleanup.vbs"/>
<CustomAction Id='RegistryCleanupCustomAction' BinaryKey="RegistryCleanup" VBScriptCall="RegistryCleanup" Return="ignore" Execute="immediate"/>
</Fragment>
</Wix>

View File

@@ -17,10 +17,10 @@ Rem Generate collect.wxs
if %ERRORLEVEL% neq 0 exit %ERRORLEVEL%
Rem Compile en-US (https://www.firegiant.com/wix/tutorial/transforms/morphing-installers/)
"%WIX%\bin\candle.exe" -dcodepage=1252 -dPlatform=%BuildArch% -arch %BuildArch% -dHarvestAppDir="%HarvestAppDir%" -ext WixUtilExtension NCMsiHelper.wxs WinShellExt.wxs collect.wxs Nextcloud.wxs
"%WIX%\bin\candle.exe" -dcodepage=1252 -dPlatform=%BuildArch% -arch %BuildArch% -dHarvestAppDir="%HarvestAppDir%" -ext WixUtilExtension NCMsiHelper.wxs WinShellExt.wxs collect.wxs Nextcloud.wxs RegistryCleanupCustomAction.wxs
if %ERRORLEVEL% neq 0 exit %ERRORLEVEL%
Rem Link MSI package
"%WIX%\bin\light.exe" -sw1076 -ext WixUIExtension -ext WixUtilExtension -cultures:en-us NCMsiHelper.wixobj WinShellExt.wixobj collect.wixobj Nextcloud.wixobj -out "@MSI_INSTALLER_FILENAME@"
"%WIX%\bin\light.exe" -sw1076 -ext WixUIExtension -ext WixUtilExtension -cultures:en-us NCMsiHelper.wixobj WinShellExt.wixobj collect.wixobj Nextcloud.wixobj RegistryCleanupCustomAction.wixobj -out "@MSI_INSTALLER_FILENAME@"
exit %ERRORLEVEL%

View File

@@ -41,7 +41,7 @@
# 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, 24, 32, 48, 64, 128, 256, 512 and 1024.
# * 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.
@@ -102,9 +102,12 @@ include(CMakeParseArguments)
function(ecm_add_app_icon appsources)
set(options)
set(oneValueArgs OUTFILE_BASENAME)
set(multiValueArgs ICONS SIDEBAR_ICONS)
set(oneValueArgs OUTFILE_BASENAME ICON_INDEX)
set(multiValueArgs ICONS SIDEBAR_ICONS RC_DEPENDENCIES)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT ARG_ICON_INDEX)
set(ARG_ICON_INDEX 1)
endif()
if(NOT ARG_ICONS)
message(FATAL_ERROR "No ICONS argument given to ecm_add_app_icon")
@@ -138,8 +141,11 @@ function(ecm_add_app_icon appsources)
endforeach()
endif()
_ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;24;32;48;64;128;256;512;1024")
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()
@@ -168,8 +174,10 @@ function(ecm_add_app_icon appsources)
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}
@@ -204,12 +212,12 @@ function(ecm_add_app_icon appsources)
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
# this bit's a little hacky to make the dependency stuff work
file(WRITE "${_outfilename}.rc.in" "IDI_ICON1 ICON DISCARDABLE \"${_outfilename}.ico\"\n")
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 "${_outfilename}.ico"
DEPENDS ${ARG_RC_DEPENDENCIES} "${_outfilename}.ico"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
)
endfunction()
@@ -226,7 +234,7 @@ function(ecm_add_app_icon appsources)
endif()
endforeach()
foreach(size 16 24 32 48 64 128 ${maxSize})
foreach(size 16 20 24 32 40 48 64 128 ${maxSize})
if(NOT icons_at_${size}px)
continue()
endif()

View File

@@ -33,6 +33,7 @@
#cmakedefine APPLICATION_FORBID_BAD_SSL "@APPLICATION_FORBID_BAD_SSL@"
#define APPLICATION_DOTVIRTUALFILE_SUFFIX "." APPLICATION_VIRTUALFILE_SUFFIX
#cmakedefine01 ENFORCE_VIRTUAL_FILES_SYNC_FOLDER
#cmakedefine DO_NOT_USE_PROXY "@DO_NOT_USE_PROXY@"
#cmakedefine ZLIB_FOUND @ZLIB_FOUND@
@@ -41,4 +42,6 @@
#cmakedefine01 GUI_TESTING
#cmakedefine BUILD_UPDATER "@BUILD_UPDATER@"
#endif

View File

@@ -21,7 +21,7 @@ result, the Nextcloud Client runs on Linux, Windows, and MacOS.
The Synchronization Process
---------------------------
The process of synchronization keeps files in two separate repositories the
The process of synchronization keeps files in two separate repositories the
same. When synchronized:
- If a file is added to one repository it is copied to the other synchronized repository.
@@ -93,7 +93,7 @@ traverses the file tree and compares the modification time of each file with an
expected value stored in its database. If the value is not the same, the client
determines that the file has been modified in the local repository.
.. note:: On the local side, the modification time is a good attribute to use for
.. note:: On the local side, the modification time is a good attribute to use for
detecting changes, because
the value does not depend on time shifts and such.
@@ -122,7 +122,7 @@ Conflict files are always created on the client and never on the server.
In ownCloud 10.0 we implemented a checksum feature which checks the file integrity on upload and download by computing a checksum after the file transfer finishes.
The client queries the server capabilities after login to decide which checksum algorithm to use.
Currently, SHA1 is hard-coded in the official server release and can't be changed by the end-user.
Currently, SHA1 is hard-coded in the official server release and can't be changed by the end-user.
Note that the server additionally also supports MD5 and Adler-32, but the desktop client will always use the checksum algorithm announced in the capabilities:
::
@@ -202,14 +202,14 @@ Conflict files are always created on the client and never on the server.
Upload
~~~~~~
A checksum is calculated with the previously negotiated algorithm by the client and sent along with the file in an HTTP Header.
A checksum is calculated with the previously negotiated algorithm by the client and sent along with the file in an HTTP Header.
```OC-Checksum: [algorithm]:[checksum]```
.. image:: ./images/checksums/client-activity.png
During file upload, the server computes SHA1, MD5, and Adler-32 checksums and compares one of them to the checksum supplied by the client.
During file upload, the server computes SHA1, MD5, and Adler-32 checksums and compares one of them to the checksum supplied by the client.
On mismatch, the server returns HTTP Status code 400 (Bad Request) thus signaling the client that the upload failed.
On mismatch, the server returns HTTP Status code 400 (Bad Request) thus signaling the client that the upload failed.
The server then discards the upload, and the client blacklists the file:
.. image:: ./images/checksums/testing-checksums.png
@@ -223,29 +223,29 @@ Conflict files are always created on the client and never on the server.
client.</s:message>
</d:error>
The client retries the upload using exponential back-off.
The client retries the upload using exponential back-off.
On success (matching checksum) the computed checksums are stored by the server in ``oc_filecache`` alongside the file.
Chunked Upload
~~~~~~~~~~~~~~
Mostly same as above.
The checksum of the full file is sent with every chunk of the file.
Mostly same as above.
The checksum of the full file is sent with every chunk of the file.
But the server only compares the checksum after receiving the checksum sent with the last chunk.
Download
~~~~~~~~
The server sends the checksum in an HTTP header with the file. (same format as above).
If no checksum is found in ``oc_filecache`` (freshly mounted external storage) it is computed and stored in ``oc_filecache`` on the first download.
The checksum is then provided on all subsequent downloads but not on the first.
If no checksum is found in ``oc_filecache`` (freshly mounted external storage) it is computed and stored in ``oc_filecache`` on the first download.
The checksum is then provided on all subsequent downloads but not on the first.
.. _ignored-files-label:
Ignored Files
-------------
The Nextcloud Client supports the ability to exclude or ignore certain files from the synchronization process.
The Nextcloud Client supports the ability to exclude or ignore certain files from the synchronization process.
Some system wide file patterns that are used to exclude or ignore files are included with the client by default and the Nextcloud Client provides the ability to add custom patterns.
By default, the Nextcloud Client ignores the following files:
@@ -262,18 +262,18 @@ By default, the Nextcloud Client ignores the following files:
If a pattern selected using a checkbox in the `ignoredFilesEditor-label` (or if
a line in the exclude file starts with the character ``]`` directly followed by
the file pattern), files matching the pattern are considered *fleeting meta
data*.
data*.
These files are ignored and *removed* by the client if found in the
synchronized folder.
synchronized folder.
This is suitable for meta files created by some applications that have no sustainable meaning.
If a pattern ends with the forward slash (``/``) character, only directories are matched.
If a pattern ends with the forward slash (``/``) character, only directories are matched.
The pattern is only applied for directory components of filenames selected using the checkbox.
To match filenames against the exclude patterns, the UNIX standard C library
function ``fnmatch`` is used.
This process checks the filename against the specified pattern using standard shell wildcard pattern matching.
function ``fnmatch`` is used.
This process checks the filename against the specified pattern using standard shell wildcard pattern matching.
For more information, please refer to `The opengroup website
<http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_13_01>`_.
@@ -369,7 +369,7 @@ is renamed or moved.
Example:
<oc:id>00000020oc5cfy6qqizm</oc:id>
End-to-end Encryption
---------------------
@@ -440,13 +440,3 @@ Files that must be removed from the local storage only, need to be dehydrated vi
.. note::
* End-to-end Encryption works with Virtual Files (VFS) but only on a per-folder level. Folders with E2EE can be made available offline in their entirety, but the individual files in them can not be retrieved on demand. This is mainly due to two technical reasons. First, the Windows VFS API is not designed for handling encrypted files. Second, while the VFS is designed to deal mostly with large files, E2EE is mostly recommended for use with small files as encrypting and decrypting large files puts large demands on the computer infrastructure.
User Status
-----------
Starting from 3.2.0, user status is displayed in the Nextcloud desktop client's tray window. The icon and a text message are displayed as long as those are set in the user's account menu in the Web UI (server's website). At the moment, setting the status from the desktop client is not available.
The status is updated almost immediately after it is set in the Web UI. Default user status is always "Online" if no other status is available from the server-side.
.. image:: images/status_feature_example.png
:alt: User Status feature in the tray window

View File

@@ -185,6 +185,8 @@ Then, in Terminal:
.. code-block:: bash
% echo 'export CMAKE_INSTALL_PREFIX=~/Builds' >> ~/.nextcloud_build_variables
# If you want to build a macOS app bundle for distribution
% echo 'export BUILD_OWNCLOUD_OSX_BUNDLE=ON' >> ~/.nextcloud_build_variables
Replace ``~/Builds`` with a different directory if you'd like the build to end up elsewhere.

View File

@@ -48,9 +48,9 @@ copyright = u'2013-2021, The Nextcloud developers'
# built documents.
#
# The short X.Y version.
version = '3.3'
version = '3.4'
# The full version, including alpha/beta/rc tags.
release = '3.3.81'
release = '3.4.50'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -8,7 +8,9 @@ There are clients for Linux, macOs, and Microsoft Windows.
The currently supported server releases are the latest three stable versions
at time of publication. It means that the |version| release series is supporting
server major version 20, 21 and 22.
stable server major versions.
See https://github.com/nextcloud/server/wiki/Maintenance-and-Release-Schedule for
supported major versions.
Installation on Mac OS X and Windows is the same as for any software
application: download the program and then double-click it to launch the
@@ -31,7 +33,7 @@ download page.
System Requirements
----------------------------------
- Windows 10+
- Windows 8.1+
- macOS 10.12+ (64-bit only)
- Linux
- FreeBSD

View File

@@ -1,3 +1,4 @@
===========
Visual Tour
===========
@@ -11,23 +12,7 @@ as an icon in the system tray (Windows, KDE), status bar
(macOS), or notification area (Ubuntu).
.. image:: images/icon.png
Main dialog
~~~~~~~~~~~
.. index:: activity, recent changes, sync activity, main dialog, adding account, account, add account, remove account
The main dialog, which can be invoked from the tray icon in the
taskbar, will show files information about the activities of the sync
client and Nextcloud. If there are any synchronization issues, they
will show up here. The dialog also gives information about other
activities or notifications like Talk mentions or file changes. It
does also show the status of the user.
.. image:: images/main_dialog.png
When clicking on the avatar a menu opens where it is possible to add a
new account or removing an existing account.
:alt: desktop client icon
Menu
----
@@ -45,8 +30,9 @@ A right click on the icon provides the following menu:
.. NOTE::
This menu is not available on macOS.
Settings
--------
~~~~~~~~
Account Settings
~~~~~~~~~~~~~~~~
@@ -218,3 +204,98 @@ while syncing for a three times. These are listed in the activity view.
There also is a button to retry the sync for another three times.
For more detailed information see :ref:`ignored-files-label`.
Main dialog
-----------
.. index:: activity, recent changes, sync activity, main dialog, adding account, account, add account, remove account, sync state, user status, unified search, share dialog
Sync State
~~~~~~~~~~
The main dialog, which can be invoked from the tray icon in the
taskbar, will show files information about the activities of the sync
client and Nextcloud.
.. image:: images/sync-state-paused.png
:alt: sync state paused
.. image:: images/sync-state-syncing.png
:alt: sync state syncing
.. image:: images/sync-state-synced.png
:alt: sync state synced
If there are any synchronization issues, they will show up here:
.. image:: images/sync-state-warnings.png
:alt: sync state warnings
For more information on how to solve these issues see :doc:`troubleshooting`.
When clicking on the avatar a menu opens where it is
possible to add a new account or removing an existing account.
.. image:: images/user-account-options.png
:alt: user account options
User Status
~~~~~~~~~~~
User status is displayed in the Nextcloud desktop client's tray window.
Default user status is always "Online" if no other status is available from the server-side.
.. image:: images/status_feature_example.png
:alt: User Status feature in the tray window
When clicking on ``Set status`` you can edit the emoji, message and the timer to clear your user status:
.. image:: images/set-user-status.png
:alt: set user status menu option
|
.. image:: images/set-user-status-menu.png
:alt: changing the user status
Activities list
~~~~~~~~~~~~~~~
The dialog also gives information about other activities or
notifications like Talk mentions or file changes.
It does also show the status of the user.
.. image:: images/main_dialog.png
:alt: main dialog activities list
Unified search
~~~~~~~~~~~~~~
With the unified search you can find everything you have in your server - files,
Talk messages, calendar appointments:
.. image:: images/unified-search-files.png
:alt: unified search files search result
.. image:: images/unified-search-talk.png
:alt: unified search Talk conversations search result
.. image:: images/unified-search-events.png
:alt: unified search calendar appointments search result
Share dialog: Talk options and View Profile
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can now share a file directly in a conversation in Talk and view the sharee user profile:
.. image:: images/open-share-dialog.png
:alt: open share dialog option
|
.. image:: images/share-dialog-view-profile.png
:alt: shared dialog sharing options

View File

@@ -23,5 +23,10 @@
<file>src/gui/tray/UnifiedSearchResultListItem.qml</file>
<file>src/gui/tray/UnifiedSearchResultNothingFound.qml</file>
<file>src/gui/tray/UnifiedSearchResultSectionItem.qml</file>
<file>src/gui/tray/CustomButton.qml</file>
<file>src/gui/tray/CustomTextButton.qml</file>
<file>src/gui/tray/ActivityItemContextMenu.qml</file>
<file>src/gui/tray/ActivityItemActions.qml</file>
<file>src/gui/tray/ActivityItemContent.qml</file>
</qresource>
</RCC>

View File

@@ -1,23 +1,26 @@
if(APPLE)
set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/${APPLICATION_ICON_NAME}.icns")
set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/${APPLICATION_ICON_NAME}.icns")
# The bundle identifier and application group need to have compatible values with the client
# to be able to open a Mach port across the extension's sandbox boundary.
# Pass the info through the xcodebuild command line and make sure that the project uses
# those user-defined settings to build the plist.
add_custom_target( mac_overlayplugin ALL
xcodebuild -project ${CMAKE_SOURCE_DIR}/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj
-target FinderSyncExt -configuration Release "SYMROOT=${CMAKE_CURRENT_BINARY_DIR}"
"OC_OEM_SHARE_ICNS=${OC_OEM_SHARE_ICNS}"
"OC_APPLICATION_NAME=${APPLICATION_NAME}"
"OC_APPLICATION_REV_DOMAIN=${APPLICATION_REV_DOMAIN}"
"OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}"
# The bundle identifier and application group need to have compatible values with the client
# to be able to open a Mach port across the extension's sandbox boundary.
# Pass the info through the xcodebuild command line and make sure that the project uses
# those user-defined settings to build the plist.
add_custom_target( mac_overlayplugin ALL
xcodebuild ARCHS=${CMAKE_OSX_ARCHITECTURES} ONLY_ACTIVE_ARCH=NO
-project ${CMAKE_SOURCE_DIR}/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj
-target FinderSyncExt -configuration Release "SYMROOT=${CMAKE_CURRENT_BINARY_DIR}"
"OC_OEM_SHARE_ICNS=${OC_OEM_SHARE_ICNS}"
"OC_APPLICATION_NAME=${APPLICATION_NAME}"
"OC_APPLICATION_REV_DOMAIN=${APPLICATION_REV_DOMAIN}"
"OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}"
COMMENT building Mac Overlay icons
VERBATIM)
add_dependencies(mac_overlayplugin nextcloud) # for the ownCloud.icns to be generated
add_dependencies(mac_overlayplugin nextcloud) # for the ownCloud.icns to be generated
INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Release/FinderSyncExt.appex
DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/PlugIns
USE_SOURCE_PERMISSIONS)
endif(APPLE)
if (BUILD_OWNCLOUD_OSX_BUNDLE)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Release/FinderSyncExt.appex
DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/PlugIns
USE_SOURCE_PERMISSIONS)
endif()
endif()

280
src/3rdparty/kirigami/wheelhandler.cpp vendored Normal file
View File

@@ -0,0 +1,280 @@
/*
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "wheelhandler.h"
#include <QWheelEvent>
#include <QQuickItem>
#include <QDebug>
class GlobalWheelFilterSingleton
{
public:
GlobalWheelFilter self;
};
Q_GLOBAL_STATIC(GlobalWheelFilterSingleton, privateGlobalWheelFilterSelf)
GlobalWheelFilter::GlobalWheelFilter(QObject *parent)
: QObject(parent)
{
}
GlobalWheelFilter::~GlobalWheelFilter() = default;
GlobalWheelFilter *GlobalWheelFilter::self()
{
return &privateGlobalWheelFilterSelf()->self;
}
void GlobalWheelFilter::setItemHandlerAssociation(QQuickItem *item, WheelHandler *handler)
{
if (!m_handlersForItem.contains(handler->target())) {
handler->target()->installEventFilter(this);
}
m_handlersForItem.insert(item, handler);
connect(item, &QObject::destroyed, this, [this](QObject *obj) {
auto item = static_cast<QQuickItem *>(obj);
m_handlersForItem.remove(item);
});
connect(handler, &QObject::destroyed, this, [this](QObject *obj) {
auto handler = static_cast<WheelHandler *>(obj);
removeItemHandlerAssociation(handler->target(), handler);
});
}
void GlobalWheelFilter::removeItemHandlerAssociation(QQuickItem *item, WheelHandler *handler)
{
if (!item || !handler) {
return;
}
m_handlersForItem.remove(item, handler);
if (!m_handlersForItem.contains(item)) {
item->removeEventFilter(this);
}
}
bool GlobalWheelFilter::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::Wheel) {
auto item = qobject_cast<QQuickItem *>(watched);
if (!item || !item->isEnabled()) {
return QObject::eventFilter(watched, event);
}
auto we = static_cast<QWheelEvent *>(event);
m_wheelEvent.initializeFromEvent(we);
bool shouldBlock = false;
bool shouldScrollFlickable = false;
for (auto *handler : m_handlersForItem.values(item)) {
if (handler->m_blockTargetWheel) {
shouldBlock = true;
}
if (handler->m_scrollFlickableTarget) {
shouldScrollFlickable = true;
}
emit handler->wheel(&m_wheelEvent);
}
if (shouldScrollFlickable && !m_wheelEvent.isAccepted()) {
manageWheel(item, we);
}
if (shouldBlock) {
return true;
}
}
return QObject::eventFilter(watched, event);
}
void GlobalWheelFilter::manageWheel(QQuickItem *target, QWheelEvent *event)
{
// Duck typing: accept everyhint that has all the properties we need
if (target->metaObject()->indexOfProperty("contentX") == -1
|| target->metaObject()->indexOfProperty("contentY") == -1
|| target->metaObject()->indexOfProperty("contentWidth") == -1
|| target->metaObject()->indexOfProperty("contentHeight") == -1
|| target->metaObject()->indexOfProperty("topMargin") == -1
|| target->metaObject()->indexOfProperty("bottomMargin") == -1
|| target->metaObject()->indexOfProperty("leftMargin") == -1
|| target->metaObject()->indexOfProperty("rightMargin") == -1
|| target->metaObject()->indexOfProperty("originX") == -1
|| target->metaObject()->indexOfProperty("originY") == -1) {
return;
}
qreal contentWidth = target->property("contentWidth").toReal();
qreal contentHeight = target->property("contentHeight").toReal();
qreal contentX = target->property("contentX").toReal();
qreal contentY = target->property("contentY").toReal();
qreal topMargin = target->property("topMargin").toReal();
qreal bottomMargin = target->property("bottomMargin").toReal();
qreal leftMargin = target->property("leftMaring").toReal();
qreal rightMargin = target->property("rightMargin").toReal();
qreal originX = target->property("originX").toReal();
qreal originY = target->property("originY").toReal();
// Scroll Y
if (contentHeight > target->height()) {
int y = event->pixelDelta().y() != 0 ? event->pixelDelta().y() : event->angleDelta().y() / 8;
//if we don't have a pixeldelta, apply the configured mouse wheel lines
if (!event->pixelDelta().y()) {
y *= 3; // Magic copied value from Kirigami::Settings
}
// Scroll one page regardless of delta:
if ((event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier)) {
if (y > 0) {
y = target->height();
} else if (y < 0) {
y = -target->height();
}
}
qreal minYExtent = topMargin - originY;
qreal maxYExtent = target->height() - (contentHeight + bottomMargin + originY);
target->setProperty("contentY", qMin(-maxYExtent, qMax(-minYExtent, contentY - y)));
}
//Scroll X
if (contentWidth > target->width()) {
int x = event->pixelDelta().x() != 0 ? event->pixelDelta().x() : event->angleDelta().x() / 8;
// Special case: when can't scroll vertically, scroll horizontally with vertical wheel as well
if (x == 0 && contentHeight <= target->height()) {
x = event->pixelDelta().y() != 0 ? event->pixelDelta().y() : event->angleDelta().y() / 8;
}
//if we don't have a pixeldelta, apply the configured mouse wheel lines
if (!event->pixelDelta().x()) {
x *= 3; // Magic copied value from Kirigami::Settings
}
// Scroll one page regardless of delta:
if ((event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier)) {
if (x > 0) {
x = target->width();
} else if (x < 0) {
x = -target->width();
}
}
qreal minXExtent = leftMargin - originX;
qreal maxXExtent = target->width() - (contentWidth + rightMargin + originX);
target->setProperty("contentX", qMin(-maxXExtent, qMax(-minXExtent, contentX - x)));
}
//this is just for making the scrollbar
target->metaObject()->invokeMethod(target, "flick", Q_ARG(double, 0), Q_ARG(double, 1));
target->metaObject()->invokeMethod(target, "cancelFlick");
}
////////////////////////////
KirigamiWheelEvent::KirigamiWheelEvent(QObject *parent)
: QObject(parent)
{}
KirigamiWheelEvent::~KirigamiWheelEvent() = default;
void KirigamiWheelEvent::initializeFromEvent(QWheelEvent *event)
{
m_x = event->position().x();
m_y = event->position().y();
m_angleDelta = event->angleDelta();
m_pixelDelta = event->pixelDelta();
m_buttons = event->buttons();
m_modifiers = event->modifiers();
m_accepted = false;
m_inverted = event->inverted();
}
qreal KirigamiWheelEvent::x() const
{
return m_x;
}
qreal KirigamiWheelEvent::y() const
{
return m_y;
}
QPointF KirigamiWheelEvent::angleDelta() const
{
return m_angleDelta;
}
QPointF KirigamiWheelEvent::pixelDelta() const
{
return m_pixelDelta;
}
int KirigamiWheelEvent::buttons() const
{
return m_buttons;
}
int KirigamiWheelEvent::modifiers() const
{
return m_modifiers;
}
bool KirigamiWheelEvent::inverted() const
{
return m_inverted;
}
bool KirigamiWheelEvent::isAccepted()
{
return m_accepted;
}
void KirigamiWheelEvent::setAccepted(bool accepted)
{
m_accepted = accepted;
}
///////////////////////////////
WheelHandler::WheelHandler(QObject *parent)
: QObject(parent)
{
}
WheelHandler::~WheelHandler() = default;
QQuickItem *WheelHandler::target() const
{
return m_target;
}
void WheelHandler::setTarget(QQuickItem *target)
{
if (m_target == target) {
return;
}
if (m_target) {
GlobalWheelFilter::self()->removeItemHandlerAssociation(m_target, this);
}
m_target = target;
GlobalWheelFilter::self()->setItemHandlerAssociation(target, this);
emit targetChanged();
}
#include "moc_wheelhandler.cpp"

213
src/3rdparty/kirigami/wheelhandler.h vendored Normal file
View File

@@ -0,0 +1,213 @@
/*
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#pragma once
#include <QtQml>
#include <QPoint>
#include <QQuickItem>
#include <QObject>
class QWheelEvent;
class WheelHandler;
/**
* Describes the mouse wheel event
*/
class KirigamiWheelEvent : public QObject
{
Q_OBJECT
/**
* x: real
*
* X coordinate of the mouse pointer
*/
Q_PROPERTY(qreal x READ x CONSTANT)
/**
* y: real
*
* Y coordinate of the mouse pointer
*/
Q_PROPERTY(qreal y READ y CONSTANT)
/**
* angleDelta: point
*
* The distance the wheel is rotated in degrees.
* The x and y coordinates indicate the horizontal and vertical wheels respectively.
* A positive value indicates it was rotated up/right, negative, bottom/left
* This value is more likely to be set in traditional mice.
*/
Q_PROPERTY(QPointF angleDelta READ angleDelta CONSTANT)
/**
* pixelDelta: point
*
* provides the delta in screen pixels available on high resolution trackpads
*/
Q_PROPERTY(QPointF pixelDelta READ pixelDelta CONSTANT)
/**
* buttons: int
*
* it contains an OR combination of the buttons that were pressed during the wheel, they can be:
* Qt.LeftButton, Qt.MiddleButton, Qt.RightButton
*/
Q_PROPERTY(int buttons READ buttons CONSTANT)
/**
* modifiers: int
*
* Keyboard mobifiers that were pressed during the wheel event, such as:
* Qt.NoModifier (default, no modifiers)
* Qt.ControlModifier
* Qt.ShiftModifier
* ...
*/
Q_PROPERTY(int modifiers READ modifiers CONSTANT)
/**
* inverted: bool
*
* Whether the delta values are inverted
* On some platformsthe returned delta are inverted, so positive values would mean bottom/left
*/
Q_PROPERTY(bool inverted READ inverted CONSTANT)
/**
* accepted: bool
*
* If set, the event shouldn't be managed anymore,
* for instance it can be used to block the handler to manage the scroll of a view on some scenarios
* @code
* // This handler handles automatically the scroll of
* // flickableItem, unless Ctrl is pressed, in this case the
* // app has custom code to handle Ctrl+wheel zooming
* Kirigami.WheelHandler {
* target: flickableItem
* blockTargetWheel: true
* scrollFlickableTarget: true
* onWheel: {
* if (wheel.modifiers & Qt.ControlModifier) {
* wheel.accepted = true;
* // Handle scaling of the view
* }
* }
* }
* @endcode
*
*/
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
KirigamiWheelEvent(QObject *parent = nullptr);
~KirigamiWheelEvent() override;
void initializeFromEvent(QWheelEvent *event);
qreal x() const;
qreal y() const;
QPointF angleDelta() const;
QPointF pixelDelta() const;
int buttons() const;
int modifiers() const;
bool inverted() const;
bool isAccepted();
void setAccepted(bool accepted);
private:
qreal m_x = 0;
qreal m_y = 0;
QPointF m_angleDelta;
QPointF m_pixelDelta;
Qt::MouseButtons m_buttons = Qt::NoButton;
Qt::KeyboardModifiers m_modifiers = Qt::NoModifier;
bool m_inverted = false;
bool m_accepted = false;
};
class GlobalWheelFilter : public QObject
{
Q_OBJECT
public:
GlobalWheelFilter(QObject *parent = nullptr);
~GlobalWheelFilter() override;
static GlobalWheelFilter *self();
void setItemHandlerAssociation(QQuickItem *item, WheelHandler *handler);
void removeItemHandlerAssociation(QQuickItem *item, WheelHandler *handler);
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
private:
void manageWheel(QQuickItem *target, QWheelEvent *wheel);
QMultiHash<QQuickItem *, WheelHandler *> m_handlersForItem;
KirigamiWheelEvent m_wheelEvent;
};
/**
* This class intercepts the mouse wheel events of its target, and gives them to the user code as a signal, which can be used for custom mouse wheel management code.
* The handler can block completely the wheel events from its target, and if it's a Flickable, it can automatically handle scrolling on it
*/
class WheelHandler : public QObject
{
Q_OBJECT
/**
* target: Item
*
* The target we want to manage wheel events.
* We will receive wheel() signals every time the user moves
* the mouse wheel (or scrolls with the touchpad) on top
* of that item.
*/
Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged)
/**
* blockTargetWheel: bool
*
* If true, the target won't receive any wheel event at all (default true)
*/
Q_PROPERTY(bool blockTargetWheel MEMBER m_blockTargetWheel NOTIFY blockTargetWheelChanged)
/**
* scrollFlickableTarget: bool
* If this property is true and the target is a Flickable, wheel events will cause the Flickable to scroll (default true)
*/
Q_PROPERTY(bool scrollFlickableTarget MEMBER m_scrollFlickableTarget NOTIFY scrollFlickableTargetChanged)
public:
explicit WheelHandler(QObject *parent = nullptr);
~WheelHandler() override;
QQuickItem *target() const;
void setTarget(QQuickItem *target);
Q_SIGNALS:
void targetChanged();
void blockTargetWheelChanged();
void scrollFlickableTargetChanged();
void wheel(KirigamiWheelEvent *wheel);
private:
QPointer<QQuickItem> m_target;
bool m_blockTargetWheel = true;
bool m_scrollFlickableTarget = true;
KirigamiWheelEvent m_wheelEvent;
friend class GlobalWheelFilter;
};

View File

@@ -337,7 +337,7 @@ ComputeChecksum *ValidateChecksumHeader::prepareStart(const QByteArray &checksum
if (!parseChecksumHeader(checksumHeader, &_expectedChecksumType, &_expectedChecksum)) {
qCWarning(lcChecksums) << "Checksum header malformed:" << checksumHeader;
emit validationFailed(tr("The checksum header is malformed."));
emit validationFailed(tr("The checksum header is malformed."), _calculatedChecksumType, _calculatedChecksum, ChecksumHeaderMalformed);
return nullptr;
}
@@ -360,15 +360,30 @@ void ValidateChecksumHeader::start(std::unique_ptr<QIODevice> device, const QByt
calculator->start(std::move(device));
}
QByteArray ValidateChecksumHeader::calculatedChecksumType() const
{
return _calculatedChecksumType;
}
QByteArray ValidateChecksumHeader::calculatedChecksum() const
{
return _calculatedChecksum;
}
void ValidateChecksumHeader::slotChecksumCalculated(const QByteArray &checksumType,
const QByteArray &checksum)
{
_calculatedChecksumType = checksumType;
_calculatedChecksum = checksum;
if (checksumType != _expectedChecksumType) {
emit validationFailed(tr("The checksum header contained an unknown checksum type \"%1\"").arg(QString::fromLatin1(_expectedChecksumType)));
emit validationFailed(tr("The checksum header contained an unknown checksum type \"%1\"").arg(QString::fromLatin1(_expectedChecksumType)),
_calculatedChecksumType, _calculatedChecksum, ChecksumTypeUnknown);
return;
}
if (checksum != _expectedChecksum) {
emit validationFailed(tr(R"(The downloaded file does not match the checksum, it will be resumed. "%1" != "%2")").arg(QString::fromUtf8(_expectedChecksum), QString::fromUtf8(checksum)));
emit validationFailed(tr(R"(The downloaded file does not match the checksum, it will be resumed. "%1" != "%2")").arg(QString::fromUtf8(_expectedChecksum), QString::fromUtf8(checksum)),
_calculatedChecksumType, _calculatedChecksum, ChecksumMismatch);
return;
}
emit validated(checksumType, checksum);

View File

@@ -140,6 +140,14 @@ class OCSYNC_EXPORT ValidateChecksumHeader : public QObject
{
Q_OBJECT
public:
enum FailureReason {
Success,
ChecksumHeaderMalformed,
ChecksumTypeUnknown,
ChecksumMismatch,
};
Q_ENUM(FailureReason)
explicit ValidateChecksumHeader(QObject *parent = nullptr);
/**
@@ -161,9 +169,13 @@ public:
*/
void start(std::unique_ptr<QIODevice> device, const QByteArray &checksumHeader);
QByteArray calculatedChecksumType() const;
QByteArray calculatedChecksum() const;
signals:
void validated(const QByteArray &checksumType, const QByteArray &checksum);
void validationFailed(const QString &errMsg);
void validationFailed(const QString &errMsg, const QByteArray &calculatedChecksumType,
const QByteArray &calculatedChecksum, const ValidateChecksumHeader::FailureReason reason);
private slots:
void slotChecksumCalculated(const QByteArray &checksumType, const QByteArray &checksum);
@@ -173,6 +185,9 @@ private:
QByteArray _expectedChecksumType;
QByteArray _expectedChecksum;
QByteArray _calculatedChecksumType;
QByteArray _calculatedChecksum;
};
/**

View File

@@ -1001,43 +1001,22 @@ qint64 SyncJournalDb::keyValueStoreGetInt(const QString &key, qint64 defaultValu
return defaultValue;
}
const auto query = _queryManager.get(PreparedSqlQueryManager::GetKeyValueStoreQuery, QByteArrayLiteral("SELECT value FROM key_value_store WHERE key = ?1;"), _db);
const auto query = _queryManager.get(PreparedSqlQueryManager::GetKeyValueStoreQuery, QByteArrayLiteral("SELECT value FROM key_value_store WHERE key=?1"), _db);
if (!query) {
return defaultValue;
}
query->bindValue(1, key);
query->exec();
auto result = query->next();
if (!query->next().hasData) {
if (!result.ok || !result.hasData) {
return defaultValue;
}
return query->int64Value(0);
}
QVariant SyncJournalDb::keyValueStoreGet(const QString &key, QVariant defaultValue)
{
QMutexLocker locker(&_mutex);
if (!checkConnect()) {
return defaultValue;
}
const auto query = _queryManager.get(PreparedSqlQueryManager::GetKeyValueStoreQuery, QByteArrayLiteral("SELECT value FROM key_value_store WHERE key = ?1;"), _db);
if (!query) {
return defaultValue;
}
query->bindValue(1, key);
query->exec();
if (!query->next().hasData) {
return defaultValue;
}
return query->stringValue(0);
}
void SyncJournalDb::keyValueStoreDelete(const QString &key)
{
const auto query = _queryManager.get(PreparedSqlQueryManager::DeleteKeyValueStoreQuery, QByteArrayLiteral("DELETE FROM key_value_store WHERE key=?1;"), _db);

View File

@@ -70,7 +70,6 @@ public:
void keyValueStoreSet(const QString &key, QVariant value);
qint64 keyValueStoreGetInt(const QString &key, qint64 defaultValue);
QVariant keyValueStoreGet(const QString &key, QVariant defaultValue = {});
void keyValueStoreDelete(const QString &key);
bool deleteFileRecord(const QString &filename, bool recursively = false);

View File

@@ -109,6 +109,11 @@ void Utility::setupFavLink(const QString &folder)
setupFavLink_private(folder);
}
void Utility::removeFavLink(const QString &folder)
{
removeFavLink_private(folder);
}
QString Utility::octetsToString(qint64 octets)
{
#define THE_FACTOR 1024

View File

@@ -55,6 +55,7 @@ namespace Utility {
OCSYNC_EXPORT void usleep(int usec);
OCSYNC_EXPORT QString formatFingerprint(const QByteArray &, bool colonSeparated = true);
OCSYNC_EXPORT void setupFavLink(const QString &folder);
OCSYNC_EXPORT void removeFavLink(const QString &folder);
OCSYNC_EXPORT bool writeRandomFile(const QString &fname, int size = -1);
OCSYNC_EXPORT QString octetsToString(qint64 octets);
OCSYNC_EXPORT QByteArray userAgentString();
@@ -241,6 +242,11 @@ namespace Utility {
*/
OCSYNC_EXPORT bool isPathWindowsDrivePartitionRoot(const QString &path);
/**
* @brief Retrieves current logged-in user name from the OS
*/
OCSYNC_EXPORT QString getCurrentUserName();
#ifdef Q_OS_WIN
OCSYNC_EXPORT bool registryKeyExists(HKEY hRootKey, const QString &subKey);
OCSYNC_EXPORT QVariant registryGetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName);

View File

@@ -41,6 +41,11 @@ static void setupFavLink_private(const QString &folder)
CFRelease(urlRef);
}
static void removeFavLink_private(const QString &folder)
{
Q_UNUSED(folder)
}
bool hasLaunchOnStartup_private(const QString &)
{
// this is quite some duplicate code with setLaunchOnStartup, at some point we should fix this FIXME.
@@ -131,4 +136,9 @@ static bool hasDarkSystray_private()
return returnValue;
}
QString Utility::getCurrentUserName()
{
return {};
}
} // namespace OCC

View File

@@ -37,6 +37,11 @@ static void setupFavLink_private(const QString &folder)
}
}
static void removeFavLink_private(const QString &folder)
{
Q_UNUSED(folder)
}
// returns the autostart directory the linux way
// and respects the XDG_CONFIG_HOME env variable
QString getUserAutostartDir_private()
@@ -103,4 +108,9 @@ static inline bool hasDarkSystray_private()
return true;
}
QString Utility::getCurrentUserName()
{
return {};
}
} // namespace OCC

View File

@@ -18,8 +18,10 @@
#include "asserts.h"
#include "utility.h"
#include "gui/configgui.h"
#include <comdef.h>
#include <Lmcons.h>
#include <shlguid.h>
#include <shlobj.h>
#include <string>
@@ -47,7 +49,14 @@ static void setupFavLink_private(const QString &folder)
desktopIni.open(QFile::WriteOnly);
desktopIni.write("[.ShellClassInfo]\r\nIconResource=");
desktopIni.write(QDir::toNativeSeparators(qApp->applicationFilePath()).toUtf8());
desktopIni.write(",0\r\n");
#ifdef APPLICATION_FOLDER_ICON_INDEX
const auto iconIndex = APPLICATION_FOLDER_ICON_INDEX;
#else
const auto iconIndex = "0";
#endif
desktopIni.write(",");
desktopIni.write(iconIndex);
desktopIni.write("\r\n");
desktopIni.close();
// Set the folder as system and Desktop.ini as hidden+system for explorer to pick it.
@@ -74,6 +83,40 @@ static void setupFavLink_private(const QString &folder)
qCWarning(lcUtility) << "linking" << folder << "to" << linkName << "failed!";
}
static void removeFavLink_private(const QString &folder)
{
const QDir folderDir(folder);
// #1 Remove the Desktop.ini to reset the folder icon
if (!QFile::remove(folderDir.absoluteFilePath(QLatin1String("Desktop.ini")))) {
qCWarning(lcUtility) << "Remove Desktop.ini from" << folder
<< " has failed. Make sure it exists and is not locked by another process.";
}
// #2 Remove the system file attribute
const auto folderAttrs = GetFileAttributesW(folder.toStdWString().c_str());
if (!SetFileAttributesW(folder.toStdWString().c_str(), folderAttrs & ~FILE_ATTRIBUTE_SYSTEM)) {
qCWarning(lcUtility) << "Remove system file attribute failed for:" << folder;
}
// #3 Remove the link to this folder
PWSTR path;
if (!SHGetKnownFolderPath(FOLDERID_Links, 0, nullptr, &path) == S_OK) {
qCWarning(lcUtility) << "SHGetKnownFolderPath for " << folder << "has failed.";
return;
}
const QDir links(QString::fromWCharArray(path));
CoTaskMemFree(path);
const auto linkName = QDir(links).absoluteFilePath(folderDir.dirName() + QLatin1String(".lnk"));
qCInfo(lcUtility) << "Removing favorite link from" << folder << "to" << linkName;
if (!QFile::remove(linkName)) {
qCWarning(lcUtility) << "Removing a favorite link from" << folder << "to" << linkName << "failed.";
}
}
bool hasSystemLaunchOnStartup_private(const QString &appName)
{
QString runPath = QLatin1String(systemRunPathC);
@@ -346,6 +389,17 @@ QString Utility::formatWinError(long errorCode)
return QStringLiteral("WindowsError: %1: %2").arg(QString::number(errorCode, 16), QString::fromWCharArray(_com_error(errorCode).ErrorMessage()));
}
QString Utility::getCurrentUserName()
{
TCHAR username[UNLEN + 1] = {0};
DWORD len = sizeof(username) / sizeof(TCHAR);
if (!GetUserName(username, &len)) {
qCWarning(lcUtility) << "Could not retrieve Windows user name." << formatWinError(GetLastError());
}
return QString::fromWCharArray(username);
}
Utility::NtfsPermissionLookupRAII::NtfsPermissionLookupRAII()
{

View File

@@ -1,5 +1,17 @@
project(gui)
find_package(Qt5 REQUIRED COMPONENTS Widgets Svg Qml Quick QuickControls2)
find_package(Qt5 REQUIRED COMPONENTS Widgets Svg Qml Quick QuickControls2 Xml Network)
if(QUICK_COMPILER)
find_package(Qt5QuickCompiler)
set_package_properties(Qt5QuickCompiler PROPERTIES
DESCRIPTION "Compile QML at build time"
TYPE REQUIRED
)
endif()
if (NOT TARGET Qt5::GuiPrivate)
message(FATAL_ERROR "Could not find GuiPrivate component of Qt5. It might be shipped as a separate package, please check that.")
endif()
if(CMAKE_BUILD_TYPE MATCHES Debug)
add_definitions(-DQT_QML_DEBUG)
@@ -10,14 +22,12 @@ IF(BUILD_UPDATER)
endif()
configure_file(${CMAKE_SOURCE_DIR}/theme.qrc.in ${CMAKE_SOURCE_DIR}/theme.qrc)
set(MIRALL_RC_SRC ../../resources.qrc)
list(APPEND MIRALL_RC_SRC ${CMAKE_SOURCE_DIR}/theme.qrc)
set(theme_dir ${CMAKE_SOURCE_DIR}/theme)
set(client_UI_SRCS
accountsettings.ui
conflictdialog.ui
internallinkwidget.ui
invalidfilenamedialog.ui
foldercreationdialog.ui
folderwizardsourcepage.ui
@@ -34,23 +44,9 @@ set(client_UI_SRCS
shareuserline.ui
sslerrordialog.ui
addcertificatedialog.ui
passwordinputdialog.ui
proxyauthdialog.ui
mnemonicdialog.ui
UserStatusSelector.qml
UserStatusSelectorDialog.qml
tray/ActivityActionButton.qml
tray/ActivityItem.qml
tray/ActivityList.qml
tray/Window.qml
tray/UserLine.qml
tray/UnifiedSearchInputContainer.qml
tray/UnifiedSearchResultFetchMoreTrigger.qml
tray/UnifiedSearchResultItem.qml
tray/UnifiedSearchResultItemSkeleton.qml
tray/UnifiedSearchResultItemSkeletonContainer.qml
tray/UnifiedSearchResultListItem.qml
tray/UnifiedSearchResultNothingFound.qml
tray/UnifiedSearchResultSectionItem.qml
wizard/flow2authwidget.ui
wizard/owncloudadvancedsetuppage.ui
wizard/owncloudconnectionmethoddialog.ui
@@ -61,6 +57,12 @@ set(client_UI_SRCS
wizard/welcomepage.ui
)
if(QUICK_COMPILER)
qtquick_compiler_add_resources(client_UI_SRCS ../../resources.qrc ${CMAKE_SOURCE_DIR}/theme.qrc)
else()
qt_add_resources(client_UI_SRCS ../../resources.qrc ${CMAKE_SOURCE_DIR}/theme.qrc)
endif()
set(client_SRCS
accountmanager.cpp
accountsettings.cpp
@@ -79,6 +81,7 @@ set(client_SRCS
folderwizard.cpp
generalsettings.cpp
legalnotice.cpp
internallinkwidget.cpp
ignorelisteditor.cpp
ignorelisttablewidget.cpp
lockwatcher.cpp
@@ -92,6 +95,7 @@ set(client_SRCS
openfilemanager.cpp
owncloudgui.cpp
owncloudsetupwizard.cpp
passwordinputdialog.cpp
selectivesyncdialog.cpp
settingsdialog.cpp
sharedialog.cpp
@@ -203,6 +207,7 @@ set(3rdparty_SRC
../3rdparty/qtsingleapplication/qtsingleapplication.cpp
../3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp
../3rdparty/kmessagewidget/kmessagewidget.cpp
../3rdparty/kirigami/wheelhandler.cpp
)
if(NOT WIN32)
@@ -228,7 +233,6 @@ IF( WIN32 )
ENDIF()
set( final_src
${MIRALL_RC_SRC}
${client_SRCS}
${client_UI_SRCS}
${guiMoc}
@@ -254,6 +258,10 @@ if (NOT DEFINED APPLICATION_ICON_NAME)
set(APPLICATION_ICON_NAME ${APPLICATION_SHORTNAME})
endif()
if(NOT DEFINED APPLICATION_FOLDER_ICON_INDEX)
set(APPLICATION_FOLDER_ICON_INDEX 0)
endif()
# Generate png icons from svg
find_program(SVG_CONVERTER
NAMES inkscape inkscape.exe rsvg-convert
@@ -265,9 +273,24 @@ if (NOT SVG_CONVERTER)
endif()
function(generate_sized_png_from_svg icon_path size)
set(options)
set(oneValueArgs OUTPUT_ICON_NAME OUTPUT_ICON_PATH)
set(multiValueArgs)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
get_filename_component(icon_name_dir ${icon_path} DIRECTORY)
get_filename_component(icon_name_wle ${icon_path} NAME_WLE)
if (ARG_OUTPUT_ICON_NAME)
set(icon_name_wle ${ARG_OUTPUT_ICON_NAME})
endif ()
if (ARG_OUTPUT_ICON_PATH)
set(icon_name_dir ${ARG_OUTPUT_ICON_PATH})
endif ()
if (EXISTS "${icon_name_dir}/${size}-${icon_name_wle}.png")
return()
endif()
@@ -313,22 +336,86 @@ if(WIN32)
endif()
set(APP_ICON_SVG "${theme_dir}/colored/${APPLICATION_ICON_NAME}-icon.svg")
generate_sized_png_from_svg(${APP_ICON_SVG} 16)
generate_sized_png_from_svg(${APP_ICON_SVG} 24)
generate_sized_png_from_svg(${APP_ICON_SVG} 32)
generate_sized_png_from_svg(${APP_ICON_SVG} 48)
generate_sized_png_from_svg(${APP_ICON_SVG} 64)
generate_sized_png_from_svg(${APP_ICON_SVG} 128)
generate_sized_png_from_svg(${APP_ICON_SVG} 256)
generate_sized_png_from_svg(${APP_ICON_SVG} 512)
generate_sized_png_from_svg(${APP_ICON_SVG} 1024)
# generate secondary icon if available (currently for Windows only)--------------------------------------
set(APP_SECONDARY_ICONS "${theme_dir}/colored/icons")
set(APP_ICON_WIN_FOLDER_SVG "${APP_SECONDARY_ICONS}/${APPLICATION_ICON_NAME}-icon-win-folder.svg")
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()
# generate primary icon from SVG (due to Win .ico vs .rc dependency issues, primary icon must always be generated last)--------------------------------------
if(WIN32)
foreach(size IN ITEMS 16;20;24;32;40;48;64;128;256;512;1024)
generate_sized_png_from_svg(${APP_ICON_SVG} ${size})
endforeach()
else()
foreach(size IN ITEMS 16;24;32;48;64;128;256;512;1024)
generate_sized_png_from_svg(${APP_ICON_SVG} ${size})
endforeach()
endif()
file(GLOB_RECURSE OWNCLOUD_ICONS "${theme_dir}/colored/*-${APPLICATION_ICON_NAME}-icon*")
if(APPLE)
file(GLOB_RECURSE OWNCLOUD_SIDEBAR_ICONS "${theme_dir}/colored/*-${APPLICATION_ICON_NAME}-sidebar*")
MESSAGE(STATUS "OWNCLOUD_SIDEBAR_ICONS: ${APPLICATION_ICON_NAME}: ${OWNCLOUD_SIDEBAR_ICONS}")
endif()
ecm_add_app_icon(APP_ICON ICONS "${OWNCLOUD_ICONS}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASENAME "${APPLICATION_ICON_NAME}")
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)
# 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)
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()
endif()
# --------------------------------------
if(UNIX AND NOT APPLE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE")
@@ -364,7 +451,6 @@ endif()
set_target_properties(nextcloudCore
PROPERTIES
AUTOUIC ON
AUTORCC ON
AUTOMOC ON
)
@@ -372,6 +458,7 @@ target_include_directories(nextcloudCore
PUBLIC
${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator
${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile
${CMAKE_SOURCE_DIR}/src/3rdparty/kirigami
${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication
${CMAKE_SOURCE_DIR}/src/3rdparty/kmessagewidget
${CMAKE_CURRENT_BINARY_DIR}
@@ -540,3 +627,5 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE AND NOT WIN32)
update_xdg_mimetypes( ${CMAKE_INSTALL_DATADIR}/mime/packages )
endif(SharedMimeInfo_FOUND)
endif()
configure_file(configgui.h.in ${CMAKE_CURRENT_BINARY_DIR}/configgui.h)

View File

@@ -24,11 +24,7 @@ ColumnLayout {
spacing: 0
property NC.UserStatusSelectorModel userStatusSelectorModel
FontMetrics {
id: metrics
}
Text {
Label {
Layout.topMargin: 16
Layout.leftMargin: 8
Layout.rightMargin: 8
@@ -89,7 +85,7 @@ ColumnLayout {
}
}
Text {
Label {
Layout.topMargin: 16
Layout.leftMargin: 8
Layout.rightMargin: 8
@@ -108,8 +104,8 @@ ColumnLayout {
Layout.fillWidth: true
Button {
Layout.preferredWidth: userStatusMessageTextField.height // metrics.height * 2
Layout.preferredHeight: userStatusMessageTextField.height // metrics.height * 2
Layout.preferredWidth: userStatusMessageTextField.height
Layout.preferredHeight: userStatusMessageTextField.height
text: userStatusSelectorModel.userStatusEmoji
onClicked: emojiDialog.open()
}
@@ -161,7 +157,7 @@ ColumnLayout {
Layout.bottomMargin: 8
Layout.alignment: Qt.AlignTop
Text {
Label {
text: qsTr("Clear status message after")
}

View File

@@ -587,6 +587,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
ac = availabilityMenu->addAction(Utility::vfsPinActionText());
connect(ac, &QAction::triggered, this, [this]() { slotSetCurrentFolderAvailability(PinState::AlwaysLocal); });
ac->setDisabled(Theme::instance()->enforceVirtualFilesSyncFolder());
ac = availabilityMenu->addAction(Utility::vfsFreeSpaceActionText());
connect(ac, &QAction::triggered, this, [this]() { slotSetCurrentFolderAvailability(PinState::OnlineOnly); });
@@ -761,6 +762,7 @@ void AccountSettings::slotRemoveCurrentFolder()
messageBox->addButton(tr("Cancel"), QMessageBox::NoRole);
connect(messageBox, &QMessageBox::finished, this, [messageBox, yesButton, folder, row, this]{
if (messageBox->clickedButton() == yesButton) {
Utility::removeFavLink(folder->path());
FolderMan::instance()->removeFolder(folder);
_model->removeRow(row);

View File

@@ -221,6 +221,19 @@ void AccountState::setDesktopNotificationsAllowed(bool isAllowed)
emit desktopNotificationsAllowedChanged();
}
AccountState::ConnectionStatus AccountState::lastConnectionStatus() const
{
return _lastConnectionValidatorStatus;
}
void AccountState::trySignIn()
{
if (isSignedOut() && account()) {
account()->resetRejectedCertificates();
signIn();
}
}
void AccountState::checkConnectivity()
{
if (isSignedOut() || _waitingForNewCredentials) {
@@ -285,6 +298,8 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
return;
}
_lastConnectionValidatorStatus = status;
// Come online gradually from 503 or maintenance mode
if (status == ConnectionValidator::Connected
&& (_connectionStatus == ConnectionValidator::ServiceUnavailable

View File

@@ -171,6 +171,10 @@ public:
*/
void setDesktopNotificationsAllowed(bool isAllowed);
ConnectionStatus lastConnectionStatus() const;
void trySignIn();
public slots:
/// Triggers a ping to the server to update state and
/// connection status and errors.
@@ -205,6 +209,7 @@ private:
AccountPtr _account;
State _state;
ConnectionStatus _connectionStatus;
ConnectionStatus _lastConnectionValidatorStatus = ConnectionStatus::Undefined;
QStringList _connectionErrors;
bool _waitingForNewCredentials;
QDateTime _timeOfLastETagCheck;

View File

@@ -250,6 +250,10 @@ Application::Application(int &argc, char **argv)
}
}
if (_theme->doNotUseProxy()) {
ConfigFile().setProxyType(QNetworkProxy::NoProxy);
}
parseOptions(arguments());
//no need to waste time;
if (_helpOnly || _versionOnly)
@@ -465,6 +469,9 @@ void Application::slotCheckConnection()
if (state != AccountState::SignedOut && state != AccountState::ConfigurationError
&& state != AccountState::AskingCredentials && !pushNotificationsAvailable) {
accountState->checkConnectivity();
} else if (state == AccountState::SignedOut && accountState->lastConnectionStatus() == AccountState::ConnectionStatus::SslError) {
qCWarning(lcApplication) << "Account is signed out due to SSL Handshake error. Going to perform a sign-in attempt...";
accountState->trySignIn();
}
}

4
src/gui/configgui.h.in Normal file
View File

@@ -0,0 +1,4 @@
#ifndef CONFIG_GUI_H
#define CONFIG_GUI_H
#cmakedefine APPLICATION_FOLDER_ICON_INDEX "@APPLICATION_FOLDER_ICON_INDEX@"
#endif

View File

@@ -136,7 +136,7 @@ void ConnectionValidator::slotStatusFound(const QUrl &url, const QJsonObject &in
void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply)
{
auto job = qobject_cast<CheckServerJob *>(sender());
qCWarning(lcConnectionValidator) << reply->error() << job->errorString() << reply->peek(1024);
qCWarning(lcConnectionValidator) << reply->error() << reply->errorString() << job->errorString() << reply->peek(1024);
if (reply->error() == QNetworkReply::SslHandshakeFailedError) {
reportResult(SslError);
return;

View File

@@ -132,6 +132,16 @@ void Flow2Auth::fetchNewToken(const TokenAction action)
_loginUrl = loginUrl;
if (_account->isUsernamePrefillSupported()) {
const auto userName = Utility::getCurrentUserName();
if (!userName.isEmpty()) {
auto query = QUrlQuery(_loginUrl);
query.addQueryItem(QStringLiteral("user"), userName);
_loginUrl.setQuery(query);
}
}
_pollToken = pollToken;
_pollEndpoint = pollEndpoint;

View File

@@ -691,6 +691,15 @@ void Folder::setRootPinState(PinState state)
void Folder::switchToVirtualFiles()
{
SyncEngine::switchToVirtualFiles(path(), _journal, *_vfs);
_hasSwitchedToVfs = true;
}
void Folder::processSwitchedToVirtualFiles()
{
if (_hasSwitchedToVfs) {
_hasSwitchedToVfs = false;
saveToSettings();
}
}
bool Folder::supportsSelectiveSync() const
@@ -866,11 +875,27 @@ void Folder::startSync(const QStringList &pathList)
_engine->setIgnoreHiddenFiles(_definition.ignoreHiddenFiles);
correctPlaceholderFiles();
QMetaObject::invokeMethod(_engine.data(), "startSync", Qt::QueuedConnection);
emit syncStarted();
}
void Folder::correctPlaceholderFiles()
{
if (_definition.virtualFilesMode == Vfs::Off) {
return;
}
static const auto placeholdersCorrectedKey = QStringLiteral("placeholders_corrected");
const auto placeholdersCorrected = _journal.keyValueStoreGetInt(placeholdersCorrectedKey, 0);
if (!placeholdersCorrected) {
qCDebug(lcFolder) << "Make sure all virtual files are placeholder files";
switchToVirtualFiles();
_journal.keyValueStoreSet(placeholdersCorrectedKey, true);
}
}
void Folder::setSyncOptions()
{
SyncOptions opt;

View File

@@ -289,6 +289,8 @@ public:
void switchToVirtualFiles();
void processSwitchedToVirtualFiles();
/** Whether this folder should show selective sync ui */
bool supportsSelectiveSync() const;
@@ -444,6 +446,8 @@ private:
void startVfs();
void correctPlaceholderFiles();
AccountStatePtr _accountState;
FolderDefinition _definition;
QString _canonicalLocalPath; // As returned with QFileInfo:canonicalFilePath. Always ends with "/"
@@ -498,6 +502,10 @@ private:
*/
bool _vfsOnOffPending = false;
/** Whether this folder has just switched to VFS or not
*/
bool _hasSwitchedToVfs = false;
/**
* Watches this folder's local directory for changes.
*

View File

@@ -211,6 +211,10 @@ int FolderMan::setupFolders()
emit folderListChanged(_folderMap);
for (const auto folder : _folderMap) {
folder->processSwitchedToVirtualFiles();
}
return _folderMap.size();
}

View File

@@ -36,8 +36,6 @@
#include "common/utility.h"
#include "logger.h"
#include "config.h"
#include "legalnotice.h"
#include <QFileDialog>

View File

@@ -15,6 +15,8 @@
#ifndef MIRALL_GENERALSETTINGS_H
#define MIRALL_GENERALSETTINGS_H
#include "config.h"
#include <QWidget>
#include <QPointer>

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "internallinkwidget.h"
#include "accountstate.h"
#include "folderman.h"
#include "theme.h"
#include "QProgressIndicator.h"
#include <QClipboard>
namespace OCC {
Q_LOGGING_CATEGORY(lcInternalLink, "nextcloud.gui.internallink", QtInfoMsg)
InternalLinkWidget::InternalLinkWidget(const QString &localPath,
QWidget *parent)
: QWidget(parent)
, _localPath(localPath)
{
_ui->setupUi(this);
const auto folder = FolderMan::instance()->folderForPath(_localPath);
const auto folderRelativePath = _localPath.mid(folder->cleanPath().length() + 1);
const auto serverRelativePath = QDir(folder->remotePath()).filePath(folderRelativePath);
const auto bindLinkSlot = [this](QString link) { slotLinkFetched(link); };
fetchPrivateLinkUrl(
folder->accountState()->account(),
serverRelativePath,
{},
this,
bindLinkSlot
);
_ui->copyInternalLinkButton->setEnabled(false);
_ui->internalLinkProgressIndicator->setVisible(true);
_ui->internalLinkProgressIndicator->startAnimation();
connect(_ui->copyInternalLinkButton, &QPushButton::clicked, this, &InternalLinkWidget::slotCopyInternalLink);
}
void InternalLinkWidget::slotLinkFetched(const QString &url)
{
_internalUrl = url;
_ui->copyInternalLinkButton->setEnabled(true);
_ui->internalLinkProgressIndicator->setVisible(false);
_ui->internalLinkProgressIndicator->stopAnimation();
_ui->horizontalSpacer->changeSize(0, 0);
_ui->horizontalSpacer_2->changeSize(0, 0);
}
void InternalLinkWidget::slotCopyInternalLink() const
{
QApplication::clipboard()->setText(_internalUrl);
}
void InternalLinkWidget::setupUiOptions()
{
customizeStyle();
}
void InternalLinkWidget::slotStyleChanged()
{
customizeStyle();
}
void InternalLinkWidget::customizeStyle()
{
_ui->copyInternalLinkButton->setIcon(Theme::createColorAwareIcon(":/client/theme/copy.svg"));
_ui->internalLinkIconLabel->setPixmap(Theme::createColorAwarePixmap(":/client/theme/external.svg"));
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef INTERNALLINKWIDGET_H
#define INTERNALLINKWIDGET_H
#include "QProgressIndicator.h"
#include <QList>
#include <QPushButton>
#include "ui_internallinkwidget.h"
namespace OCC {
/**
* @brief The ShareDialog class
* @ingroup gui
*/
class InternalLinkWidget : public QWidget
{
Q_OBJECT
public:
explicit InternalLinkWidget(const QString &localPath,
QWidget *parent = nullptr);
~InternalLinkWidget() override = default;
void setupUiOptions();
public slots:
void slotStyleChanged();
private slots:
void slotLinkFetched(const QString &url);
void slotCopyInternalLink() const;
private:
void customizeStyle();
std::unique_ptr<Ui::InternalLinkWidget> _ui = std::make_unique<Ui::InternalLinkWidget>();
QString _localPath;
QString _internalUrl;
QPushButton *_copyInternalLinkButton{};
};
}
#endif // INTERNALLINKWIDGET_H

View File

@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OCC::InternalLinkWidget</class>
<widget class="QWidget" name="OCC::InternalLinkWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>238</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>20</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="internalLinkIconLabel">
<property name="text">
<string notr="true"/>
</property>
<property name="pixmap">
<pixmap resource="../../theme.qrc">:/client/theme/external.svg</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalTextLayout">
<item>
<widget class="QLabel" name="internalLinkLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Internal link</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="infoMessage">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="enabled">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(118, 118, 118)</string>
</property>
<property name="text">
<string>Only works for users with access to this folder</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>25</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QProgressIndicator" name="internalLinkProgressIndicator" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>28</width>
<height>27</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>25</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="copyInternalLinkButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../theme.qrc">
<normaloff>:/client/theme/copy.svg</normaloff>:/client/theme/copy.svg</iconset>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>QProgressIndicator</class>
<extends>QWidget</extends>
<header>QProgressIndicator.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../theme.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -34,42 +34,51 @@ NetworkSettings::NetworkSettings(QWidget *parent)
{
_ui->setupUi(this);
_ui->hostLineEdit->setPlaceholderText(tr("Hostname of proxy server"));
_ui->userLineEdit->setPlaceholderText(tr("Username for proxy server"));
_ui->passwordLineEdit->setPlaceholderText(tr("Password for proxy server"));
_ui->proxyGroupBox->setVisible(!Theme::instance()->doNotUseProxy());
_ui->typeComboBox->addItem(tr("HTTP(S) proxy"), QNetworkProxy::HttpProxy);
_ui->typeComboBox->addItem(tr("SOCKS5 proxy"), QNetworkProxy::Socks5Proxy);
if (!Theme::instance()->doNotUseProxy()) {
_ui->hostLineEdit->setPlaceholderText(tr("Hostname of proxy server"));
_ui->userLineEdit->setPlaceholderText(tr("Username for proxy server"));
_ui->passwordLineEdit->setPlaceholderText(tr("Password for proxy server"));
_ui->authRequiredcheckBox->setEnabled(true);
_ui->typeComboBox->addItem(tr("HTTP(S) proxy"), QNetworkProxy::HttpProxy);
_ui->typeComboBox->addItem(tr("SOCKS5 proxy"), QNetworkProxy::Socks5Proxy);
// Explicitly set up the enabled status of the proxy auth widgets to ensure
// toggling the parent enables/disables the children
_ui->userLineEdit->setEnabled(true);
_ui->passwordLineEdit->setEnabled(true);
_ui->authWidgets->setEnabled(_ui->authRequiredcheckBox->isChecked());
connect(_ui->authRequiredcheckBox, &QAbstractButton::toggled,
_ui->authWidgets, &QWidget::setEnabled);
_ui->authRequiredcheckBox->setEnabled(true);
connect(_ui->manualProxyRadioButton, &QAbstractButton::toggled,
_ui->manualSettings, &QWidget::setEnabled);
connect(_ui->manualProxyRadioButton, &QAbstractButton::toggled,
_ui->typeComboBox, &QWidget::setEnabled);
connect(_ui->manualProxyRadioButton, &QAbstractButton::toggled,
this, &NetworkSettings::checkAccountLocalhost);
// Explicitly set up the enabled status of the proxy auth widgets to ensure
// toggling the parent enables/disables the children
_ui->userLineEdit->setEnabled(true);
_ui->passwordLineEdit->setEnabled(true);
_ui->authWidgets->setEnabled(_ui->authRequiredcheckBox->isChecked());
connect(_ui->authRequiredcheckBox, &QAbstractButton::toggled, _ui->authWidgets, &QWidget::setEnabled);
connect(_ui->manualProxyRadioButton, &QAbstractButton::toggled, _ui->manualSettings, &QWidget::setEnabled);
connect(_ui->manualProxyRadioButton, &QAbstractButton::toggled, _ui->typeComboBox, &QWidget::setEnabled);
connect(_ui->manualProxyRadioButton, &QAbstractButton::toggled, this, &NetworkSettings::checkAccountLocalhost);
loadProxySettings();
connect(_ui->typeComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&NetworkSettings::saveProxySettings);
connect(_ui->proxyButtonGroup, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this,
&NetworkSettings::saveProxySettings);
connect(_ui->hostLineEdit, &QLineEdit::editingFinished, this, &NetworkSettings::saveProxySettings);
connect(_ui->userLineEdit, &QLineEdit::editingFinished, this, &NetworkSettings::saveProxySettings);
connect(_ui->passwordLineEdit, &QLineEdit::editingFinished, this, &NetworkSettings::saveProxySettings);
connect(_ui->portSpinBox, &QAbstractSpinBox::editingFinished, this, &NetworkSettings::saveProxySettings);
connect(_ui->authRequiredcheckBox, &QAbstractButton::toggled, this, &NetworkSettings::saveProxySettings);
// Warn about empty proxy host
connect(_ui->hostLineEdit, &QLineEdit::textChanged, this, &NetworkSettings::checkEmptyProxyHost);
checkEmptyProxyHost();
checkAccountLocalhost();
} else {
_ui->noProxyRadioButton->setChecked(false);
}
loadProxySettings();
loadBWLimitSettings();
// proxy
connect(_ui->typeComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &NetworkSettings::saveProxySettings);
connect(_ui->proxyButtonGroup, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &NetworkSettings::saveProxySettings);
connect(_ui->hostLineEdit, &QLineEdit::editingFinished, this, &NetworkSettings::saveProxySettings);
connect(_ui->userLineEdit, &QLineEdit::editingFinished, this, &NetworkSettings::saveProxySettings);
connect(_ui->passwordLineEdit, &QLineEdit::editingFinished, this, &NetworkSettings::saveProxySettings);
connect(_ui->portSpinBox, &QAbstractSpinBox::editingFinished, this, &NetworkSettings::saveProxySettings);
connect(_ui->authRequiredcheckBox, &QAbstractButton::toggled, this, &NetworkSettings::saveProxySettings);
connect(_ui->uploadLimitRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings);
connect(_ui->noUploadLimitRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings);
connect(_ui->autoUploadLimitRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings);
@@ -78,11 +87,6 @@ NetworkSettings::NetworkSettings(QWidget *parent)
connect(_ui->autoDownloadLimitRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings);
connect(_ui->downloadSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &NetworkSettings::saveBWLimitSettings);
connect(_ui->uploadSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &NetworkSettings::saveBWLimitSettings);
// Warn about empty proxy host
connect(_ui->hostLineEdit, &QLineEdit::textChanged, this, &NetworkSettings::checkEmptyProxyHost);
checkEmptyProxyHost();
checkAccountLocalhost();
}
NetworkSettings::~NetworkSettings()

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) by Oleksandr Zolotov <alex@nextcloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "passwordinputdialog.h"
#include "ui_passwordinputdialog.h"
namespace OCC {
PasswordInputDialog::PasswordInputDialog(const QString &description, const QString &error, QWidget *parent)
: QDialog(parent)
, _ui(new Ui::PasswordInputDialog)
{
_ui->setupUi(this);
_ui->passwordLineEditLabel->setText(description);
_ui->passwordLineEditLabel->setVisible(!description.isEmpty());
_ui->labelErrorMessage->setText(error);
_ui->labelErrorMessage->setVisible(!error.isEmpty());
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
}
PasswordInputDialog::~PasswordInputDialog() = default;
QString PasswordInputDialog::password() const
{
return _ui->passwordLineEdit->text();
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) by Oleksandr Zolotov <alex@nextcloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#pragma once
#include <QDialog>
namespace OCC {
namespace Ui {
class PasswordInputDialog;
}
class PasswordInputDialog : public QDialog
{
Q_OBJECT
public:
explicit PasswordInputDialog(const QString &description, const QString &error, QWidget *parent = nullptr);
~PasswordInputDialog() override;
QString password() const;
private:
std::unique_ptr<Ui::PasswordInputDialog> _ui;
};
}

View File

@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OCC::PasswordInputDialog</class>
<widget class="QDialog" name="OCC::PasswordInputDialog">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>276</width>
<height>125</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Password for share required</string>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="passwordLineEditLabel">
<property name="text">
<string>Please enter a password for your share:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="passwordLineEdit">
<property name="inputMask">
<string notr="true"/>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
<property name="placeholderText">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelErrorMessage">
<property name="enabled">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(118, 118, 118)</string>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>OCC::PasswordInputDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>OCC::PasswordInputDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -1,4 +1,4 @@
/*
/*
* Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
*
* This program is free software; you can redistribute it and/or modify
@@ -16,7 +16,9 @@
#include "sharedialog.h"
#include "sharee.h"
#include "sharelinkwidget.h"
#include "internallinkwidget.h"
#include "shareusergroupwidget.h"
#include "passwordinputdialog.h"
#include "sharemanager.h"
@@ -135,28 +137,24 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
connect(job, &PropfindJob::finishedWithError, this, &ShareDialog::slotPropfindError);
job->start();
bool sharingPossible = true;
if (!accountState->account()->capabilities().sharePublicLink()) {
qCWarning(lcSharing) << "Link shares have been disabled";
sharingPossible = false;
} else if (!(maxSharingPermissions & SharePermissionShare)) {
qCWarning(lcSharing) << "The file cannot be shared because it does not have sharing permission.";
sharingPossible = false;
}
initShareManager();
_scrollAreaViewPort = new QWidget(_ui->scrollArea);
_scrollAreaLayout = new QVBoxLayout(_scrollAreaViewPort);
_scrollAreaLayout->setContentsMargins(0, 0, 0, 0);
_ui->scrollArea->setWidget(_scrollAreaViewPort);
if (sharingPossible) {
_manager = new ShareManager(accountState->account(), this);
connect(_manager, &ShareManager::sharesFetched, this, &ShareDialog::slotSharesFetched);
connect(_manager, &ShareManager::linkShareCreated, this, &ShareDialog::slotAddLinkShareWidget);
connect(_manager, &ShareManager::linkShareRequiresPassword, this, &ShareDialog::slotLinkShareRequiresPassword);
}
_internalLinkWidget = new InternalLinkWidget(localPath, this);
_ui->verticalLayout->addWidget(_internalLinkWidget);
_internalLinkWidget->setupUiOptions();
connect(this, &ShareDialog::styleChanged, _internalLinkWidget, &InternalLinkWidget::slotStyleChanged);
}
ShareLinkWidget *ShareDialog::addLinkShareWidget(const QSharedPointer<LinkShare> &linkShare)
{
_linkWidgetList.append(new ShareLinkWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, this));
const auto linkShareWidget = new ShareLinkWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, _ui->scrollArea);
_linkWidgetList.append(linkShareWidget);
const auto linkShareWidget = _linkWidgetList.at(_linkWidgetList.size() - 1);
linkShareWidget->setLinkShare(linkShare);
connect(linkShare.data(), &Share::serverError, linkShareWidget, &ShareLinkWidget::slotServerError);
@@ -171,13 +169,13 @@ ShareLinkWidget *ShareDialog::addLinkShareWidget(const QSharedPointer<LinkShare>
connect(linkShareWidget, &ShareLinkWidget::createLinkShare, this, &ShareDialog::slotCreateLinkShare);
connect(linkShareWidget, &ShareLinkWidget::deleteLinkShare, this, &ShareDialog::slotDeleteShare);
connect(linkShareWidget, &ShareLinkWidget::createPassword, this, &ShareDialog::slotCreatePasswordForLinkShare);
//connect(_linkWidgetList.at(index), &ShareLinkWidget::resizeRequested, this, &ShareDialog::slotAdjustScrollWidgetSize);
// Connect styleChanged events to our widget, so it can adapt (Dark-/Light-Mode switching)
connect(this, &ShareDialog::styleChanged, linkShareWidget, &ShareLinkWidget::slotStyleChanged);
_ui->verticalLayout->insertWidget(_linkWidgetList.size() + 1, linkShareWidget);
_scrollAreaLayout->addWidget(linkShareWidget);
linkShareWidget->setupUiOptions();
return linkShareWidget;
@@ -186,18 +184,17 @@ ShareLinkWidget *ShareDialog::addLinkShareWidget(const QSharedPointer<LinkShare>
void ShareDialog::initLinkShareWidget()
{
if(_linkWidgetList.size() == 0) {
_emptyShareLinkWidget = new ShareLinkWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, this);
_emptyShareLinkWidget = new ShareLinkWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, _ui->scrollArea);
_linkWidgetList.append(_emptyShareLinkWidget);
connect(_emptyShareLinkWidget, &ShareLinkWidget::resizeRequested, this, &ShareDialog::slotAdjustScrollWidgetSize);
connect(this, &ShareDialog::toggleShareLinkAnimation, _emptyShareLinkWidget, &ShareLinkWidget::slotToggleShareLinkAnimation);
connect(_emptyShareLinkWidget, &ShareLinkWidget::createLinkShare, this, &ShareDialog::slotCreateLinkShare);
connect(_emptyShareLinkWidget, &ShareLinkWidget::createPassword, this, &ShareDialog::slotCreatePasswordForLinkShare);
_ui->verticalLayout->insertWidget(_linkWidgetList.size()+1, _emptyShareLinkWidget);
_scrollAreaLayout->addWidget(_emptyShareLinkWidget);
_emptyShareLinkWidget->show();
} else if(_emptyShareLinkWidget) {
} else if (_emptyShareLinkWidget) {
_emptyShareLinkWidget->hide();
_ui->verticalLayout->removeWidget(_emptyShareLinkWidget);
_linkWidgetList.removeAll(_emptyShareLinkWidget);
@@ -210,6 +207,7 @@ void ShareDialog::slotAddLinkShareWidget(const QSharedPointer<LinkShare> &linkSh
emit toggleShareLinkAnimation(true);
const auto addedLinkShareWidget = addLinkShareWidget(linkShare);
initLinkShareWidget();
adjustScrollWidgetSize();
if (linkShare->isPasswordSet()) {
addedLinkShareWidget->focusPasswordLineEdit();
}
@@ -222,6 +220,7 @@ void ShareDialog::slotSharesFetched(const QList<QSharedPointer<Share>> &shares)
const QString versionString = _accountState->account()->serverVersion();
qCInfo(lcSharing) << versionString << "Fetched" << shares.count() << "shares";
foreach (auto share, shares) {
if (share->getShareType() != Share::TypeLink || share->getUidOwner() != share->account()->davUser()) {
continue;
@@ -232,17 +231,20 @@ void ShareDialog::slotSharesFetched(const QList<QSharedPointer<Share>> &shares)
}
initLinkShareWidget();
adjustScrollWidgetSize();
emit toggleShareLinkAnimation(false);
}
void ShareDialog::slotAdjustScrollWidgetSize()
void ShareDialog::adjustScrollWidgetSize()
{
int count = this->findChildren<ShareLinkWidget *>().count();
_ui->scrollArea->setVisible(count > 0);
if (count > 0 && count <= 3) {
_ui->scrollArea->setFixedHeight(_ui->scrollArea->widget()->sizeHint().height());
}
_ui->scrollArea->setFrameShape(count > 3 ? QFrame::StyledPanel : QFrame::NoFrame);
const auto count = _scrollAreaLayout->count();
const auto margin = 10;
const auto height = _linkWidgetList.empty() ? 0 : _linkWidgetList.last()->sizeHint().height() + margin;
const auto totalHeight = height * count;
_ui->scrollArea->setFixedWidth(_ui->verticalLayout->sizeHint().width());
_ui->scrollArea->setFixedHeight(totalHeight > 400 ? 400 : totalHeight);
_ui->scrollArea->setVisible(height > 0);
_ui->scrollArea->setFrameShape(count > 6 ? QFrame::StyledPanel : QFrame::NoFrame);
}
ShareDialog::~ShareDialog()
@@ -303,21 +305,19 @@ void ShareDialog::showSharingUi()
return;
}
// We only do user/group sharing from 8.2.0
bool userGroupSharing =
theme->userGroupSharing()
&& _accountState->account()->serverVersionInt() >= Account::makeServerVersion(8, 2, 0);
if (userGroupSharing) {
_userGroupWidget = new ShareUserGroupWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, _privateLinkUrl, this);
if (theme->userGroupSharing()) {
_userGroupWidget = new ShareUserGroupWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, _privateLinkUrl, _ui->scrollArea);
_userGroupWidget->getShares();
// Connect styleChanged events to our widget, so it can adapt (Dark-/Light-Mode switching)
connect(this, &ShareDialog::styleChanged, _userGroupWidget, &ShareUserGroupWidget::slotStyleChanged);
_ui->verticalLayout->insertWidget(1, _userGroupWidget);
_userGroupWidget->getShares();
_scrollAreaLayout->addLayout(_userGroupWidget->shareUserGroupLayout());
}
initShareManager();
if (theme->linkSharing()) {
if(_manager) {
_manager->fetchShares(_sharePath);
@@ -325,6 +325,25 @@ void ShareDialog::showSharingUi()
}
}
void ShareDialog::initShareManager()
{
bool sharingPossible = true;
if (!_accountState->account()->capabilities().sharePublicLink()) {
qCWarning(lcSharing) << "Link shares have been disabled";
sharingPossible = false;
} else if (!(_maxSharingPermissions & SharePermissionShare)) {
qCWarning(lcSharing) << "The file cannot be shared because it does not have sharing permission.";
sharingPossible = false;
}
if (!_manager && sharingPossible) {
_manager = new ShareManager(_accountState->account(), this);
connect(_manager, &ShareManager::sharesFetched, this, &ShareDialog::slotSharesFetched);
connect(_manager, &ShareManager::linkShareCreated, this, &ShareDialog::slotAddLinkShareWidget);
connect(_manager, &ShareManager::linkShareRequiresPassword, this, &ShareDialog::slotLinkShareRequiresPassword);
}
}
void ShareDialog::slotCreateLinkShare()
{
if(_manager) {
@@ -359,26 +378,21 @@ void ShareDialog::slotCreatePasswordForLinkShareProcessed()
}
}
void ShareDialog::slotLinkShareRequiresPassword()
void ShareDialog::slotLinkShareRequiresPassword(const QString &message)
{
bool ok = false;
QString password = QInputDialog::getText(this,
tr("Password for share required"),
tr("Please enter a password for your link share:"),
QLineEdit::Password,
QString(),
&ok);
const auto passwordInputDialog = new PasswordInputDialog(tr("Please enter a password for your link share:"), message, this);
passwordInputDialog->setWindowTitle(tr("Password for share required"));
passwordInputDialog->setAttribute(Qt::WA_DeleteOnClose);
passwordInputDialog->open();
if (!ok) {
// The dialog was canceled so no need to do anything
connect(passwordInputDialog, &QDialog::finished, this, [this, passwordInputDialog](const int result) {
if (result == QDialog::Accepted && _manager) {
// Try to create the link share again with the newly entered password
_manager->createLinkShare(_sharePath, QString(), passwordInputDialog->password());
return;
}
emit toggleShareLinkAnimation(false);
return;
}
if(_manager) {
// Try to create the link share again with the newly entered password
_manager->createLinkShare(_sharePath, QString(), password);
}
});
}
void ShareDialog::slotDeleteShare()
@@ -386,8 +400,10 @@ void ShareDialog::slotDeleteShare()
auto sharelinkWidget = dynamic_cast<ShareLinkWidget*>(sender());
sharelinkWidget->hide();
_ui->verticalLayout->removeWidget(sharelinkWidget);
_scrollAreaLayout->removeWidget(sharelinkWidget);
_linkWidgetList.removeAll(sharelinkWidget);
initLinkShareWidget();
adjustScrollWidgetSize();
}
void ShareDialog::slotThumbnailFetched(const int &statusCode, const QByteArray &reply)

View File

@@ -26,6 +26,7 @@
#include <QWidget>
class QProgressIndicator;
class QVBoxLayout;
namespace OCC {
@@ -34,6 +35,7 @@ namespace Ui {
}
class ShareLinkWidget;
class InternalLinkWidget;
class ShareUserGroupWidget;
class ShareManager;
class LinkShare;
@@ -66,8 +68,7 @@ private slots:
void slotCreateLinkShare();
void slotCreatePasswordForLinkShare(const QString &password);
void slotCreatePasswordForLinkShareProcessed();
void slotLinkShareRequiresPassword();
void slotAdjustScrollWidgetSize();
void slotLinkShareRequiresPassword(const QString &message);
signals:
void toggleShareLinkAnimation(bool start);
@@ -78,8 +79,10 @@ protected:
private:
void showSharingUi();
void initShareManager();
ShareLinkWidget *addLinkShareWidget(const QSharedPointer<LinkShare> &linkShare);
void initLinkShareWidget();
void adjustScrollWidgetSize();
Ui::ShareDialog *_ui;
@@ -94,8 +97,12 @@ private:
QList<ShareLinkWidget*> _linkWidgetList;
ShareLinkWidget* _emptyShareLinkWidget = nullptr;
InternalLinkWidget* _internalLinkWidget = nullptr;
ShareUserGroupWidget *_userGroupWidget = nullptr;
QProgressIndicator *_progressIndicator = nullptr;
QWidget *_scrollAreaViewPort = nullptr;
QVBoxLayout *_scrollAreaLayout = nullptr;
};
} // namespace OCC

View File

@@ -6,18 +6,36 @@
<rect>
<x>0</x>
<y>0</y>
<width>385</width>
<height>150</height>
<width>480</width>
<height>280</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>480</width>
<height>250</height>
</size>
</property>
<layout class="QVBoxLayout" name="shareDialogVerticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0" columnstretch="0,0">
@@ -33,6 +51,31 @@
<property name="spacing">
<number>10</number>
</property>
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="label_icon">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Icon</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_name">
<property name="sizePolicy">
@@ -89,49 +132,36 @@
</property>
</widget>
</item>
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="label_icon">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Icon</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustIgnored</enum>
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
@@ -141,11 +171,10 @@
<rect>
<x>0</x>
<y>0</y>
<width>69</width>
<height>69</height>
<width>460</width>
<height>200</height>
</rect>
</property>
<layout class="QVBoxLayout" name="scrollAreaVerticalLayout"/>
</widget>
</widget>
</item>

View File

@@ -411,22 +411,6 @@ void ShareLinkWidget::slotPasswordSetError(const int code, const QString &messag
emit createPasswordProcessed();
}
void ShareLinkWidget::startAnimation(const int start, const int end)
{
auto *animation = new QPropertyAnimation(this, "maximumHeight", this);
animation->setDuration(500);
animation->setStartValue(start);
animation->setEndValue(end);
connect(animation, &QAbstractAnimation::finished, this, &ShareLinkWidget::slotAnimationFinished);
if (end < start) // that is to remove the widget, not to show it
connect(animation, &QAbstractAnimation::finished, this, &ShareLinkWidget::slotDeleteAnimationFinished);
connect(animation, &QVariantAnimation::valueChanged, this, &ShareLinkWidget::resizeRequested);
animation->start();
}
void ShareLinkWidget::slotDeleteShareFetched()
{
slotToggleShareLinkAnimation(false);
@@ -450,12 +434,6 @@ void ShareLinkWidget::toggleNoteOptions(const bool enable)
}
}
void ShareLinkWidget::slotAnimationFinished()
{
emit resizeRequested();
deleteLater();
}
void ShareLinkWidget::slotCreateLabel()
{
const auto labelText = _shareLinkEdit->text();
@@ -474,14 +452,6 @@ void ShareLinkWidget::slotLabelSet()
displayShareLinkLabel();
}
void ShareLinkWidget::slotDeleteAnimationFinished()
{
// There is a painting bug where a small line of this widget isn't
// properly cleared. This explicit repaint() call makes sure any trace of
// the share widget is removed once it's destroyed. #4189
connect(this, SIGNAL(destroyed(QObject *)), parentWidget(), SLOT(repaint()));
}
void ShareLinkWidget::slotCreateShareRequiresPassword(const QString &message)
{
slotToggleShareLinkAnimation(message.isEmpty());

View File

@@ -90,9 +90,6 @@ private slots:
void slotContextMenuButtonClicked();
void slotLinkContextMenuActionTriggered(QAction *action);
void slotDeleteAnimationFinished();
void slotAnimationFinished();
void slotCreateLabel();
void slotLabelSet();
@@ -100,7 +97,6 @@ private slots:
signals:
void createLinkShare();
void deleteLinkShare();
void resizeRequested();
void visualDeletionDone();
void createPassword(const QString &password);
void createPasswordProcessed();
@@ -119,8 +115,6 @@ private:
/** Retrieve a share's name, accounting for _namesSupported */
QString shareName() const;
void startAnimation(const int start, const int end);
void customizeStyle();
void displayShareLinkLabel();

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>365</width>
<width>400</width>
<height>238</height>
</rect>
</property>
@@ -17,8 +17,29 @@
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>20</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="shareLinkIconLabel">
<property name="text">

View File

@@ -154,16 +154,18 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account,
_completionTimer.setInterval(600);
_ui->errorLabel->hide();
// TODO Progress Indicator where should it go?
// Setup the sharee search progress indicator
//_ui->shareeHorizontalLayout->addWidget(&_pi_sharee);
_parentScrollArea = parentWidget()->findChild<QScrollArea*>("scrollArea");
_shareUserGroup = new QVBoxLayout(_parentScrollArea);
_shareUserGroup->setContentsMargins(0, 0, 0, 0);
customizeStyle();
}
QVBoxLayout *ShareUserGroupWidget::shareUserGroupLayout()
{
return _shareUserGroup;
}
ShareUserGroupWidget::~ShareUserGroupWidget()
{
delete _ui;
@@ -247,17 +249,16 @@ void ShareUserGroupWidget::slotShareCreated(const QSharedPointer<Share> &share)
void ShareUserGroupWidget::slotSharesFetched(const QList<QSharedPointer<Share>> &shares)
{
QScrollArea *scrollArea = _parentScrollArea;
auto newViewPort = new QWidget(scrollArea);
auto layout = new QVBoxLayout(newViewPort);
layout->setContentsMargins(0, 0, 0, 0);
int x = 0;
int height = 0;
QList<QString> linkOwners({});
ShareUserLine *justCreatedShareThatNeedsPassword = nullptr;
while (QLayoutItem *shareUserLine = _shareUserGroup->takeAt(0)) {
delete shareUserLine->widget();
delete shareUserLine;
}
foreach (const auto &share, shares) {
// We don't handle link shares, only TypeUser or TypeGroup
if (share->getShareType() == Share::TypeLink) {
@@ -278,14 +279,12 @@ void ShareUserGroupWidget::slotSharesFetched(const QList<QSharedPointer<Share>>
Q_ASSERT(Share::isShareTypeUserGroupEmailRoomOrRemote(share->getShareType()));
auto userGroupShare = qSharedPointerDynamicCast<UserGroupShare>(share);
auto *s = new ShareUserLine(_account, userGroupShare, _maxSharingPermissions, _isFile, _parentScrollArea);
connect(s, &ShareUserLine::resizeRequested, this, &ShareUserGroupWidget::slotAdjustScrollWidgetSize);
connect(s, &ShareUserLine::visualDeletionDone, this, &ShareUserGroupWidget::getShares);
s->setBackgroundRole(layout->count() % 2 == 0 ? QPalette::Base : QPalette::AlternateBase);
s->setBackgroundRole(_shareUserGroup->count() % 2 == 0 ? QPalette::Base : QPalette::AlternateBase);
// Connect styleChanged events to our widget, so it can adapt (Dark-/Light-Mode switching)
connect(this, &ShareUserGroupWidget::styleChanged, s, &ShareUserLine::slotStyleChanged);
layout->addWidget(s);
_shareUserGroup->addWidget(s);
if (!_lastCreatedShareId.isEmpty() && share->getId() == _lastCreatedShareId) {
_lastCreatedShareId = QString();
@@ -295,27 +294,14 @@ void ShareUserGroupWidget::slotSharesFetched(const QList<QSharedPointer<Share>>
}
x++;
if (x <= 3) {
height = newViewPort->sizeHint().height();
}
}
foreach (const QString &owner, linkOwners) {
auto ownerLabel = new QLabel(QString(owner + " shared via link"));
layout->addWidget(ownerLabel);
_shareUserGroup->addWidget(ownerLabel);
ownerLabel->setVisible(true);
x++;
if (x <= 6) {
height = newViewPort->sizeHint().height();
}
}
scrollArea->setFrameShape(x > 6 ? QFrame::StyledPanel : QFrame::NoFrame);
scrollArea->setVisible(!shares.isEmpty());
scrollArea->setFixedHeight(height);
scrollArea->setWidget(newViewPort);
_disableCompleterActivated = false;
activateShareeLineEdit();
@@ -325,24 +311,6 @@ void ShareUserGroupWidget::slotSharesFetched(const QList<QSharedPointer<Share>>
}
}
void ShareUserGroupWidget::slotAdjustScrollWidgetSize()
{
QScrollArea *scrollArea = _parentScrollArea;
const auto shareUserLineChilds = scrollArea->findChildren<ShareUserLine *>();
// Ask the child widgets to calculate their size
for (const auto shareUserLineChild : shareUserLineChilds) {
shareUserLineChild->adjustSize();
}
const auto shareUserLineChildsCount = shareUserLineChilds.count();
scrollArea->setVisible(shareUserLineChildsCount > 0);
if (shareUserLineChildsCount > 0 && shareUserLineChildsCount <= 3) {
scrollArea->setFixedHeight(scrollArea->widget()->sizeHint().height());
}
scrollArea->setFrameShape(shareUserLineChildsCount > 3 ? QFrame::StyledPanel : QFrame::NoFrame);
}
void ShareUserGroupWidget::slotPrivateLinkShare()
{
auto menu = new QMenu(this);
@@ -380,22 +348,11 @@ void ShareUserGroupWidget::slotCompleterActivated(const QModelIndex &index)
return;
}
// TODO Progress Indicator where should it go?
// auto indicator = new QProgressIndicator(viewPort);
// indicator->startAnimation();
// if (layout->count() == 1) {
// // No shares yet! Remove the label, add some stretch.
// delete layout->itemAt(0)->widget();
// layout->addStretch(1);
// }
// layout->insertWidget(layout->count() - 1, indicator);
/*
* Don't send the reshare permissions for federated shares for servers <9.1
* https://github.com/owncloud/core/issues/22122#issuecomment-185637344
* https://github.com/owncloud/client/issues/4996
*/
_lastCreatedShareId = QString();
QString password;

View File

@@ -77,6 +77,8 @@ public:
const QString &privateLinkUrl,
QWidget *parent = nullptr);
~ShareUserGroupWidget() override;
QVBoxLayout *shareUserGroupLayout();
signals:
void togglePublicLinkShare(bool);
@@ -98,7 +100,6 @@ private slots:
void slotCompleterActivated(const QModelIndex &index);
void slotCompleterHighlighted(const QModelIndex &index);
void slotShareesReady();
void slotAdjustScrollWidgetSize();
void slotPrivateLinkShare();
void displayError(int code, const QString &message);
@@ -113,6 +114,7 @@ private:
Ui::ShareUserGroupWidget *_ui;
QScrollArea *_parentScrollArea;
QVBoxLayout *_shareUserGroup;
AccountPtr _account;
QString _sharePath;
QString _localPath;

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>350</width>
<height>70</height>
<height>106</height>
</rect>
</property>
<property name="sizePolicy">
@@ -17,6 +17,21 @@
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="mainOwnerLabel">
<property name="sizePolicy">

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>899</width>
<width>400</width>
<height>310</height>
</rect>
</property>
@@ -26,8 +26,26 @@
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>20</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>6</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
@@ -75,11 +93,11 @@
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<width>20</width>
<height>20</height>
</size>
</property>

View File

@@ -1225,9 +1225,11 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
auto makePinContextMenu = [&](bool makeAvailableLocally, bool freeSpace) {
listener->sendMessage(QLatin1String("MENU_ITEM:CURRENT_PIN:d:")
+ Utility::vfsCurrentAvailabilityText(*combined));
listener->sendMessage(QLatin1String("MENU_ITEM:MAKE_AVAILABLE_LOCALLY:")
+ (makeAvailableLocally ? QLatin1String(":") : QLatin1String("d:"))
+ Utility::vfsPinActionText());
if (!Theme::instance()->enforceVirtualFilesSyncFolder()) {
listener->sendMessage(QLatin1String("MENU_ITEM:MAKE_AVAILABLE_LOCALLY:")
+ (makeAvailableLocally ? QLatin1String(":") : QLatin1String("d:")) + Utility::vfsPinActionText());
}
listener->sendMessage(QLatin1String("MENU_ITEM:MAKE_ONLINE_ONLY:")
+ (freeSpace ? QLatin1String(":") : QLatin1String("d:"))
+ Utility::vfsFreeSpaceActionText());

View File

@@ -19,8 +19,10 @@
#include "common/utility.h"
#include "tray/svgimageprovider.h"
#include "tray/usermodel.h"
#include "wheelhandler.h"
#include "tray/unifiedsearchresultimageprovider.h"
#include "configfile.h"
#include "accessmanager.h"
#include <QCursor>
#include <QGuiApplication>
@@ -58,6 +60,8 @@ void Systray::setTrayEngine(QQmlApplicationEngine *trayEngine)
{
_trayEngine = trayEngine;
_trayEngine->setNetworkAccessManagerFactory(&_accessManagerFactory);
_trayEngine->addImportPath("qrc:/qml/theme");
_trayEngine->addImageProvider("avatars", new ImageProvider);
_trayEngine->addImageProvider(QLatin1String("svgimage-custom-color"), new OCC::Ui::SvgImageProvider);
@@ -91,6 +95,8 @@ Systray::Systray()
}
);
qmlRegisterType<WheelHandler>("com.nextcloud.desktopclient", 1, 0, "WheelHandler");
#ifndef Q_OS_MAC
auto contextMenu = new QMenu();
if (AccountManager::instance()->accounts().isEmpty()) {
@@ -502,4 +508,14 @@ QPoint Systray::calcTrayIconCenter() const
#endif
}
AccessManagerFactory::AccessManagerFactory()
: QQmlNetworkAccessManagerFactory()
{
}
QNetworkAccessManager* AccessManagerFactory::create(QObject *parent)
{
return new AccessManager(parent);
}
} // namespace OCC

View File

@@ -20,6 +20,8 @@
#include "accountmanager.h"
#include "tray/usermodel.h"
#include <QQmlNetworkAccessManagerFactory>
class QScreen;
class QQmlApplicationEngine;
class QQuickWindow;
@@ -28,6 +30,14 @@ class QQuickWindow;
namespace OCC {
class AccessManagerFactory : public QQmlNetworkAccessManagerFactory
{
public:
AccessManagerFactory();
QNetworkAccessManager* create(QObject *parent) override;
};
#ifdef Q_OS_OSX
bool canOsXSendUserNotification();
void sendOsXUserNotification(const QString &title, const QString &message);
@@ -105,6 +115,8 @@ private:
bool _isOpen = false;
bool _syncIsPaused = true;
QPointer<QQmlApplicationEngine> _trayEngine;
AccessManagerFactory _accessManagerFactory;
};
} // namespace OCC

Binary file not shown.

View File

@@ -1,109 +1,65 @@
import QtQuick 2.5
import QtQuick 2.15
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.15
import Style 1.0
Item {
id: root
readonly property bool labelVisible: label.visible
readonly property bool iconVisible: icon.visible
// label value
property string text: ""
// font value
property var font: label.font
property string toolTipText: ""
property bool bold: false
// icon value
property string imageSource: ""
property string imageSourceHover: ""
// Tooltip value
property string tooltipText: text
// text color
property color textColor: Style.ncTextColor
property color textColorHovered: Style.lightHover
// text background color
property color textBgColor: "transparent"
property color textBgColorHovered: Style.lightHover
// icon background color
property color iconBgColor: "transparent"
property color iconBgColorHovered: Style.lightHover
// text border color
property color textBorderColor: "transparent"
property alias hovered: mouseArea.containsMouse
property color textColor: Style.unifiedSearchResulTitleColor
property color textColorHovered: Style.unifiedSearchResulSublineColor
signal clicked()
Accessible.role: Accessible.Button
Accessible.name: text !== "" ? text : (tooltipText !== "" ? tooltipText : qsTr("Activity action button"))
Accessible.onPressAction: clicked()
// background with border around the Text
Rectangle {
visible: parent.labelVisible
Loader {
active: root.imageSource === ""
anchors.fill: parent
// padding
anchors.topMargin: 10
anchors.bottomMargin: 10
sourceComponent: CustomTextButton {
anchors.fill: parent
text: root.text
toolTipText: root.toolTipText
border.color: parent.textBorderColor
border.width: 1
textColor: root.textColor
textColorHovered: root.textColorHovered
color: parent.hovered ? parent.textBgColorHovered : parent.textBgColor
radius: 25
onClicked: root.clicked()
}
}
// background with border around the Image
Rectangle {
visible: parent.iconVisible
Loader {
active: root.imageSource !== ""
anchors.fill: parent
color: parent.hovered ? parent.iconBgColorHovered : parent.iconBgColor
}
sourceComponent: CustomButton {
anchors.fill: parent
anchors.topMargin: Style.roundedButtonBackgroundVerticalMargins
anchors.bottomMargin: Style.roundedButtonBackgroundVerticalMargins
// label
Text {
id: label
visible: parent.text !== ""
text: parent.text
font: parent.font
color: parent.hovered ? parent.textColorHovered : parent.textColor
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
text: root.text
toolTipText: root.toolTipText
// icon
Image {
id: icon
visible: parent.imageSource !== ""
anchors.centerIn: parent
source: parent.imageSource
sourceSize.width: visible ? 32 : 0
sourceSize.height: visible ? 32 : 0
}
textColor: root.textColor
textColorHovered: root.textColorHovered
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: parent.clicked()
hoverEnabled: true
}
bold: root.bold
ToolTip {
text: parent.tooltipText
delay: 1000
visible: text != "" && parent.hovered
imageSource: root.imageSource
imageSourceHover: root.imageSourceHover
bgColor: Style.ncBlue
onClicked: root.clicked()
}
}
}

View File

@@ -1,260 +1,84 @@
import QtQml 2.12
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.2
import QtQml 2.15
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Style 1.0
import com.nextcloud.desktopclient 1.0
MouseArea {
id: activityMouseArea
id: root
readonly property int maxActionButtons: 2
property Flickable flickable
property bool isFileActivityList: false
property bool isChatActivity: model.objectType === "chat" || model.objectType === "room"
signal fileActivityButtonClicked(string absolutePath)
enabled: (path !== "" || link !== "")
enabled: (model.path !== "" || model.link !== "" || model.isCurrentUserFileActivity === true)
hoverEnabled: true
height: childrenRect.height
ToolTip.visible: containsMouse && !activityContent.childHovered && model.displayLocation !== ""
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("In %1").arg(model.displayLocation)
Accessible.role: Accessible.ListItem
Accessible.name: (model.path !== "" && model.displayPath !== "") ? qsTr("Open %1 locally").arg(model.displayPath) : model.message
Accessible.onPressAction: root.clicked()
Rectangle {
id: activityHover
anchors.fill: parent
color: (parent.containsMouse ? Style.lightHover : "transparent")
}
RowLayout {
id: activityItem
readonly property variant links: model.links
readonly property int itemIndex: model.index
width: activityMouseArea.width
height: Style.trayWindowHeaderHeight
ColumnLayout {
anchors.left: root.left
anchors.right: root.right
anchors.leftMargin: 15
anchors.rightMargin: 10
spacing: 0
Accessible.role: Accessible.ListItem
Accessible.name: path !== "" ? qsTr("Open %1 locally").arg(displayPath)
: message
Accessible.onPressAction: activityMouseArea.clicked()
Image {
id: activityIcon
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
Layout.leftMargin: 20
Layout.preferredWidth: shareButton.icon.width
Layout.preferredHeight: shareButton.icon.height
verticalAlignment: Qt.AlignCenter
cache: true
source: icon
sourceSize.height: 64
sourceSize.width: 64
}
Column {
id: activityTextColumn
Layout.leftMargin: 14
Layout.topMargin: 4
Layout.bottomMargin: 4
Layout.fillWidth: true
spacing: 4
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Text {
id: activityTextTitle
text: (type === "Activity" || type === "Notification") ? subject : message
width: parent.width
elide: Text.ElideRight
font.pixelSize: Style.topLinePixelSize
color: activityTextTitleColor
}
Text {
id: activityTextInfo
text: (type === "Sync") ? displayPath
: (type === "File") ? subject
: (type === "Notification") ? message
: ""
height: (text === "") ? 0 : activityTextTitle.height
width: parent.width
elide: Text.ElideRight
font.pixelSize: Style.subLinePixelSize
}
Text {
id: activityTextDateTime
text: dateTime
height: (text === "") ? 0 : activityTextTitle.height
width: parent.width
elide: Text.ElideRight
font.pixelSize: Style.subLinePixelSize
color: "#808080"
}
}
RowLayout {
id: activityActionsLayout
spacing: 0
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.minimumWidth: 28
Layout.fillWidth: true
function actionButtonIcon(actionIndex) {
const verb = String(model.links[actionIndex].verb);
if (verb === "WEB" && (model.objectType === "chat" || model.objectType === "call")) {
return "qrc:///client/theme/reply.svg";
} else if (verb === "DELETE") {
return "qrc:///client/theme/close.svg";
}
return "qrc:///client/theme/confirm.svg";
}
Repeater {
model: activityItem.links.length > maxActionButtons ? 1 : activityItem.links.length
ActivityActionButton {
id: activityActionButton
readonly property int actionIndex: model.index
readonly property bool primary: model.index === 0 && String(activityItem.links[actionIndex].verb) !== "DELETE"
Layout.fillHeight: true
text: !primary ? "" : activityItem.links[actionIndex].label
imageSource: !primary ? activityActionsLayout.actionButtonIcon(actionIndex) : ""
textColor: primary ? Style.ncBlue : "black"
textColorHovered: Style.lightHover
textBorderColor: Style.ncBlue
textBgColor: "transparent"
textBgColorHovered: Style.ncBlue
tooltipText: activityItem.links[actionIndex].label
Layout.minimumWidth: primary ? 80 : -1
Layout.minimumHeight: parent.height
Layout.preferredWidth: primary ? -1 : parent.height
onClicked: activityModel.triggerAction(activityItem.itemIndex, actionIndex)
}
}
Button {
id: shareButton
Layout.preferredWidth: parent.height
Layout.fillHeight: true
Layout.alignment: Qt.AlignRight
flat: true
hoverEnabled: true
visible: displayActions && (path !== "")
display: AbstractButton.IconOnly
icon.source: "qrc:///client/theme/share.svg"
icon.color: "transparent"
background: Rectangle {
color: parent.hovered ? Style.lightHover : "transparent"
}
ToolTip.visible: hovered
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Open share dialog")
onClicked: Systray.openShareDialog(displayPath, absolutePath)
Accessible.role: Accessible.Button
Accessible.name: qsTr("Share %1").arg(displayPath)
Accessible.onPressAction: shareButton.clicked()
}
Button {
id: moreActionsButton
Layout.preferredWidth: parent.height
Layout.preferredHeight: parent.height
Layout.alignment: Qt.AlignRight
flat: true
hoverEnabled: true
visible: displayActions && ((path !== "") || (activityItem.links.length > maxActionButtons))
display: AbstractButton.IconOnly
icon.source: "qrc:///client/theme/more.svg"
icon.color: "transparent"
background: Rectangle {
color: parent.hovered ? Style.lightHover : "transparent"
}
ToolTip.visible: hovered
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Show more actions")
Accessible.role: Accessible.Button
Accessible.name: qsTr("Show more actions")
Accessible.onPressAction: moreActionsButton.clicked()
onClicked: moreActionsButtonContextMenu.popup();
Connections {
target: flickable
function onMovementStarted() {
moreActionsButtonContextMenu.close();
}
}
Container {
id: moreActionsButtonContextMenuContainer
visible: moreActionsButtonContextMenu.opened
width: moreActionsButtonContextMenu.width
height: moreActionsButtonContextMenu.height
anchors.right: moreActionsButton.right
anchors.top: moreActionsButton.top
Menu {
id: moreActionsButtonContextMenu
anchors.centerIn: parent
// transform model to contain indexed actions with primary action filtered out
function actionListToContextMenuList(actionList) {
// early out with non-altered data
if (activityItem.links.length <= maxActionButtons) {
return actionList;
}
// add index to every action and filter 'primary' action out
var reducedActionList = actionList.reduce(function(reduced, action, index) {
if (!action.primary) {
var actionWithIndex = { actionIndex: index, label: action.label };
reduced.push(actionWithIndex);
}
return reduced;
}, []);
return reducedActionList;
}
ActivityItemContent {
id: activityContent
MenuItem {
text: qsTr("View activity")
onClicked: fileActivityButtonClicked(absolutePath)
}
Repeater {
id: moreActionsButtonContextMenuRepeater
model: moreActionsButtonContextMenu.actionListToContextMenuList(activityItem.links)
delegate: MenuItem {
id: moreActionsButtonContextMenuEntry
text: model.modelData.label
onTriggered: activityModel.triggerAction(activityItem.itemIndex, model.modelData.actionIndex)
}
}
}
}
}
Layout.fillWidth: true
showDismissButton: model.links.length > 0 && model.linksForActionButtons.length === 0
activityData: model
Layout.preferredHeight: Style.trayWindowHeaderHeight
onShareButtonClicked: Systray.openShareDialog(model.displayPath, model.absolutePath)
onDismissButtonClicked: activityModel.slotTriggerDismiss(model.index)
}
ActivityItemActions {
id: activityActions
visible: !root.isFileActivityList && model.linksForActionButtons.length > 0
Layout.preferredHeight: Style.trayWindowHeaderHeight * 0.85
Layout.fillWidth: true
Layout.leftMargin: 40
Layout.bottomMargin: model.links.length > 1 ? 5 : 0
displayActions: model.displayActions
objectType: model.objectType
linksForActionButtons: model.linksForActionButtons
linksContextMenu: model.linksContextMenu
moreActionsButtonColor: activityHover.color
maxActionButtons: activityModel.maxActionButtons
flickable: root.flickable
onTriggerAction: activityModel.slotTriggerAction(model.index, actionIndex)
}
}
}

View File

@@ -0,0 +1,103 @@
import QtQml 2.15
import QtQuick 2.15
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import Style 1.0
RowLayout {
id: root
spacing: 20
property string objectType: ""
property variant linksForActionButtons: []
property variant linksContextMenu: []
property bool displayActions: false
property color moreActionsButtonColor: "transparent"
property int maxActionButtons: 0
property Flickable flickable
signal triggerAction(int actionIndex)
Repeater {
id: actionsRepeater
// a max of maxActionButtons will get dispayed as separate buttons
model: root.linksForActionButtons
ActivityActionButton {
id: activityActionButton
readonly property bool primary: model.index === 0 && model.modelData.verb !== "DELETE"
Layout.minimumWidth: primary ? Style.activityItemActionPrimaryButtonMinWidth : Style.activityItemActionSecondaryButtonMinWidth
Layout.preferredHeight: primary ? parent.height : parent.height * 0.3
Layout.preferredWidth: primary ? -1 : parent.height
text: model.modelData.label
toolTipText: model.modelData.label
imageSource: model.modelData.imageSource
imageSourceHover: model.modelData.imageSourceHovered
textColor: imageSource !== "" ? Style.ncBlue : Style.unifiedSearchResulSublineColor
textColorHovered: imageSource !== "" ? Style.lightHover : Style.unifiedSearchResulTitleColor
bold: primary
onClicked: root.triggerAction(model.index)
}
}
Loader {
// actions that do not fit maxActionButtons limit, must be put into a context menu
id: moreActionsButtonContainer
Layout.preferredWidth: parent.height
Layout.topMargin: Style.roundedButtonBackgroundVerticalMargins
Layout.bottomMargin: Style.roundedButtonBackgroundVerticalMargins
Layout.fillHeight: true
active: root.displayActions && (root.linksContextMenu.length > 0)
sourceComponent: Button {
id: moreActionsButton
icon.source: "qrc:///client/theme/more.svg"
background: Rectangle {
color: parent.hovered ? "white" : root.moreActionsButtonColor
radius: width / 2
}
ToolTip.visible: hovered
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Show more actions")
Accessible.name: qsTr("Show more actions")
onClicked: moreActionsButtonContextMenu.popup(moreActionsButton.x, moreActionsButton.y);
Connections {
target: root.flickable
function onMovementStarted() {
moreActionsButtonContextMenu.close();
}
}
ActivityItemContextMenu {
id: moreActionsButtonContextMenu
maxActionButtons: root.maxActionButtons
linksContextMenu: root.linksContextMenu
onMenuEntryTriggered: function(entryIndex) {
root.triggerAction(entryIndex)
}
}
}
}
}

View File

@@ -0,0 +1,127 @@
import QtQml 2.15
import QtQuick 2.15
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import Style 1.0
import com.nextcloud.desktopclient 1.0
RowLayout {
id: root
property variant activityData: {{}}
property color activityTextTitleColor: Style.ncTextColor
property bool showDismissButton: false
property bool childHovered: shareButton.hovered || dismissActionButton.hovered
signal dismissButtonClicked()
signal shareButtonClicked()
spacing: 10
Image {
id: activityIcon
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
Layout.preferredWidth: 32
Layout.preferredHeight: 32
verticalAlignment: Qt.AlignCenter
source: icon
sourceSize.height: 64
sourceSize.width: 64
}
Column {
id: activityTextColumn
Layout.topMargin: 4
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
spacing: 4
Label {
id: activityTextTitle
text: (root.activityData.type === "Activity" || root.activityData.type === "Notification") ? root.activityData.subject : root.activityData.message
width: parent.width
elide: Text.ElideRight
font.pixelSize: Style.topLinePixelSize
color: root.activityData.activityTextTitleColor
}
Label {
id: activityTextInfo
text: (root.activityData.type === "Sync") ? root.activityData.displayPath
: (root.activityData.type === "File") ? root.activityData.subject
: (root.activityData.type === "Notification") ? root.activityData.message
: ""
height: (text === "") ? 0 : activityTextTitle.height
width: parent.width
elide: Text.ElideRight
font.pixelSize: Style.subLinePixelSize
}
Label {
id: activityTextDateTime
text: root.activityData.dateTime
height: (text === "") ? 0 : activityTextTitle.height
width: parent.width
elide: Text.ElideRight
font.pixelSize: Style.subLinePixelSize
color: "#808080"
}
}
Button {
id: dismissActionButton
Layout.preferredWidth: parent.height * 0.40
Layout.preferredHeight: parent.height * 0.40
Layout.alignment: Qt.AlignCenter
Layout.margins: Style.roundButtonBackgroundVerticalMargins
ToolTip.visible: hovered
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Dismiss")
Accessible.name: qsTr("Dismiss")
visible: root.showDismissButton && !shareButton.visible
background: Rectangle {
color: "transparent"
}
contentItem: Image {
anchors.fill: parent
source: parent.hovered ? "image://svgimage-custom-color/clear.svg/black" : "image://svgimage-custom-color/clear.svg/grey"
sourceSize.width: 24
sourceSize.height: 24
}
onClicked: root.dismissButtonClicked()
}
CustomButton {
id: shareButton
Layout.preferredWidth: parent.height * 0.70
Layout.preferredHeight: parent.height * 0.70
visible: root.activityData.isShareable
imageSource: "image://svgimage-custom-color/share.svg" + "/" + Style.ncBlue
imageSourceHover: "image://svgimage-custom-color/share.svg" + "/" + Style.ncTextColor
toolTipText: qsTr("Open share dialog")
bgColor: Style.ncBlue
onClicked: root.shareButtonClicked()
}
}

View File

@@ -0,0 +1,25 @@
import QtQml 2.15
import QtQuick 2.15
import QtQuick.Controls 2.3
AutoSizingMenu {
id: moreActionsButtonContextMenu
property int maxActionButtons: 0
property var linksContextMenu: []
signal menuEntryTriggered(int index)
Repeater {
id: moreActionsButtonContextMenuRepeater
model: moreActionsButtonContextMenu.linksContextMenu
delegate: MenuItem {
id: moreActionsButtonContextMenuEntry
text: model.modelData.label
onTriggered: menuEntryTriggered(model.modelData.actionIndex)
}
}
}

View File

@@ -1,20 +1,26 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import Style 1.0
import com.nextcloud.desktopclient 1.0 as NC
ScrollView {
id: controlRoot
property alias model: activityList.model
property bool isFileActivityList: false
signal showFileActivity(string displayPath, string absolutePath)
signal activityItemClicked(int index)
contentWidth: availableWidth
padding: 1
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
data: NC.WheelHandler {
target: controlRoot.contentItem
}
ListView {
id: activityList
@@ -25,12 +31,19 @@ ScrollView {
clip: true
spacing: 10
delegate: ActivityItem {
isFileActivityList: controlRoot.isFileActivityList
width: activityList.contentWidth
height: Style.trayWindowHeaderHeight
flickable: activityList
onClicked: activityItemClicked(model.index)
onFileActivityButtonClicked: showFileActivity(displayPath, absolutePath)
onClicked: {
if (model.isCurrentUserFileActivity) {
showFileActivity(model.displayPath, model.absolutePath)
} else {
activityItemClicked(model.index)
}
}
}
}
}

View File

@@ -0,0 +1,61 @@
import QtQuick 2.15
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
Button {
id: root
property string imageSource: ""
property string imageSourceHover: ""
property string toolTipText: ""
property color textColor
property color textColorHovered
property color bgColor: "transparent"
property bool bold: false
background: Rectangle {
color: root.bgColor
opacity: parent.hovered ? 1.0 : 0.3
radius: width / 2
}
leftPadding: root.text === "" ? 5 : 10
rightPadding: root.text === "" ? 5 : 10
contentItem: RowLayout {
Image {
id: icon
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
source: root.hovered ? root.imageSourceHover : root.imageSource
}
Label {
Layout.maximumWidth: icon.width > 0 ? parent.width - icon.width - parent.spacing : parent.width
Layout.fillWidth: icon.status !== Image.Ready
text: root.text
font.bold: root.bold
visible: root.text !== ""
color: root.hovered ? root.textColorHovered : root.textColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
}
ToolTip {
text: root.toolTipText
delay: Qt.styleHints.mousePressAndHoldInterval
visible: root.toolTipText !== "" && root.hovered
}
}

View File

@@ -0,0 +1,49 @@
import QtQuick 2.15
import QtQuick.Controls 2.3
import Style 1.0
Label {
id: root
property string toolTipText: ""
property Action action: null
property alias acceptedButtons: mouseArea.acceptedButtons
property bool hovered: mouseArea.containsMouse
height: implicitHeight
property color textColor: Style.unifiedSearchResulTitleColor
property color textColorHovered: Style.unifiedSearchResulSublineColor
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.onPressAction: root.clicked(null)
text: action ? action.text : ""
enabled: !action || action.enabled
onClicked: if (action) action.trigger()
font.underline: true
color: root.hovered ? root.textColorHovered : root.textColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
signal pressed(QtObject mouse)
signal clicked(QtObject mouse)
ToolTip {
text: root.toolTipText
delay: Qt.styleHints.mousePressAndHoldInterval
visible: root.toolTipText !== "" && root.hovered
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: root.clicked(mouse)
onPressed: root.pressed(mouse)
}
}

View File

@@ -15,6 +15,7 @@ Window {
height: 500
ActivityList {
isFileActivityList: true
anchors.fill: parent
model: dialog.model
}

View File

@@ -25,7 +25,7 @@ Button {
Layout.preferredHeight: Style.trayWindowHeaderHeight
background: Rectangle {
color: root.hovered ? "white" : "transparent"
color: root.hovered || root.visualFocus ? "white" : "transparent"
opacity: 0.2
}
}

View File

@@ -16,6 +16,7 @@ MenuItem {
property variant dialog;
property variant comp;
activeFocusOnTab: false
signal showUserStatusSelectorDialog(int id)
@@ -35,29 +36,19 @@ MenuItem {
Accessible.role: Accessible.Button
Accessible.name: qsTr("Switch to account") + " " + name
MouseArea {
anchors.fill: parent
hoverEnabled: true
onContainsMouseChanged: {
accountStatusIndicatorBackground.color = (containsMouse ? "#f6f6f6" : "white")
}
onClicked: {
if (!isCurrentUser) {
UserModel.switchCurrentUser(id)
} else {
accountMenu.close()
}
}
onClicked: if (!isCurrentUser) {
UserModel.switchCurrentUser(id)
} else {
accountMenu.close()
}
background: Item {
height: parent.height
width: userLine.menu ? userLine.menu.width : 0
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered ? Style.lightHover : "transparent"
color: parent.parent.hovered || parent.parent.visualFocus ? Style.lightHover : "transparent"
}
}
@@ -81,7 +72,7 @@ MenuItem {
height: width
anchors.bottom: accountAvatar.bottom
anchors.right: accountAvatar.right
color: "white"
color: accountButton.hovered || accountButton.visualFocus ? "#f6f6f6" : "white"
radius: width*0.5
}
Image {
@@ -120,14 +111,12 @@ MenuItem {
width: parent.width
Label {
id: emoji
height: Style.topLinePixelSize
visible: model.statusEmoji !== ""
text: statusEmoji
topPadding: -Style.accountLabelsSpacing
}
Label {
id: message
height: Style.topLinePixelSize
width: parent.width - parent.spacing - emoji.width
visible: model.statusMessage !== ""
text: statusMessage
@@ -163,29 +152,23 @@ MenuItem {
Accessible.name: qsTr("Account actions")
Accessible.onPressAction: userMoreButtonMouseArea.clicked()
MouseArea {
id: userMoreButtonMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (userMoreButtonMenu.visible) {
userMoreButtonMenu.close()
} else {
userMoreButtonMenu.popup()
}
onClicked: {
if (userMoreButtonMenu.visible) {
userMoreButtonMenu.close()
} else {
userMoreButtonMenu.popup()
}
}
background:
Rectangle {
color: userMoreButtonMouseArea.containsMouse ? "grey" : "transparent"
color: userMoreButton.hovered || userMoreButton.visualFocus ? "grey" : "transparent"
opacity: 0.2
height: userMoreButton.height - 2
y: userMoreButton.y + 1
}
Menu {
AutoSizingMenu {
id: userMoreButtonMenu
width: 120
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape
background: Rectangle {
@@ -196,7 +179,6 @@ MenuItem {
MenuItem {
visible: model.isConnected && model.serverHasUserStatus
height: visible ? implicitHeight : 0
text: qsTr("Set status")
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true

View File

@@ -142,187 +142,196 @@ Window {
Accessible.name: qsTr("Current account")
Accessible.onPressAction: currentAccountButton.clicked()
MouseArea {
id: accountBtnMouseArea
// We call open() instead of popup() because we want to position it
// exactly below the dropdown button, not the mouse
onClicked: {
syncPauseButton.text = Systray.syncIsPaused() ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
if (accountMenu.visible) {
accountMenu.close()
} else {
accountMenu.open()
}
}
anchors.fill: parent
hoverEnabled: Style.hoverEffectsEnabled
Loader {
id: userStatusSelectorDialogLoader
}
// We call open() instead of popup() because we want to position it
// exactly below the dropdown button, not the mouse
onClicked: {
syncPauseButton.text = Systray.syncIsPaused() ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
if (accountMenu.visible) {
accountMenu.close()
} else {
accountMenu.open()
Menu {
id: accountMenu
// x coordinate grows towards the right
// y coordinate grows towards the bottom
x: (currentAccountButton.x + 2)
y: (currentAccountButton.y + Style.trayWindowHeaderHeight + 2)
width: (Style.currentAccountButtonWidth - 2)
height: Math.min(implicitHeight, maxMenuHeight)
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape
background: Rectangle {
border.color: Style.menuBorder
radius: Style.currentAccountButtonRadius
}
contentItem: ScrollView {
id: accMenuScrollView
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
data: WheelHandler {
target: accMenuScrollView.contentItem
}
ListView {
implicitHeight: contentHeight
model: accountMenu.contentModel
interactive: true
clip: true
currentIndex: accountMenu.currentIndex
}
}
Loader {
id: userStatusSelectorDialogLoader
onClosed: {
// HACK: reload account Instantiator immediately by restting it - could be done better I guess
// see also onVisibleChanged above
userLineInstantiator.active = false;
userLineInstantiator.active = true;
}
Menu {
id: accountMenu
// x coordinate grows towards the right
// y coordinate grows towards the bottom
x: (currentAccountButton.x + 2)
y: (currentAccountButton.y + Style.trayWindowHeaderHeight + 2)
width: (Style.currentAccountButtonWidth - 2)
height: Math.min(implicitHeight, maxMenuHeight)
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape
background: Rectangle {
border.color: Style.menuBorder
radius: Style.currentAccountButtonRadius
}
onClosed: {
// HACK: reload account Instantiator immediately by restting it - could be done better I guess
// see also onVisibleChanged above
userLineInstantiator.active = false;
userLineInstantiator.active = true;
}
Instantiator {
id: userLineInstantiator
model: UserModel
delegate: UserLine {
onShowUserStatusSelectorDialog: {
userStatusSelectorDialogLoader.source = "qrc:/qml/src/gui/UserStatusSelectorDialog.qml"
userStatusSelectorDialogLoader.item.title = qsTr("Set user status")
userStatusSelectorDialogLoader.item.model.load(index)
userStatusSelectorDialogLoader.item.show()
}
Instantiator {
id: userLineInstantiator
model: UserModel
delegate: UserLine {
onShowUserStatusSelectorDialog: {
userStatusSelectorDialogLoader.source = "qrc:/qml/src/gui/UserStatusSelectorDialog.qml"
userStatusSelectorDialogLoader.item.title = qsTr("Set user status")
userStatusSelectorDialogLoader.item.model.load(index)
userStatusSelectorDialogLoader.item.show()
}
onObjectAdded: accountMenu.insertItem(index, object)
onObjectRemoved: accountMenu.removeItem(object)
}
onObjectAdded: accountMenu.insertItem(index, object)
onObjectRemoved: accountMenu.removeItem(object)
}
MenuItem {
id: addAccountButton
height: Style.addAccountButtonHeight
hoverEnabled: true
MenuItem {
id: addAccountButton
height: Style.addAccountButtonHeight
hoverEnabled: true
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered ? Style.lightHover : "transparent"
}
}
RowLayout {
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
spacing: 0
Image {
Layout.leftMargin: 12
verticalAlignment: Qt.AlignCenter
source: "qrc:///client/theme/black/add.svg"
sourceSize.width: Style.headerButtonIconSize
sourceSize.height: Style.headerButtonIconSize
}
Label {
Layout.leftMargin: 14
text: qsTr("Add account")
color: "black"
font.pixelSize: Style.topLinePixelSize
}
// Filler on the right
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
onClicked: UserModel.addAccount()
Accessible.role: Accessible.MenuItem
Accessible.name: qsTr("Add new account")
Accessible.onPressAction: addAccountButton.clicked()
}
MenuSeparator {
contentItem: Rectangle {
implicitHeight: 1
color: Style.menuBorder
anchors.margins: 1
color: parent.parent.hovered || parent.parent.visualFocus ? Style.lightHover : "transparent"
}
}
MenuItem {
id: syncPauseButton
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.pauseResumeSync()
RowLayout {
anchors.fill: parent
spacing: 0
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered ? Style.lightHover : "transparent"
}
Image {
Layout.leftMargin: 12
verticalAlignment: Qt.AlignCenter
source: "qrc:///client/theme/black/add.svg"
sourceSize.width: Style.headerButtonIconSize
sourceSize.height: Style.headerButtonIconSize
}
Label {
Layout.leftMargin: 14
text: qsTr("Add account")
color: "black"
font.pixelSize: Style.topLinePixelSize
}
// Filler on the right
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
onClicked: UserModel.addAccount()
Accessible.role: Accessible.MenuItem
Accessible.name: Systray.syncIsPaused() ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
Accessible.onPressAction: syncPauseButton.clicked()
Accessible.role: Accessible.MenuItem
Accessible.name: qsTr("Add new account")
Accessible.onPressAction: addAccountButton.clicked()
}
MenuSeparator {
contentItem: Rectangle {
implicitHeight: 1
color: Style.menuBorder
}
}
MenuItem {
id: syncPauseButton
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.pauseResumeSync()
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered || parent.parent.visualFocus ? Style.lightHover : "transparent"
}
}
MenuItem {
id: settingsButton
text: qsTr("Settings")
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.openSettings()
Accessible.role: Accessible.MenuItem
Accessible.name: Systray.syncIsPaused() ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
Accessible.onPressAction: syncPauseButton.clicked()
}
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered ? Style.lightHover : "transparent"
}
MenuItem {
id: settingsButton
text: qsTr("Settings")
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.openSettings()
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered || parent.parent.visualFocus ? Style.lightHover : "transparent"
}
Accessible.role: Accessible.MenuItem
Accessible.name: text
Accessible.onPressAction: settingsButton.clicked()
}
MenuItem {
id: exitButton
text: qsTr("Exit");
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.shutdown()
Accessible.role: Accessible.MenuItem
Accessible.name: text
Accessible.onPressAction: settingsButton.clicked()
}
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered ? Style.lightHover : "transparent"
}
MenuItem {
id: exitButton
text: qsTr("Exit");
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.shutdown()
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered || parent.parent.visualFocus ? Style.lightHover : "transparent"
}
Accessible.role: Accessible.MenuItem
Accessible.name: text
Accessible.onPressAction: exitButton.clicked()
}
Accessible.role: Accessible.MenuItem
Accessible.name: text
Accessible.onPressAction: exitButton.clicked()
}
}
background: Rectangle {
color: accountBtnMouseArea.containsMouse ? "white" : "transparent"
color: parent.hovered || parent.visualFocus ? "white" : "transparent"
opacity: 0.2
}
@@ -658,51 +667,61 @@ Window {
iconColor: "#afafaf"
}
ListView {
id: unifiedSearchResultsListView
ScrollView {
id: controlRoot
padding: 1
contentWidth: availableWidth
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
data: WheelHandler {
target: controlRoot.contentItem
}
visible: unifiedSearchResultsListView.count > 0
anchors.top: trayWindowUnifiedSearchInputContainer.bottom
anchors.left: trayWindowBackground.left
anchors.right: trayWindowBackground.right
anchors.bottom: trayWindowBackground.bottom
spacing: 4
visible: count > 0
clip: true
ScrollBar.vertical: ScrollBar {
id: unifiedSearchResultsListViewScrollbar
}
keyNavigationEnabled: true
ListView {
id: unifiedSearchResultsListView
spacing: 4
clip: true
reuseItems: true
keyNavigationEnabled: true
Accessible.role: Accessible.List
Accessible.name: qsTr("Unified search results list")
reuseItems: true
model: UserModel.currentUser.unifiedSearchResultsListModel
Accessible.role: Accessible.List
Accessible.name: qsTr("Unified search results list")
delegate: UnifiedSearchResultListItem {
width: unifiedSearchResultsListView.width
height: trayWindowBackground.Style.unifiedSearchItemHeight
isSearchInProgress: unifiedSearchResultsListView.model.isSearchInProgress
textLeftMargin: trayWindowBackground.Style.unifiedSearchResultTextLeftMargin
textRightMargin: trayWindowBackground.Style.unifiedSearchResultTextRightMargin
iconWidth: trayWindowBackground.Style.unifiedSearchResulIconWidth
iconLeftMargin: trayWindowBackground.Style.unifiedSearchResulIconLeftMargin
titleFontSize: trayWindowBackground.Style.unifiedSearchResulTitleFontSize
sublineFontSize: trayWindowBackground.Style.unifiedSearchResulSublineFontSize
titleColor: trayWindowBackground.Style.unifiedSearchResulTitleColor
sublineColor: trayWindowBackground.Style.unifiedSearchResulSublineColor
currentFetchMoreInProgressProviderId: unifiedSearchResultsListView.model.currentFetchMoreInProgressProviderId
fetchMoreTriggerClicked: unifiedSearchResultsListView.model.fetchMoreTriggerClicked
resultClicked: unifiedSearchResultsListView.model.resultClicked
ListView.onPooled: isPooled = true
ListView.onReused: isPooled = false
}
model: UserModel.currentUser.unifiedSearchResultsListModel
section.property: "providerName"
section.criteria: ViewSection.FullString
section.delegate: UnifiedSearchResultSectionItem {
width: unifiedSearchResultsListView.width
delegate: UnifiedSearchResultListItem {
width: unifiedSearchResultsListView.width
height: trayWindowBackground.Style.unifiedSearchItemHeight
isSearchInProgress: unifiedSearchResultsListView.model.isSearchInProgress
textLeftMargin: trayWindowBackground.Style.unifiedSearchResultTextLeftMargin
textRightMargin: trayWindowBackground.Style.unifiedSearchResultTextRightMargin
iconWidth: trayWindowBackground.Style.unifiedSearchResulIconWidth
iconLeftMargin: trayWindowBackground.Style.unifiedSearchResulIconLeftMargin
titleFontSize: trayWindowBackground.Style.unifiedSearchResulTitleFontSize
sublineFontSize: trayWindowBackground.Style.unifiedSearchResulSublineFontSize
titleColor: trayWindowBackground.Style.unifiedSearchResulTitleColor
sublineColor: trayWindowBackground.Style.unifiedSearchResulSublineColor
currentFetchMoreInProgressProviderId: unifiedSearchResultsListView.model.currentFetchMoreInProgressProviderId
fetchMoreTriggerClicked: unifiedSearchResultsListView.model.fetchMoreTriggerClicked
resultClicked: unifiedSearchResultsListView.model.resultClicked
ListView.onPooled: isPooled = true
ListView.onReused: isPooled = false
}
section.property: "providerName"
section.criteria: ViewSection.FullString
section.delegate: UnifiedSearchResultSectionItem {
width: unifiedSearchResultsListView.width
}
}
}
@@ -723,12 +742,13 @@ Window {
anchors.right: trayWindowBackground.right
anchors.bottom: trayWindowBackground.bottom
activeFocusOnTab: true
model: activityModel
onShowFileActivity: {
openFileActivityDialog(displayPath, absolutePath)
}
onActivityItemClicked: {
model.triggerDefaultAction(index)
model.slotTriggerDefaultAction(index)
}
}

View File

@@ -33,4 +33,15 @@ Activity::Identifier Activity::ident() const
{
return Identifier(_id, _accName);
}
ActivityLink ActivityLink::createFomJsonObject(const QJsonObject &obj)
{
ActivityLink activityLink;
activityLink._label = QUrl::fromPercentEncoding(obj.value(QStringLiteral("label")).toString().toUtf8());
activityLink._link = obj.value(QStringLiteral("link")).toString();
activityLink._verb = obj.value(QStringLiteral("type")).toString().toUtf8();
activityLink._primary = obj.value(QStringLiteral("primary")).toBool();
return activityLink;
}
}

Some files were not shown because too many files have changed in this diff Show More