Compare commits
211 commits
fix/dialog
...
local/libc
| Author | SHA1 | Date | |
|---|---|---|---|
| 20842026d7 | |||
| 4b5fcfe5d7 | |||
|
|
d1f0af78e6 | ||
|
|
87f407e3ed | ||
|
|
e2cad979ce | ||
|
|
b326e48cf6 | ||
|
|
c0b8a8c60a | ||
|
|
fe71aca0e5 | ||
|
|
d6e7618b76 | ||
|
|
c576bbcd60 | ||
|
|
762fc8fb48 | ||
|
|
7ba55f66cc | ||
|
|
b7005660f1 | ||
|
|
77f062647c | ||
|
|
00233428b3 | ||
| 98159d317c | |||
| e8eb42b507 | |||
| dd57647ced | |||
|
|
089ad2b417 | ||
|
|
1761b9b84b | ||
|
|
06278ee7f1 | ||
|
|
a412787d69 | ||
|
|
f3b8f6f585 | ||
|
|
de739425cb | ||
| 35334ba93b | |||
| deb6000c10 | |||
|
|
67af455898 | ||
|
|
b46b77f3a7 | ||
|
|
9642fef9cd | ||
|
|
0afac77e83 | ||
|
|
e84d3be4fd | ||
|
|
bb8b61f48b | ||
|
|
7cebbef82f | ||
| 0f6f9509ae | |||
|
|
08a637767d | ||
|
|
3d520858f3 | ||
|
|
28546795b0 | ||
|
|
cca10d2fd3 | ||
|
|
eebf5a9d4c | ||
|
|
a5422167cf | ||
|
|
3aa55b07b7 | ||
| 1488e2b01b | |||
| d78a22d87c | |||
|
|
fd6d4523cd | ||
|
|
9bdba47514 | ||
|
|
3c9a78ac9f | ||
|
|
e96b915f63 | ||
|
|
9ff853c46d | ||
|
|
6e210a3753 | ||
|
|
9c841dbacd | ||
|
|
991e5b234e | ||
|
|
78ac96c613 | ||
|
|
55c1b0afe0 | ||
|
|
e3aa2a0ec5 | ||
|
|
6273fe6505 | ||
|
|
5bdfffa494 | ||
|
|
95035166d3 | ||
|
|
24e34eaa0f | ||
|
|
0875cda769 | ||
|
|
ebce599f3a | ||
|
|
d6f6179e67 | ||
|
|
9a4f78aa75 | ||
|
|
a1c1aff60c | ||
| 9e0c2726ef | |||
| 30e562c8c2 | |||
| 542657b557 | |||
| 8234e11ff3 | |||
|
|
9a21542825 | ||
|
|
7ae2621f70 | ||
|
|
eb04ae28da | ||
|
|
dc36bdc8d3 | ||
|
|
8ef1082a4c | ||
|
|
a93fc1d9ee | ||
|
|
5278c67957 | ||
|
|
6c45017b24 | ||
|
|
b1fbc1d538 | ||
|
|
c4a9096ae2 | ||
|
|
fab53e54d6 | ||
| d6e1572611 | |||
| fd4a2ba610 | |||
| 2a74748083 | |||
|
|
17fc1cabbb | ||
|
|
168ff6d6f8 | ||
|
|
43a4228ca4 | ||
|
|
908218e3e0 | ||
|
|
8fc5182486 | ||
|
|
e00725a450 | ||
|
|
0e761ba811 | ||
|
|
674a33e3de | ||
|
|
6fe510a67a | ||
|
|
3ef7913abd | ||
|
|
49107187a4 | ||
|
|
50deb28a11 | ||
|
|
8883d7cd64 | ||
|
|
4d6788c9fd | ||
|
|
cd427c746c | ||
|
|
c653b69c75 | ||
|
|
65f5f454be | ||
|
|
53a33dbe74 | ||
|
|
7d3487da7b | ||
|
|
9a3eb1cdea | ||
|
|
e8fdb28a24 | ||
|
|
eda8291678 | ||
|
|
6d4af69318 | ||
|
|
256a6cba19 | ||
|
|
3cd0d0c00a | ||
|
|
64998b37a1 | ||
|
|
48eef3906d | ||
|
|
54f77f95a6 | ||
|
|
42577aa467 | ||
|
|
4ac289f40a | ||
|
|
c6c9eac97e | ||
|
|
af65a6b1c0 | ||
|
|
1d88fd16d6 | ||
|
|
d04c92229c | ||
|
|
814b49114b | ||
|
|
245dfaa9b0 | ||
|
|
7c7970d0c0 | ||
|
|
fc8f2b4000 | ||
|
|
c605dbecaf | ||
|
|
fca0753781 | ||
|
|
bb1ae9d458 | ||
|
|
0b3d8a9f3f | ||
|
|
53cfeebe57 | ||
|
|
bf01bb31a3 | ||
|
|
18eb41930a | ||
|
|
355423a027 | ||
|
|
cdec335ac7 | ||
|
|
2a3c9f12d9 | ||
|
|
5d1c056bed | ||
|
|
784200a253 | ||
|
|
03e537abad | ||
|
|
accb9fd418 | ||
|
|
f9b215dbd4 | ||
|
|
77615fc6b5 | ||
|
|
048ed3187b | ||
|
|
b4df354585 | ||
|
|
4b5c6d2c1b | ||
|
|
3548615d40 | ||
|
|
55c8a54f28 | ||
|
|
095940e9a9 | ||
|
|
6946e95c88 | ||
|
|
750c92c841 | ||
|
|
b8e02b7df8 | ||
|
|
cd48e4fa30 | ||
|
|
72b40aece3 | ||
|
|
7c47cbbb29 | ||
|
|
f8323171e7 | ||
|
|
506f86eff1 | ||
|
|
37e8734e7b | ||
|
|
1fcd0dccc8 | ||
|
|
d775f3e5e8 | ||
|
|
d5dbcc7677 | ||
|
|
e91a984da9 | ||
|
|
93e31d433a | ||
|
|
e7fa2d0fa5 | ||
|
|
accf5b2ba6 | ||
|
|
38e4fd3ec7 | ||
|
|
b3af8bf211 | ||
|
|
4afacccc8a | ||
|
|
8c57060db2 | ||
|
|
62bfcc3550 | ||
|
|
9c0eb63b82 | ||
|
|
33890633b5 | ||
|
|
b895b07bb2 | ||
|
|
afca6aef73 | ||
|
|
9a89100088 | ||
|
|
908f30a571 | ||
|
|
1c4ab75814 | ||
|
|
e21989aaa3 | ||
|
|
15e40461e5 | ||
|
|
0bd20e57e7 | ||
|
|
e35d5123f0 | ||
|
|
971374f60b | ||
|
|
93dd775f3c | ||
|
|
bb15f30fe5 | ||
|
|
e2bdcf8da4 | ||
|
|
b299f1a172 | ||
|
|
c114759c9e | ||
|
|
91243b99b5 | ||
|
|
da05a85fc5 | ||
|
|
109f83799d | ||
|
|
fc25260a5f | ||
|
|
1c1f8fdf6e | ||
|
|
cf328771c3 | ||
|
|
e60ae4e41f | ||
|
|
175f8ba724 | ||
|
|
ad0e66dceb | ||
|
|
b17f8889a8 | ||
|
|
9547da2b25 | ||
|
|
d38d55525b | ||
|
|
781e99d293 | ||
|
|
11b2617b6c | ||
|
|
079c82ee43 | ||
|
|
635bff7c1e | ||
|
|
39281a6336 | ||
|
|
23b5d98dcc | ||
|
|
4c6f2db5f2 | ||
|
|
41cdf89604 | ||
|
|
17325a5f5a | ||
|
|
e50c41aa24 | ||
|
|
75fe043e73 | ||
|
|
0b7294d4e4 | ||
|
|
3a88d7fcf7 | ||
|
|
f6ca0cb460 | ||
|
|
34f35842df | ||
|
|
88bfd76a95 | ||
|
|
6e2eafe16c | ||
|
|
3b1bc4430b | ||
|
|
4414d2f4b2 | ||
|
|
b041feec48 |
86 changed files with 10077 additions and 4589 deletions
15
.zed/settings.json
Normal file
15
.zed/settings.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"format_on_save": "on",
|
||||||
|
"lsp": {
|
||||||
|
"rust-analyzer": {
|
||||||
|
"initialization_options": {
|
||||||
|
"check": {
|
||||||
|
"command": "clippy",
|
||||||
|
},
|
||||||
|
"rustfmt": {
|
||||||
|
"extraArgs": ["+nightly"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
3213
Cargo.lock
generated
3213
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
91
Cargo.toml
91
Cargo.toml
|
|
@ -1,30 +1,30 @@
|
||||||
[package]
|
[package]
|
||||||
name = "cosmic-files"
|
name = "cosmic-files"
|
||||||
version = "1.0.8"
|
version = "1.9.0"
|
||||||
authors = ["Jeremy Soller <jeremy@system76.com>"]
|
authors = ["Jeremy Soller <jeremy@system76.com>"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
rust-version = "1.90"
|
rust-version = "1.93"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
chrono = { version = "0.4", features = ["unstable-locales"] }
|
jiff = "0.2"
|
||||||
icu = { version = "2.1.1", features = ["compiled_data"] }
|
jiff-icu = "0.2"
|
||||||
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "d0e95be", optional = true }
|
icu = { version = "2.2.0", features = ["compiled_data"] }
|
||||||
|
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "c0cff4d", optional = true }
|
||||||
cosmic-mime-apps = { git = "https://github.com/pop-os/cosmic-mime-apps.git", optional = true }
|
cosmic-mime-apps = { git = "https://github.com/pop-os/cosmic-mime-apps.git", optional = true }
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
env_logger = "0.11"
|
|
||||||
gio = { version = "0.21", optional = true }
|
gio = { version = "0.21", optional = true }
|
||||||
glib = { version = "0.21", optional = true }
|
glib = { version = "0.21", optional = true }
|
||||||
glob = "0.3"
|
glob = "0.3"
|
||||||
ignore = "0.4"
|
ignore = "0.4"
|
||||||
image = "0.25"
|
image.workspace = true
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
log = "0.4"
|
log.workspace = true
|
||||||
mime_guess = "2"
|
mime_guess = "2"
|
||||||
notify-debouncer-full = "0.7"
|
notify-debouncer-full = "0.7"
|
||||||
notify-rust = { version = "4", optional = true }
|
notify-rust = { version = "4", optional = true }
|
||||||
open = "5.3.3"
|
open = "5.3.4"
|
||||||
paste = "1.0"
|
paste = "1.0"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
rustc-hash = "2.1"
|
rustc-hash = "2.1"
|
||||||
|
|
@ -33,18 +33,18 @@ shlex = { version = "1.3" }
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
tikv-jemallocator = { version = "0.6", optional = true }
|
tikv-jemallocator = { version = "0.6", optional = true }
|
||||||
tokio = { version = "1", features = ["process", "sync"] }
|
tokio = { version = "1", features = ["process", "sync"] }
|
||||||
trash = { git = "https://github.com/jackpot51/trash-rs.git", branch = "cosmic" }
|
trash = "5.2.6"
|
||||||
url = "2.5"
|
url = "2.5"
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
wayland-client = { version = "0.31.12", optional = true }
|
wayland-client = { version = "0.31.14", optional = true }
|
||||||
xdg = { version = "3.0", optional = true }
|
xdg = { version = "3.0", optional = true }
|
||||||
xdg-mime = { git = "https://github.com/ebassi/xdg-mime-rs" }
|
xdg-mime = { git = "https://github.com/ebassi/xdg-mime-rs" }
|
||||||
# Compression
|
# Compression
|
||||||
bzip2 = { version = "0.6", optional = true } #TODO: replace with pure Rust crate
|
bzip2 = { version = "0.6", optional = true } #TODO: replace with pure Rust crate
|
||||||
flate2 = "1.1"
|
flate2 = "1.1"
|
||||||
tar = "0.4.44"
|
tar = "0.4.45"
|
||||||
lzma-rust2 = { version = "0.15.7", optional = true }
|
lzma-rust2 = { version = "0.16", optional = true }
|
||||||
ordermap = { version = "1.1.0", features = ["serde"] }
|
ordermap = { version = "1.2.0", features = ["serde"] }
|
||||||
# Internationalization
|
# Internationalization
|
||||||
i18n-embed = { version = "0.16", features = [
|
i18n-embed = { version = "0.16", features = [
|
||||||
"fluent-system",
|
"fluent-system",
|
||||||
|
|
@ -54,13 +54,19 @@ i18n-embed-fl = "0.10"
|
||||||
rust-embed = "8"
|
rust-embed = "8"
|
||||||
slotmap = "1.1.1"
|
slotmap = "1.1.1"
|
||||||
recently-used-xbel = "1.2.0"
|
recently-used-xbel = "1.2.0"
|
||||||
zip = "7"
|
zip = "8"
|
||||||
uzers = "0.12.2"
|
|
||||||
md-5 = "0.10.6"
|
md-5 = "0.10.6"
|
||||||
|
sha2 = "0.10"
|
||||||
png = "0.18"
|
png = "0.18"
|
||||||
jxl-oxide = { version = "0.12.5", features = ["image"] }
|
jxl-oxide = { version = "0.12.5", features = ["image"] }
|
||||||
num_cpus = "1.17.0"
|
num_cpus = "1.17.0"
|
||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
|
tracing = "0.1.44"
|
||||||
|
tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
|
||||||
|
thiserror = "2.0.18"
|
||||||
|
atomic_float = "1.1.0"
|
||||||
|
num_enum = "0.7.6"
|
||||||
|
bstr = "1.12.1"
|
||||||
|
|
||||||
# Completion-based IO runtime to enable io_uring / IOCP file IO support.
|
# Completion-based IO runtime to enable io_uring / IOCP file IO support.
|
||||||
[dependencies.compio]
|
[dependencies.compio]
|
||||||
|
|
@ -68,17 +74,18 @@ version = "0.18"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["fs", "io", "macros", "polling", "runtime"]
|
features = ["fs", "io", "macros", "polling", "runtime"]
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
# Yoda fork — depend on libcosmic-yoda directly by path (no git/no patch).
|
||||||
git = "https://github.com/pop-os/libcosmic.git"
|
[dependencies.libcosmic-yoda]
|
||||||
|
path = "../libcosmic"
|
||||||
default-features = false
|
default-features = false
|
||||||
#TODO: a11y feature crashes
|
#TODO: a11y feature crashes
|
||||||
features = [
|
features = [
|
||||||
"about",
|
"about",
|
||||||
|
"advanced-shaping",
|
||||||
"autosize",
|
"autosize",
|
||||||
"desktop",
|
|
||||||
"multi-window",
|
"multi-window",
|
||||||
"tokio",
|
"tokio",
|
||||||
"winit",
|
"wayland",
|
||||||
"surface-message",
|
"surface-message",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -106,15 +113,15 @@ default = [
|
||||||
"wayland",
|
"wayland",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
]
|
]
|
||||||
dbus-config = ["libcosmic/dbus-config"]
|
dbus-config = ["libcosmic-yoda/dbus-config"]
|
||||||
desktop = ["dep:cosmic-mime-apps", "dep:xdg"]
|
desktop = ["libcosmic-yoda/desktop", "dep:cosmic-mime-apps", "dep:xdg"]
|
||||||
desktop-applet = []
|
desktop-applet = []
|
||||||
gvfs = ["dep:gio", "dep:glib"]
|
gvfs = ["dep:gio", "dep:glib"]
|
||||||
io-uring = ["compio/io-uring"]
|
io-uring = ["compio/io-uring"]
|
||||||
jemalloc = ["dep:tikv-jemallocator"]
|
jemalloc = ["dep:tikv-jemallocator"]
|
||||||
notify = ["dep:notify-rust"]
|
notify = ["dep:notify-rust"]
|
||||||
wayland = ["libcosmic/wayland", "dep:cctk", "dep:wayland-client"]
|
wayland = ["libcosmic-yoda/wayland", "dep:cctk", "dep:wayland-client"]
|
||||||
wgpu = ["libcosmic/wgpu"]
|
wgpu = ["libcosmic-yoda/wgpu"]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 1
|
opt-level = 1
|
||||||
|
|
@ -124,7 +131,8 @@ inherits = "release"
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
fork = "0.6"
|
fork = "0.7"
|
||||||
|
uzers = "0.12.2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
procfs = "0.18"
|
procfs = "0.18"
|
||||||
|
|
@ -139,20 +147,27 @@ fastrand = "2"
|
||||||
test-log = "0.2"
|
test-log = "0.2"
|
||||||
tokio = { version = "1", features = ["rt", "macros"] }
|
tokio = { version = "1", features = ["rt", "macros"] }
|
||||||
|
|
||||||
# [patch.'https://github.com/pop-os/cosmic-text']
|
# Yoda fork — libcosmic dep is now a direct path dep (libcosmic-yoda above),
|
||||||
# cosmic-text = { path = "../cosmic-text" }
|
# no [patch] block needed anymore. Keeping the block below would be a no-op
|
||||||
|
# since nothing in the dep graph still asks for pop-os/libcosmic.git.
|
||||||
|
|
||||||
# [patch.'https://github.com/pop-os/libcosmic']
|
# Yoda wayland cut: redirect window_clipboard (x11 gated behind opt-in
|
||||||
# libcosmic = { path = "../libcosmic" }
|
# feature) to our public Forgejo fork and cosmic-text to the local checkout
|
||||||
# cosmic-config = { path = "../libcosmic/cosmic-config" }
|
# carrying PR#503 plus the EAW monospace width fix. The window_clipboard patch
|
||||||
# cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
# is needed to consolidate the
|
||||||
# libcosmic = { git = "https://github.com/pop-os/libcosmic//", branch = "iced-rebase" }
|
# upstream pop-os/libcosmic chain (pulled by cosmic-settings-daemon) onto
|
||||||
# cosmic-config = { git = "https://github.com/pop-os/libcosmic//", branch = "iced-rebase" }
|
# the same fork iced uses, otherwise cargo compiles two versions.
|
||||||
# cosmic-theme = { git = "https://github.com/pop-os/libcosmic//", branch = "iced-rebase" }
|
[patch.'https://github.com/pop-os/window_clipboard.git']
|
||||||
|
window_clipboard = { git = "https://forge.aditua.com/leyoda/window_clipboard.git", branch = "yoda-x11-optional" }
|
||||||
|
dnd = { git = "https://forge.aditua.com/leyoda/window_clipboard.git", branch = "yoda-x11-optional" }
|
||||||
|
mime = { git = "https://forge.aditua.com/leyoda/window_clipboard.git", branch = "yoda-x11-optional" }
|
||||||
|
|
||||||
|
[patch.'https://github.com/pop-os/cosmic-text.git']
|
||||||
# [patch.'https://github.com/pop-os/smithay-clipboard']
|
cosmic-text = { path = "../cosmic-text" }
|
||||||
# smithay-clipboard = { path = "../smithay-clipboard" }
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["cosmic-files-applet"]
|
members = ["cosmic-files-applet", "cosmic-files-thumbnailer"]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
image = "0.25"
|
||||||
|
log = "0.4"
|
||||||
|
|
|
||||||
3
build.rs
3
build.rs
|
|
@ -1,4 +1,5 @@
|
||||||
use std::{env, fs, path::PathBuf};
|
use std::path::PathBuf;
|
||||||
|
use std::{env, fs};
|
||||||
use xdgen::{App, Context, FluentString};
|
use xdgen::{App, Context, FluentString};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
[package]
|
[package]
|
||||||
name = "cosmic-files-applet"
|
name = "cosmic-files-applet"
|
||||||
version = "1.0.8"
|
version = "1.9.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4"
|
log.workspace = true
|
||||||
zbus = "4" # Blocking connection in zbus 5 hangs
|
zbus = "4" # Blocking connection in zbus 5 hangs
|
||||||
|
|
||||||
[dependencies.cosmic-files]
|
[dependencies.cosmic-files]
|
||||||
|
|
|
||||||
8
cosmic-files-thumbnailer/Cargo.toml
Normal file
8
cosmic-files-thumbnailer/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "cosmic-files-thumbnailer"
|
||||||
|
version = "1.9.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
image.workspace = true
|
||||||
|
pelite = "0.10"
|
||||||
254
cosmic-files-thumbnailer/src/main.rs
Normal file
254
cosmic-files-thumbnailer/src/main.rs
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use image::{DynamicImage, ImageFormat, RgbaImage};
|
||||||
|
use std::error::Error;
|
||||||
|
use std::ffi::{OsStr, OsString};
|
||||||
|
use std::io;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
thumbnail_from_args(std::env::args_os().skip(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn thumbnail_from_args(mut args: impl Iterator<Item = OsString>) -> Result<(), Box<dyn Error>> {
|
||||||
|
let usage = || {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"usage: cosmic-files-thumbnailer OUTPUT --size SIZE INPUT",
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let output = args.next().ok_or_else(usage)?;
|
||||||
|
if args.next().as_deref() != Some(OsStr::new("--size")) {
|
||||||
|
return Err(usage().into());
|
||||||
|
}
|
||||||
|
let size = args
|
||||||
|
.next()
|
||||||
|
.and_then(|arg| arg.into_string().ok())
|
||||||
|
.and_then(|arg| arg.parse::<u32>().ok())
|
||||||
|
.filter(|size| *size > 0)
|
||||||
|
.ok_or_else(usage)?;
|
||||||
|
let input = args.next().ok_or_else(usage)?;
|
||||||
|
if args.next().is_some() {
|
||||||
|
return Err(usage().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
thumbnail(Path::new(&input), Path::new(&output), size)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn thumbnail(input: &Path, output: &Path, size: u32) -> Result<(), Box<dyn Error>> {
|
||||||
|
let icon = thumbnail_image(input, size)?;
|
||||||
|
DynamicImage::ImageRgba8(icon).save_with_format(output, ImageFormat::Png)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn thumbnail_image(input: &Path, size: u32) -> Result<RgbaImage, Box<dyn Error>> {
|
||||||
|
if size == 0 {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"thumbnail size must be positive",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the PE so only the headers and resource pages touched by pelite are read.
|
||||||
|
let data = pelite::FileMap::open(input)?;
|
||||||
|
let bytes = data.as_ref();
|
||||||
|
if bytes.len() < 64 || !bytes.starts_with(b"MZ") {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::InvalidData, "input is not a PE file").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = pelite::PeFile::from_bytes(bytes).map_err(invalid_data)?;
|
||||||
|
let resources = file.resources().map_err(invalid_data)?;
|
||||||
|
let (_name, group) = resources
|
||||||
|
.icons()
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "PE file has no icon group"))?
|
||||||
|
.map_err(invalid_data)?;
|
||||||
|
|
||||||
|
let mut ico = Vec::new();
|
||||||
|
group.write(&mut ico).map_err(invalid_data)?;
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
DynamicImage::ImageRgba8(largest_icon(&ico).map_err(invalid_data)?)
|
||||||
|
.thumbnail(size, size)
|
||||||
|
.into_rgba8(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalid_data(error: impl std::fmt::Display) -> io::Error {
|
||||||
|
io::Error::new(io::ErrorKind::InvalidData, error.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn largest_icon(ico: &[u8]) -> Result<RgbaImage, String> {
|
||||||
|
if ico.len() < 6 {
|
||||||
|
return Err("ICO header is truncated".to_string());
|
||||||
|
}
|
||||||
|
let count = usize::from(u16::from_le_bytes([ico[4], ico[5]]));
|
||||||
|
let mut best: Option<(u64, RgbaImage)> = None;
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
for i in 0..count.min(32) {
|
||||||
|
let entry = 6 + i * 16;
|
||||||
|
if ico.len() < entry + 16 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let size = u32::from_le_bytes([
|
||||||
|
ico[entry + 8],
|
||||||
|
ico[entry + 9],
|
||||||
|
ico[entry + 10],
|
||||||
|
ico[entry + 11],
|
||||||
|
]) as usize;
|
||||||
|
let offset = u32::from_le_bytes([
|
||||||
|
ico[entry + 12],
|
||||||
|
ico[entry + 13],
|
||||||
|
ico[entry + 14],
|
||||||
|
ico[entry + 15],
|
||||||
|
]) as usize;
|
||||||
|
let Some(end) = offset.checked_add(size) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(data) = ico.get(offset..end) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
match decode_icon(&ico[entry..entry + 16], data) {
|
||||||
|
Ok(image) => {
|
||||||
|
let pixels = u64::from(image.width()) * u64::from(image.height());
|
||||||
|
if best
|
||||||
|
.as_ref()
|
||||||
|
.is_none_or(|(best_pixels, _)| pixels > *best_pixels)
|
||||||
|
{
|
||||||
|
best = Some((pixels, image));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => errors.push(format!("frame {i}: {err}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
best.map(|(_, image)| image).ok_or_else(|| {
|
||||||
|
if errors.is_empty() {
|
||||||
|
"PE icon contains no complete frames".to_string()
|
||||||
|
} else {
|
||||||
|
format!("PE icon could not be decoded ({})", errors.join("; "))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_icon(entry: &[u8], data: &[u8]) -> Result<RgbaImage, String> {
|
||||||
|
if data.starts_with(b"\x89PNG\r\n\x1a\n") {
|
||||||
|
return image::ImageReader::with_format(io::Cursor::new(data), ImageFormat::Png)
|
||||||
|
.decode()
|
||||||
|
.map(DynamicImage::into_rgba8)
|
||||||
|
.map_err(|err| err.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let capacity = 22usize
|
||||||
|
.checked_add(data.len())
|
||||||
|
.ok_or_else(|| "frame is too large".to_string())?;
|
||||||
|
let data_len = u32::try_from(data.len()).map_err(|_| "frame is too large".to_string())?;
|
||||||
|
let mut ico = Vec::with_capacity(capacity);
|
||||||
|
ico.extend_from_slice(&[0, 0, 1, 0, 1, 0]);
|
||||||
|
ico.extend_from_slice(entry);
|
||||||
|
ico[14..18].copy_from_slice(&data_len.to_le_bytes());
|
||||||
|
ico[18..22].copy_from_slice(&22u32.to_le_bytes());
|
||||||
|
ico.extend_from_slice(data);
|
||||||
|
|
||||||
|
image::ImageReader::with_format(io::Cursor::new(ico), ImageFormat::Ico)
|
||||||
|
.decode()
|
||||||
|
.map(DynamicImage::into_rgba8)
|
||||||
|
.map_err(|err| err.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{largest_icon, thumbnail_from_args};
|
||||||
|
use image::{DynamicImage, ImageFormat, Rgb, RgbImage, Rgba, RgbaImage};
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_thumbnail_request_returns_an_error() {
|
||||||
|
let args = [OsString::from("output.png")];
|
||||||
|
assert!(thumbnail_from_args(args.into_iter()).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn largest_icon_skips_invalid_entries() {
|
||||||
|
let image = RgbaImage::from_pixel(2, 2, Rgba([1, 2, 3, 255]));
|
||||||
|
let mut png = Cursor::new(Vec::new());
|
||||||
|
DynamicImage::ImageRgba8(image)
|
||||||
|
.write_to(&mut png, ImageFormat::Png)
|
||||||
|
.unwrap();
|
||||||
|
let png = png.into_inner();
|
||||||
|
|
||||||
|
let mut ico = vec![0, 0, 1, 0, 2, 0];
|
||||||
|
let mut invalid_entry = [0; 16];
|
||||||
|
invalid_entry[8..12].copy_from_slice(&4u32.to_le_bytes());
|
||||||
|
invalid_entry[12..16].copy_from_slice(&u32::MAX.to_le_bytes());
|
||||||
|
ico.extend_from_slice(&invalid_entry);
|
||||||
|
|
||||||
|
let mut valid_entry = [0; 16];
|
||||||
|
valid_entry[..8].copy_from_slice(&[2, 2, 0, 0, 1, 0, 32, 0]);
|
||||||
|
valid_entry[8..12].copy_from_slice(&u32::try_from(png.len()).unwrap().to_le_bytes());
|
||||||
|
valid_entry[12..16].copy_from_slice(&38u32.to_le_bytes());
|
||||||
|
ico.extend_from_slice(&valid_entry);
|
||||||
|
ico.extend_from_slice(&png);
|
||||||
|
|
||||||
|
let decoded = largest_icon(&ico).expect("valid icon after malformed entry");
|
||||||
|
assert_eq!(decoded.dimensions(), (2, 2));
|
||||||
|
assert_eq!(decoded.get_pixel(0, 0), &Rgba([1, 2, 3, 255]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dib_and_mask_is_applied() {
|
||||||
|
let mut dib = vec![0; 40];
|
||||||
|
dib[0..4].copy_from_slice(&40u32.to_le_bytes());
|
||||||
|
dib[4..8].copy_from_slice(&2i32.to_le_bytes());
|
||||||
|
dib[8..12].copy_from_slice(&4i32.to_le_bytes());
|
||||||
|
dib[12..14].copy_from_slice(&1u16.to_le_bytes());
|
||||||
|
dib[14..16].copy_from_slice(&24u16.to_le_bytes());
|
||||||
|
|
||||||
|
// Two bottom-up BGR rows, padded to four-byte boundaries.
|
||||||
|
dib.extend_from_slice(&[0, 0, 255, 0, 0, 255, 0, 0]);
|
||||||
|
dib.extend_from_slice(&[0, 0, 255, 0, 0, 255, 0, 0]);
|
||||||
|
// The first mask row is the bottom image row; its first pixel is transparent.
|
||||||
|
dib.extend_from_slice(&[0b1000_0000, 0, 0, 0]);
|
||||||
|
dib.extend_from_slice(&[0, 0, 0, 0]);
|
||||||
|
|
||||||
|
let mut ico = vec![0, 0, 1, 0, 1, 0];
|
||||||
|
let mut entry = [0; 16];
|
||||||
|
entry[..8].copy_from_slice(&[2, 2, 0, 0, 1, 0, 24, 0]);
|
||||||
|
entry[8..12].copy_from_slice(&u32::try_from(dib.len()).unwrap().to_le_bytes());
|
||||||
|
entry[12..16].copy_from_slice(&22u32.to_le_bytes());
|
||||||
|
ico.extend_from_slice(&entry);
|
||||||
|
ico.extend_from_slice(&dib);
|
||||||
|
|
||||||
|
let decoded = largest_icon(&ico).expect("valid DIB icon");
|
||||||
|
assert_eq!(decoded.get_pixel(0, 0), &Rgba([255, 0, 0, 255]));
|
||||||
|
assert_eq!(decoded.get_pixel(0, 1), &Rgba([255, 0, 0, 0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgb_png_icon_is_decoded() {
|
||||||
|
let image = RgbImage::from_pixel(2, 2, Rgb([1, 2, 3]));
|
||||||
|
let mut png = Cursor::new(Vec::new());
|
||||||
|
DynamicImage::ImageRgb8(image)
|
||||||
|
.write_to(&mut png, ImageFormat::Png)
|
||||||
|
.unwrap();
|
||||||
|
let png = png.into_inner();
|
||||||
|
|
||||||
|
let mut ico = vec![0, 0, 1, 0, 1, 0];
|
||||||
|
let mut entry = [0; 16];
|
||||||
|
entry[..8].copy_from_slice(&[2, 2, 0, 0, 1, 0, 24, 0]);
|
||||||
|
entry[8..12].copy_from_slice(&u32::try_from(png.len()).unwrap().to_le_bytes());
|
||||||
|
entry[12..16].copy_from_slice(&22u32.to_le_bytes());
|
||||||
|
ico.extend_from_slice(&entry);
|
||||||
|
ico.extend_from_slice(&png);
|
||||||
|
|
||||||
|
let decoded = largest_icon(&ico).expect("valid RGB PNG icon");
|
||||||
|
assert_eq!(decoded.get_pixel(0, 0), &Rgba([1, 2, 3, 255]));
|
||||||
|
}
|
||||||
|
}
|
||||||
96
debian/changelog
vendored
96
debian/changelog
vendored
|
|
@ -1,3 +1,99 @@
|
||||||
|
cosmic-files (1.9.0) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.9.0 version update
|
||||||
|
|
||||||
|
-- Michael Murphy <michael@mmurphy.dev> Mon, 14 Sep 2026 20:35:19 +0200
|
||||||
|
|
||||||
|
cosmic-files (1.8.0) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.8.0 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Wed, 02 Sep 2026 08:14:53 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.7.0) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.7.0 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 25 Aug 2026 09:20:22 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.6.0) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.6.0 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 18 Aug 2026 07:13:48 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.5.0) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.5.0 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 28 Jul 2026 09:14:09 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.4.0) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.4.0 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Mon, 20 Jul 2026 14:36:06 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.3.0) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.3.0 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Sun, 12 Jul 2026 13:21:18 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.2.0) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.2.0 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 30 Jun 2026 12:21:07 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.1.0) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.1.0 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 23 Jun 2026 08:47:18 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.0.16) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.0.16 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 09 Jun 2026 10:26:05 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.0.15) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.0.15 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 02 Jun 2026 11:02:29 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.0.14) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.0.14 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 26 May 2026 08:30:59 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.0.13) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.0.13 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 12 May 2026 09:39:14 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.0.12) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.0.12 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 05 May 2026 10:23:57 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.0.11) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.0.11 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 14 Apr 2026 11:09:44 -0600
|
||||||
|
|
||||||
|
cosmic-files (1.0.9) noble; urgency=medium
|
||||||
|
|
||||||
|
* Epoch 1.0.9 version update
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Mon, 06 Apr 2026 15:10:13 -0600
|
||||||
|
|
||||||
cosmic-files (1.0.8) noble; urgency=medium
|
cosmic-files (1.0.8) noble; urgency=medium
|
||||||
|
|
||||||
* Epoch 1.0.8 version update
|
* Epoch 1.0.8 version update
|
||||||
|
|
|
||||||
6
debian/source/options
vendored
6
debian/source/options
vendored
|
|
@ -1,4 +1,6 @@
|
||||||
tar-ignore=.github
|
tar-ignore=.github
|
||||||
tar-ignore=.vscode
|
tar-ignore=target/.rustc_info.json
|
||||||
|
tar-ignore=target/CACHEDIR.TAG
|
||||||
|
tar-ignore=target/debug
|
||||||
|
tar-ignore=target/release
|
||||||
tar-ignore=vendor
|
tar-ignore=vendor
|
||||||
tar-ignore=target
|
|
||||||
78
docs/local-performance-and-portal-notes.md
Normal file
78
docs/local-performance-and-portal-notes.md
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
# Local performance and portal notes
|
||||||
|
|
||||||
|
Date: 2026-05-05
|
||||||
|
|
||||||
|
This repository carries a local patch aimed at improving initial directory
|
||||||
|
display latency in large folders, plus a system-level workaround for a COSMIC
|
||||||
|
portal FileChooser crash observed with Firefox and Chromium.
|
||||||
|
|
||||||
|
## Directory listing latency
|
||||||
|
|
||||||
|
The slow path was the initial construction of `Item` values in `src/tab.rs`.
|
||||||
|
On a test folder with about 2000 entries, raw filesystem enumeration and stat
|
||||||
|
calls completed in a few milliseconds, while COSMIC Files took multiple seconds
|
||||||
|
before showing the directory.
|
||||||
|
|
||||||
|
The local patch keeps initial item construction cheap:
|
||||||
|
|
||||||
|
- directory child counts are no longer computed synchronously in
|
||||||
|
`item_from_entry` and `item_from_gvfs_info`;
|
||||||
|
- initial MIME detection uses extension-based `mime_guess`;
|
||||||
|
- regular files use a generic file icon during the initial scan instead of
|
||||||
|
resolving the full MIME icon immediately.
|
||||||
|
|
||||||
|
This keeps folders and `.desktop` files special-cased, while avoiding expensive
|
||||||
|
per-file work for ordinary files during first paint.
|
||||||
|
|
||||||
|
Measured locally during investigation:
|
||||||
|
|
||||||
|
- before the MIME/icon changes: about 3.1 seconds for `~/Téléchargements`;
|
||||||
|
- after avoiding full MIME icon resolution during scan: below the temporary
|
||||||
|
100 ms perf-log threshold for the same folder.
|
||||||
|
|
||||||
|
## File chooser portal workaround
|
||||||
|
|
||||||
|
Firefox and Chromium were failing to open `Save As` on the first attempt because
|
||||||
|
`xdg-desktop-portal-cosmic` crashed while handling `FileChooser`.
|
||||||
|
|
||||||
|
Logs showed:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Backend call failed: Remote peer disconnected
|
||||||
|
xdg-desktop-portal-cosmic ... status=11/SEGV
|
||||||
|
```
|
||||||
|
|
||||||
|
The working local system workaround is to remove
|
||||||
|
`org.freedesktop.impl.portal.FileChooser` from:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/usr/share/xdg-desktop-portal/portals/cosmic.portal
|
||||||
|
```
|
||||||
|
|
||||||
|
so the file chooser falls back to GTK. The resulting local `cosmic.portal`
|
||||||
|
keeps COSMIC for the other interfaces:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[portal]
|
||||||
|
DBusName=org.freedesktop.impl.portal.desktop.cosmic
|
||||||
|
Interfaces=org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.Settings;org.freedesktop.impl.portal.ScreenCast
|
||||||
|
UseIn=COSMIC
|
||||||
|
```
|
||||||
|
|
||||||
|
Backups created locally:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/usr/share/xdg-desktop-portal/portals/cosmic.portal.bak-codex-20260505-filechooser
|
||||||
|
/usr/share/xdg-desktop-portal/cosmic-portals.conf.bak-codex-20260505
|
||||||
|
/usr/share/xdg-desktop-portal/cosmic-portals.conf.bak-codex-20260505-2
|
||||||
|
```
|
||||||
|
|
||||||
|
After editing portal files, restart:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
systemctl --user restart xdg-desktop-portal.service xdg-desktop-portal-gtk.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Package updates may overwrite `/usr/share/xdg-desktop-portal/portals/cosmic.portal`.
|
||||||
|
If `Save As` starts needing two attempts again, re-check that `FileChooser` has
|
||||||
|
not been reintroduced in `cosmic.portal`.
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use cosmic_files::operation::recursive::Method;
|
use cosmic_files::operation::recursive::{Context, Method};
|
||||||
use cosmic_files::operation::{Controller, ReplaceResult, recursive::Context};
|
use cosmic_files::operation::{Controller, ReplaceResult};
|
||||||
use std::{error::Error, io, path::PathBuf};
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[compio::main]
|
#[compio::main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,30 @@
|
||||||
use cosmic::{
|
use cosmic::app::{self, Core, Settings, Task};
|
||||||
Application, Element,
|
use cosmic::iced::{Subscription, window};
|
||||||
app::{self, Core, Settings, Task},
|
use cosmic::{Application, Element, executor, widget};
|
||||||
executor,
|
|
||||||
iced::{Subscription, window},
|
|
||||||
widget,
|
|
||||||
};
|
|
||||||
use cosmic_files::dialog::{
|
use cosmic_files::dialog::{
|
||||||
Dialog, DialogChoice, DialogChoiceOption, DialogFilter, DialogFilterPattern, DialogKind,
|
Dialog, DialogChoice, DialogChoiceOption, DialogFilter, DialogFilterPattern, DialogKind,
|
||||||
DialogMessage, DialogResult, DialogSettings,
|
DialogMessage, DialogResult, DialogSettings,
|
||||||
};
|
};
|
||||||
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
|
let log_format = tracing_subscriber::fmt::format()
|
||||||
|
.pretty()
|
||||||
|
.without_time()
|
||||||
|
.with_line_number(true)
|
||||||
|
.with_file(true)
|
||||||
|
.with_target(false)
|
||||||
|
.with_thread_names(true);
|
||||||
|
|
||||||
|
let log_layer = tracing_subscriber::fmt::Layer::default()
|
||||||
|
.with_writer(std::io::stderr)
|
||||||
|
.event_format(log_format);
|
||||||
|
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(tracing_subscriber::EnvFilter::from_env("RUST_LOG"))
|
||||||
|
.with(log_layer)
|
||||||
|
.init();
|
||||||
|
|
||||||
let settings = Settings::default();
|
let settings = Settings::default();
|
||||||
app::run::<App>(settings, ())?;
|
app::run::<App>(settings, ())?;
|
||||||
|
|
@ -148,7 +161,7 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self) -> Element<'_, Message> {
|
fn view(&self) -> Element<'_, Message> {
|
||||||
let mut column = widget::column().spacing(8).padding(8);
|
let mut column = widget::column::with_capacity(8).spacing(8).padding(8);
|
||||||
{
|
{
|
||||||
let mut button = widget::button::standard("Open File");
|
let mut button = widget::button::standard("Open File");
|
||||||
if self.dialog_opt.is_none() {
|
if self.dialog_opt.is_none() {
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,10 @@ empty-trash-warning = سيتم حذف العناصر الموجودة في مج
|
||||||
|
|
||||||
## New File/Folder Dialog
|
## New File/Folder Dialog
|
||||||
|
|
||||||
create-new-file = إنشاء ملف جديد
|
create-new-file = أنشئ ملف جديد
|
||||||
create-new-folder = إنشاء مجلد جديد
|
create-new-folder = أنشئ مجلَّد جديد
|
||||||
file-name = اسم الملف
|
file-name = اسم الملف
|
||||||
folder-name = اسم المجلد
|
folder-name = اسم المجلَّد
|
||||||
file-already-exists = يوجد ملف بهذا الاسم بالفعل
|
file-already-exists = يوجد ملف بهذا الاسم بالفعل
|
||||||
folder-already-exists = يوجد مجلد بهذا الاسم بالفعل
|
folder-already-exists = يوجد مجلد بهذا الاسم بالفعل
|
||||||
name-hidden = الاسماء التي تبدأ بنقطة «.» ستكون مخفية
|
name-hidden = الاسماء التي تبدأ بنقطة «.» ستكون مخفية
|
||||||
|
|
@ -34,10 +34,10 @@ name-no-slashes = لا يمكن أن يحتوي الاسم على شرطات م
|
||||||
|
|
||||||
## Open/Save Dialog
|
## Open/Save Dialog
|
||||||
|
|
||||||
cancel = إلغاء
|
cancel = ألغِ
|
||||||
open = فتح
|
open = افتح
|
||||||
open-file = فتح ملف
|
open-file = افتح ملف
|
||||||
open-folder = افتح مجلد
|
open-folder = افتح مجلَّد
|
||||||
open-in-new-tab = افتح في لسان جديد
|
open-in-new-tab = افتح في لسان جديد
|
||||||
open-in-new-window = افتح في نافذة جديدة
|
open-in-new-window = افتح في نافذة جديدة
|
||||||
open-multiple-files = افتح عدة ملفات
|
open-multiple-files = افتح عدة ملفات
|
||||||
|
|
@ -84,7 +84,7 @@ settings = الإعدادات
|
||||||
### Appearance
|
### Appearance
|
||||||
|
|
||||||
appearance = المظهر
|
appearance = المظهر
|
||||||
theme = النسق
|
theme = السمة
|
||||||
match-desktop = طابق سطح المكتب
|
match-desktop = طابق سطح المكتب
|
||||||
dark = داكن
|
dark = داكن
|
||||||
light = فاتح
|
light = فاتح
|
||||||
|
|
@ -110,7 +110,7 @@ new-tab = لسان جديد
|
||||||
new-window = نافذة جديدة
|
new-window = نافذة جديدة
|
||||||
rename = غيِّر الاسم...
|
rename = غيِّر الاسم...
|
||||||
close-tab = أغلق اللسان
|
close-tab = أغلق اللسان
|
||||||
quit = أنهِ
|
quit = إنهِ
|
||||||
|
|
||||||
## Edit
|
## Edit
|
||||||
|
|
||||||
|
|
@ -147,15 +147,15 @@ icon-size-and-spacing = حجم الأيقونة والتباعد
|
||||||
icon-size = حجم الأيقونة
|
icon-size = حجم الأيقونة
|
||||||
grid-spacing = تباعد شبكي
|
grid-spacing = تباعد شبكي
|
||||||
trashed-on = مهمل
|
trashed-on = مهمل
|
||||||
details = التفاصيل
|
details = تفاصيل
|
||||||
pause = ألبث
|
pause = ألبث
|
||||||
resume = استئناف
|
resume = استئناف
|
||||||
create-archive = إنشاء أرشيف
|
create-archive = أنشئ أرشيف
|
||||||
extract-password-required = كلمة السر مطلوبة
|
extract-password-required = كلمة السر مطلوبة
|
||||||
extract-to = استخرِج إلى...
|
extract-to = استخرِج إلى...
|
||||||
extract-to-title = استخرِج إلى مجلّد
|
extract-to-title = استخرِج إلى مجلّد
|
||||||
mount-error = تعذر الوصول إلى القرص
|
mount-error = تعذر الوصول إلى القرص
|
||||||
create = إنشاء
|
create = أنشئ
|
||||||
open-item-location = افتح مكان العنصر
|
open-item-location = افتح مكان العنصر
|
||||||
set-executable-and-launch-description = أتريد تعيين «{ $name }» كقابل للتنفيذ وتشغيله؟
|
set-executable-and-launch-description = أتريد تعيين «{ $name }» كقابل للتنفيذ وتشغيله؟
|
||||||
favorite-path-error-description =
|
favorite-path-error-description =
|
||||||
|
|
@ -259,7 +259,7 @@ related-apps = تطبيقات ذات صلة
|
||||||
selected-items = العناصر { $items } المحدّدة
|
selected-items = العناصر { $items } المحدّدة
|
||||||
permanently-delete-question = احذف نهائيًا؟
|
permanently-delete-question = احذف نهائيًا؟
|
||||||
delete = احذف
|
delete = احذف
|
||||||
permanently-delete-warning = هل أنت متأكد من أنك تريد حذف { $target } نهائيًا؟ لا يمكن التراجع عن هذا الإجراء.
|
permanently-delete-warning = سيُحذف { $target } نهائيًا. لا يمكن التراجع عن هذا الإجراء.
|
||||||
replace-warning-operation = أتريد استبداله؟ استبداله سيكتب فوق محتواه.
|
replace-warning-operation = أتريد استبداله؟ استبداله سيكتب فوق محتواه.
|
||||||
original-file = الملف الأصلي
|
original-file = الملف الأصلي
|
||||||
replace-with = استبدل بـ
|
replace-with = استبدل بـ
|
||||||
|
|
@ -371,3 +371,19 @@ move-to = انقل إلى...
|
||||||
show-recents = مجلد الحديثة في الشريط الجانبي
|
show-recents = مجلد الحديثة في الشريط الجانبي
|
||||||
clear-recents-history = امحُ التأريخ الحديث
|
clear-recents-history = امحُ التأريخ الحديث
|
||||||
copy-path = انسخ المسار
|
copy-path = انسخ المسار
|
||||||
|
mixed = مختلط
|
||||||
|
context-action = إجراء السياق
|
||||||
|
context-action-confirm-title = شغِّل "{ $name }"؟
|
||||||
|
context-action-confirm-warning =
|
||||||
|
سيُشغِّل هذا على { $items } { $items ->
|
||||||
|
[one] عنصر
|
||||||
|
[two] عنصرين
|
||||||
|
[few] عناصر
|
||||||
|
[many] عنصرًا
|
||||||
|
*[other] عنصر
|
||||||
|
}.
|
||||||
|
run = شغِّل
|
||||||
|
checksum = تدقيق المجموع { $kind }
|
||||||
|
calculate = احسب
|
||||||
|
error = خطأ
|
||||||
|
rename-confirm = أعد التسمية
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
cosmic-files = Fitxers del COSMIC
|
cosmic-files = Fitxers del COSMIC
|
||||||
empty-folder = Carpeta buida
|
empty-folder = Carpeta buida
|
||||||
empty-folder-hidden = Carpeta buida (té elements ocults)
|
empty-folder-hidden = Carpeta buida (conté elements ocults)
|
||||||
no-results = No s'ha trobat cap resultat
|
no-results = No s'ha trobat cap resultat
|
||||||
filesystem = Sistema de fitxers
|
filesystem = Sistema de fitxers
|
||||||
home = Inici
|
home = Inici
|
||||||
networks = Xarxes
|
networks = Xarxes
|
||||||
notification-in-progress = Hi ha operacions amb fitxers en curs.
|
notification-in-progress = Operacions de fitxers en curs
|
||||||
trash = Paperera
|
trash = Paperera
|
||||||
recents = Recents
|
recents = Recents
|
||||||
undo = Desfés
|
undo = Desfés
|
||||||
|
|
@ -29,14 +29,14 @@ details = Detalls
|
||||||
dismiss = Descarta el missatge
|
dismiss = Descarta el missatge
|
||||||
operations-running =
|
operations-running =
|
||||||
{ $running } { $running ->
|
{ $running } { $running ->
|
||||||
[one] operacion
|
[one] operació
|
||||||
*[other] operacions
|
*[other] operacions
|
||||||
} en curs ({ $percent }%)...
|
} en curs ({ $percent }%)...
|
||||||
operations-running-finished =
|
operations-running-finished =
|
||||||
{ $running }{ $running ->
|
{ $running }{ $running ->
|
||||||
[one] operacion
|
[one] operació
|
||||||
*[other] operacions
|
*[other] operacions
|
||||||
} en curs ({ $percent }%), { $finished } acabat...
|
} en curs ({ $percent }%), { $finished } acabada/es...
|
||||||
pause = Pausa
|
pause = Pausa
|
||||||
resume = Reprèn
|
resume = Reprèn
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ extract-password-required = Cal una contrasenya
|
||||||
## Empty Trash Dialog
|
## Empty Trash Dialog
|
||||||
|
|
||||||
empty-trash = Buida la paperera
|
empty-trash = Buida la paperera
|
||||||
empty-trash-warning = Voleu suprimir permanentment tots els fitxers de la paperera?
|
empty-trash-warning = Els elements dins la Paperera s'eliminaran permanentment
|
||||||
|
|
||||||
## Mount Error Dialog
|
## Mount Error Dialog
|
||||||
|
|
||||||
|
|
@ -64,20 +64,20 @@ mount-error = No es pot accedir a la unitat
|
||||||
|
|
||||||
create-new-file = Crea un nou fitxer
|
create-new-file = Crea un nou fitxer
|
||||||
create-new-folder = Crea una nova carpeta
|
create-new-folder = Crea una nova carpeta
|
||||||
file-name = Nom del fixer
|
file-name = Nom del fitxer
|
||||||
folder-name = Nom de la carpeta
|
folder-name = Nom de la carpeta
|
||||||
file-already-exists = Ja existeix un fitxer amb aquest nom.
|
file-already-exists = Ja existeix un fitxer amb aquest nom
|
||||||
folder-already-exists = Ja existeix una carpeta amb aquest nom.
|
folder-already-exists = Ja existeix una carpeta amb aquest nom
|
||||||
name-hidden = Els noms que comencin amb "." seran ocults.
|
name-hidden = Els noms que comencin amb "." s'ocultaran.
|
||||||
name-invalid = El nom no pot ser "{ $filename }".
|
name-invalid = El nom no pot ser "{ $filename }"
|
||||||
name-no-slashes = El nom no pot contenir barres.
|
name-no-slashes = El nom no pot contenir barres.
|
||||||
|
|
||||||
## Open/Save Dialog
|
## Open/Save Dialog
|
||||||
|
|
||||||
cancel = Cancel·la
|
cancel = Cancel·lar
|
||||||
create = Crea
|
create = Crea
|
||||||
open = Obre
|
open = Obre
|
||||||
open-file = Obre el fixer
|
open-file = Obre el fitxer
|
||||||
open-folder = Obre la carpeta
|
open-folder = Obre la carpeta
|
||||||
open-in-new-tab = Obre en una pestanya nova
|
open-in-new-tab = Obre en una pestanya nova
|
||||||
open-in-new-window = Obre en una finestra nova
|
open-in-new-window = Obre en una finestra nova
|
||||||
|
|
@ -352,3 +352,14 @@ sort-newest-first = Primer més recents
|
||||||
sort-oldest-first = Primer més antics
|
sort-oldest-first = Primer més antics
|
||||||
sort-smallest-to-largest = De petit a gran
|
sort-smallest-to-largest = De petit a gran
|
||||||
sort-largest-to-smallest = De gran a petit
|
sort-largest-to-smallest = De gran a petit
|
||||||
|
run = Executa
|
||||||
|
extract-to-title = Extreu a una carpeta
|
||||||
|
empty-trash-title = Voleu buidar la paperera?
|
||||||
|
extract-to = Extreu a...
|
||||||
|
delete = Suprimeix
|
||||||
|
comment = Gestor de fitxers de l'escriptori COSMIC
|
||||||
|
copy-to-title = Escolliu el destí de la còpia
|
||||||
|
copy-to-button-label = Copia
|
||||||
|
move-to-title = Escolliu el destí del trasllat
|
||||||
|
move-to-button-label = Mou
|
||||||
|
keywords = Carpeta;Fitxers;Arxius;Gestor;Explorador;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ size = Velikost
|
||||||
|
|
||||||
## Empty Trash Dialog
|
## Empty Trash Dialog
|
||||||
|
|
||||||
empty-trash = Vysypat koš
|
empty-trash = Vyprázdnit koš
|
||||||
empty-trash-warning = Položky v koši budou trvale smazány
|
empty-trash-warning = Položky v koši budou trvale smazány
|
||||||
|
|
||||||
## New File/Folder Dialog
|
## New File/Folder Dialog
|
||||||
|
|
@ -176,7 +176,7 @@ set-executable-and-launch-description = Chcete povolit spouštění souboru „{
|
||||||
set-and-launch = Povolit a spustit
|
set-and-launch = Povolit a spustit
|
||||||
open-with = Otevřít pomocí
|
open-with = Otevřít pomocí
|
||||||
other = Ostatní
|
other = Ostatní
|
||||||
none = Žádný
|
none = Žádné
|
||||||
icon-size-and-spacing = Velikost a rozestupy ikon
|
icon-size-and-spacing = Velikost a rozestupy ikon
|
||||||
grid-spacing = Rozestupy mřížky
|
grid-spacing = Rozestupy mřížky
|
||||||
deleting =
|
deleting =
|
||||||
|
|
@ -195,8 +195,8 @@ deleted =
|
||||||
[few] položky
|
[few] položky
|
||||||
*[other] položek
|
*[other] položek
|
||||||
} z koše
|
} z koše
|
||||||
emptying-trash = Vysypávání koše ({ $progress })...
|
emptying-trash = Vyprazdňování koše ({ $progress })...
|
||||||
emptied-trash = Koš vysypán
|
emptied-trash = Koš vyprázdněn
|
||||||
restoring =
|
restoring =
|
||||||
Obnovování { $items } { $items ->
|
Obnovování { $items } { $items ->
|
||||||
[one] položky
|
[one] položky
|
||||||
|
|
@ -403,7 +403,7 @@ sort-largest-to-smallest = Od největšího po nejmenší
|
||||||
gallery-preview = Náhled galerie
|
gallery-preview = Náhled galerie
|
||||||
sort = Řazení
|
sort = Řazení
|
||||||
sort-a-z = A-Z
|
sort-a-z = A-Z
|
||||||
empty-trash-title = Vysypat koš?
|
empty-trash-title = Vyprázdnit koš?
|
||||||
type-to-search-select = Vybere první shodující se soubor nebo složku
|
type-to-search-select = Vybere první shodující se soubor nebo složku
|
||||||
pasted-image = Vložený obrázek
|
pasted-image = Vložený obrázek
|
||||||
pasted-text = Vložený text
|
pasted-text = Vložený text
|
||||||
|
|
@ -417,3 +417,20 @@ move-to-title = Vyberte cíl přesunutí
|
||||||
show-recents = Složka „Nedávné“ v postranním panelu
|
show-recents = Složka „Nedávné“ v postranním panelu
|
||||||
copy-path = Kopírovat cestu
|
copy-path = Kopírovat cestu
|
||||||
clear-recents-history = Vymazat historii „Nedávné“
|
clear-recents-history = Vymazat historii „Nedávné“
|
||||||
|
mixed = Různé
|
||||||
|
context-action = Kontextová akce
|
||||||
|
context-action-confirm-title = Spustit „{ $name }“?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Tato akce se spustí pro { $items } { $items ->
|
||||||
|
[one] položku
|
||||||
|
[few] položky
|
||||||
|
*[other] položek
|
||||||
|
}.
|
||||||
|
run = Spustit
|
||||||
|
rename-confirm = Přejmenovat
|
||||||
|
checksum = Kontrolní součet { $kind }
|
||||||
|
calculate = Vypočítat
|
||||||
|
error = Chyba
|
||||||
|
search-application = Hledat podle názvu aplikace
|
||||||
|
sidebar-label = Popisek v postranním panelu
|
||||||
|
change-sidebar-label = Změnit popisek
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,43 @@
|
||||||
cosmic-files = COSMIC Filer
|
cosmic-files = COSMIC Filhåndtering
|
||||||
empty-folder = Tom mappe
|
empty-folder = Tom mappe
|
||||||
empty-folder-hidden = Tom mappe (har skjulte filer)
|
empty-folder-hidden = Tom mappe (har skjulte emner)
|
||||||
no-results = Ingen resultater
|
no-results = Fandt ingen resultater
|
||||||
filesystem = Filsystem
|
filesystem = Filsystem
|
||||||
home = Hjem
|
home = Hjem
|
||||||
networks = Netværk
|
networks = Netværk
|
||||||
notification-in-progress = Filoperationer er igangværende.
|
notification-in-progress = Fil-operationer er igang
|
||||||
trash = Skraldespand
|
trash = Papirkurv
|
||||||
recents = Seneste
|
recents = Seneste
|
||||||
undo = Fortryd
|
undo = Fortryd
|
||||||
today = I dag
|
today = I dag
|
||||||
# Desktop view options
|
# Desktop view options
|
||||||
desktop-view-options = Indstillinger for skrivebordsudseende...
|
desktop-view-options = Valgmuligheder for skrivebordsvisning...
|
||||||
show-on-desktop = Vis på Skrivebordet
|
show-on-desktop = Vis på Skrivebord
|
||||||
desktop-folder-content = Indhold på Skrivebordet
|
desktop-folder-content = Skrivebords-mappeindhold
|
||||||
mounted-drives = Monterede drev
|
mounted-drives = Monterede drev
|
||||||
trash-folder-icon = Skraldespandsikon
|
trash-folder-icon = Papirkurv-ikon
|
||||||
icon-size-and-spacing = Ikonstørrelse og afstand
|
icon-size-and-spacing = Ikonstørrelse og -afstand
|
||||||
icon-size = Ikonstørrelse
|
icon-size = Ikonstørrelse
|
||||||
# List view
|
# List view
|
||||||
name = Navn
|
name = Navn
|
||||||
modified = Ændret
|
modified = Ændret
|
||||||
trashed-on = Smidt ud
|
trashed-on = Kasseret
|
||||||
size = Størrelse
|
size = Størrelse
|
||||||
# Progress footer
|
# Progress footer
|
||||||
details = Detaljer
|
details = Detaljer
|
||||||
dismiss = Afvis besked
|
dismiss = Luk meddelelse
|
||||||
operations-running = { $running } operationer er i gang ({ $percent }%)...
|
operations-running =
|
||||||
operations-running-finished = { $running } operationer er i gang ({ $percent }%), { $finished } færdiggjort...
|
{ $running } { $running ->
|
||||||
|
[one] operation
|
||||||
|
*[other] operationer
|
||||||
|
} kører ({ $percent }%)...
|
||||||
|
operations-running-finished =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] operation
|
||||||
|
*[other] operationer
|
||||||
|
} kører ({ $percent }%), { $finished } færdig...
|
||||||
pause = Pause
|
pause = Pause
|
||||||
resume = Fortsæt
|
resume = Genoptag
|
||||||
|
|
||||||
# Dialogs
|
# Dialogs
|
||||||
|
|
||||||
|
|
@ -40,12 +48,12 @@ create-archive = Opret arkiv
|
||||||
|
|
||||||
## Empty Trash Dialog
|
## Empty Trash Dialog
|
||||||
|
|
||||||
empty-trash = Tøm skraldespand
|
empty-trash = Tøm papirkurv
|
||||||
empty-trash-warning = Er du sikker på du vil slette alle objekter i Skraldespanden permanent?
|
empty-trash-warning = Emner i Papirkurv-mappen vil blive slettet permanent
|
||||||
|
|
||||||
## Mount Error Dialog
|
## Mount Error Dialog
|
||||||
|
|
||||||
mount-error = Kan ikke tilgå drev
|
mount-error = Ude af stand til at tilgå drev
|
||||||
|
|
||||||
## New File/Folder Dialog
|
## New File/Folder Dialog
|
||||||
|
|
||||||
|
|
@ -53,22 +61,22 @@ create-new-file = Opret ny fil
|
||||||
create-new-folder = Opret ny mappe
|
create-new-folder = Opret ny mappe
|
||||||
file-name = Filnavn
|
file-name = Filnavn
|
||||||
folder-name = Mappenavn
|
folder-name = Mappenavn
|
||||||
file-already-exists = En fil med det navn eksisterer allerede.
|
file-already-exists = En fil med det navn eksisterer allerede
|
||||||
folder-already-exists = En mappe med det navn eksisterer allerede.
|
folder-already-exists = En mappe med det navn eksisterer allerede
|
||||||
name-hidden = Navne begyndende med "." vil blive skjult.
|
name-hidden = Navne begyndende med "." vil være skjulte
|
||||||
name-invalid = Navn kan ikke være "{ $filename }".
|
name-invalid = Navn må ikke være "{ $filename }"
|
||||||
name-no-slashes = Navn kan ikke indeholde skråstreg.
|
name-no-slashes = Navn må ikke indeholde skråstreger
|
||||||
|
|
||||||
## Open/Save Dialog
|
## Open/Save Dialog
|
||||||
|
|
||||||
cancel = Annullér
|
cancel = Afbryd
|
||||||
create = Opret
|
create = Opret
|
||||||
open = Åbn
|
open = Åbn
|
||||||
open-file = Åbn fil
|
open-file = Åbn fil
|
||||||
open-folder = Åbn mappe
|
open-folder = Åbn mappe
|
||||||
open-in-new-tab = Åbn i ny fane
|
open-in-new-tab = Åbn i nyt faneblad
|
||||||
open-in-new-window = Åbn i nyt vindue
|
open-in-new-window = Åbn i nyt vindue
|
||||||
open-item-location = Åbn placering for objekt
|
open-item-location = Åbn emne-placering
|
||||||
open-multiple-files = Åbn flere filer
|
open-multiple-files = Åbn flere filer
|
||||||
open-multiple-folders = Åbn flere mapper
|
open-multiple-folders = Åbn flere mapper
|
||||||
save = Gem
|
save = Gem
|
||||||
|
|
@ -87,20 +95,20 @@ rename-folder = Omdøb mappe
|
||||||
## Replace Dialog
|
## Replace Dialog
|
||||||
|
|
||||||
replace = Erstat
|
replace = Erstat
|
||||||
replace-title = "{ $filename }" eksisterer allerede på denne placering.
|
replace-title = "{ $filename }" eksisterer allerede i denne placering
|
||||||
replace-warning = Vil du erstatte den med den du er ved at gemme? Hvis du erstatter den, overskriver du dens indhold.
|
replace-warning = Ønsker du at erstatte den med den, du er ved at gemme? Erstatning af den vil overskrive dens indhold.
|
||||||
replace-warning-operation = Vil du erstatte den? Hvis du erstatter den, overskriver du dens indhold.
|
replace-warning-operation = Ønsker du at erstatte den? Erstatning af den vil overskrive dens indhold.
|
||||||
original-file = Original fil
|
original-file = Original fil
|
||||||
replace-with = Erstat med
|
replace-with = Erstat med
|
||||||
apply-to-all = Anvend for alle
|
apply-to-all = Anvend på alle
|
||||||
keep-both = Behold begge
|
keep-both = Behold begge
|
||||||
skip = Spring over
|
skip = Spring over
|
||||||
|
|
||||||
## Set as Executable and Launch Dialog
|
## Set as Executable and Launch Dialog
|
||||||
|
|
||||||
set-executable-and-launch = Indstil som eksekverbar fil og start
|
set-executable-and-launch = Angiv som eksekverbar og start
|
||||||
set-executable-and-launch-description = Vil du indstille "{ $name }" som en eksekverbar fil og starte den?
|
set-executable-and-launch-description = Vil du angive "{ $name }" som eksekverbar og starte den?
|
||||||
set-and-launch = Indstil og start
|
set-and-launch = Angiv og start
|
||||||
|
|
||||||
## Metadata Dialog
|
## Metadata Dialog
|
||||||
|
|
||||||
|
|
@ -239,13 +247,13 @@ settings = Indstillinger
|
||||||
|
|
||||||
appearance = Udseende
|
appearance = Udseende
|
||||||
theme = Tema
|
theme = Tema
|
||||||
match-desktop = Match skrivebord
|
match-desktop = Følg skrivebordets indstillinger
|
||||||
dark = Mørk
|
dark = Mørkt
|
||||||
light = Lys
|
light = Lyst
|
||||||
# Context menu
|
# Context menu
|
||||||
add-to-sidebar = Tilføj til sidebjælke
|
add-to-sidebar = Tilføj til sidebjælke
|
||||||
compress = Komprimér
|
compress = Komprimer…
|
||||||
extract-here = Extract
|
extract-here = Uddrag
|
||||||
new-file = Ny fil...
|
new-file = Ny fil...
|
||||||
new-folder = Ny mappe...
|
new-folder = Ny mappe...
|
||||||
open-in-terminal = Åbn i terminal
|
open-in-terminal = Åbn i terminal
|
||||||
|
|
@ -280,15 +288,15 @@ quit = Afslut
|
||||||
edit = Redigér
|
edit = Redigér
|
||||||
cut = Klip
|
cut = Klip
|
||||||
copy = Kopiér
|
copy = Kopiér
|
||||||
paste = Sæt ind
|
paste = Indsæt
|
||||||
select-all = Vælg alt
|
select-all = Vælg alle
|
||||||
|
|
||||||
## View
|
## View
|
||||||
|
|
||||||
zoom-in = Zoom ind
|
zoom-in = Zoom ind
|
||||||
default-size = Standardstørrelse
|
default-size = Standardstørrelse
|
||||||
zoom-out = Zoom ud
|
zoom-out = Zoom ud
|
||||||
view = Visning
|
view = Vis
|
||||||
grid-view = Gitter-visning
|
grid-view = Gitter-visning
|
||||||
list-view = Liste-visning
|
list-view = Liste-visning
|
||||||
show-hidden-files = Vis skjulte filer
|
show-hidden-files = Vis skjulte filer
|
||||||
|
|
@ -306,3 +314,102 @@ sort-newest-first = Nyeste først
|
||||||
sort-oldest-first = Ældste først
|
sort-oldest-first = Ældste først
|
||||||
sort-smallest-to-largest = Mindste til største
|
sort-smallest-to-largest = Mindste til største
|
||||||
sort-largest-to-smallest = Største til mindste
|
sort-largest-to-smallest = Største til mindste
|
||||||
|
delete = Slet
|
||||||
|
run = Kør
|
||||||
|
repository = Pakkearkiv
|
||||||
|
grid-spacing = Gitter-afstand
|
||||||
|
extract-password-required = Adgangskode påkrævet
|
||||||
|
extract-to = Udpak Til...
|
||||||
|
extract-to-title = Udpak til mappe
|
||||||
|
empty-trash-title = Tøm papirkurv?
|
||||||
|
keywords = Mappe;Håndtering;
|
||||||
|
comment = Filhåndteringsprogram til COSMIC-skrivebordet
|
||||||
|
copy-to-title = Vælg kopi destination
|
||||||
|
copy-to-button-label = Kopier
|
||||||
|
move-to-title = Vælg flytte destination
|
||||||
|
move-to-button-label = Flyt
|
||||||
|
deleted =
|
||||||
|
Slettede { $items } { $items ->
|
||||||
|
[one] element
|
||||||
|
*[other] elementer
|
||||||
|
} fra { trash }
|
||||||
|
removing-from-recents =
|
||||||
|
Fjerner { $items } { $items ->
|
||||||
|
[one] element
|
||||||
|
*[other] elementer
|
||||||
|
} fra { recents }
|
||||||
|
type-to-search-enter-path = Indtast stien til mappen eller filen
|
||||||
|
permanently-deleting =
|
||||||
|
Sletter permanent { $items } { $items ->
|
||||||
|
[one] element
|
||||||
|
*[other] elementer
|
||||||
|
}
|
||||||
|
execute-only = Kun kørsel
|
||||||
|
removed-from-recents =
|
||||||
|
Fjernede { $items } { $items ->
|
||||||
|
[one] element
|
||||||
|
*[other] elementer
|
||||||
|
} fra { recents }
|
||||||
|
permanently-deleted =
|
||||||
|
Slettede permanent { $items } { $items ->
|
||||||
|
[one] element
|
||||||
|
*[other] elementer
|
||||||
|
}
|
||||||
|
write-only = Kun skrivning
|
||||||
|
remove = Fjern
|
||||||
|
context-action = Konteksthandling
|
||||||
|
context-action-confirm-title = Kør “{ $name }”?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Dette vil køre på { $items } { $items ->
|
||||||
|
[one] item
|
||||||
|
*[other] items
|
||||||
|
}.
|
||||||
|
selected-items = De { $items } valgte elementer
|
||||||
|
mixed = Blandet
|
||||||
|
none = Ingen
|
||||||
|
write-execute = Skriv og kør
|
||||||
|
read-only = Kun læsning
|
||||||
|
read-execute = Læs og kør
|
||||||
|
read-write = Læs og skriv
|
||||||
|
read-write-execute = Læs, skriv og kør
|
||||||
|
keep = Behold
|
||||||
|
support = Support
|
||||||
|
progress-failed = { $percent } %, mislykkedes
|
||||||
|
pasted-image = Indsat billede
|
||||||
|
pasted-text = Indsat tekst
|
||||||
|
pasted-video = Indsat video
|
||||||
|
deleting =
|
||||||
|
Sletter { $items } { $items ->
|
||||||
|
[one] element
|
||||||
|
*[other] elementer
|
||||||
|
} fra { trash } ({ $progress })...
|
||||||
|
setting-permissions = Indstiller tilladelser for “{ $name }” til { $mode }
|
||||||
|
set-permissions = Sæt tilladelser for “{ $name }” til { $mode }
|
||||||
|
show-recents = Seneste-mappe i sidepanelet
|
||||||
|
type-to-search = Skriv for at søge
|
||||||
|
type-to-search-recursive = Søger i den aktuelle mappe og alle undermapper
|
||||||
|
type-to-search-select = Vælger den første matchende fil eller mappe
|
||||||
|
clear-recents-history = Ryd seneste historik
|
||||||
|
copy-to = Kopiér til…
|
||||||
|
delete-permanently = Slet permanent
|
||||||
|
eject = Skub ud
|
||||||
|
move-to = Flyt til…
|
||||||
|
remove-from-recents = Fjern fra seneste
|
||||||
|
reload-folder = Genindlæs mappe
|
||||||
|
copy-path = Kopiér sti
|
||||||
|
rename-confirm = Omdøb
|
||||||
|
calculate = Udregn
|
||||||
|
error = Fejl
|
||||||
|
checksum = { $kind }-kontrolsum
|
||||||
|
other-apps = Andre applikationer
|
||||||
|
related-apps = Relaterede applikationer
|
||||||
|
permanently-delete-question = Slet permanent?
|
||||||
|
permanently-delete-warning = { $target } vil blive slettet permanent. Denne handling kan ikke fortrydes.
|
||||||
|
open-with = Åbn med
|
||||||
|
favorite-path-error = Fejl ved åbning af sti
|
||||||
|
favorite-path-error-description =
|
||||||
|
Ude af stand til at åbne "{ $path }"
|
||||||
|
"{ $path }" Eksisterer muligvis ikke, eller du har måske ikketilladelse til at åbne den
|
||||||
|
|
||||||
|
Ønsker du at fjerne den fra sidebjælken?
|
||||||
|
single-click = Enkelt klik for at åbne
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ recents = Zuletzt verwendet
|
||||||
undo = Rückgängig
|
undo = Rückgängig
|
||||||
today = Heute
|
today = Heute
|
||||||
# Optionen für die Desktop-Ansicht
|
# Optionen für die Desktop-Ansicht
|
||||||
desktop-view-options = Optionen für die Desktop-Ansicht…
|
desktop-view-options = Optionen für die Desktop-Ansicht ...
|
||||||
show-on-desktop = Auf Desktop anzeigen
|
show-on-desktop = Auf Desktop anzeigen
|
||||||
desktop-folder-content = Inhalt des Desktop-Ordners
|
desktop-folder-content = Inhalt des Desktop-Ordners
|
||||||
mounted-drives = Eingehängte Laufwerke
|
mounted-drives = Eingehängte Laufwerke
|
||||||
|
|
@ -31,12 +31,12 @@ operations-running =
|
||||||
{ $running } { $running ->
|
{ $running } { $running ->
|
||||||
[one] laufender Vorgang
|
[one] laufender Vorgang
|
||||||
*[other] laufende Vorgänge
|
*[other] laufende Vorgänge
|
||||||
} ({ $percent } %)...
|
} ({ $percent } %) ...
|
||||||
operations-running-finished =
|
operations-running-finished =
|
||||||
{ $running } { $running ->
|
{ $running } { $running ->
|
||||||
[one] laufender Vorgang
|
[one] laufender Vorgang
|
||||||
*[other] laufende Vorgänge
|
*[other] laufende Vorgänge
|
||||||
} ({ $percent } %), { $finished } abgeschlossen...
|
} ({ $percent } %), { $finished } abgeschlossen ...
|
||||||
pause = Pause
|
pause = Pause
|
||||||
resume = Fortsetzen
|
resume = Fortsetzen
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ create-archive = Archiv erstellen
|
||||||
## Entpacken-Dialog
|
## Entpacken-Dialog
|
||||||
|
|
||||||
extract-password-required = Passwort erforderlich
|
extract-password-required = Passwort erforderlich
|
||||||
extract-to = Entpacken nach...
|
extract-to = Entpacken nach ...
|
||||||
extract-to-title = In Ordner entpacken
|
extract-to-title = In Ordner entpacken
|
||||||
|
|
||||||
## Dialog zum Leeren des Papierkorbs
|
## Dialog zum Leeren des Papierkorbs
|
||||||
|
|
@ -179,7 +179,7 @@ keep = Behalten
|
||||||
add-network-drive = Netzlaufwerk hinzufügen
|
add-network-drive = Netzlaufwerk hinzufügen
|
||||||
connect = Verbinden
|
connect = Verbinden
|
||||||
connect-anonymously = Anonym verbinden
|
connect-anonymously = Anonym verbinden
|
||||||
connecting = Wird verbunden...
|
connecting = Wird verbunden ...
|
||||||
domain = Domain
|
domain = Domain
|
||||||
enter-server-address = Serveradresse eingeben
|
enter-server-address = Serveradresse eingeben
|
||||||
network-drive-description =
|
network-drive-description =
|
||||||
|
|
@ -218,7 +218,7 @@ compressing =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wird
|
[one] Element wird
|
||||||
*[other] Elemente werden
|
*[other] Elemente werden
|
||||||
} von „{ $from }“ nach „{ $to }“ komprimiert ({ $progress })...
|
} von „{ $from }“ nach „{ $to }“ komprimiert ({ $progress }) ...
|
||||||
compressed =
|
compressed =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wurde
|
[one] Element wurde
|
||||||
|
|
@ -231,7 +231,7 @@ copying =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wird
|
[one] Element wird
|
||||||
*[other] Elemente werden
|
*[other] Elemente werden
|
||||||
} von „{ $from }“ nach „{ $to }“ kopiert ({ $progress })...
|
} von „{ $from }“ nach „{ $to }“ kopiert ({ $progress }) ...
|
||||||
copied =
|
copied =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wurde
|
[one] Element wurde
|
||||||
|
|
@ -241,19 +241,19 @@ deleting =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wird
|
[one] Element wird
|
||||||
*[other] Elemente werden
|
*[other] Elemente werden
|
||||||
} aus dem { trash } gelöscht ({ $progress })...
|
} aus dem { trash } gelöscht ({ $progress }) ...
|
||||||
deleted =
|
deleted =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wurde
|
[one] Element wurde
|
||||||
*[other] Elemente wurden
|
*[other] Elemente wurden
|
||||||
} aus dem { trash } gelöscht
|
} aus dem { trash } gelöscht
|
||||||
emptying-trash = { trash } wird geleert ({ $progress })...
|
emptying-trash = { trash } wird geleert ({ $progress }) ...
|
||||||
emptied-trash = { trash } geleert
|
emptied-trash = { trash } geleert
|
||||||
extracting =
|
extracting =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wird
|
[one] Element wird
|
||||||
*[other] Elemente werden
|
*[other] Elemente werden
|
||||||
} von „{ $from }“ nach „{ $to }“ entpackt ({ $progress })...
|
} von „{ $from }“ nach „{ $to }“ entpackt ({ $progress }) ...
|
||||||
extracted =
|
extracted =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wurde
|
[one] Element wurde
|
||||||
|
|
@ -267,7 +267,7 @@ moving =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wird
|
[one] Element wird
|
||||||
*[other] Elemente werden
|
*[other] Elemente werden
|
||||||
} von „{ $from }“ nach „{ $to }“ verschoben ({ $progress })...
|
} von „{ $from }“ nach „{ $to }“ verschoben ({ $progress }) ...
|
||||||
moved =
|
moved =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wurde
|
[one] Element wurde
|
||||||
|
|
@ -289,7 +289,7 @@ restoring =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wird
|
[one] Element wird
|
||||||
*[other] Elemente werden
|
*[other] Elemente werden
|
||||||
} aus dem { trash } wiederhergestellt ({ $progress })...
|
} aus dem { trash } wiederhergestellt ({ $progress }) ...
|
||||||
restored =
|
restored =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] Element wurde
|
[one] Element wurde
|
||||||
|
|
@ -299,7 +299,7 @@ unknown-folder = unbekannter Ordner
|
||||||
|
|
||||||
## Öffnen mit
|
## Öffnen mit
|
||||||
|
|
||||||
menu-open-with = Öffnen mit...
|
menu-open-with = Öffnen mit ...
|
||||||
default-app = { $name } (Standard)
|
default-app = { $name } (Standard)
|
||||||
|
|
||||||
## Details anzeigen
|
## Details anzeigen
|
||||||
|
|
@ -311,7 +311,7 @@ item-size = Größe: { $size }
|
||||||
item-created = Erstellt: { $created }
|
item-created = Erstellt: { $created }
|
||||||
item-modified = Geändert: { $modified }
|
item-modified = Geändert: { $modified }
|
||||||
item-accessed = Zugegriffen: { $accessed }
|
item-accessed = Zugegriffen: { $accessed }
|
||||||
calculating = Wird berechnet...
|
calculating = Wird berechnet ...
|
||||||
|
|
||||||
## Einstellungen
|
## Einstellungen
|
||||||
|
|
||||||
|
|
@ -330,18 +330,18 @@ light = Hell
|
||||||
|
|
||||||
type-to-search = Zum Suchen tippen
|
type-to-search = Zum Suchen tippen
|
||||||
type-to-search-recursive = Durchsucht den aktuellen Ordner und alle Unterordner
|
type-to-search-recursive = Durchsucht den aktuellen Ordner und alle Unterordner
|
||||||
type-to-search-enter-path = Gibt den Pfad zu einem Verzeichnis oder einer Datei ein
|
type-to-search-enter-path = Gib den Pfad zum Verzeichnis oder zur Datei ein
|
||||||
# Kontextmenü
|
# Kontextmenü
|
||||||
add-to-sidebar = Zur Seitenleiste hinzufügen
|
add-to-sidebar = Zur Seitenleiste hinzufügen
|
||||||
compress = Komprimieren...
|
compress = Komprimieren ...
|
||||||
delete-permanently = Endgültig löschen
|
delete-permanently = Endgültig löschen
|
||||||
extract-here = Entpacken
|
extract-here = Entpacken
|
||||||
new-file = Neue Datei...
|
new-file = Neue Datei ...
|
||||||
new-folder = Neuer Ordner...
|
new-folder = Neuer Ordner ...
|
||||||
open-in-terminal = Im Terminal öffnen
|
open-in-terminal = Im Terminal öffnen
|
||||||
move-to-trash = In den Papierkorb verschieben
|
move-to-trash = In den Papierkorb verschieben
|
||||||
restore-from-trash = Aus dem Papierkorb wiederherstellen
|
restore-from-trash = Aus dem Papierkorb wiederherstellen
|
||||||
remove-from-sidebar = Von der Seitenleiste entfernen
|
remove-from-sidebar = Aus der Seitenleiste entfernen
|
||||||
sort-by-name = Nach Name sortieren
|
sort-by-name = Nach Name sortieren
|
||||||
sort-by-modified = Nach Änderung sortieren
|
sort-by-modified = Nach Änderung sortieren
|
||||||
sort-by-size = Nach Größe sortieren
|
sort-by-size = Nach Größe sortieren
|
||||||
|
|
@ -349,9 +349,9 @@ sort-by-trashed = Nach Löschzeitpunkt sortieren
|
||||||
|
|
||||||
## Desktop
|
## Desktop
|
||||||
|
|
||||||
change-wallpaper = Hintergrundbild ändern...
|
change-wallpaper = Hintergrundbild ändern ...
|
||||||
desktop-appearance = Desktop-Aussehen...
|
desktop-appearance = Desktop-Aussehen ...
|
||||||
display-settings = Anzeigeeinstellungen...
|
display-settings = Anzeigeeinstellungen ...
|
||||||
|
|
||||||
# Menü
|
# Menü
|
||||||
|
|
||||||
|
|
@ -362,7 +362,7 @@ file = Datei
|
||||||
new-tab = Neuer Tab
|
new-tab = Neuer Tab
|
||||||
new-window = Neues Fenster
|
new-window = Neues Fenster
|
||||||
reload-folder = Ordner neu laden
|
reload-folder = Ordner neu laden
|
||||||
rename = Umbenennen...
|
rename = Umbenennen ...
|
||||||
close-tab = Tab schließen
|
close-tab = Tab schließen
|
||||||
quit = Beenden
|
quit = Beenden
|
||||||
|
|
||||||
|
|
@ -376,28 +376,28 @@ select-all = Alles auswählen
|
||||||
|
|
||||||
## Ansicht
|
## Ansicht
|
||||||
|
|
||||||
zoom-in = Vergrößern
|
zoom-in = Hineinzoomen
|
||||||
default-size = Standardgröße
|
default-size = Standardgröße
|
||||||
zoom-out = Verkleinern
|
zoom-out = Herauszoomen
|
||||||
view = Ansicht
|
view = Ansicht
|
||||||
grid-view = Rasteransicht
|
grid-view = Rasteransicht
|
||||||
list-view = Listenansicht
|
list-view = Listenansicht
|
||||||
show-hidden-files = Versteckte Dateien anzeigen
|
show-hidden-files = Versteckte Dateien anzeigen
|
||||||
list-directories-first = Verzeichnisse zuerst auflisten
|
list-directories-first = Verzeichnisse zuerst auflisten
|
||||||
gallery-preview = Galerie-Vorschau
|
gallery-preview = Galerie-Vorschau
|
||||||
menu-settings = Einstellungen...
|
menu-settings = Einstellungen ...
|
||||||
menu-about = Über COSMIC Dateien...
|
menu-about = Über COSMIC Dateien ...
|
||||||
|
|
||||||
## Sortieren
|
## Sortieren
|
||||||
|
|
||||||
sort = Sortieren
|
sort = Sortierung
|
||||||
sort-a-z = A-Z
|
sort-a-z = A-Z
|
||||||
sort-z-a = Z-A
|
sort-z-a = Z-A
|
||||||
sort-newest-first = Neueste zuerst
|
sort-newest-first = Neueste zuerst
|
||||||
sort-oldest-first = Älteste zuerst
|
sort-oldest-first = Älteste zuerst
|
||||||
sort-smallest-to-largest = Von klein nach groß
|
sort-smallest-to-largest = Von klein nach groß
|
||||||
sort-largest-to-smallest = Von groß nach klein
|
sort-largest-to-smallest = Von groß nach klein
|
||||||
repository = Repository
|
repository = Repositorium
|
||||||
empty-trash-title = Papierkorb leeren?
|
empty-trash-title = Papierkorb leeren?
|
||||||
copy-to-title = Kopierziel auswählen
|
copy-to-title = Kopierziel auswählen
|
||||||
copy-to-button-label = Kopieren
|
copy-to-button-label = Kopieren
|
||||||
|
|
@ -418,9 +418,28 @@ type-to-search-select = Wählt die erste übereinstimmende Datei oder den ersten
|
||||||
pasted-image = Eingefügtes Bild
|
pasted-image = Eingefügtes Bild
|
||||||
pasted-text = Eingefügter Text
|
pasted-text = Eingefügter Text
|
||||||
pasted-video = Eingefügtes Video
|
pasted-video = Eingefügtes Video
|
||||||
copy-to = Kopieren nach...
|
copy-to = Kopieren nach ...
|
||||||
show-recents = Ordner zuletzt verwendeter Elemente in der Seitenleiste
|
show-recents = Ordner zuletzt verwendeter Elemente in der Seitenleiste
|
||||||
clear-recents-history = Verlauf zuletzt verwendeter Elemente leeren
|
clear-recents-history = Verlauf zuletzt verwendeter Elemente leeren
|
||||||
comment = Dateimanager für den COSMIC Desktop
|
comment = Dateimanager für den COSMIC Desktop
|
||||||
keywords = Ordner;Manager;
|
keywords = Ordner;Manager;
|
||||||
move-to-button-label = Verschieben
|
move-to-button-label = Verschieben
|
||||||
|
move-to-title = Verschiebeziel auswählen
|
||||||
|
remove-from-recents = Aus den zuletzt verwendeten Elementen entfernen
|
||||||
|
move-to = Verschieben nach ...
|
||||||
|
copy-path = Pfad kopieren
|
||||||
|
mixed = Gemischt
|
||||||
|
context-action = Kontextaktion
|
||||||
|
context-action-confirm-title = „{ $name }“ ausführen?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Dies wird ausgeführt auf { $items } { $items ->
|
||||||
|
[one] Element
|
||||||
|
*[other] Elementen
|
||||||
|
}.
|
||||||
|
run = Ausführen
|
||||||
|
rename-confirm = Umbenennen
|
||||||
|
checksum = { $kind } Prüfsumme
|
||||||
|
calculate = Berechnen
|
||||||
|
error = Fehler
|
||||||
|
sidebar-label = Bezeichnung in der Seitenleiste
|
||||||
|
search-application = Nach App-Name suchen
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,336 @@
|
||||||
empty-folder = Άδειος φάκελος
|
empty-folder = Άδειος φάκελος
|
||||||
no-results = Δεν βρέθηκαν αποτελέσματα
|
no-results = Δεν βρέθηκαν αποτελέσματα
|
||||||
trash = Κάδος Ανακύκλωσης
|
trash = Απορρίμματα
|
||||||
recents = Πρόσφατα
|
recents = Πρόσφατα
|
||||||
cosmic-files = COSMIC Αρχεία
|
cosmic-files = Αρχεία COSMIC
|
||||||
empty-folder-hidden = Άδειος φάκελος (περιέχει κρυφά αντικείμενα)
|
empty-folder-hidden = Άδειος φάκελος (περιέχει κρυφά στοιχεία)
|
||||||
filesystem = Σύστημα αρχείων
|
filesystem = Σύστημα αρχείων
|
||||||
home = Προσωπικός φάκελος
|
home = Προσωπικός φάκελος
|
||||||
networks = Δίκτυο
|
networks = Δίκτυα
|
||||||
comment = Αρχεία για το COSMIC περιβάλλον
|
comment = Διαχείριση αρχείων για το περιβάλλον επιφάνειας εργασίας COSMIC
|
||||||
keywords = Φάκελος;Διαχειριστής;
|
keywords = Αρχείο;Φάκελος;Διαχείριση;Folder;Manager;
|
||||||
rename = Μετονομασία...
|
rename = Μετονομασία...
|
||||||
close-tab = Κλείσιμο καρτέλας
|
close-tab = Κλείσιμο καρτέλας
|
||||||
light = Φωτεινό
|
light = Ανοιχτόχρωμο
|
||||||
dark = Σκοτεινό
|
dark = Σκουρόχρωμο
|
||||||
|
connect = Σύνδεση
|
||||||
|
dismiss = Απόρριψη μηνύματος
|
||||||
|
copy_noun = Αντιγραφή
|
||||||
|
open-file = Άνοιγμα αρχείου
|
||||||
|
save = Αποθήκευση
|
||||||
|
password = Κωδικός πρόσβασης
|
||||||
|
remove = Αφαίρεση
|
||||||
|
create = Δημιουργία
|
||||||
|
pause = Παύση
|
||||||
|
quit = Τερματισμός
|
||||||
|
calculating = Υπολογισμός...
|
||||||
|
keep = Διατήρηση
|
||||||
|
edit = Επεξεργασία
|
||||||
|
connecting = Σύνδεση...
|
||||||
|
copy = Αντιγραφή
|
||||||
|
theme = Θέμα
|
||||||
|
appearance = Εμφάνιση
|
||||||
|
name = Όνομα
|
||||||
|
resume = Συνέχιση
|
||||||
|
username = Όνομα χρήστη
|
||||||
|
delete = Διαγραφή
|
||||||
|
repository = Αποθετήριο
|
||||||
|
support = Υποστήριξη
|
||||||
|
eject = Εξαγωγή
|
||||||
|
group = Ομάδα
|
||||||
|
skip = Παράλειψη
|
||||||
|
paste = Επικόλληση
|
||||||
|
menu-settings = Ρυθμίσεις...
|
||||||
|
view = Προβολή
|
||||||
|
undo = Αναίρεση
|
||||||
|
details = Λεπτομέρειες
|
||||||
|
sort-a-z = Α-Ω
|
||||||
|
extract-here = Αποσυμπίεση
|
||||||
|
cancel = Ακύρωση
|
||||||
|
sort-z-a = Ω-Α
|
||||||
|
open = Άνοιγμα
|
||||||
|
history = Ιστορικό
|
||||||
|
domain = Τομέας
|
||||||
|
sort = Ταξινόμηση
|
||||||
|
settings = Ρυθμίσεις
|
||||||
|
pending = Σε εκκρεμότητα
|
||||||
|
open-folder = Άνοιγμα φακέλου
|
||||||
|
replace = Αντικατάσταση
|
||||||
|
cut = Αποκοπή
|
||||||
|
file = Αρχείο
|
||||||
|
today = Σήμερα
|
||||||
|
compress = Συμπίεση...
|
||||||
|
size = Μέγεθος
|
||||||
|
related-apps = Σχετικές εφαρμογές
|
||||||
|
zoom-in = Μεγέθυνση
|
||||||
|
select-all = Επιλογή όλων
|
||||||
|
icon-size-and-spacing = Μέγεθος και απόσταση εικονιδίων
|
||||||
|
new-window = Νέο παράθυρο
|
||||||
|
zoom-out = Σμίκρυνση
|
||||||
|
default-size = Προεπιλεγμένο μέγεθος
|
||||||
|
create-archive = Δημιουργία συμπιεσμένου αρχείου
|
||||||
|
other-apps = Άλλες εφαρμογές
|
||||||
|
rename-folder = Μετονομασία φακέλου
|
||||||
|
folder-name = Όνομα φακέλου
|
||||||
|
connect-anonymously = Ανώνυμη σύνδεση
|
||||||
|
replace-with = Αντικατάσταση με
|
||||||
|
mounted-drives = Προσαρτημένες μονάδες
|
||||||
|
desktop-view-options = Επιλογές προβολής επιφάνειας εργασίας...
|
||||||
|
show-on-desktop = Εμφάνιση στην επιφάνεια εργασίας
|
||||||
|
trash-folder-icon = Εικονίδιο φακέλου απορριμμάτων
|
||||||
|
open-with = Άνοιγμα με
|
||||||
|
keep-both = Διατήρηση αμφότερων
|
||||||
|
icon-size = Μέγεθος εικονιδίων
|
||||||
|
open-with-title = Πώς θέλετε να ανοίξετε το «{ $name }»;
|
||||||
|
extract-password-required = Απαιτείται κωδικός πρόσβασης
|
||||||
|
rename-file = Μετονομασία αρχείου
|
||||||
|
file-name = Όνομα αρχείου
|
||||||
|
save-file = Αποθήκευση αρχείου
|
||||||
|
name-hidden = Τα ονόματα που ξεκινούν με «.» θα αποκρύπτονται
|
||||||
|
folder-already-exists = Υπάρχει ήδη ένας φάκελος με αυτό το όνομα
|
||||||
|
empty-trash = Άδειασμα απορριμμάτων
|
||||||
|
permanently-delete-question = Οριστική διαγραφή;
|
||||||
|
copy-to-button-label = Αντιγραφή
|
||||||
|
move-to-button-label = Μετακίνηση
|
||||||
|
run = Εκτέλεση
|
||||||
|
copy-to-title = Επιλογή προορισμού αντιγραφής
|
||||||
|
sort-newest-first = Πρώτα τα νεότερα
|
||||||
|
default-app = { $name } (προεπιλογή)
|
||||||
|
renamed = Έγινε μετονομασία από «{ $from }» σε «{ $to }»
|
||||||
|
read-execute = Ανάγνωση και εκτέλεση
|
||||||
|
deleted =
|
||||||
|
Έγινε διαγραφή { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τα { trash }
|
||||||
|
item-modified = Ημερομηνία τροποποίησης: { $modified }
|
||||||
|
list-view = Προβολή λίστας
|
||||||
|
reload-folder = Επαναφόρτωση φακέλου
|
||||||
|
favorite-path-error = Σφάλμα ανοίγματος καταλόγου
|
||||||
|
progress = { $percent }%
|
||||||
|
remove-from-sidebar = Αφαίρεση από την πλαϊνή στήλη
|
||||||
|
restoring =
|
||||||
|
Ανάκτηση { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τα { trash } ({ $progress })...
|
||||||
|
network-drive-error = Αδυναμία πρόσβασης σε μονάδα δικτύου
|
||||||
|
gallery-preview = Προεπισκόπηση συλλογής
|
||||||
|
sort-smallest-to-largest = Από τα μικρότερα στα μεγαλύτερα
|
||||||
|
removing-from-recents =
|
||||||
|
Αφαίρεση { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τα { recents }
|
||||||
|
type-to-search-enter-path = Εισέρχεται στη διαδρομή προς τον κατάλογο ή το αρχείο
|
||||||
|
emptying-trash = Άδειασμα φακέλου «{ trash }» ({ $progress })...
|
||||||
|
trashed-on = Ημερομηνία διαγραφής
|
||||||
|
compressing =
|
||||||
|
Συμπίεση { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τον φάκελο «{ $from }» στο αρχείο «{ $to }» ({ $progress })...
|
||||||
|
move-to-trash = Μετακίνηση στα απορρίμματα
|
||||||
|
menu-about = Σχετικά με τα Αρχεία COSMIC...
|
||||||
|
setting-executable-and-launching = Ορισμός του «{ $name }» ως εκτελέσιμου και εκκίνηση
|
||||||
|
open-multiple-files = Άνοιγμα πολλαπλών αρχείων
|
||||||
|
menu-open-with = Άνοιγμα με...
|
||||||
|
extracted =
|
||||||
|
Έγινε αποσυμπίεση { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από το αρχείο «{ $from }» στον φάκελο «{ $to }»
|
||||||
|
create-new-folder = Δημιουργία νέου φακέλου
|
||||||
|
original-file = Πρωτότυπο αρχείο
|
||||||
|
read-write-execute = Ανάγνωση, εγγραφή και εκτέλεση
|
||||||
|
set-permissions = Έγινε ορισμός των δικαιωμάτων για το «{ $name }» σε: { $mode }
|
||||||
|
sort-by-size = Ταξινόμηση κατά μέγεθος
|
||||||
|
item-size = Μέγεθος: { $size }
|
||||||
|
permanently-deleting =
|
||||||
|
Οριστική διαγραφή { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
}
|
||||||
|
read-write = Ανάγνωση και εγγραφή
|
||||||
|
none = Κανένα
|
||||||
|
items = Στοιχεία: { $items }
|
||||||
|
type = Τύπος: { $mime }
|
||||||
|
compressed =
|
||||||
|
Έγινε συμπίεση { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τον φάκελο «{ $from }» στο αρχείο «{ $to }»
|
||||||
|
replace-warning = Θέλετε να το αντικαταστήσετε με αυτό που αποθηκεύετε; Αυτό θα οδηγήσει στην αντικατάσταση του περιεχομένου του.
|
||||||
|
new-file = Νέο αρχείο...
|
||||||
|
open-in-terminal = Άνοιγμα σε τερματικό
|
||||||
|
open-multiple-folders = Άνοιγμα πολλαπλών φακέλων
|
||||||
|
remember-password = Απομνημόνευση κωδικού πρόσβασης
|
||||||
|
show-details = Εμφάνιση λεπτομερειών
|
||||||
|
grid-spacing = Απόσταση πλέγματος
|
||||||
|
extract-to = Αποσυμπίεση σε...
|
||||||
|
add-network-drive = Προσθήκη μονάδας δικτύου
|
||||||
|
copying =
|
||||||
|
Αντιγραφή { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τον φάκελο «{ $from }» στον φάκελο «{ $to }» ({ $progress })...
|
||||||
|
sort-oldest-first = Πρώτα τα παλαιότερα
|
||||||
|
create-new-file = Δημιουργία νέου αρχείου
|
||||||
|
sort-by-trashed = Ταξινόμηση κατά ημερομηνία διαγραφής
|
||||||
|
replace-warning-operation = Θέλετε να το αντικαταστήσετε; Αυτό θα οδηγήσει στην αντικατάσταση του περιεχομένου του.
|
||||||
|
try-again = Δοκιμή ξανά
|
||||||
|
copied =
|
||||||
|
Έγινε αντιγραφή { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τον φάκελο «{ $from }» στον φάκελο «{ $to }»
|
||||||
|
other = Άλλο
|
||||||
|
open-in-new-window = Άνοιγμα σε νέο παράθυρο
|
||||||
|
sort-by-modified = Ταξινόμηση κατά ημερομηνία τροποποίησης
|
||||||
|
list-directories-first = Παράθεση των καταλόγων πρώτα
|
||||||
|
read-only = Μόνο ανάγνωση
|
||||||
|
browse-store = Περιήγηση στο { $store }
|
||||||
|
enter-server-address = Εισαγάγετε τη διεύθυνση διακομιστή
|
||||||
|
remove-from-recents = Αφαίρεση από τα πρόσφατα
|
||||||
|
apply-to-all = Εφαρμογή σε όλα
|
||||||
|
moving =
|
||||||
|
Μετακίνηση { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τον φάκελο «{ $from }» στον φάκελο «{ $to }» ({ $progress })...
|
||||||
|
change-wallpaper = Αλλαγή ταπετσαρίας...
|
||||||
|
network-drive-description =
|
||||||
|
Οι διευθύνσεις διακομιστών αποτελούνται από ένα πρόθεμα πρωτοκόλλου και μια διεύθυνση.
|
||||||
|
Παραδείγματα: ssh://192.168.0.1, ftp://[2001:db8::1]
|
||||||
|
deleting =
|
||||||
|
Διαγραφή { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τα { trash } ({ $progress })...
|
||||||
|
single-click = Μονό κλικ για άνοιγμα
|
||||||
|
setting-permissions = Ορισμός των δικαιωμάτων για το «{ $name }» σε: { $mode }
|
||||||
|
owner = Κάτοχος
|
||||||
|
creating = Δημιουργία του «{ $name }» στον φάκελο «{ $parent }»
|
||||||
|
execute-only = Μόνο εκτέλεση
|
||||||
|
open-item-location = Άνοιγμα τοποθεσίας στοιχείου
|
||||||
|
set-executable-and-launched = Έγινε ορισμός του «{ $name }» ως εκτελέσιμου και εκκινήθηκε
|
||||||
|
mount-error = Αδυναμία πρόσβασης στη μονάδα
|
||||||
|
grid-view = Προβολή πλέγματος
|
||||||
|
set-and-launch = Ορισμός και εκκίνηση
|
||||||
|
removed-from-recents =
|
||||||
|
Έγινε αφαίρεση { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τα { recents }
|
||||||
|
add-to-sidebar = Προσθήκη στην πλαϊνή στήλη
|
||||||
|
item-created = Ημερομηνία δημιουργίας: { $created }
|
||||||
|
network-drive-schemes =
|
||||||
|
Διαθέσιμα πρωτόκολλα,Πρόθεμα
|
||||||
|
AppleTalk,afp://
|
||||||
|
File Transfer Protocol,ftp:// ή ftps://
|
||||||
|
Network File System,nfs://
|
||||||
|
Server Message Block,smb://
|
||||||
|
SSH File Transfer Protocol,sftp:// ή ssh://
|
||||||
|
WebDAV,dav:// ή davs://
|
||||||
|
set-executable-and-launch = Ορισμός ως εκτελέσιμο και εκκίνηση
|
||||||
|
restored =
|
||||||
|
Έγινε ανάκτηση { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τα { trash }
|
||||||
|
type-to-search-recursive = Κάνει αναζήτηση στον τρέχοντα φάκελο και όλους τους υποφακέλους
|
||||||
|
progress-paused = { $percent }%, σε παύση
|
||||||
|
cancelled = Ακυρωμένες
|
||||||
|
new-folder = Νέος φάκελος...
|
||||||
|
match-desktop = Συμφωνία με την επιφάνεια εργασίας
|
||||||
|
operations-running-finished =
|
||||||
|
Εκτέλεση { $running } { $running ->
|
||||||
|
[one] διεργασίας
|
||||||
|
*[other] διεργασιών
|
||||||
|
} ({ $percent }%), { $finished } ολοκληρωμένες...
|
||||||
|
sort-by-name = Ταξινόμηση κατά όνομα
|
||||||
|
edit-history = Ιστορικό επεξεργασιών
|
||||||
|
show-hidden-files = Εμφάνιση κρυφών αρχείων
|
||||||
|
progress-failed = { $percent }%, απέτυχε
|
||||||
|
item-accessed = Ημερομηνία πρόσβασης: { $accessed }
|
||||||
|
extract-to-title = Αποσυμπίεση σε φάκελο
|
||||||
|
extracting =
|
||||||
|
Αποσυμπίεση { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από το αρχείο «{ $from }» στον φάκελο «{ $to }» ({ $progress })...
|
||||||
|
permanently-deleted =
|
||||||
|
Έγινε οριστική διαγραφή { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
}
|
||||||
|
complete = Ολοκληρωμένες
|
||||||
|
write-execute = Εγγραφή και εκτέλεση
|
||||||
|
desktop-folder-content = Περιεχόμενο φακέλου επιφάνειας εργασίας
|
||||||
|
renaming = Μετονομασία από «{ $from }» σε «{ $to }»
|
||||||
|
set-executable-and-launch-description = Θέλετε να ορίσετε το «{ $name }» ως εκτελέσιμο και να το εκκινήσετε;
|
||||||
|
no-history = Δεν υπάρχουν στοιχεία στο ιστορικό.
|
||||||
|
emptied-trash = Έγινε άδειασμα του φακέλου «{ trash }»
|
||||||
|
sort-largest-to-smallest = Από τα μεγαλύτερα στα μικρότερα
|
||||||
|
restore-from-trash = Ανάκτηση από τα απορρίμματα
|
||||||
|
moved =
|
||||||
|
Έγινε μετακίνηση { $items } { $items ->
|
||||||
|
[one] στοιχείου
|
||||||
|
*[other] στοιχείων
|
||||||
|
} από τον φάκελο «{ $from }» στον φάκελο «{ $to }»
|
||||||
|
progress-cancelled = { $percent }%, ακυρώθηκε
|
||||||
|
open-in-new-tab = Άνοιγμα σε νέα καρτέλα
|
||||||
|
unknown-folder = άγνωστος φάκελος
|
||||||
|
created = Έγινε δημιουργία του «{ $name }» στον φάκελο «{ $parent }»
|
||||||
|
delete-permanently = Οριστική διαγραφή
|
||||||
|
write-only = Μόνο εγγραφή
|
||||||
|
display-settings = Ρυθμίσεις οθόνης...
|
||||||
|
new-tab = Νέα καρτέλα
|
||||||
|
failed = Αποτυχημένες
|
||||||
|
modified = Ημερομηνία τροποποίησης
|
||||||
|
desktop-appearance = Εμφάνιση επιφάνειας εργασίας...
|
||||||
|
file-already-exists = Υπάρχει ήδη ένα αρχείο με αυτό το όνομα
|
||||||
|
permanently-delete-warning = Θα διαγραφούν οριστικά τα εξής: { $target }. Δεν είναι δυνατή η αναίρεση αυτής της ενέργειας.
|
||||||
|
favorite-path-error-description =
|
||||||
|
Αδυναμία ανοίγματος του «{ $path }»
|
||||||
|
Το «{ $path }» ενδέχεται να μην υπάρχει ή να μην έχετε το δικαίωμα να το ανοίξετε
|
||||||
|
|
||||||
|
Θέλετε να το αφαιρέσετε από την πλαϊνή στήλη;
|
||||||
|
empty-trash-warning = Τα στοιχεία του φακέλου «Απορρίμματα» θα διαγραφούν οριστικά
|
||||||
|
empty-trash-title = Άδειασμα απορριμμάτων;
|
||||||
|
type-to-search = Πληκτρολόγηση για αναζήτηση
|
||||||
|
notification-in-progress = Βρίσκονται σε εξέλιξη διεργασίες αρχείων
|
||||||
|
name-no-slashes = Το όνομα δεν μπορεί να περιέχει καθέτους
|
||||||
|
replace-title = Το «{ $filename }» υπάρχει ήδη σε αυτήν την τοποθεσία
|
||||||
|
name-invalid = Το όνομα δεν μπορεί να είναι «{ $filename }»
|
||||||
|
operations-running =
|
||||||
|
Εκτέλεση { $running } { $running ->
|
||||||
|
[one] διεργασίας
|
||||||
|
*[other] διεργασιών
|
||||||
|
} ({ $percent }%)...
|
||||||
|
context-action-confirm-title = Εκτέλεση του «{ $name }»;
|
||||||
|
pasted-image = Επικολλημένη εικόνα
|
||||||
|
pasted-text = Επικολλημένο κείμενο
|
||||||
|
pasted-video = Επικολλημένο βίντεο
|
||||||
|
show-recents = Φάκελος «Πρόσφατα» στην πλαϊνή στήλη
|
||||||
|
move-to = Μετακίνηση σε...
|
||||||
|
copy-path = Αντιγραφή διαδρομής
|
||||||
|
move-to-title = Επιλογή προορισμού μετακίνησης
|
||||||
|
selected-items = { $items } επιλεγμένα στοιχεία
|
||||||
|
context-action = Ενέργεια περιβάλλοντος
|
||||||
|
copy-to = Αντιγραφή σε...
|
||||||
|
mixed = Μικτό
|
||||||
|
type-to-search-select = Επιλέγει την πρώτη αντιστοιχία αρχείου ή φακέλου
|
||||||
|
clear-recents-history = Απαλοιφή ιστορικού πρόσφατων
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Θα εκτελεστεί σε { $items } { $items ->
|
||||||
|
[one] στοιχείο
|
||||||
|
*[other] στοιχεία
|
||||||
|
}.
|
||||||
|
rename-confirm = Μετονομασία
|
||||||
|
checksum = Άθροισμα ελέγχου { $kind }
|
||||||
|
calculate = Υπολογισμός
|
||||||
|
error = Σφάλμα
|
||||||
|
sidebar-label = Ετικέτα πλαϊνής στήλης
|
||||||
|
search-application = Αναζήτηση με όνομα εφαρμογής
|
||||||
|
change-sidebar-label = Αλλαγή ετικέτας πλαϊνής στήλης
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
save = Save
|
||||||
|
quit = Quit
|
||||||
|
name = Name
|
||||||
|
delete = Delete
|
||||||
|
cancel = Cancel
|
||||||
|
run = Run
|
||||||
|
new-window = New window
|
||||||
|
theme = Theme
|
||||||
|
appearance = Appearance
|
||||||
|
light = Light
|
||||||
|
repository = Repository
|
||||||
|
support = Support
|
||||||
|
dark = Dark
|
||||||
|
match-desktop = Match desktop
|
||||||
|
settings = Settings
|
||||||
|
replace = Replace
|
||||||
|
file = File
|
||||||
|
zoom-in = Zoom in
|
||||||
|
select-all = Select all
|
||||||
|
zoom-out = Zoom out
|
||||||
|
default-size = Default size
|
||||||
|
edit = Edit
|
||||||
|
copy = Copy
|
||||||
|
paste = Paste
|
||||||
|
view = View
|
||||||
|
undo = Undo
|
||||||
|
cut = Cut
|
||||||
|
icon-size-and-spacing = Icon size and spacing
|
||||||
|
cosmic-files = COSMIC Files
|
||||||
|
trash = Rubbish
|
||||||
|
empty-folder-hidden = Empty folder (has hidden items)
|
||||||
|
no-results = No results found
|
||||||
|
empty-folder = Empty folder
|
||||||
|
menu-settings = Settings...
|
||||||
|
recents = Recents
|
||||||
|
mounted-drives = Mounted drives
|
||||||
|
filesystem = Filesystem
|
||||||
|
home = Home
|
||||||
|
desktop-view-options = Desktop view options...
|
||||||
|
show-on-desktop = Show on Desktop
|
||||||
|
trash-folder-icon = Bin folder icon
|
||||||
|
desktop-folder-content = Desktop folder content
|
||||||
|
networks = Networks
|
||||||
|
today = Today
|
||||||
|
notification-in-progress = File operations are in progress
|
||||||
|
comment = File manager for the COSMIC desktop
|
||||||
|
keywords = Folder;Manager;
|
||||||
|
size = Size
|
||||||
|
dismiss = Dismiss message
|
||||||
|
trashed-on = Binned
|
||||||
|
create-archive = Create archive
|
||||||
|
pause = Pause
|
||||||
|
resume = Resume
|
||||||
|
grid-spacing = Grid spacing
|
||||||
|
details = Details
|
||||||
|
operations-running-finished =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] operation
|
||||||
|
*[other] operations
|
||||||
|
} running ({ $percent }%), { $finished } finished...
|
||||||
|
icon-size = Icon size
|
||||||
|
modified = Modified
|
||||||
|
operations-running =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] operation
|
||||||
|
*[other] operations
|
||||||
|
} running ({ $percent }%)...
|
||||||
|
copy-to-title = Select copy destination
|
||||||
|
open-file = Open file
|
||||||
|
open-multiple-files = Open multiple files
|
||||||
|
create-new-folder = Create new folder
|
||||||
|
create = Create
|
||||||
|
other-apps = Other applications
|
||||||
|
open-multiple-folders = Open multiple folders
|
||||||
|
extract-to = Extract To...
|
||||||
|
create-new-file = Create new file
|
||||||
|
open-in-new-window = Open in new window
|
||||||
|
folder-name = Folder name
|
||||||
|
browse-store = Browse { $store }
|
||||||
|
open-item-location = Open item location
|
||||||
|
mount-error = Unable to access drive
|
||||||
|
open = Open
|
||||||
|
extract-to-title = Extract to folder
|
||||||
|
open-with-title = How do you want to open "{ $name }"?
|
||||||
|
extract-password-required = Password required
|
||||||
|
open-folder = Open folder
|
||||||
|
open-in-new-tab = Open in new tab
|
||||||
|
file-name = File name
|
||||||
|
save-file = Save file
|
||||||
|
file-already-exists = A file with that name already exists
|
||||||
|
name-hidden = Names starting with "." will be hidden
|
||||||
|
folder-already-exists = A folder with that name already exists
|
||||||
|
empty-trash = Empty bin
|
||||||
|
empty-trash-title = Empty bin?
|
||||||
|
name-no-slashes = Name cannot contain slashes
|
||||||
|
name-invalid = Name cannot be "{ $filename }"
|
||||||
|
copy-to-button-label = Copy
|
||||||
|
move-to-title = Select move destination
|
||||||
|
move-to-button-label = Move
|
||||||
|
context-action = Context action
|
||||||
|
|
@ -96,9 +96,17 @@ save-file = Save file
|
||||||
|
|
||||||
## Open With Dialog
|
## Open With Dialog
|
||||||
open-with-title = How do you want to open "{$name}"?
|
open-with-title = How do you want to open "{$name}"?
|
||||||
|
open-with-set-default = Always use this app for this file type
|
||||||
browse-store = Browse {$store}
|
browse-store = Browse {$store}
|
||||||
other-apps = Other applications
|
other-apps = Other applications
|
||||||
related-apps = Related applications
|
related-apps = Related applications
|
||||||
|
context-action = Context action
|
||||||
|
context-action-confirm-title = Run "{$name}"?
|
||||||
|
context-action-confirm-warning = This will run on {$items} {$items ->
|
||||||
|
[one] item
|
||||||
|
*[other] items
|
||||||
|
}.
|
||||||
|
run = Run
|
||||||
|
|
||||||
## Permanently delete Dialog
|
## Permanently delete Dialog
|
||||||
selected-items = The {$items} selected items
|
selected-items = The {$items} selected items
|
||||||
|
|
@ -109,6 +117,10 @@ permanently-delete-warning = {$target} will be permanently deleted. This action
|
||||||
## Rename Dialog
|
## Rename Dialog
|
||||||
rename-file = Rename file
|
rename-file = Rename file
|
||||||
rename-folder = Rename folder
|
rename-folder = Rename folder
|
||||||
|
rename-confirm = Rename
|
||||||
|
|
||||||
|
## Change Sidebar Label Dialog
|
||||||
|
sidebar-label = Sidebar label
|
||||||
|
|
||||||
## Replace Dialog
|
## Replace Dialog
|
||||||
replace = Replace
|
replace = Replace
|
||||||
|
|
@ -131,6 +143,12 @@ open-with = Open with
|
||||||
owner = Owner
|
owner = Owner
|
||||||
group = Group
|
group = Group
|
||||||
other = Other
|
other = Other
|
||||||
|
toolbar = Toolbar
|
||||||
|
toolbar-available = Available
|
||||||
|
toolbar-empty-hint = No buttons. Drag or add from below.
|
||||||
|
toolbar-reset = Reset to defaults
|
||||||
|
parent-directory = Parent directory
|
||||||
|
mixed = Mixed
|
||||||
### Mode 0
|
### Mode 0
|
||||||
none = None
|
none = None
|
||||||
### Mode 1 (unusual)
|
### Mode 1 (unusual)
|
||||||
|
|
@ -284,6 +302,7 @@ unknown-folder = unknown folder
|
||||||
## Open with
|
## Open with
|
||||||
menu-open-with = Open with...
|
menu-open-with = Open with...
|
||||||
default-app = {$name} (default)
|
default-app = {$name} (default)
|
||||||
|
search-application = Search by app name
|
||||||
|
|
||||||
## Show details
|
## Show details
|
||||||
show-details = Show details
|
show-details = Show details
|
||||||
|
|
@ -294,6 +313,9 @@ item-created = Created: {$created}
|
||||||
item-modified = Modified: {$modified}
|
item-modified = Modified: {$modified}
|
||||||
item-accessed = Accessed: {$accessed}
|
item-accessed = Accessed: {$accessed}
|
||||||
calculating = Calculating...
|
calculating = Calculating...
|
||||||
|
checksum = {$kind} checksum
|
||||||
|
calculate = Calculate
|
||||||
|
error = Error
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
settings = Settings
|
settings = Settings
|
||||||
|
|
@ -324,10 +346,12 @@ extract-here = Extract
|
||||||
new-file = New file...
|
new-file = New file...
|
||||||
new-folder = New folder...
|
new-folder = New folder...
|
||||||
open-in-terminal = Open in terminal
|
open-in-terminal = Open in terminal
|
||||||
|
edit-desktop-entry = Edit desktop entry
|
||||||
move-to = Move to...
|
move-to = Move to...
|
||||||
move-to-trash = Move to trash
|
move-to-trash = Move to trash
|
||||||
restore-from-trash = Restore from trash
|
restore-from-trash = Restore from trash
|
||||||
remove-from-sidebar = Remove from sidebar
|
remove-from-sidebar = Remove from sidebar
|
||||||
|
change-sidebar-label = Change sidebar label
|
||||||
sort-by-name = Sort by name
|
sort-by-name = Sort by name
|
||||||
sort-by-modified = Sort by modified
|
sort-by-modified = Sort by modified
|
||||||
sort-by-size = Sort by size
|
sort-by-size = Sort by size
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ no-results = No se encontraron resultados
|
||||||
filesystem = Sistema de archivos
|
filesystem = Sistema de archivos
|
||||||
home = Inicio
|
home = Inicio
|
||||||
networks = Redes
|
networks = Redes
|
||||||
notification-in-progress = Las operaciones de archivo están en progreso.
|
notification-in-progress = Las operaciones de archivo están en progreso
|
||||||
trash = Papelera
|
trash = Papelera
|
||||||
recents = Recientes
|
recents = Recientes
|
||||||
undo = Deshacer
|
undo = Deshacer
|
||||||
|
|
|
||||||
0
i18n/eu/cosmic_files.ftl
Normal file
0
i18n/eu/cosmic_files.ftl
Normal file
|
|
@ -345,3 +345,4 @@ sort-newest-first = جدیدترین در ابتدا
|
||||||
sort-oldest-first = قدیمیترین در ابتدا
|
sort-oldest-first = قدیمیترین در ابتدا
|
||||||
sort-smallest-to-largest = کوچکترین به بزرگترین
|
sort-smallest-to-largest = کوچکترین به بزرگترین
|
||||||
sort-largest-to-smallest = بزرگترین به کوچکترین
|
sort-largest-to-smallest = بزرگترین به کوچکترین
|
||||||
|
run = اجرا
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ no-results = Ei tuloksia
|
||||||
filesystem = Tiedostojärjestelmä
|
filesystem = Tiedostojärjestelmä
|
||||||
home = Koti
|
home = Koti
|
||||||
networks = Verkot
|
networks = Verkot
|
||||||
notification-in-progress = Tiedostotoimintoja käynnissä.
|
notification-in-progress = Tiedostotoimintoja käynnissä
|
||||||
trash = Roskakori
|
trash = Roskakori
|
||||||
recents = Viimeaikaiset
|
recents = Viimeaikaiset
|
||||||
undo = Kumoa
|
undo = Kumoa
|
||||||
|
|
@ -16,7 +16,7 @@ today = Tänään
|
||||||
desktop-view-options = Työpöytänäkymän asetukset…
|
desktop-view-options = Työpöytänäkymän asetukset…
|
||||||
show-on-desktop = Näytä työpöydällä
|
show-on-desktop = Näytä työpöydällä
|
||||||
desktop-folder-content = Työpöytäkansion sisältö
|
desktop-folder-content = Työpöytäkansion sisältö
|
||||||
mounted-drives = Tiedostojärjestelmään liitetyt kovalevyt
|
mounted-drives = Liitetyt asemat
|
||||||
trash-folder-icon = Roskakorikansion kuvake
|
trash-folder-icon = Roskakorikansion kuvake
|
||||||
icon-size-and-spacing = Kuvakkeen koko ja välistys
|
icon-size-and-spacing = Kuvakkeen koko ja välistys
|
||||||
icon-size = Kuvakkeen koko
|
icon-size = Kuvakkeen koko
|
||||||
|
|
@ -38,7 +38,7 @@ create-archive = Luo arkisto
|
||||||
## Empty Trash Dialog
|
## Empty Trash Dialog
|
||||||
|
|
||||||
empty-trash = Tyhjennä roskakori
|
empty-trash = Tyhjennä roskakori
|
||||||
empty-trash-warning = Haluatko varmasti tyhjentää koko roskakorin pysyvästi?
|
empty-trash-warning = Roskakorikansion kohteet poistetaan pysyvästi
|
||||||
|
|
||||||
## Mount Error Dialog
|
## Mount Error Dialog
|
||||||
|
|
||||||
|
|
@ -73,8 +73,8 @@ save-file = Tallenna tiedosto
|
||||||
|
|
||||||
## Open With Dialog
|
## Open With Dialog
|
||||||
|
|
||||||
open-with-title = Kuinka haluat avata kohteen "{ $name }"?
|
open-with-title = Miten haluat avata kohteen "{ $name }"?
|
||||||
browse-store = Selaa { $store }
|
browse-store = Selaa { $store }a
|
||||||
|
|
||||||
## Rename Dialog
|
## Rename Dialog
|
||||||
|
|
||||||
|
|
@ -88,15 +88,15 @@ replace-title = "{ $filename }" on jo olemassa tässä sijainnissa
|
||||||
replace-warning = Haluatko korvata sen tallentamallasi kohteella? Korvaaminen ylikirjoittaa kohteen sisällön.
|
replace-warning = Haluatko korvata sen tallentamallasi kohteella? Korvaaminen ylikirjoittaa kohteen sisällön.
|
||||||
replace-warning-operation = Haluatko korvata sen? Korvaaminen ylikirjoittaa sen sisällön.
|
replace-warning-operation = Haluatko korvata sen? Korvaaminen ylikirjoittaa sen sisällön.
|
||||||
original-file = Alkuperäinen tiedosto
|
original-file = Alkuperäinen tiedosto
|
||||||
replace-with = Korvaa kohteella
|
replace-with = Korvaa käyttäen
|
||||||
apply-to-all = Sovella kaikkiin
|
apply-to-all = Toteuta kaikkiin
|
||||||
keep-both = Pidä molemmat
|
keep-both = Pidä molemmat
|
||||||
skip = Ohita
|
skip = Ohita
|
||||||
|
|
||||||
## Set as Executable and Launch Dialog
|
## Set as Executable and Launch Dialog
|
||||||
|
|
||||||
set-executable-and-launch = Aseta käynnistettäväksi ja käynnistä
|
set-executable-and-launch = Aseta käynnistettäväksi ja käynnistä
|
||||||
set-executable-and-launch-description = Haluatko asettaa kohteen "{ $name }" käynnistettväksi ja käynnistää sen?
|
set-executable-and-launch-description = Haluatko asettaa kohteen "{ $name }" käynnistettäväksi ja käynnistää sen?
|
||||||
set-and-launch = Aseta ja käynnistä
|
set-and-launch = Aseta ja käynnistä
|
||||||
|
|
||||||
## Metadata Dialog
|
## Metadata Dialog
|
||||||
|
|
@ -116,9 +116,9 @@ other = Muut
|
||||||
add-network-drive = Lisää verkkolevy
|
add-network-drive = Lisää verkkolevy
|
||||||
connect = Yhdistä
|
connect = Yhdistä
|
||||||
connect-anonymously = Yhdistä nimettömästi
|
connect-anonymously = Yhdistä nimettömästi
|
||||||
connecting = Yhdistää…
|
connecting = Yhdistetään…
|
||||||
domain = Verkkotunnus
|
domain = Verkkotunnus
|
||||||
enter-server-address = Syötä palvelimen osoite
|
enter-server-address = Kirjoita palvelimen osoite
|
||||||
network-drive-description =
|
network-drive-description =
|
||||||
Palvelinosoitteet sisältävät protokollaetuliitteen sekä osoitteen.
|
Palvelinosoitteet sisältävät protokollaetuliitteen sekä osoitteen.
|
||||||
Esimerkkejä: ssh://192.168.0.1, ftp://[2001:db8::1]
|
Esimerkkejä: ssh://192.168.0.1, ftp://[2001:db8::1]
|
||||||
|
|
@ -126,91 +126,91 @@ network-drive-description =
|
||||||
### Make sure to keep the comma which separates the columns
|
### Make sure to keep the comma which separates the columns
|
||||||
|
|
||||||
network-drive-schemes =
|
network-drive-schemes =
|
||||||
Saatavissa olevat protokollat,Etuliite
|
Saatavilla olevat yhteyskäytännöt,Etuliite
|
||||||
AppleTalk,afp://
|
AppleTalk,afp://
|
||||||
File Transfer Protocol,ftp:// or ftps://
|
File Transfer Protocol,ftp:// tai ftps://
|
||||||
Network File System,nfs://
|
Network File System,nfs://
|
||||||
Server Message Block,smb://
|
Server Message Block,smb://
|
||||||
SSH File Transfer Protocol,sftp:// or ssh://
|
SSH File Transfer Protocol,sftp:// tai ssh://
|
||||||
WebDav,dav:// or davs://
|
WebDav,dav:// tai davs://
|
||||||
network-drive-error = Verkkolevy saavuttamattomissa
|
network-drive-error = Verkkolevy ei saatavilla
|
||||||
password = Salasana
|
password = Salasana
|
||||||
remember-password = Muista salasana
|
remember-password = Muista salasana
|
||||||
try-again = Yritä uudelleen
|
try-again = Yritä uudelleen
|
||||||
username = Käyttäjänimi
|
username = Käyttäjätunnus
|
||||||
|
|
||||||
## Operations
|
## Operations
|
||||||
|
|
||||||
edit-history = Muokkaa historiaa
|
edit-history = Muokkaa historiaa
|
||||||
history = Historia
|
history = Historia
|
||||||
no-history = Historia on tyhjä.
|
no-history = Historia on tyhjä.
|
||||||
pending = Odottaa käsittelyä
|
pending = Jonossa
|
||||||
failed = Epäonnistui
|
failed = Epäonnistuneet
|
||||||
complete = Valmis
|
complete = Valmiit
|
||||||
compressing =
|
compressing =
|
||||||
Tiivistetään { $items } { $items ->
|
Pakataan { $items } { $items ->
|
||||||
[one] kohde
|
[one] kohde
|
||||||
*[other] kohteita
|
*[other] kohdetta
|
||||||
} lähteestä "{ $from }" arkistoon "{ $to }"
|
} sijainnista "{ $from }" arkistoon "{ $to }" ({ $progress })…
|
||||||
compressed =
|
compressed =
|
||||||
Tiivistetty { $items } { $items ->
|
Pakattu { $items } { $items ->
|
||||||
[one] kohde
|
[one] kohde
|
||||||
*[other] kohteet
|
*[other] kohdetta
|
||||||
} lähteestä "{ $from }" arkistoon "{ $to }"
|
} sijainnista "{ $from }" arkistoon "{ $to }"
|
||||||
copy_noun = Kopio
|
copy_noun = Kopio
|
||||||
creating = Luodaan kohdetta "{ $name }" kohteen "{ $parent }" alle
|
creating = Luodaan "{ $name }" kohteen "{ $parent }" alle
|
||||||
created = Luotu kohde "{ $name }" kohteen "{ $parent }" alle
|
created = Luotu "{ $name }" kohteen "{ $parent }" alle
|
||||||
copying =
|
copying =
|
||||||
Kopioidaan { $items } { $items ->
|
Kopioidaan { $items } { $items ->
|
||||||
[one] kohde
|
[one] kohde
|
||||||
*[other] kohteita
|
*[other] kohdetta
|
||||||
} lähteestä "{ $from }" kohteeseen "{ $to }"
|
} sijainnista "{ $from }" kohteeseen "{ $to }" ({ $progress })…
|
||||||
copied =
|
copied =
|
||||||
Kopioitu { $items } { $items ->
|
Kopioitu { $items } { $items ->
|
||||||
[one] kohde
|
[one] kohde
|
||||||
*[other] kohteet
|
*[other] kohdetta
|
||||||
} lähteestä "{ $from }" kohteeseen "{ $to }"
|
} sijainnista "{ $from }" kohteeseen "{ $to }"
|
||||||
emptying-trash = Tyhjennetään { trash }
|
emptying-trash = Tyhjennetään { trash } ({ $progress })…
|
||||||
emptied-trash = Tyhjennetty { trash }
|
emptied-trash = Tyhjennetty { trash }
|
||||||
extracting =
|
extracting =
|
||||||
Puretaan { $items } { $items ->
|
Puretaan { $items } { $items ->
|
||||||
[one] kohde
|
[one] kohde
|
||||||
*[other] kohteet
|
*[other] kohdetta
|
||||||
} arkistosta "{ $from }" kohteeseen "{ $to }"
|
} arkistosta "{ $from }" kohteeseen "{ $to }" ({ $progress })…
|
||||||
extracted =
|
extracted =
|
||||||
Purettu { $items } { $items ->
|
Purettu { $items } { $items ->
|
||||||
[one] kohde
|
[one] kohde
|
||||||
*[other] kohteet
|
*[other] kohdetta
|
||||||
} arkistosta "{ $from }" kohteeseen "{ $to }"
|
} arkistosta "{ $from }" kohteeseen "{ $to }"
|
||||||
setting-executable-and-launching = Asetetaan "{ $name }" käynnistettäväksi ja käynnistetään
|
setting-executable-and-launching = Asetetaan "{ $name }" käynnistettäväksi ja käynnistetään
|
||||||
set-executable-and-launched = Asetettu "{ $name }" käynnistettäväksi ja käynnistetty
|
set-executable-and-launched = Asetettu "{ $name }" käynnistettäväksi ja käynnistetty
|
||||||
moving =
|
moving =
|
||||||
Siirretään { $items } { $items ->
|
Siirretään { $items } { $items ->
|
||||||
[one] kohde
|
[one] kohde
|
||||||
*[other] kohteet
|
*[other] kohdetta
|
||||||
} lähteestä "{ $from }" kohteeseen "{ $to }"
|
} sijainnista "{ $from }" kohteeseen "{ $to }" ({ $progress })…
|
||||||
moved =
|
moved =
|
||||||
Siirretty { $items } { $items ->
|
Siirretty { $items } { $items ->
|
||||||
[one] kohde
|
[one] kohde
|
||||||
*[other] kohteet
|
*[other] kohdetta
|
||||||
} lähteestä "{ $from }" kohteeseen "{ $to }"
|
} sijainnista "{ $from }" kohteeseen "{ $to }"
|
||||||
renaming = Nimetään kohde "{ $from }" muotoon "{ $to }"
|
renaming = Nimetään kohde "{ $from }" muotoon "{ $to }"
|
||||||
renamed = Nimetty kohde "{ $from }" muotoon "{ $to }"
|
renamed = Nimetty kohde "{ $from }" muotoon "{ $to }"
|
||||||
restoring =
|
restoring =
|
||||||
Palautetaan { $items } { $items ->
|
Palautetaan { $items } { $items ->
|
||||||
[one] kohde
|
[one] kohde
|
||||||
*[other] kohteet
|
*[other] kohdetta
|
||||||
} { trash }sta
|
} roskakorista ({ $progress })…
|
||||||
restored =
|
restored =
|
||||||
Palautettu { $items } { $items ->
|
Palautettu { $items } { $items ->
|
||||||
[one] kohde
|
[one] kohde
|
||||||
*[other] kohteet
|
*[other] kohdetta
|
||||||
} { trash }sta
|
} roskakorista
|
||||||
unknown-folder = Tuntematon kansio
|
unknown-folder = tuntematon kansio
|
||||||
|
|
||||||
## Open with
|
## Open with
|
||||||
|
|
||||||
menu-open-with = Avaa ohjelmalla…
|
menu-open-with = Avaa sovelluksella…
|
||||||
default-app = { $name } (oletus)
|
default-app = { $name } (oletus)
|
||||||
|
|
||||||
## Show details
|
## Show details
|
||||||
|
|
@ -225,14 +225,14 @@ settings = Asetukset
|
||||||
|
|
||||||
appearance = Ulkoasu
|
appearance = Ulkoasu
|
||||||
theme = Teema
|
theme = Teema
|
||||||
match-desktop = Sovita yhteen työpöydän kanssa
|
match-desktop = Sama kuin työpöytä
|
||||||
dark = Tumma
|
dark = Tumma
|
||||||
light = Vaalea
|
light = Vaalea
|
||||||
|
|
||||||
# Context menu
|
# Context menu
|
||||||
|
|
||||||
add-to-sidebar = Lisää sivupalkkiin
|
add-to-sidebar = Lisää sivupalkkiin
|
||||||
compress = Pakkaa
|
compress = Pakkaa…
|
||||||
extract-here = Pura
|
extract-here = Pura
|
||||||
new-file = Uusi tiedosto…
|
new-file = Uusi tiedosto…
|
||||||
new-folder = Uusi kansio…
|
new-folder = Uusi kansio…
|
||||||
|
|
@ -241,9 +241,9 @@ move-to-trash = Siirrä roskakoriin
|
||||||
restore-from-trash = Palauta roskakorista
|
restore-from-trash = Palauta roskakorista
|
||||||
remove-from-sidebar = Poista sivupalkista
|
remove-from-sidebar = Poista sivupalkista
|
||||||
sort-by-name = Järjestä nimen mukaan
|
sort-by-name = Järjestä nimen mukaan
|
||||||
sort-by-modified = Järjestä muokkauspäivämäärän mukaan
|
sort-by-modified = Järjestä muokkausajan mukaan
|
||||||
sort-by-size = Järjestä koon mukaan
|
sort-by-size = Järjestä koon mukaan
|
||||||
sort-by-trashed = Järjestä poistamispäivämäärän mukaan
|
sort-by-trashed = Järjestä poistamisajan mukaan
|
||||||
|
|
||||||
## Desktop
|
## Desktop
|
||||||
|
|
||||||
|
|
@ -281,9 +281,9 @@ grid-view = Ruudukkonäkymä
|
||||||
list-view = Listanäkymä
|
list-view = Listanäkymä
|
||||||
show-hidden-files = Näytä piilotetut tiedostot
|
show-hidden-files = Näytä piilotetut tiedostot
|
||||||
list-directories-first = Näytä kansiot ensin
|
list-directories-first = Näytä kansiot ensin
|
||||||
gallery-preview = Gallerian esinäkymä
|
gallery-preview = Gallerian esikatselu
|
||||||
menu-settings = Asetukset…
|
menu-settings = Asetukset…
|
||||||
menu-about = Tietoa COSMIC Tiedostoista…
|
menu-about = Tietoa COSMICin tiedostonhallinnasta…
|
||||||
|
|
||||||
## Sort
|
## Sort
|
||||||
|
|
||||||
|
|
@ -318,3 +318,106 @@ move-to-button-label = Siirrä
|
||||||
clear-recents-history = Tyhjennä viimeaikaisten historia
|
clear-recents-history = Tyhjennä viimeaikaisten historia
|
||||||
copy-path = Kopioi polku
|
copy-path = Kopioi polku
|
||||||
dismiss = Hylkää viesti
|
dismiss = Hylkää viesti
|
||||||
|
operations-running =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] toiminto
|
||||||
|
*[other] toimintoa
|
||||||
|
} käynnissä ({ $percent } %)...
|
||||||
|
operations-running-finished =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] toiminto
|
||||||
|
*[other] toimintoa
|
||||||
|
} käynnissä ({ $percent } %), { $finished } valmistunut…
|
||||||
|
pause = Keskeytä
|
||||||
|
extract-to = Pura sijaintiin…
|
||||||
|
permanently-delete-warning = { $target } tullaan poistamaan pysyvästi. Tätä toimintoa ei voi perua.
|
||||||
|
execute-only = Vain suoritus
|
||||||
|
write-only = Vain kirjoitus
|
||||||
|
write-execute = Kirjoita ja suorita
|
||||||
|
read-only = Vain luku
|
||||||
|
read-execute = Lue ja suorita
|
||||||
|
read-write = Lue ja kirjoita
|
||||||
|
read-write-execute = Lue, kirjoita sekä suorita
|
||||||
|
calculating = Lasketaan…
|
||||||
|
single-click = Yhden napsautuksen avaus
|
||||||
|
type-to-search = Kirjoita etsiäksesi
|
||||||
|
type-to-search-recursive = Etsii nykyisestä kansiosta ja kaikista alikansioista
|
||||||
|
remove-from-recents = Poista viimeaikaisista
|
||||||
|
selected-items = { $items } valittua kohdetta
|
||||||
|
show-recents = Viimeaikaisten kansio sivupalkissa
|
||||||
|
copy-to = Kopioi…
|
||||||
|
move-to = Siirrä…
|
||||||
|
details = Yksityiskohdat
|
||||||
|
grid-spacing = Ruudukkovälit
|
||||||
|
none = Ei mitään
|
||||||
|
favorite-path-error = Virhe avattaessa kansiota
|
||||||
|
favorite-path-error-description =
|
||||||
|
Polun { $path } avaaminen ei onnistunut
|
||||||
|
"{ $path }" ei välttämättä ole olemassa tai oikeutesi eivät riitä sen avaamiseen
|
||||||
|
|
||||||
|
Haluatko poistaa sen sivupalkista?
|
||||||
|
keep = Pidä
|
||||||
|
repository = Tietovarasto
|
||||||
|
support = Tuki
|
||||||
|
progress = { $percent } %
|
||||||
|
progress-cancelled = { $percent } %, peruttu
|
||||||
|
progress-failed = { $percent } %, epäonnistui
|
||||||
|
progress-paused = { $percent } %, keskeytetty
|
||||||
|
setting-permissions = Asetetaan kohteen "{ $name }" käyttöoikeudeksi { $mode }
|
||||||
|
set-permissions = Asetettu kohteen { $name } käyttöoikeudeksi { $mode }
|
||||||
|
permanently-deleting =
|
||||||
|
Poistetaan pysyvästi { $items } { $items ->
|
||||||
|
[one] kohde
|
||||||
|
*[other] kohdetta
|
||||||
|
}
|
||||||
|
permanently-deleted =
|
||||||
|
Poistettu pysyvästi { $items } { $items ->
|
||||||
|
[one] kohde
|
||||||
|
*[other] kohdetta
|
||||||
|
}
|
||||||
|
items = Kohteita: { $items }
|
||||||
|
item-accessed = Käytetty: { $accessed }
|
||||||
|
type-to-search-enter-path = Kirjoittaa polun kansioon tai tiedostoon
|
||||||
|
eject = Poista asemasta
|
||||||
|
copy-to-title = Valitse mihin kopioidaan
|
||||||
|
move-to-title = Valitse mihin siirretään
|
||||||
|
pasted-image = Liitetty kuva
|
||||||
|
pasted-text = Liitetty teksti
|
||||||
|
pasted-video = Liitetty video
|
||||||
|
type-to-search-select = Valitsee ensimmäisen täsmäävän tiedoston tai kansion
|
||||||
|
deleting =
|
||||||
|
Poistetaan { $items } { $items ->
|
||||||
|
[one] kohde
|
||||||
|
*[other] kohdetta
|
||||||
|
} roskakorista ({ $progress })…
|
||||||
|
deleted =
|
||||||
|
Poistettu { $items } { $items ->
|
||||||
|
[one] kohde
|
||||||
|
*[other] kohdetta
|
||||||
|
} roskakorista
|
||||||
|
removing-from-recents =
|
||||||
|
Poistetaan { $items } { $items ->
|
||||||
|
[one] kohde
|
||||||
|
*[other] kohdetta
|
||||||
|
} viimeaikaisista
|
||||||
|
removed-from-recents =
|
||||||
|
Poistettu { $items } { $items ->
|
||||||
|
[one] kohde
|
||||||
|
*[other] kohdetta
|
||||||
|
} viimeaikaisista
|
||||||
|
mixed = Sekoitettu
|
||||||
|
context-action = Kontekstitoiminto
|
||||||
|
context-action-confirm-title = Suoritetaanko "{ $name }"?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Tämä suorittaa { $items } { $items ->
|
||||||
|
[one] kohteen
|
||||||
|
*[other] kohdetta
|
||||||
|
}.
|
||||||
|
run = Suorita
|
||||||
|
rename-confirm = Nimeä uudelleen
|
||||||
|
checksum = { $kind }-tarkistussumma
|
||||||
|
calculate = Laske
|
||||||
|
error = Virhe
|
||||||
|
search-application = Hae sovelluksen nimellä
|
||||||
|
sidebar-label = Sivupalkin nimike
|
||||||
|
change-sidebar-label = Muuta sivupalkin nimikettä
|
||||||
|
|
|
||||||
0
i18n/fil/cosmic_files.ftl
Normal file
0
i18n/fil/cosmic_files.ftl
Normal file
|
|
@ -80,18 +80,19 @@ cancel = Annuler
|
||||||
create = Créer
|
create = Créer
|
||||||
open = Ouvrir
|
open = Ouvrir
|
||||||
open-file = Ouvrir le fichier
|
open-file = Ouvrir le fichier
|
||||||
open-folder = Ouvrir dossier
|
open-folder = Ouvrir le dossier
|
||||||
open-in-new-tab = Ouvrir dans un nouvel onglet
|
open-in-new-tab = Ouvrir dans un nouvel onglet
|
||||||
open-in-new-window = Ouvrir dans une nouvelle fenêtre
|
open-in-new-window = Ouvrir dans une nouvelle fenêtre
|
||||||
open-item-location = Ouvrir l'emplacement de l'élément
|
open-item-location = Ouvrir l'emplacement de l'élément
|
||||||
open-multiple-files = Ouvrir plusieurs fichiers
|
open-multiple-files = Ouvrir plusieurs fichiers
|
||||||
open-multiple-folders = Ouvrir plusieurs dossiers
|
open-multiple-folders = Ouvrir plusieurs dossiers
|
||||||
save = Enregistrer
|
save = Enregistrer
|
||||||
save-file = Enregistrer fichier
|
save-file = Enregistrer le fichier
|
||||||
|
|
||||||
## Open With Dialog
|
## Open With Dialog
|
||||||
|
|
||||||
open-with-title = Comment souhaitez-vous ouvrir "{ $name }" ?
|
open-with-title = Comment souhaitez-vous ouvrir "{ $name }" ?
|
||||||
|
open-with-set-default = Toujours utiliser cette application pour ce type de fichier
|
||||||
browse-store = Parcourir { $store }
|
browse-store = Parcourir { $store }
|
||||||
|
|
||||||
## Permanently delete Dialog
|
## Permanently delete Dialog
|
||||||
|
|
@ -130,6 +131,11 @@ open-with = Ouvrir avec
|
||||||
owner = Propriétaire
|
owner = Propriétaire
|
||||||
group = Groupe
|
group = Groupe
|
||||||
other = Autre
|
other = Autre
|
||||||
|
toolbar = Barre d'outils
|
||||||
|
toolbar-available = Disponibles
|
||||||
|
toolbar-empty-hint = Aucun bouton. Glisser-déposer ou ajouter depuis la liste ci-dessous.
|
||||||
|
toolbar-reset = Rétablir par défaut
|
||||||
|
parent-directory = Dossier parent
|
||||||
|
|
||||||
### Mode 0
|
### Mode 0
|
||||||
|
|
||||||
|
|
@ -183,7 +189,7 @@ keep = Conserver
|
||||||
## Add Network Drive
|
## Add Network Drive
|
||||||
|
|
||||||
add-network-drive = Ajouter un lecteur réseau
|
add-network-drive = Ajouter un lecteur réseau
|
||||||
connect = Connecter
|
connect = Se connecter
|
||||||
connect-anonymously = Se connecter anonymement
|
connect-anonymously = Se connecter anonymement
|
||||||
connecting = Connexion...
|
connecting = Connexion...
|
||||||
domain = Domaine
|
domain = Domaine
|
||||||
|
|
@ -200,8 +206,8 @@ network-drive-schemes =
|
||||||
File Transfer Protocol,ftp:// or ftps://
|
File Transfer Protocol,ftp:// or ftps://
|
||||||
Network File System,nfs://
|
Network File System,nfs://
|
||||||
Server Message Block,smb://
|
Server Message Block,smb://
|
||||||
SSH File Transfer Protocol,sftp:// or ssh://
|
SSH File Transfer Protocol,sftp:// ou ssh://
|
||||||
WebDav,dav:// or davs://
|
WebDAV,dav:// ou davs://
|
||||||
network-drive-error = Impossible d'accéder au lecteur réseau
|
network-drive-error = Impossible d'accéder au lecteur réseau
|
||||||
password = Mot de passe
|
password = Mot de passe
|
||||||
remember-password = Se souvenir du mot de passe
|
remember-password = Se souvenir du mot de passe
|
||||||
|
|
@ -216,7 +222,7 @@ history = Historique
|
||||||
no-history = Aucun élément dans l'historique.
|
no-history = Aucun élément dans l'historique.
|
||||||
pending = En attente
|
pending = En attente
|
||||||
progress = { $percent }%
|
progress = { $percent }%
|
||||||
progress-cancelled = { $percent }%, annulation
|
progress-cancelled = { $percent }%, annulé
|
||||||
progress-paused = { $percent }%, en pause
|
progress-paused = { $percent }%, en pause
|
||||||
failed = Échoué
|
failed = Échoué
|
||||||
complete = Terminé
|
complete = Terminé
|
||||||
|
|
@ -253,7 +259,7 @@ deleted =
|
||||||
[one] élément supprimé
|
[one] élément supprimé
|
||||||
*[other] éléments supprimés
|
*[other] éléments supprimés
|
||||||
} depuis { trash }
|
} depuis { trash }
|
||||||
emptying-trash = { trash } en cours de nettoyage ({ $progress })...
|
emptying-trash = Nettoyage en cours de { trash } ({ $progress })...
|
||||||
emptied-trash = { trash } vidée
|
emptied-trash = { trash } vidée
|
||||||
extracting =
|
extracting =
|
||||||
Extraction de { $items } { $items ->
|
Extraction de { $items } { $items ->
|
||||||
|
|
@ -265,7 +271,7 @@ extracted =
|
||||||
[one] élément extrait
|
[one] élément extrait
|
||||||
*[other] éléments extraits
|
*[other] éléments extraits
|
||||||
} depuis "{ $from }" vers "{ $to }"
|
} depuis "{ $from }" vers "{ $to }"
|
||||||
setting-executable-and-launching = Paramétrage de "{ $name }" comme exécutable et prêt à être lancé
|
setting-executable-and-launching = Définition de "{ $name }" comme exécutable et lancement en cours
|
||||||
set-executable-and-launched = Défini "{ $name }" comme exécutable et lancé
|
set-executable-and-launched = Défini "{ $name }" comme exécutable et lancé
|
||||||
moving =
|
moving =
|
||||||
Déplacement de { $items } { $items ->
|
Déplacement de { $items } { $items ->
|
||||||
|
|
@ -276,7 +282,7 @@ moved =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] élément déplacé
|
[one] élément déplacé
|
||||||
*[other] éléments déplacés
|
*[other] éléments déplacés
|
||||||
} de "{ $from }" à "{ $to }"
|
} de "{ $from }" vers "{ $to }"
|
||||||
permanently-deleting =
|
permanently-deleting =
|
||||||
Suppression définitive de { $items } { $items ->
|
Suppression définitive de { $items } { $items ->
|
||||||
[one] élément
|
[one] élément
|
||||||
|
|
@ -304,7 +310,7 @@ unknown-folder = Dossier inconnu
|
||||||
## Open with
|
## Open with
|
||||||
|
|
||||||
menu-open-with = Ouvrir avec...
|
menu-open-with = Ouvrir avec...
|
||||||
default-app = { $name } (défaut)
|
default-app = { $name } (par défaut)
|
||||||
|
|
||||||
## Show details
|
## Show details
|
||||||
|
|
||||||
|
|
@ -343,9 +349,10 @@ extract-here = Extraire
|
||||||
new-file = Nouveau fichier...
|
new-file = Nouveau fichier...
|
||||||
new-folder = Nouveau dossier...
|
new-folder = Nouveau dossier...
|
||||||
open-in-terminal = Ouvrir dans le terminal
|
open-in-terminal = Ouvrir dans le terminal
|
||||||
|
edit-desktop-entry = Modifier le fichier .desktop
|
||||||
move-to-trash = Déplacer vers la corbeille
|
move-to-trash = Déplacer vers la corbeille
|
||||||
restore-from-trash = Restaurer depuis la corbeille
|
restore-from-trash = Restaurer depuis la corbeille
|
||||||
remove-from-sidebar = Supprimer de la barre latérale
|
remove-from-sidebar = Retirer de la barre latérale
|
||||||
sort-by-name = Trier par nom
|
sort-by-name = Trier par nom
|
||||||
sort-by-modified = Trier par date de modification
|
sort-by-modified = Trier par date de modification
|
||||||
sort-by-size = Trier par taille
|
sort-by-size = Trier par taille
|
||||||
|
|
@ -408,11 +415,11 @@ removing-from-recents =
|
||||||
*[other] éléments
|
*[other] éléments
|
||||||
} de { recents }
|
} de { recents }
|
||||||
other-apps = Autres applications
|
other-apps = Autres applications
|
||||||
set-permissions = Définir les permissions pour "{ $name }" à { $mode }
|
set-permissions = Permissions définies pour "{ $name }" à { $mode }
|
||||||
repository = Dépôt
|
repository = Dépôt
|
||||||
support = Support
|
support = Support
|
||||||
eject = Éjecter
|
eject = Éjecter
|
||||||
remove-from-recents = Supprimer des récents
|
remove-from-recents = Retirer des récents
|
||||||
empty-trash-title = Vider la corbeille ?
|
empty-trash-title = Vider la corbeille ?
|
||||||
setting-permissions = Définition des permissions pour "{ $name }" à { $mode }
|
setting-permissions = Définition des permissions pour "{ $name }" à { $mode }
|
||||||
removed-from-recents =
|
removed-from-recents =
|
||||||
|
|
@ -421,9 +428,9 @@ removed-from-recents =
|
||||||
*[other] éléments supprimés
|
*[other] éléments supprimés
|
||||||
} de { recents }
|
} de { recents }
|
||||||
progress-failed = { $percent }%, échec
|
progress-failed = { $percent }%, échec
|
||||||
type-to-search-select = Sélectionne le premier fichier ou dossier qui convient
|
type-to-search-select = Sélectionne le premier fichier ou dossier correspondant
|
||||||
pasted-image = Image collée
|
pasted-image = Image collée
|
||||||
pasted-text = Text collé
|
pasted-text = Texte collé
|
||||||
pasted-video = Vidéo collée
|
pasted-video = Vidéo collée
|
||||||
copy-to-title = Sélectionner la destination de la copie
|
copy-to-title = Sélectionner la destination de la copie
|
||||||
copy-to-button-label = Copier
|
copy-to-button-label = Copier
|
||||||
|
|
@ -436,3 +443,19 @@ keywords = Dossier;Gestionnaire;
|
||||||
show-recents = Dossier Récents dans la barre latérale
|
show-recents = Dossier Récents dans la barre latérale
|
||||||
copy-path = Copier le chemin
|
copy-path = Copier le chemin
|
||||||
clear-recents-history = Effacer l'historique des Récents
|
clear-recents-history = Effacer l'historique des Récents
|
||||||
|
mixed = Mixte
|
||||||
|
context-action-confirm-title = Exécuter "{ $name }" ?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Cela exécutera sur { $items } { $items ->
|
||||||
|
[one] élément
|
||||||
|
*[other] éléments
|
||||||
|
}.
|
||||||
|
run = Exécuter
|
||||||
|
context-action = Action contextuelle
|
||||||
|
rename-confirm = Renommer
|
||||||
|
calculate = Calculer
|
||||||
|
error = Erreur
|
||||||
|
checksum = Somme de contrôle { $kind }
|
||||||
|
sidebar-label = Libellé de la barre latérale
|
||||||
|
search-application = Chercher par nom d'appli
|
||||||
|
change-sidebar-label = Changer le libellé de la barre latérale
|
||||||
|
|
|
||||||
|
|
@ -307,7 +307,7 @@ single-click = Cliceáil amháin le hoscailt
|
||||||
|
|
||||||
### Appearance
|
### Appearance
|
||||||
|
|
||||||
appearance = Cuma
|
appearance = Dealramh
|
||||||
theme = Téama
|
theme = Téama
|
||||||
match-desktop = Meaitseáil deasc
|
match-desktop = Meaitseáil deasc
|
||||||
dark = Dorcha
|
dark = Dorcha
|
||||||
|
|
@ -433,3 +433,16 @@ keywords = Fillteán;Bainisteoir;
|
||||||
show-recents = Fillteán le déanaí sa bharra taoibh
|
show-recents = Fillteán le déanaí sa bharra taoibh
|
||||||
clear-recents-history = Glan stair na n-earraí le déanaí
|
clear-recents-history = Glan stair na n-earraí le déanaí
|
||||||
copy-path = Cóipeáil an chosán
|
copy-path = Cóipeáil an chosán
|
||||||
|
mixed = Measctha
|
||||||
|
context-action = Gníomh comhthéacsúil
|
||||||
|
context-action-confirm-title = Rith "{ $name }"?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Rithfidh sé seo ar { $items } { $items ->
|
||||||
|
[one] mhír
|
||||||
|
*[other] míreanna
|
||||||
|
}.
|
||||||
|
run = Rith
|
||||||
|
rename-confirm = Athainmnigh
|
||||||
|
checksum = { $kind } suim sheiceála
|
||||||
|
calculate = Ríomh
|
||||||
|
error = Earráid
|
||||||
|
|
|
||||||
333
i18n/gl/cosmic_files.ftl
Normal file
333
i18n/gl/cosmic_files.ftl
Normal file
|
|
@ -0,0 +1,333 @@
|
||||||
|
name = Nome
|
||||||
|
save = Gardar
|
||||||
|
cancel = Cancelar
|
||||||
|
run = Executar
|
||||||
|
delete = Eliminar
|
||||||
|
repository = Repositorio
|
||||||
|
support = Soporte
|
||||||
|
settings = Axustes
|
||||||
|
appearance = Apariencia
|
||||||
|
theme = Tema
|
||||||
|
match-desktop = Axustar ao escritorio
|
||||||
|
dark = Escuro
|
||||||
|
light = Claro
|
||||||
|
replace = Remplazar
|
||||||
|
file = Ficheiro
|
||||||
|
new-window = Nova xanela
|
||||||
|
quit = Pechar
|
||||||
|
edit = Editar
|
||||||
|
undo = Desfacer
|
||||||
|
cut = Cortar
|
||||||
|
copy = Copiar
|
||||||
|
paste = Pegar
|
||||||
|
select-all = Seleccionar todo
|
||||||
|
view = Ver
|
||||||
|
zoom-in = Aumentar Zoom
|
||||||
|
default-size = Tamaño por defecto
|
||||||
|
zoom-out = Afastar Zoom
|
||||||
|
menu-settings = Axustes...
|
||||||
|
cosmic-files = Ficheiros COSMIC
|
||||||
|
comment = Xestor de ficheiros para o escritorio COSMIC
|
||||||
|
keywords = Cartafol;Xestor;
|
||||||
|
empty-folder = Cartafol baleiro
|
||||||
|
empty-folder-hidden = Cartafol baleiro (contén elementos agochados)
|
||||||
|
no-results = Non se atoparon resultados
|
||||||
|
filesystem = Sistema de ficheiros
|
||||||
|
home = Inicio
|
||||||
|
networks = Redes
|
||||||
|
notification-in-progress = Operacións de ficheiros en progreso
|
||||||
|
trash = Lixo
|
||||||
|
recents = Recentes
|
||||||
|
today = Hoxe
|
||||||
|
desktop-view-options = Axustes da vista do escritorio…
|
||||||
|
show-on-desktop = Amosar no Escritorio
|
||||||
|
desktop-folder-content = Contido do cartafol Escritorio
|
||||||
|
mounted-drives = Unidades montadas
|
||||||
|
trash-folder-icon = Icona do cartafol do Lixo
|
||||||
|
icon-size-and-spacing = Tamaño e espazado das iconas
|
||||||
|
icon-size = Tamaño de icona
|
||||||
|
grid-spacing = Espazado da grella
|
||||||
|
modified = Modificado
|
||||||
|
trashed-on = No lixo
|
||||||
|
size = Tamaño
|
||||||
|
details = Detalles
|
||||||
|
dismiss = Descartar a mensaxe
|
||||||
|
operations-running =
|
||||||
|
{ $running } { $executandose ->
|
||||||
|
[unha] operacion
|
||||||
|
*[outra] operacions
|
||||||
|
} executandose ({ $percent }%)...
|
||||||
|
operations-running-finished =
|
||||||
|
{ $running } { $executandose ->
|
||||||
|
[unha] operacion
|
||||||
|
*[outra] operacions
|
||||||
|
} executandose ({ $percent }%), { $finished } rematadas...
|
||||||
|
pause = Pausa
|
||||||
|
resume = Retomar
|
||||||
|
create-archive = Crear arquivo comprimido
|
||||||
|
copy-to-title = Seleccionar o destino da copia
|
||||||
|
copy-to-button-label = Copiar
|
||||||
|
extract-password-required = Contrasinal necesario
|
||||||
|
extract-to = Extraer en…
|
||||||
|
extract-to-title = Extraer a cartafol
|
||||||
|
empty-trash = Baleirar Lixo
|
||||||
|
empty-trash-title = Baleirar o lixo?
|
||||||
|
empty-trash-warning = Os elementos do cartafol do lixo eliminaranse de forma permanente
|
||||||
|
mount-error = Non é posible acceder a unidade
|
||||||
|
move-to-title = Seleccionar o destino do movemento
|
||||||
|
move-to-button-label = Mover
|
||||||
|
create-new-file = Crear un novo ficheiro
|
||||||
|
create-new-folder = Crear novo cartafol
|
||||||
|
file-name = Nome do ficheiro
|
||||||
|
folder-name = Nome do cartafol
|
||||||
|
file-already-exists = Existe un ficheiro co mesmo nome
|
||||||
|
folder-already-exists = Existe un cartafol co mesmo nome
|
||||||
|
name-hidden = Os nomes que comecen por "." agocharanse
|
||||||
|
name-invalid = O nome non pode ser "{ $filename }"
|
||||||
|
name-no-slashes = O nome non pode conter barras
|
||||||
|
create = Crear
|
||||||
|
open = Abrir
|
||||||
|
open-file = Abrir ficheiro
|
||||||
|
open-folder = Abrir cartafol
|
||||||
|
open-in-new-tab = Abrir nunha nova lapela
|
||||||
|
open-in-new-window = Abrir nunha nova xanela
|
||||||
|
open-item-location = Abrir ubicación do ítem
|
||||||
|
open-multiple-files = Abrir multiples ficheiros
|
||||||
|
open-multiple-folders = Abrir multiples cartafoles
|
||||||
|
save-file = Gardar ficheiro
|
||||||
|
open-with-title = Como queres abrir «{ $name }»?
|
||||||
|
browse-store = Atopar { $store }
|
||||||
|
other-apps = Outras aplicacións
|
||||||
|
related-apps = Aplicacións relacionadas
|
||||||
|
context-action = Acción contextual
|
||||||
|
context-action-confirm-title = Executar "{ $name }"?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Esto vaise executar en { $items } { $items ->
|
||||||
|
[un] item
|
||||||
|
*[outro] items
|
||||||
|
}.
|
||||||
|
selected-items = Os { $items } elementos seleccionados
|
||||||
|
permanently-delete-question = Eliminar permanentemente?
|
||||||
|
permanently-delete-warning = { $target } será eliminado permanentemente. Esta acción non se poderá desfacer.
|
||||||
|
rename-file = Renomear ficheiro
|
||||||
|
rename-folder = Renomear cartafol
|
||||||
|
rename-confirm = Renomear
|
||||||
|
replace-title = "{ $filename }" xa existe nesta ubicación
|
||||||
|
replace-warning = Queres substituílo polo que estás a gardar? Ao substituílo sobrescribirase o seu contido.
|
||||||
|
replace-warning-operation = Queres substituílo? Ao substituílo sobrescribirase o seu contido.
|
||||||
|
original-file = Ficheiro orixinal
|
||||||
|
replace-with = Remplazar con
|
||||||
|
apply-to-all = Aplicar a todo
|
||||||
|
keep-both = Manter ambos
|
||||||
|
skip = Saltar
|
||||||
|
set-executable-and-launch = Marcar como executable e lanzalo
|
||||||
|
set-executable-and-launch-description = Queres marcar «{ $name }» como executábel e lanzalo?
|
||||||
|
set-and-launch = Marcar e executar
|
||||||
|
open-with = Abrir con
|
||||||
|
owner = Propietario
|
||||||
|
group = Grupo
|
||||||
|
other = Outro
|
||||||
|
mixed = Mesturar
|
||||||
|
none = Ningún
|
||||||
|
execute-only = Só execución
|
||||||
|
write-only = Só escritura
|
||||||
|
write-execute = Escritura e execución
|
||||||
|
read-only = Só lectura
|
||||||
|
read-execute = Lectura e execución
|
||||||
|
read-write = Lectura e escritura
|
||||||
|
read-write-execute = Lectura, escritura e execución
|
||||||
|
favorite-path-error = Erro abrindo o directorio
|
||||||
|
favorite-path-error-description =
|
||||||
|
Non foi posíbel abrir «{ $path }».
|
||||||
|
É posíbel que «{ $path }» non exista ou que non teñas permisos para abrilo.
|
||||||
|
|
||||||
|
Queres retiralo da barra lateral?
|
||||||
|
remove = Eliminar
|
||||||
|
keep = Manter
|
||||||
|
add-network-drive = Engadir unidade de rede
|
||||||
|
connect = Conectar
|
||||||
|
connect-anonymously = Conectar de xeito anonimo
|
||||||
|
connecting = Conectando...
|
||||||
|
domain = Dominio
|
||||||
|
enter-server-address = Introducir a dirección do servidor
|
||||||
|
network-drive-description =
|
||||||
|
Os enderezos dos servidores inclúen un prefixo de protocolo e un enderezo.
|
||||||
|
Exemplos: ssh://192.168.0.1, ftp://[2001:db8::1]
|
||||||
|
network-drive-schemes =
|
||||||
|
Protocolos dispoñíbeis,Prefixo
|
||||||
|
AppleTalk,afp://
|
||||||
|
Protocolo de transferencia de ficheiros,ftp:// ou ftps://
|
||||||
|
Sistema de ficheiros en rede,nfs://
|
||||||
|
Server Message Block,smb://
|
||||||
|
Protocolo de transferencia de ficheiros por SSH,sftp:// ou ssh://
|
||||||
|
WebDAV,dav:// ou davs://
|
||||||
|
network-drive-error = Non foi posible acceder a unidade de rede
|
||||||
|
password = Contrasinal
|
||||||
|
remember-password = Recordar o contrasinal
|
||||||
|
try-again = Voltar a intentar
|
||||||
|
username = Nome de usuario
|
||||||
|
cancelled = Cancelado
|
||||||
|
edit-history = Editar historial
|
||||||
|
history = Historial
|
||||||
|
no-history = Non hai elementos no historial.
|
||||||
|
pending = Pendente
|
||||||
|
progress = { $percent }%
|
||||||
|
progress-cancelled = { $percent }%, cancelado
|
||||||
|
progress-failed = { $percent }%, fallou
|
||||||
|
progress-paused = { $percent }%, pausado
|
||||||
|
failed = Fallou
|
||||||
|
complete = Completado
|
||||||
|
compressing =
|
||||||
|
Comprimindo { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} de «{ $from }» a «{ $to }» ({ $progress })...
|
||||||
|
compressed =
|
||||||
|
Comprimidos { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} de «{ $from }» a «{ $to }»
|
||||||
|
copy_noun = Copiar
|
||||||
|
pasted-image = Imaxe pegada
|
||||||
|
pasted-text = Texto Pegado
|
||||||
|
pasted-video = Video pegado
|
||||||
|
creating = Creando "{ $name }" en "{ $parent }"
|
||||||
|
created = Creado "{ $name }" en "{ $parent }"
|
||||||
|
copying =
|
||||||
|
Copiando { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} de «{ $from }» a «{ $to }» ({ $progress })...
|
||||||
|
copied =
|
||||||
|
Copiados { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} de «{ $from }» a «{ $to }»
|
||||||
|
deleted =
|
||||||
|
Eliminando { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} do lixo ({ $progress })
|
||||||
|
emptying-trash = Baleirando { trash } ({ $progress })...
|
||||||
|
emptied-trash = Baleirado o { trash }
|
||||||
|
extracting =
|
||||||
|
Extraendo { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} de «{ $from }» a «{ $to }» ({ $progress })...
|
||||||
|
extracted =
|
||||||
|
Extraídos { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} de «{ $from }» a «{ $to }»
|
||||||
|
setting-executable-and-launching = Marcando «{ $name }» como executábel e executándoo
|
||||||
|
set-executable-and-launched = «{ $name }» marcouse como executábel e executouse
|
||||||
|
setting-permissions = Establecendo os permisos de «{ $name }» en { $mode }
|
||||||
|
set-permissions = Establecéronse os permisos de «{ $name }» en { $mode }
|
||||||
|
moving =
|
||||||
|
Movendo { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} de «{ $from }» a «{ $to }» ({ $progress })...
|
||||||
|
moved =
|
||||||
|
Movidos { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} de «{ $from }» a «{ $to }»
|
||||||
|
permanently-deleting =
|
||||||
|
Eliminando permanentemente { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
}
|
||||||
|
permanently-deleted =
|
||||||
|
Eliminados permanentemente { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
}
|
||||||
|
removing-from-recents =
|
||||||
|
Retirando { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} dos { recents }
|
||||||
|
removed-from-recents =
|
||||||
|
Retirado { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} de { recents }
|
||||||
|
renaming = Renomeando "{ $from }" a "{ $to }"
|
||||||
|
renamed = Renomeado "{ $from }" a "{ $to }"
|
||||||
|
restoring =
|
||||||
|
Restaurando { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} de { trash } ({ $progress })...
|
||||||
|
restored =
|
||||||
|
Restaurado { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} de { trash }
|
||||||
|
unknown-folder = cartafol descoñecido
|
||||||
|
menu-open-with = Abrir con...
|
||||||
|
default-app = { $name } (por defecto)
|
||||||
|
show-details = Amosar detalles
|
||||||
|
type = Tipo: { $mime }
|
||||||
|
items = Elementos: { $items }
|
||||||
|
item-size = Tamaño: { $size }
|
||||||
|
item-created = Creado: { $created }
|
||||||
|
item-modified = Modificado: { $modified }
|
||||||
|
item-accessed = Accedido: { $accessed }
|
||||||
|
calculating = Calculando...
|
||||||
|
checksum = { $kind } checksum
|
||||||
|
calculate = Calculado
|
||||||
|
error = Erro
|
||||||
|
single-click = Clic único para abrir
|
||||||
|
show-recents = Cartafol de recentes no panel lateral
|
||||||
|
type-to-search = Escribe para atopar
|
||||||
|
type-to-search-recursive = Busca no cartafol actual e en todos os subcartafoles
|
||||||
|
type-to-search-enter-path = Introduce a ruta do cartafol ou ficheiro
|
||||||
|
type-to-search-select = Selecciona o primeiro ficheiro ou cartafol coincidente
|
||||||
|
add-to-sidebar = Engadir o panel lateral
|
||||||
|
clear-recents-history = Limpar historial de recentes
|
||||||
|
compress = Comprimir...
|
||||||
|
copy-to = Copiar a...
|
||||||
|
delete-permanently = Eliminar permanentemente
|
||||||
|
eject = Expulsar
|
||||||
|
extract-here = Extraer
|
||||||
|
new-file = Novo ficheiro...
|
||||||
|
new-folder = Novo cartafol...
|
||||||
|
open-in-terminal = Abrir no terminal
|
||||||
|
move-to = Mover a...
|
||||||
|
move-to-trash = Mover ao lixo
|
||||||
|
restore-from-trash = Recuperar do lixo
|
||||||
|
remove-from-sidebar = Eliminar do panel lateral
|
||||||
|
sort-by-name = Ordenar por nome
|
||||||
|
sort-by-modified = Ordenar por modificación
|
||||||
|
sort-by-size = Ordenar por tamaño
|
||||||
|
sort-by-trashed = Ordenar pola data de eliminación
|
||||||
|
remove-from-recents = Eliminar de recentes
|
||||||
|
change-wallpaper = Cambiar fondo de pantalla...
|
||||||
|
desktop-appearance = Apariencia do Escritorio...
|
||||||
|
display-settings = Axustes de pantalla...
|
||||||
|
new-tab = Nova lapela
|
||||||
|
reload-folder = Recargar cartafol
|
||||||
|
rename = Renomear...
|
||||||
|
close-tab = Pechar lapela
|
||||||
|
copy-path = Copiar ruta
|
||||||
|
grid-view = Vista de grella
|
||||||
|
list-view = Vista de lista
|
||||||
|
show-hidden-files = Amosar ficheiros ocultos
|
||||||
|
list-directories-first = Listar primeiro os directorios
|
||||||
|
gallery-preview = Vista previa da galería
|
||||||
|
menu-about = Sobre Ficheiros COSMIC...
|
||||||
|
sort = Ordenar
|
||||||
|
sort-a-z = A-Z
|
||||||
|
sort-z-a = Z-A
|
||||||
|
sort-newest-first = Novos primeiro
|
||||||
|
sort-oldest-first = Antigos primeiro
|
||||||
|
sort-smallest-to-largest = Do máis pequeno ao máis grande
|
||||||
|
sort-largest-to-smallest = Do máis grande ao máis pequeno
|
||||||
|
deleting =
|
||||||
|
Eliminando { $items } { $items ->
|
||||||
|
[one] elemento
|
||||||
|
*[other] elementos
|
||||||
|
} da { trash } ({ $progress })...
|
||||||
|
|
@ -5,7 +5,7 @@ no-results = कोई परिणाम नहीं
|
||||||
filesystem = फाइल सिस्टम
|
filesystem = फाइल सिस्टम
|
||||||
home = होम
|
home = होम
|
||||||
networks = नेटवर्क्स
|
networks = नेटवर्क्स
|
||||||
notification-in-progress = फाइल संचालन प्रगति पर है।
|
notification-in-progress = फ़ाइल संचालन प्रगति पर हैं
|
||||||
trash = कचरा
|
trash = कचरा
|
||||||
recents = हाल के
|
recents = हाल के
|
||||||
undo = पूर्ववत करें
|
undo = पूर्ववत करें
|
||||||
|
|
@ -33,8 +33,8 @@ create-archive = संग्रह बनाएँ
|
||||||
|
|
||||||
## Empty Trash Dialog
|
## Empty Trash Dialog
|
||||||
|
|
||||||
empty-trash = कचरा खाली करें
|
empty-trash = रद्दी साफ़ करें
|
||||||
empty-trash-warning = क्या आप वाकई कचरे में सभी आइटमों को स्थायी रूप से हटाना चाहते हैं?
|
empty-trash-warning = रद्दी फ़ोल्डर में मौजूद आइटम स्थायी रूप से हटा दिए जाएंगे
|
||||||
|
|
||||||
## New File/Folder Dialog
|
## New File/Folder Dialog
|
||||||
|
|
||||||
|
|
@ -42,11 +42,11 @@ create-new-file = नई फाइल बनाएँ
|
||||||
create-new-folder = नया फ़ोल्डर बनाएँ
|
create-new-folder = नया फ़ोल्डर बनाएँ
|
||||||
file-name = फाइल का नाम
|
file-name = फाइल का नाम
|
||||||
folder-name = फ़ोल्डर का नाम
|
folder-name = फ़ोल्डर का नाम
|
||||||
file-already-exists = इसी नाम की फाइल पहले से मौजूद है।
|
file-already-exists = उस नाम की फ़ाइल पहले से मौजूद है
|
||||||
folder-already-exists = इसी नाम का फ़ोल्डर पहले से मौजूद है।
|
folder-already-exists = उस नाम का फ़ोल्डर पहले से मौजूद है
|
||||||
name-hidden = "." से शुरू होने वाले नाम छिपे रहेंगे।
|
name-hidden = "." से शुरू होने वाले नाम छिपे होंगे
|
||||||
name-invalid = नाम "{ $filename }" मान्य नहीं है।
|
name-invalid = नाम "{ $filename }" नहीं हो सकता
|
||||||
name-no-slashes = नाम में स्लैश का उपयोग नहीं किया जा सकता है।
|
name-no-slashes = नाम में स्लैश नहीं हो सकते
|
||||||
|
|
||||||
## Open/Save Dialog
|
## Open/Save Dialog
|
||||||
|
|
||||||
|
|
@ -75,8 +75,8 @@ rename-folder = फ़ोल्डर का नाम बदलें
|
||||||
|
|
||||||
## Replace Dialog
|
## Replace Dialog
|
||||||
|
|
||||||
replace = प्रतिस्थापित करें
|
replace = बदलें
|
||||||
replace-title = { $filename } पहले से इस स्थान पर मौजूद है।
|
replace-title = { $filename } पहले से इस स्थान पर मौजूद है
|
||||||
replace-warning = क्या आप इसे प्रतिस्थापित करना चाहते हैं? यदि प्रतिस्थापित किया गया, तो मौजूदा फ़ाइल को ओवरराइट किया जाएगा।
|
replace-warning = क्या आप इसे प्रतिस्थापित करना चाहते हैं? यदि प्रतिस्थापित किया गया, तो मौजूदा फ़ाइल को ओवरराइट किया जाएगा।
|
||||||
replace-warning-operation = क्या आप इसे बदलना चाहते हैं? प्रतिस्थापित करने पर मौजूदा फ़ाइल ओवरराइट हो जाएगी।
|
replace-warning-operation = क्या आप इसे बदलना चाहते हैं? प्रतिस्थापित करने पर मौजूदा फ़ाइल ओवरराइट हो जाएगी।
|
||||||
original-file = मूल फ़ाइल
|
original-file = मूल फ़ाइल
|
||||||
|
|
@ -211,13 +211,13 @@ show-details = विवरण दिखाएँ
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
settings = सेटिंग
|
settings = सेटिंग्स
|
||||||
|
|
||||||
### Appearance
|
### Appearance
|
||||||
|
|
||||||
appearance = रूप
|
appearance = रुप-रंग
|
||||||
theme = थीम
|
theme = प्रसंग
|
||||||
match-desktop = सिस्टम सेटिंग्स से मेल खाएँ
|
match-desktop = डेस्कटॉप से मेल खाएँ
|
||||||
dark = डार्क
|
dark = डार्क
|
||||||
light = लाइट
|
light = लाइट
|
||||||
# Context menu
|
# Context menu
|
||||||
|
|
@ -251,27 +251,27 @@ new-tab = नया टैब
|
||||||
new-window = नई विंडो
|
new-window = नई विंडो
|
||||||
rename = नाम बदलें...
|
rename = नाम बदलें...
|
||||||
close-tab = टैब बंद करें
|
close-tab = टैब बंद करें
|
||||||
quit = बंद करें
|
quit = बाहर जाएँ
|
||||||
|
|
||||||
## Edit
|
## Edit
|
||||||
|
|
||||||
edit = संपादित करें
|
edit = संपादन
|
||||||
cut = कट करें
|
cut = काटें
|
||||||
copy = नकल करें
|
copy = कॉपी करें
|
||||||
paste = चिपकाएँ
|
paste = चिपकाएँ
|
||||||
select-all = सभी चुनें
|
select-all = सभी चुनें
|
||||||
|
|
||||||
## View
|
## View
|
||||||
|
|
||||||
zoom-in = ज़ूम इन करें
|
zoom-in = बड़ा करें
|
||||||
default-size = डिफ़ॉल्ट आकार
|
default-size = मूल आकार
|
||||||
zoom-out = ज़ूम आउट करें
|
zoom-out = छोटा करें
|
||||||
view = दृश्य
|
view = दृश्य
|
||||||
grid-view = ग्रिड दृश्य
|
grid-view = ग्रिड दृश्य
|
||||||
list-view = सूची दृश्य
|
list-view = सूची दृश्य
|
||||||
show-hidden-files = छिपी हुई फाइलें दिखाएँ
|
show-hidden-files = छिपी हुई फाइलें दिखाएँ
|
||||||
list-directories-first = सबसे पहले डाइरेक्ट्री दिखाएँ
|
list-directories-first = सबसे पहले डाइरेक्ट्री दिखाएँ
|
||||||
menu-settings = सेटिंग्स...
|
menu-settings = सेटिंग्स..।
|
||||||
menu-about = कॉस्मिक फाइल्स के बारे में...
|
menu-about = कॉस्मिक फाइल्स के बारे में...
|
||||||
|
|
||||||
## Sort
|
## Sort
|
||||||
|
|
@ -284,4 +284,98 @@ sort-oldest-first = पुराने से नए
|
||||||
sort-smallest-to-largest = छोटे से बड़े
|
sort-smallest-to-largest = छोटे से बड़े
|
||||||
sort-largest-to-smallest = बड़े से छोटे
|
sort-largest-to-smallest = बड़े से छोटे
|
||||||
repository = रिपॉजिटरी
|
repository = रिपॉजिटरी
|
||||||
support = सहायता
|
support = समर्थन
|
||||||
|
read-execute = पढ़ें और निष्पादित करें
|
||||||
|
deleted =
|
||||||
|
{ trash } से { $items } { $items ->
|
||||||
|
[one] आइटम मिटाया गया
|
||||||
|
*[other] आइटम मिटाए गए
|
||||||
|
}
|
||||||
|
dismiss = संदेश खारिज करें
|
||||||
|
favorite-path-error = निर्देशिका खोलने में त्रुटि
|
||||||
|
progress = { $percent }%
|
||||||
|
related-apps = संबंधित ऐप्स
|
||||||
|
removing-from-recents =
|
||||||
|
{ recents } से { $items } { $items ->
|
||||||
|
[one] आइटम हटाया जा रहा है
|
||||||
|
*[other] आइटम हटाए जा रहे हैं
|
||||||
|
}
|
||||||
|
remove = हटाएँ
|
||||||
|
read-write-execute = पढ़ें, लिखें और निष्पादित करें
|
||||||
|
other-apps = अन्य ऐप्स
|
||||||
|
pause = विराम
|
||||||
|
keep = रखें
|
||||||
|
permanently-deleting =
|
||||||
|
{ $items } { $items ->
|
||||||
|
[one] आइटम स्थायी रूप से मिटाया जा रहा है
|
||||||
|
*[other] आइटम स्थायी रूप से मिटाए जा रहे हैं
|
||||||
|
}
|
||||||
|
read-write = पढ़ें और लिखें
|
||||||
|
none = कोई नहीं
|
||||||
|
resume = फिर से शुरू करें
|
||||||
|
grid-spacing = ग्रिड स्पेसिंग
|
||||||
|
extract-to = इस रूप में निकालें..।
|
||||||
|
delete = हटाएं
|
||||||
|
read-only = केवल पढ़ने के लिए
|
||||||
|
deleting =
|
||||||
|
{ trash } से { $items } { $items ->
|
||||||
|
[one] आइटम मिटाया जा रहा है
|
||||||
|
*[other] आइटम मिटाए जा रहे हैं
|
||||||
|
} ({ $progress })..।
|
||||||
|
execute-only = केवल निष्पादित करें
|
||||||
|
details = विवरण
|
||||||
|
mount-error = ड्राइव तक पहुँचने में असमर्थ
|
||||||
|
removed-from-recents =
|
||||||
|
{ recents } से { $items } { $items ->
|
||||||
|
[one] आइटम हटाया गया
|
||||||
|
*[other] आइटम हटाए गए
|
||||||
|
}
|
||||||
|
progress-paused = { $percent }%, रुका हुआ
|
||||||
|
cancelled = रद्द किया गया
|
||||||
|
operations-running-finished =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] ऑपरेशन
|
||||||
|
*[other] ऑपरेशन
|
||||||
|
} चल रहा है ({ $percent }%), { $finished } समाप्त..।
|
||||||
|
progress-failed = { $percent }%, विफल
|
||||||
|
extract-to-title = फ़ोल्डर में निकालें
|
||||||
|
open-with = इससे खोलें
|
||||||
|
permanently-deleted =
|
||||||
|
{ $items } { $items ->
|
||||||
|
[one] आइटम स्थायी रूप से मिटाया गया
|
||||||
|
*[other] आइटम स्थायी रूप से मिटाए गए
|
||||||
|
}
|
||||||
|
write-execute = लिखें और निष्पादित करें
|
||||||
|
extract-password-required = पासवर्ड आवश्यक है
|
||||||
|
progress-cancelled = { $percent }%, रद्द किया गया
|
||||||
|
write-only = केवल लिखने के लिए
|
||||||
|
permanently-delete-warning = { $target } को स्थाई रूप से हटा दिया जाएगा। इस कार्रवाई को पूर्ववत नहीं किया जा सकता है।
|
||||||
|
favorite-path-error-description =
|
||||||
|
"{ $path }" को खोला नहीं जा सकता
|
||||||
|
"{ $path }" मौजूद नहीं हो सकता है या आपके पास इसे खोलने की अनुमति नहीं हो सकती है
|
||||||
|
|
||||||
|
क्या आप इसे साइडबार से हटाना चाहते हैं?
|
||||||
|
empty-trash-title = रद्दी साफ़ करें?
|
||||||
|
permanently-delete-question = स्थाई रूप से हटाएं?
|
||||||
|
operations-running =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] ऑपरेशन
|
||||||
|
*[other] ऑपरेशन
|
||||||
|
} चल रहा है ({ $percent }%)..।
|
||||||
|
copy-to-title = कॉपी गंतव्य चुनें
|
||||||
|
copy-to-button-label = कॉपी
|
||||||
|
move-to-title = मूव गंतव्य चुनें
|
||||||
|
move-to-button-label = मूव
|
||||||
|
context-action = संदर्भ क्रिया
|
||||||
|
context-action-confirm-title = "{ $name }" चलाएँ?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
यह { $items } { $items ->
|
||||||
|
[one] आइटम
|
||||||
|
*[other] आइटम
|
||||||
|
} पर चलेगा।
|
||||||
|
run = चलाएँ
|
||||||
|
rename-confirm = नाम बदलें
|
||||||
|
mixed = मिश्रित
|
||||||
|
pasted-image = चिपकाई गई छवि
|
||||||
|
pasted-text = चिपकाया गया पाठ
|
||||||
|
pasted-video = चिपकाया गया वीडियो
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ cosmic-files = COSMIC Fájlok
|
||||||
comment = Fájlkezelő a COSMIC asztali környezethez
|
comment = Fájlkezelő a COSMIC asztali környezethez
|
||||||
keywords = mappa;fájl;kezelő;
|
keywords = mappa;fájl;kezelő;
|
||||||
empty-folder = Üres mappa
|
empty-folder = Üres mappa
|
||||||
empty-folder-hidden = Üres mappa (Rejtett elemeket tartalmaz)
|
empty-folder-hidden = Üres mappa (rejtett elemeket tartalmaz)
|
||||||
no-results = Nincs találat
|
no-results = Nincs találat
|
||||||
filesystem = Fájlrendszer
|
filesystem = Fájlrendszer
|
||||||
home = Saját mappa
|
home = Saját mappa
|
||||||
|
|
@ -17,7 +17,7 @@ desktop-view-options = Asztali nézet beállításai…
|
||||||
show-on-desktop = Megjelenítés az asztalon
|
show-on-desktop = Megjelenítés az asztalon
|
||||||
desktop-folder-content = Asztal mappa tartalma
|
desktop-folder-content = Asztal mappa tartalma
|
||||||
mounted-drives = Csatolt meghajtók
|
mounted-drives = Csatolt meghajtók
|
||||||
trash-folder-icon = Kuka ikonja
|
trash-folder-icon = Kuka ikon
|
||||||
icon-size-and-spacing = Ikonméret és távolság
|
icon-size-and-spacing = Ikonméret és távolság
|
||||||
icon-size = Ikonméret
|
icon-size = Ikonméret
|
||||||
grid-spacing = Rácsköz
|
grid-spacing = Rácsköz
|
||||||
|
|
@ -47,18 +47,18 @@ resume = Folytatás
|
||||||
|
|
||||||
## Compress Dialog
|
## Compress Dialog
|
||||||
|
|
||||||
create-archive = Tömörített fájl létrehozása
|
create-archive = Archívum létrehozása
|
||||||
|
|
||||||
## Extract Dialog
|
## Extract Dialog
|
||||||
|
|
||||||
extract-password-required = Jelszó szükséges
|
extract-password-required = Jelszó szükséges
|
||||||
extract-to = Kibontás ide…
|
extract-to = Kibontás ide…
|
||||||
extract-to-title = Kibontás mappába
|
extract-to-title = Kibontási cél kiválasztása
|
||||||
|
|
||||||
## Empty Trash Dialog
|
## Empty Trash Dialog
|
||||||
|
|
||||||
empty-trash = Kuka ürítése
|
empty-trash = A Kuka ürítése
|
||||||
empty-trash-warning = A kukában lévő összes elem véglegesen törölve lesz
|
empty-trash-warning = A Kukában lévő összes elem véglegesen törölve lesz
|
||||||
|
|
||||||
## Mount Error Dialog
|
## Mount Error Dialog
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ save-file = Fájl mentése
|
||||||
|
|
||||||
## Open With Dialog
|
## Open With Dialog
|
||||||
|
|
||||||
open-with-title = Hogyan szeretnéd megnyitni a következőt: „{ $name }”?
|
open-with-title = Hogyan szeretnéd megnyitni a(z) „{ $name }” fájlt?
|
||||||
browse-store = { $store } böngészése
|
browse-store = { $store } böngészése
|
||||||
other-apps = Egyéb alkalmazások
|
other-apps = Egyéb alkalmazások
|
||||||
related-apps = Hasonló alkalmazások
|
related-apps = Hasonló alkalmazások
|
||||||
|
|
@ -103,7 +103,7 @@ related-apps = Hasonló alkalmazások
|
||||||
selected-items = A(z) { $items } kijelölt elem
|
selected-items = A(z) { $items } kijelölt elem
|
||||||
permanently-delete-question = Végleges törlés?
|
permanently-delete-question = Végleges törlés?
|
||||||
delete = Törlés
|
delete = Törlés
|
||||||
permanently-delete-warning = { $target } véglegesen törölve lesz. A művelet nem visszavonható.
|
permanently-delete-warning = { $target } véglegesen törölve lesz. A művelet nem vonható vissza.
|
||||||
|
|
||||||
## Rename Dialog
|
## Rename Dialog
|
||||||
|
|
||||||
|
|
@ -113,7 +113,7 @@ rename-folder = Mappa átnevezése
|
||||||
## Replace Dialog
|
## Replace Dialog
|
||||||
|
|
||||||
replace = Csere
|
replace = Csere
|
||||||
replace-title = „{ $filename }” már létezik
|
replace-title = „{ $filename }” már létezik ezen a helyen
|
||||||
replace-warning = Szeretnéd lecserélni a meglévő fájlt a mentendő fájllal? A cserével felülírod annak tartalmát.
|
replace-warning = Szeretnéd lecserélni a meglévő fájlt a mentendő fájllal? A cserével felülírod annak tartalmát.
|
||||||
replace-warning-operation = Szeretnéd lecserélni? A csere felülírja annak tartalmát.
|
replace-warning-operation = Szeretnéd lecserélni? A csere felülírja annak tartalmát.
|
||||||
original-file = Eredeti fájl
|
original-file = Eredeti fájl
|
||||||
|
|
@ -133,7 +133,7 @@ set-and-launch = Beállítás és indítás
|
||||||
open-with = Megnyitás ezzel
|
open-with = Megnyitás ezzel
|
||||||
owner = Tulajdonos
|
owner = Tulajdonos
|
||||||
group = Csoport
|
group = Csoport
|
||||||
other = Többi
|
other = Mások
|
||||||
|
|
||||||
### Mode 0
|
### Mode 0
|
||||||
|
|
||||||
|
|
@ -172,7 +172,7 @@ read-write-execute = Olvasás, írás és végrehajtás
|
||||||
favorite-path-error = Hiba a könyvtár megnyitásakor
|
favorite-path-error = Hiba a könyvtár megnyitásakor
|
||||||
favorite-path-error-description =
|
favorite-path-error-description =
|
||||||
Nem sikerült megnyitni ezt: „{ $path }”
|
Nem sikerült megnyitni ezt: „{ $path }”
|
||||||
„{ $path }” lehet nem létezik, vagy nincs jogosultságod a megnyitásához
|
A(z) „{ $path }” útvonal lehet, hogy nem létezik, vagy nincs jogosultságod a megnyitásához
|
||||||
|
|
||||||
Szeretnéd eltávolítani az oldalsávról?
|
Szeretnéd eltávolítani az oldalsávról?
|
||||||
remove = Eltávolítás
|
remove = Eltávolítás
|
||||||
|
|
@ -187,9 +187,9 @@ keep = Megtartás
|
||||||
## Add Network Drive
|
## Add Network Drive
|
||||||
|
|
||||||
add-network-drive = Hálózati meghajtó hozzáadása
|
add-network-drive = Hálózati meghajtó hozzáadása
|
||||||
connect = Csatlakozás
|
connect = Kapcsolódás
|
||||||
connect-anonymously = Csatlakozás névtelenül
|
connect-anonymously = Kapcsolódás névtelenül
|
||||||
connecting = Csatlakozás…
|
connecting = Kapcsolódás…
|
||||||
domain = Tartomány
|
domain = Tartomány
|
||||||
enter-server-address = Add meg a kiszolgáló címét
|
enter-server-address = Add meg a kiszolgáló címét
|
||||||
network-drive-description =
|
network-drive-description =
|
||||||
|
|
@ -209,7 +209,7 @@ network-drive-schemes =
|
||||||
network-drive-error = Nem érhető el a hálózati meghajtó
|
network-drive-error = Nem érhető el a hálózati meghajtó
|
||||||
password = Jelszó
|
password = Jelszó
|
||||||
remember-password = Jelszó megjegyzése
|
remember-password = Jelszó megjegyzése
|
||||||
try-again = Újra
|
try-again = Próbáld újra
|
||||||
username = Felhasználónév
|
username = Felhasználónév
|
||||||
|
|
||||||
## Operations
|
## Operations
|
||||||
|
|
@ -217,7 +217,7 @@ username = Felhasználónév
|
||||||
cancelled = Megszakítva
|
cancelled = Megszakítva
|
||||||
edit-history = Fájlműveleti előzmények
|
edit-history = Fájlműveleti előzmények
|
||||||
history = Előzmények
|
history = Előzmények
|
||||||
no-history = Nem találhatók elemek az előzményekben
|
no-history = Nem találhatók elemek az előzményekben.
|
||||||
pending = Függőben
|
pending = Függőben
|
||||||
progress = { $percent }%
|
progress = { $percent }%
|
||||||
progress-cancelled = { $percent }%, megszakítva
|
progress-cancelled = { $percent }%, megszakítva
|
||||||
|
|
@ -251,13 +251,13 @@ deleting =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] elem
|
[one] elem
|
||||||
*[other] elem
|
*[other] elem
|
||||||
} törlése a kukából ({ $progress })…
|
} törlése a Kukából ({ $progress })…
|
||||||
deleted =
|
deleted =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] elem
|
[one] elem
|
||||||
*[other] elem
|
*[other] elem
|
||||||
} törölve a kukából
|
} törölve a Kukából
|
||||||
emptying-trash = { trash } kiürítése ({ $progress })…
|
emptying-trash = { trash } ürítése ({ $progress })…
|
||||||
emptied-trash = { trash } kiürítve
|
emptied-trash = { trash } kiürítve
|
||||||
extracting =
|
extracting =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
|
|
@ -309,12 +309,12 @@ restoring =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] elem
|
[one] elem
|
||||||
*[other] elem
|
*[other] elem
|
||||||
} visszaállítása a kukából ({ $progress })…
|
} visszaállítása a Kukából ({ $progress })…
|
||||||
restored =
|
restored =
|
||||||
{ $items } { $items ->
|
{ $items } { $items ->
|
||||||
[one] elem
|
[one] elem
|
||||||
*[other] elem
|
*[other] elem
|
||||||
} visszaállítva a kukából
|
} visszaállítva a Kukából
|
||||||
unknown-folder = ismeretlen mappa
|
unknown-folder = ismeretlen mappa
|
||||||
|
|
||||||
## Open with
|
## Open with
|
||||||
|
|
@ -326,11 +326,11 @@ default-app = { $name } (alapértelmezett)
|
||||||
|
|
||||||
show-details = Részletek megjelenítése
|
show-details = Részletek megjelenítése
|
||||||
type = Típus: { $mime }
|
type = Típus: { $mime }
|
||||||
items = Elemek: { $items }
|
items = Elemek száma: { $items }
|
||||||
item-size = Méret: { $size }
|
item-size = Méret: { $size }
|
||||||
item-created = Létrehozva: { $created }
|
item-created = Létrehozva: { $created }
|
||||||
item-modified = Módosítva: { $modified }
|
item-modified = Módosítva: { $modified }
|
||||||
item-accessed = Hozzáférve: { $accessed }
|
item-accessed = Utolsó hozzáférés: { $accessed }
|
||||||
calculating = Számítás…
|
calculating = Számítás…
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
@ -349,8 +349,8 @@ light = Világos
|
||||||
### Type to Search
|
### Type to Search
|
||||||
|
|
||||||
type-to-search = Gépeléssel keresés
|
type-to-search = Gépeléssel keresés
|
||||||
type-to-search-recursive = Keresés a jelenlegi mappában és almappákban
|
type-to-search-recursive = Keresés a jelenlegi mappában és az almappákban
|
||||||
type-to-search-enter-path = Elérési út megadása
|
type-to-search-enter-path = Elérési út megnyitása
|
||||||
# Context menu
|
# Context menu
|
||||||
add-to-sidebar = Hozzáadás az oldalsávhoz
|
add-to-sidebar = Hozzáadás az oldalsávhoz
|
||||||
compress = Tömörítés…
|
compress = Tömörítés…
|
||||||
|
|
@ -360,8 +360,8 @@ extract-here = Kibontás
|
||||||
new-file = Új fájl…
|
new-file = Új fájl…
|
||||||
new-folder = Új mappa…
|
new-folder = Új mappa…
|
||||||
open-in-terminal = Megnyitás a terminálban
|
open-in-terminal = Megnyitás a terminálban
|
||||||
move-to-trash = Kukába helyezés
|
move-to-trash = Áthelyezés a Kukába
|
||||||
restore-from-trash = Visszaállítás a kukából
|
restore-from-trash = Visszaállítás a Kukából
|
||||||
remove-from-sidebar = Eltávolítás az oldalsávról
|
remove-from-sidebar = Eltávolítás az oldalsávról
|
||||||
sort-by-name = Név szerinti rendezés
|
sort-by-name = Név szerinti rendezés
|
||||||
sort-by-modified = Módosítás szerinti rendezés
|
sort-by-modified = Módosítás szerinti rendezés
|
||||||
|
|
@ -372,8 +372,8 @@ remove-from-recents = Eltávolítás a legutóbbiak közül
|
||||||
## Desktop
|
## Desktop
|
||||||
|
|
||||||
change-wallpaper = Háttérkép cseréje…
|
change-wallpaper = Háttérkép cseréje…
|
||||||
desktop-appearance = Asztali megjelenés…
|
desktop-appearance = Asztal megjelenése…
|
||||||
display-settings = Képernyő-beállítások…
|
display-settings = Kijelzőbeállítások…
|
||||||
|
|
||||||
# Menu
|
# Menu
|
||||||
|
|
||||||
|
|
@ -406,7 +406,7 @@ grid-view = Rácsnézet
|
||||||
list-view = Listanézet
|
list-view = Listanézet
|
||||||
show-hidden-files = Rejtett fájlok megjelenítése
|
show-hidden-files = Rejtett fájlok megjelenítése
|
||||||
list-directories-first = Könyvtárak listázása először
|
list-directories-first = Könyvtárak listázása először
|
||||||
gallery-preview = Galéria előnézet
|
gallery-preview = Galéria-előnézet
|
||||||
menu-settings = Beállítások…
|
menu-settings = Beállítások…
|
||||||
menu-about = A COSMIC Fájlok névjegye…
|
menu-about = A COSMIC Fájlok névjegye…
|
||||||
|
|
||||||
|
|
@ -415,14 +415,14 @@ menu-about = A COSMIC Fájlok névjegye…
|
||||||
sort = Rendezés
|
sort = Rendezés
|
||||||
sort-a-z = A-Z
|
sort-a-z = A-Z
|
||||||
sort-z-a = Z-A
|
sort-z-a = Z-A
|
||||||
sort-newest-first = Legújabb előre
|
sort-newest-first = Legújabb elöl
|
||||||
sort-oldest-first = Legrégibb előre
|
sort-oldest-first = Legrégibb elöl
|
||||||
sort-smallest-to-largest = Legkisebbtől a legnagyobbig
|
sort-smallest-to-largest = Legkisebbtől a legnagyobbig
|
||||||
sort-largest-to-smallest = Legnagyobbtól a legkisebbig
|
sort-largest-to-smallest = Legnagyobbtól a legkisebbig
|
||||||
repository = Tároló
|
repository = Tároló
|
||||||
support = Támogatás
|
support = Támogatás
|
||||||
progress-failed = { $percent }%, sikertelen
|
progress-failed = { $percent }%, sikertelen
|
||||||
empty-trash-title = Kuka ürítése?
|
empty-trash-title = Kiüríted a Kukát?
|
||||||
type-to-search-select = Kijelöli az első egyező fájlt vagy mappát
|
type-to-search-select = Kijelöli az első egyező fájlt vagy mappát
|
||||||
pasted-image = Beillesztett kép
|
pasted-image = Beillesztett kép
|
||||||
pasted-text = Beillesztett szöveg
|
pasted-text = Beillesztett szöveg
|
||||||
|
|
@ -436,3 +436,19 @@ move-to = Áthelyezés ide…
|
||||||
show-recents = Legutóbbiak mappa megjelenítése az oldalsávban
|
show-recents = Legutóbbiak mappa megjelenítése az oldalsávban
|
||||||
copy-path = Útvonal másolása
|
copy-path = Útvonal másolása
|
||||||
clear-recents-history = Legutóbbiak előzményének törlése
|
clear-recents-history = Legutóbbiak előzményének törlése
|
||||||
|
mixed = Vegyes
|
||||||
|
context-action = Helyi művelet
|
||||||
|
context-action-confirm-title = Futtatod ezt: „{ $name }”?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Ez a művelet { $items } { $items ->
|
||||||
|
[one] elemen
|
||||||
|
*[other] elemen
|
||||||
|
} fog lefutni.
|
||||||
|
run = Futtatás
|
||||||
|
rename-confirm = Átnevezés
|
||||||
|
checksum = { $kind }-ellenőrzőösszeg
|
||||||
|
calculate = Számítás
|
||||||
|
error = Hiba
|
||||||
|
sidebar-label = Oldalsávcímke
|
||||||
|
search-application = Keresés alkalmazásnév alapján
|
||||||
|
change-sidebar-label = Oldalsávcímke módosítása
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ settings = Pengaturan
|
||||||
single-click = Klik sekali untuk membuka
|
single-click = Klik sekali untuk membuka
|
||||||
appearance = Tampilan
|
appearance = Tampilan
|
||||||
theme = Tema
|
theme = Tema
|
||||||
match-desktop = Cocokkan desktop
|
match-desktop = Cocok desktop
|
||||||
dark = Gelap
|
dark = Gelap
|
||||||
light = Terang
|
light = Terang
|
||||||
type-to-search = Ketik untuk mencari
|
type-to-search = Ketik untuk mencari
|
||||||
|
|
@ -318,3 +318,19 @@ comment = Pengelola berkas untuk desktop COSMIC
|
||||||
show-recents = Map terbaru di bilah sisi
|
show-recents = Map terbaru di bilah sisi
|
||||||
clear-recents-history = Bersihkan riwayat Terbaru
|
clear-recents-history = Bersihkan riwayat Terbaru
|
||||||
copy-path = Salin jalur
|
copy-path = Salin jalur
|
||||||
|
mixed = Bercampur
|
||||||
|
context-action = Tindakan konteks
|
||||||
|
context-action-confirm-title = Jalankan "{ $name }"?
|
||||||
|
run = Jalankan
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Ini akan dijalankan pada { $items } { $items ->
|
||||||
|
[one] item
|
||||||
|
*[other] item
|
||||||
|
}.
|
||||||
|
rename-confirm = Ganti nama
|
||||||
|
checksum = { $kind } checksum
|
||||||
|
calculate = Hitung
|
||||||
|
error = Galat
|
||||||
|
sidebar-label = Label bilah sisi
|
||||||
|
search-application = Cari berdasarkan nama aplikasi
|
||||||
|
change-sidebar-label = Ubah label bilah sisi
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
cosmic-files = COSMIC Files
|
cosmic-files = COSMIC Files
|
||||||
comment = File manager di COSMIC
|
comment = File manager per il desktop COSMIC
|
||||||
keywords = File;Archivi;Cartelle;Explorer;
|
keywords = File;Archivi;Cartelle;Explorer;
|
||||||
empty-folder = Cartella vuota
|
empty-folder = Cartella vuota
|
||||||
empty-folder-hidden = Cartella vuota (con elementi nascosti)
|
empty-folder-hidden = Cartella vuota (con elementi nascosti)
|
||||||
|
|
@ -7,7 +7,7 @@ no-results = Nessun risultato trovato
|
||||||
filesystem = Filesystem
|
filesystem = Filesystem
|
||||||
home = Home
|
home = Home
|
||||||
networks = Reti
|
networks = Reti
|
||||||
notification-in-progress = Operazioni sui file in corso.
|
notification-in-progress = Operazioni sui file in corso
|
||||||
trash = Cestino
|
trash = Cestino
|
||||||
recents = Recenti
|
recents = Recenti
|
||||||
undo = Annulla
|
undo = Annulla
|
||||||
|
|
@ -322,7 +322,7 @@ single-click = Click singolo per aprire
|
||||||
|
|
||||||
appearance = Aspetto
|
appearance = Aspetto
|
||||||
theme = Tema
|
theme = Tema
|
||||||
match-desktop = Sistema
|
match-desktop = Abbina al desktop
|
||||||
dark = Scuro
|
dark = Scuro
|
||||||
light = Chiaro
|
light = Chiaro
|
||||||
|
|
||||||
|
|
@ -422,3 +422,4 @@ removed-from-recents =
|
||||||
[one] elemento
|
[one] elemento
|
||||||
*[other] elementi
|
*[other] elementi
|
||||||
} da { recents }
|
} da { recents }
|
||||||
|
run = Esegui
|
||||||
|
|
|
||||||
|
|
@ -1 +1,322 @@
|
||||||
change-wallpaper = Beddel aɣrab n ugdil…
|
change-wallpaper = Beddel aɣrab n ugdil…
|
||||||
|
cosmic-files = Ifuyla COSMIC
|
||||||
|
empty-folder = Akaram d ilem
|
||||||
|
empty-folder-hidden = Akaram d ilem (yesɛa iferdisen yeffren)
|
||||||
|
no-results = Ulac igmaḍ yettwafen
|
||||||
|
home = Agejdan
|
||||||
|
networks = Iẓeḍwa
|
||||||
|
notification-in-progress = Timhalin ɣef ifuyla la tteddunt
|
||||||
|
trash = Iḍumman
|
||||||
|
recents = Melmi kan
|
||||||
|
undo = Ssemmet
|
||||||
|
today = Ass-a
|
||||||
|
desktop-view-options = Iɣewwaṛen n tmeẓri n tnarit…
|
||||||
|
show-on-desktop = Sken deg tnarit
|
||||||
|
desktop-folder-content = Agbur n ukaram n tnarit
|
||||||
|
trash-folder-icon = Tignit n ukaram n iḍumman
|
||||||
|
icon-size-and-spacing = Tiddi n tignit akked tallunt
|
||||||
|
icon-size = Tiddi n tignit
|
||||||
|
name = Isem
|
||||||
|
modified = Ittusnifel
|
||||||
|
trashed-on = Yettwakkes ɣer tqecwalt n yiḍumman
|
||||||
|
size = Tiddi
|
||||||
|
details = Ifatusen
|
||||||
|
pause = Serǧu
|
||||||
|
resume = Kemmel
|
||||||
|
create-archive = Snulfu-d aɣbaṛ
|
||||||
|
extract-to = Ssef ɣer...
|
||||||
|
extract-to-title = Ssef ɣer ukaram
|
||||||
|
empty-trash = Silem iḍumman
|
||||||
|
rename-folder = Snifel isem n ukaram
|
||||||
|
filesystem = Anagraw n yifuyla
|
||||||
|
dismiss = Zgel izen
|
||||||
|
empty-trash-title = Silem iḍumman?
|
||||||
|
empty-trash-warning = Iferdisen n ukaram n iḍumman ad ttwakksen i lebda
|
||||||
|
create-new-file = Snulfu-d afaylu amaynut
|
||||||
|
create-new-folder = Snulfu-d akaram amaynut
|
||||||
|
file-name = Isem n ufaylu
|
||||||
|
folder-name = Isem n ukaram
|
||||||
|
file-already-exists = Afaylu s yisem-agi yella yakan
|
||||||
|
folder-already-exists = Akaram s yisem-agi yella yakan
|
||||||
|
name-hidden = Ismawen ibeddun s "." ad ttwaffren
|
||||||
|
name-invalid = Isem ur yezmir ara ad yili "{ $filename }"
|
||||||
|
cancel = Semmet
|
||||||
|
create = Snulfu-d
|
||||||
|
open = Ldi
|
||||||
|
open-file = Ldi afaylu
|
||||||
|
open-folder = Ldi akaram
|
||||||
|
open-in-new-tab = Ldi deg yiccer amaynut
|
||||||
|
open-in-new-window = Ldi deg usfaylu amaynut
|
||||||
|
open-item-location = Ldi adig n uferdis
|
||||||
|
open-multiple-files = Ldi aget n ifuyla
|
||||||
|
mounted-drives = imeɣriyen yettuserkben
|
||||||
|
mount-error = Ulamek anekcum ɣer umeɣri
|
||||||
|
operations-running =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] n temhelt la tteddu
|
||||||
|
*[other] n temhal la tteddunt
|
||||||
|
} ({ $percent }%)...
|
||||||
|
operations-running-finished =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] n temhelt la tteddu
|
||||||
|
*[other] n temhal la tteddunt
|
||||||
|
} ({ $percent }%), { $finished } { $finished ->
|
||||||
|
[one] tfukk
|
||||||
|
*[other] fukkent
|
||||||
|
}...
|
||||||
|
copy-to-title = Fren taɣerwaḍt n unɣel
|
||||||
|
copy-to-button-label = Nɣel
|
||||||
|
move-to-title = Fren taɣerwaḍt n usmutti
|
||||||
|
move-to-button-label = Smutti
|
||||||
|
comment = Amsefrak n yifuyla i tnarit COSMIC
|
||||||
|
keywords = Akaram;Amsefrak;
|
||||||
|
delete = kkes
|
||||||
|
replace = Semselsi
|
||||||
|
support = Tallelt
|
||||||
|
settings = Iɣewwaṛen
|
||||||
|
appearance = Timeẓri
|
||||||
|
theme = Asentel
|
||||||
|
dark = Aɣmayan
|
||||||
|
light = Aceɛlal
|
||||||
|
paste = Senteḍ
|
||||||
|
select-all = Fren akk
|
||||||
|
zoom-in = Asemɣeṛ
|
||||||
|
default-size = Tiddi tamezwert
|
||||||
|
zoom-out = Asemẓi
|
||||||
|
view = Wali
|
||||||
|
menu-settings = Iɣewwaṛen…
|
||||||
|
save = Sekles
|
||||||
|
match-desktop = Semṣada akked tnarit
|
||||||
|
file = Afaylu
|
||||||
|
new-window = Asfaylu amaynut
|
||||||
|
quit = Ffeɣ
|
||||||
|
edit = Ẓreg
|
||||||
|
cut = Gzem
|
||||||
|
copy = Nɣel
|
||||||
|
repository = Asarsay
|
||||||
|
pasted-image = Tettwasenṭeḍ tugna
|
||||||
|
pasted-text = Yettwasenṭeḍ uḍris
|
||||||
|
pasted-video = Tettwasenṭeḍ tvidyut
|
||||||
|
compress = Skussem…
|
||||||
|
grid-spacing = Tallunt n iẓiki
|
||||||
|
extract-password-required = Awal uffir yettwasra
|
||||||
|
permanently-delete-question = Kkes s wudem imezgi?
|
||||||
|
permanently-delete-warning = { $target } ad yettwakkes s wudem imezgi. Tigawt-agi ur tezmir ara ad tettwasefsex.
|
||||||
|
rename-file = Snifel isem n ufaylu
|
||||||
|
replace-title = "{ $filename }" yella yakan deg wadig-a
|
||||||
|
replace-warning = Tebɣiḍ ad t-tsemselsiḍ s win ara teskelseḍ? Asemselsi-ines ad yaru sennig ugbur-is.
|
||||||
|
replace-warning-operation = Tebɣiḍ ad t-tsemselsiḍ? Asemselsi-ines ad yaru sennig ugbur-is.
|
||||||
|
original-file = Afaylu aneṣli
|
||||||
|
replace-with = Semselsi s
|
||||||
|
apply-to-all = Snes i meṛṛa
|
||||||
|
keep-both = Eǧǧ-iten deg sin
|
||||||
|
skip = Zgel
|
||||||
|
set-executable-and-launch = Sbeddet am umselkam syinna senker
|
||||||
|
set-executable-and-launch-description = Tebɣiḍ ad tesbeddeḍ "{ $name }" am umselkam syinna ad tessenkreḍ?
|
||||||
|
set-and-launch = Sbadu sakin senker
|
||||||
|
add-network-drive = Rnu ameɣri n uẓeṭṭa
|
||||||
|
connect = Qqen
|
||||||
|
connect-anonymously = Qqen s wudem udrig
|
||||||
|
connecting = Tuqqna…
|
||||||
|
domain = Taɣult
|
||||||
|
enter-server-address = Sekcem tansa n uqeddac
|
||||||
|
network-drive-description =
|
||||||
|
Tansiwin n uqeddac gebrent azwir n uneggaf akked tansa.
|
||||||
|
Imedyaten: ssh://192.168.0.1, ftp://[2001:db8::1]
|
||||||
|
network-drive-error = Ur izmir ara ad yekcem ɣer umeɣri n uzeṭṭa
|
||||||
|
password = Awal uffir
|
||||||
|
remember-password = Cfu ɣef wawal uffir
|
||||||
|
try-again = ɛreḍ tikelt nniḍen
|
||||||
|
username = Isem n useqdac
|
||||||
|
cancelled = Yettwasefsex
|
||||||
|
edit-history = Ẓreg amazray
|
||||||
|
history = Amazray
|
||||||
|
no-history = Ulac iferdisen deg umazray.
|
||||||
|
pending = Yettṛaǧu
|
||||||
|
progress-cancelled = { $percent }%, yettwasefsex
|
||||||
|
progress-failed = { $percent }%, ur yeddi ara
|
||||||
|
failed = Ur yeddi ara
|
||||||
|
renaming = Asnifel n yisem "{ $from }" ɣer "{ $to }"
|
||||||
|
renamed = Yettwasenfel yisem n "{ $from }" ɣer "{ $to }"
|
||||||
|
unknown-folder = akaram arussin
|
||||||
|
menu-open-with = Ldi s…
|
||||||
|
default-app = { $name } (amezwer)
|
||||||
|
show-details = Sken talqayt
|
||||||
|
type = Anaw: { $mime }
|
||||||
|
items = Iferdisen: { $items }
|
||||||
|
item-size = Tiddi: { $size }
|
||||||
|
item-created = Yettwarna: { $created }
|
||||||
|
item-modified = Ittusnifel: { $modified }
|
||||||
|
item-accessed = Yettwakcem: { $accessed }
|
||||||
|
calculating = Asiḍen…
|
||||||
|
single-click = Asiti asuf i ulday
|
||||||
|
type-to-search = Aru iwakken ad tnadiḍ
|
||||||
|
type-to-search-recursive = Nadi akaram amiran akked ikaramen inaddawen meṛṛa
|
||||||
|
type-to-search-enter-path = Sekcem abrid ɣer ukaram neɣ afaylu
|
||||||
|
add-to-sidebar = Rnu ɣer ufeggag adisan
|
||||||
|
delete-permanently = Kkes i lebda
|
||||||
|
grid-view = Askan s iẓiki
|
||||||
|
list-view = Askan s tebdart
|
||||||
|
show-hidden-files = Sken ifuyla uffiren
|
||||||
|
gallery-preview = Taskant n temidelt
|
||||||
|
menu-about = Ɣef Ifuyla COSMIC…
|
||||||
|
sort = Asmizzwer
|
||||||
|
sort-newest-first = Amaynut d amezwaru
|
||||||
|
sort-oldest-first = Aqbur d amezwaru
|
||||||
|
sort-smallest-to-largest = Seg umeẓyan akk ɣer umeqqran
|
||||||
|
sort-largest-to-smallest = Seg umeqqran akk ɣer umeẓyan
|
||||||
|
open-multiple-folders = Ldi usgit n ikaramen
|
||||||
|
save-file = Sekles afaylu
|
||||||
|
open-with-title = Amek tebɣiḍ ad teldiḍ "{ $name }"?
|
||||||
|
browse-store = Snirem { $store }
|
||||||
|
other-apps = Isnasen-nniḍen
|
||||||
|
emptying-trash = Silem { trash } ({ $progress })…
|
||||||
|
emptied-trash = D ilem { trash }
|
||||||
|
set-permissions = Sbadu isirigen i "{ $name }" ɣer { $mode }
|
||||||
|
eject = Ḍeqqer
|
||||||
|
extract-here = Ssef
|
||||||
|
new-file = Afaylu amaynut…
|
||||||
|
new-folder = Akaram amaynut…
|
||||||
|
open-in-terminal = Ldi deg yixef
|
||||||
|
move-to-trash = Smutti ɣer tqecwalt n yiḍumman
|
||||||
|
restore-from-trash = Err-d seg tqecwalt n yiḍumman
|
||||||
|
remove-from-sidebar = Kkes seg ugalis adisan
|
||||||
|
sort-by-name = Smizzwer s yisem
|
||||||
|
sort-by-modified = Asmizzwer s usnifel
|
||||||
|
sort-by-size = Asmizzwer s tiddi
|
||||||
|
sort-by-trashed = Asmizzwer s wakud n tukksa
|
||||||
|
remove-from-recents = Kkes seg ineggura
|
||||||
|
desktop-appearance = Timeẓri n tnarit…
|
||||||
|
display-settings = Iɣewwaṛen n ubeqqeḍ...
|
||||||
|
new-tab = Iccer amaynut
|
||||||
|
reload-folder = Ales asali n ukaram
|
||||||
|
rename = Snifel isem...
|
||||||
|
close-tab = Mdel iccer
|
||||||
|
list-directories-first = Sken di tazwara ikaramen
|
||||||
|
open-with = Ldi s
|
||||||
|
owner = Bab
|
||||||
|
group = Agraw
|
||||||
|
other = Ayen nniḍen
|
||||||
|
none = Ula Yiwen
|
||||||
|
execute-only = Selkem kan
|
||||||
|
write-only = Aru kan
|
||||||
|
write-execute = Aru u selkem
|
||||||
|
read-only = I tɣuri kan
|
||||||
|
read-execute = Ɣeṛ u selkem
|
||||||
|
read-write = Ɣeṛ u aru
|
||||||
|
read-write-execute = Ɣeṛ, aru, u selkem
|
||||||
|
favorite-path-error = Tuccḍa deg ulday n ukaram
|
||||||
|
remove = Kkes
|
||||||
|
keep = Eǧǧ
|
||||||
|
progress = { $percent }%
|
||||||
|
progress-paused = { $percent }%, ibedd
|
||||||
|
complete = Immed
|
||||||
|
copy_noun = Nɣel
|
||||||
|
creating = Asnulfu n "{ $name }" deg "{ $parent }"
|
||||||
|
created = Yettwarna "{ $name }" deg "{ $parent }"
|
||||||
|
setting-executable-and-launching = Asbadu n "{ $name }" am umselkam syin ad yettwasekker
|
||||||
|
set-executable-and-launched = Sbadu "{ $name }" am umselkam syin sekker-it
|
||||||
|
setting-permissions = Asbadu n tsirag i "{ $name }" ɣer { $mode }
|
||||||
|
related-apps = Isnasen icudden
|
||||||
|
favorite-path-error-description =
|
||||||
|
Ur izmir ara ad yeldi "{ $path }"
|
||||||
|
"{ $path }" yezmer lḥal ulac-it neɣ ahat ur tesɛiḍ ara tisirag akken ad t-teldiḍ
|
||||||
|
|
||||||
|
Tebɣiḍ ad t-tekkseḍ seg ufeggag adisan?
|
||||||
|
compressing =
|
||||||
|
La issekkussum { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg "{ $from }" ɣer "{ $to }" ({ $progress })...
|
||||||
|
compressed =
|
||||||
|
Yekussem { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg "{ $from }" ɣer "{ $to }"
|
||||||
|
copying =
|
||||||
|
Anɣal { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg "{ $from }" ɣer "{ $to }" ({ $progress })...
|
||||||
|
copied =
|
||||||
|
Yettwanɣel { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg "{ $from }" ɣer "{ $to }"
|
||||||
|
deleting =
|
||||||
|
Tukksa { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg { trash } ({ $progress })...
|
||||||
|
deleted =
|
||||||
|
Yettwakkes { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg { trash }
|
||||||
|
extracting =
|
||||||
|
Tussfa { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg "{ $from }" ɣer "{ $to }" ({ $progress })...
|
||||||
|
extracted =
|
||||||
|
Yettusef { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg "{ $from }" ɣer "{ $to }"
|
||||||
|
moving =
|
||||||
|
Asmutti { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg "{ $from }" ɣer "{ $to }" ({ $progress })...
|
||||||
|
moved =
|
||||||
|
Yettwasenkez { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg "{ $from }" ɣer "{ $to }"
|
||||||
|
permanently-deleting =
|
||||||
|
Tukksa i lebda { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
}
|
||||||
|
permanently-deleted =
|
||||||
|
I lebda yettwakkes { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
}
|
||||||
|
removing-from-recents =
|
||||||
|
Tukksa { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg { recents }
|
||||||
|
removed-from-recents =
|
||||||
|
Yettwakkes { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg { recents }
|
||||||
|
restoring =
|
||||||
|
Tiririt { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg { trash } ({ $progress })…
|
||||||
|
restored =
|
||||||
|
Yettwarr-d { $items } { $items ->
|
||||||
|
[one] n uferdis
|
||||||
|
*[other] n yiferdisen
|
||||||
|
} seg { trash }
|
||||||
|
network-drive-schemes =
|
||||||
|
Ineggafen iwejden, azwir
|
||||||
|
AppleTalk,afp://
|
||||||
|
Aneggaf n usiweḍ n yifuyla,ftp:// neɣ ftps://
|
||||||
|
Anagraw n yifuyla n uzeṭṭa,nfs://
|
||||||
|
Iḥder n yizen n uqeddac,smb://
|
||||||
|
SSH Aneggaf n usiweḍ n yifuyla,sftp:// neɣ ssh://
|
||||||
|
WebDAV:// neɣ davs://
|
||||||
|
sort-a-z = A-Ẓ
|
||||||
|
sort-z-a = Ẓ-A
|
||||||
|
selected-items = { $items } n yiferdisen yettwafernen
|
||||||
|
type-to-search-select = Fren afaylu amezwaru neɣ akaram yemṣadan
|
||||||
|
copy-to = Nɣel ɣer...
|
||||||
|
move-to = Smutti ɣer…
|
||||||
|
show-recents = Akaram n melmi kan deg ufeggag adisan
|
||||||
|
clear-recents-history = Sfeḍ azray n melmi kan
|
||||||
|
copy-path = Nɣel abrid
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ name = Аты
|
||||||
modified = Өзгертілген
|
modified = Өзгертілген
|
||||||
trashed-on = Қоқыс шелегіне тасталған
|
trashed-on = Қоқыс шелегіне тасталған
|
||||||
size = Өлшемі
|
size = Өлшемі
|
||||||
details = Ақпараты
|
details = Толық ақпарат
|
||||||
dismiss = Хабарламаны елемеу
|
dismiss = Хабарламаны елемеу
|
||||||
operations-running =
|
operations-running =
|
||||||
{ $running } { $running ->
|
{ $running } { $running ->
|
||||||
|
|
@ -246,17 +246,17 @@ item-accessed = Қол жеткізілген: { $accessed }
|
||||||
calculating = Есептеу...
|
calculating = Есептеу...
|
||||||
settings = Баптаулар
|
settings = Баптаулар
|
||||||
single-click = Ашу үшін бір рет шерту
|
single-click = Ашу үшін бір рет шерту
|
||||||
appearance = Сыртқы түрі
|
appearance = Сыртқы көрініс
|
||||||
theme = Тақырып
|
theme = Тема
|
||||||
match-desktop = Жұмыс үстеліне сәйкес келу
|
match-desktop = Жұмыс үстеліне сәйкес болу
|
||||||
dark = Күңгірт
|
dark = Қою
|
||||||
light = Ашық
|
light = Ашық
|
||||||
type-to-search = Іздеу үшін теру
|
type-to-search = Іздеу үшін теру
|
||||||
type-to-search-recursive = Ағымдағы бума мен барлық ішкі бумаларды іздейді
|
type-to-search-recursive = Ағымдағы бума мен барлық ішкі бумаларды іздейді
|
||||||
type-to-search-enter-path = Бумаға немесе файлға жолды енгізеді
|
type-to-search-enter-path = Бумаға немесе файлға жолды енгізеді
|
||||||
type-to-search-select = Бірінші сәйкес келетін файлды немесе буманы таңдайды
|
type-to-search-select = Бірінші сәйкес келетін файлды немесе буманы таңдайды
|
||||||
add-to-sidebar = Бүйірлік панельге қосу
|
add-to-sidebar = Бүйірлік панельге қосу
|
||||||
compress = Сығу
|
compress = Сығу...
|
||||||
delete-permanently = Біржолата өшіру
|
delete-permanently = Біржолата өшіру
|
||||||
eject = Шығару
|
eject = Шығару
|
||||||
extract-here = Тарқату
|
extract-here = Тарқату
|
||||||
|
|
@ -318,3 +318,16 @@ keywords = Folder;Manager;Бума;Басқарушы;
|
||||||
show-recents = Бүйір панеліндегі «Жуырдағы құжаттар» бумасы
|
show-recents = Бүйір панеліндегі «Жуырдағы құжаттар» бумасы
|
||||||
clear-recents-history = Жуырдағылар тарихын өшіру
|
clear-recents-history = Жуырдағылар тарихын өшіру
|
||||||
copy-path = Орналасқан жолын көшіру
|
copy-path = Орналасқан жолын көшіру
|
||||||
|
mixed = Аралас
|
||||||
|
context-action = Контекст әрекеті
|
||||||
|
context-action-confirm-title = "{ $name }" орындау керек пе?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Бұл { $items } орындалады { $items ->
|
||||||
|
[one] нәрсеге
|
||||||
|
*[other] нәрсеге
|
||||||
|
}.
|
||||||
|
run = Орындау
|
||||||
|
rename-confirm = Атын өзгерту
|
||||||
|
checksum = { $kind } бақылау сомасы
|
||||||
|
calculate = Есептеу
|
||||||
|
error = Қате
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,336 @@
|
||||||
|
sort-newest-first = Pêşî ya herî nû
|
||||||
|
connect = Girê bide
|
||||||
|
deleted =
|
||||||
|
{ $items } hat jêbirin{ $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} ji { trash }
|
||||||
|
dismiss = Peyamê paşguh bike
|
||||||
|
list-view = Dîtina lîsteyê
|
||||||
|
copy_noun = Jê bigire
|
||||||
|
favorite-path-error = Şaşetiya vekirina rêgehê
|
||||||
|
progress = { $percent }%
|
||||||
|
open-file = Pelê veke
|
||||||
|
remove-from-sidebar = Ji benda kêlekê rake
|
||||||
|
related-apps = Bernameyên têkildar
|
||||||
|
network-drive-error = Nikaribû bigihêje ajokarê torê
|
||||||
|
gallery-preview = Pêşdîtina galeriyê
|
||||||
|
sort-smallest-to-largest = Biçûktir bo mezintir
|
||||||
|
zoom-in = Nêzîk bike
|
||||||
|
select-all = Hemûyan hilbijêre
|
||||||
|
removing-from-recents =
|
||||||
|
{ $items } tê rakirin { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} ji { recents }
|
||||||
|
password = Borînpeyv
|
||||||
|
cosmic-files = Pelên COSMIC
|
||||||
|
emptying-trash = { trash } tê valakirin({ $progress })...
|
||||||
|
new-window = Çarçoveya nû
|
||||||
|
remove = Rake
|
||||||
|
zoom-out = Dûr bike
|
||||||
|
compressing =
|
||||||
|
{ $items } { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} tê guvaştin ji "{ $from }" bo "{ $to }" ({ $progress })...
|
||||||
|
move-to-trash = Bilivîne bo jêbirdankê
|
||||||
|
menu-about = Derbarê Pelên COSMIC...
|
||||||
|
file-already-exists = Pelek bi heman navî jixwe heye
|
||||||
|
setting-executable-and-launching = "{ $name }" wekî pelê xebitandinê tê sazkirin û xebitandin
|
||||||
|
open-multiple-files = Gelek pelan veke
|
||||||
|
default-size = Mezinahiya berdest
|
||||||
|
name-hidden = Navên ku bi "." tê destpêkirin wê werin veşartin
|
||||||
|
extracted =
|
||||||
|
{ $items } { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} hate derxistin ji "{ $from }" bo "{ $to }"
|
||||||
|
create-new-folder = Peldanka nû biafrîne
|
||||||
|
folder-already-exists = Peldankek bi heman navî jixwe heye
|
||||||
|
create-archive = Erşîvê biafrîne
|
||||||
|
permanently-delete-warning = { $target } wê bi mayînde were jêbirin û nayê vegerandin.
|
||||||
|
favorite-path-error-description =
|
||||||
|
Nikaribû "{ $path }" veke.
|
||||||
|
"{ $path }" dibe ku tune be yan jî mafê te yê
|
||||||
|
vekirina wê tune be.
|
||||||
|
Tu dixwazî wê ji benda kêlekê rakî?
|
||||||
|
empty-trash-warning = Hêmanên di jêbirdankê de wê bi mayînde werin jêbirin
|
||||||
|
other-apps = Bernameyên din
|
||||||
|
set-permissions = Maf hatine sazkirin ji bo "{ $name }" bo { $mode }
|
||||||
|
quit = Biqedîne
|
||||||
|
sort-by-size = Li gorî mezinahiyê rêz bike
|
||||||
|
rename = Navê biguhêrîne...
|
||||||
|
empty-folder-hidden = Peldanka vala (tiştên veşartî tê de hene)
|
||||||
|
keep = Bihêle
|
||||||
|
operations-running =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] pêvajo
|
||||||
|
*[other] pêvajo
|
||||||
|
} dixebite ({ $percent }%)...
|
||||||
|
permanently-deleting =
|
||||||
|
{ $items } bi mayînde tê jêbirin { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
}
|
||||||
|
edit = Biguhêrîne
|
||||||
|
copy = Jê bigire
|
||||||
|
none = Ne yek
|
||||||
|
no-results = Tu encam nehatin dîtin
|
||||||
|
theme = Rûkar
|
||||||
|
compressed =
|
||||||
|
{ $items } { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} hate guvaştin ji "{ $from }" bo "{ $to }"
|
||||||
|
appearance = Xuyang
|
||||||
|
rename-folder = Navê peldankê biguherîne
|
||||||
|
new-file = Pela nû...
|
||||||
|
close-tab = Rûgerê bigire
|
||||||
|
open-in-terminal = Di termînalê de veke
|
||||||
|
open-multiple-folders = Gelek peldankan veke
|
||||||
|
remember-password = Borînpeyvê bi bîr bîne
|
||||||
|
username = Navê bikarhêner
|
||||||
|
light = Ronî
|
||||||
|
extract-to = Derxîne bo...
|
||||||
|
add-network-drive = Ajokarê torê tevlî bike
|
||||||
|
copying =
|
||||||
|
{ $items } { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} tê jêgirtin ji "{ $from }" bo "{ $to }" ({ $progress })...
|
||||||
|
create-new-file = Pelê nû biafrîne
|
||||||
|
sort-by-trashed = Li gorî demê rêz bike
|
||||||
|
support = Piştgirî
|
||||||
|
try-again = Dîsa hewl bide
|
||||||
|
eject = Biavêje
|
||||||
|
copied =
|
||||||
|
{ $items } { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} hate jêgirtin ji "{ $from }" bo "{ $to }"
|
||||||
|
open-in-new-window = Di çarçoveyeke nû de veke
|
||||||
|
empty-folder = Peldanka vala
|
||||||
|
empty-trash = Jêbirdankê vala bike
|
||||||
|
sort-by-modified = Li gorî guhertinê rêz bike
|
||||||
|
list-directories-first = Pêşî rêgehan lîste bike
|
||||||
|
folder-name = Navê peldankê
|
||||||
|
browse-store = { $store } bigere
|
||||||
|
remove-from-recents = Ji dawiyê rake
|
||||||
|
paste = Pê ve bike
|
||||||
|
empty-trash-title = Jêbirdankê vala bike?
|
||||||
|
menu-settings = Sazkarî...
|
||||||
|
moving =
|
||||||
|
{ $items } { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} tê livandin ji "{ $from }" bo "{ $to }" ({ $progress })...
|
||||||
|
change-wallpaper = Wêneyê dîwêr biguhêrîne...
|
||||||
|
network-drive-description =
|
||||||
|
Di nav navnîşanên rajekarê de pêşgira protokolekê û navnîşanek heye.
|
||||||
|
Mînak: ssh://192.168.0.1, ftp://[2001:db8::1]
|
||||||
|
deleting =
|
||||||
|
{ $items } tê jêbirin { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} ji { trash } ({ $progress })...
|
||||||
|
single-click = Tikandina yekane bo vekriinê
|
||||||
|
view = Dîtin
|
||||||
|
setting-permissions = Maf tên sazkirin ji bo "{ $name }" bo { $mode }
|
||||||
|
creating = "{ $name }" tê afirandin di "{ $parent }" de
|
||||||
|
execute-only = Tenê xebitandin
|
||||||
|
open-item-location = Cihê hêmanê veke
|
||||||
|
type-to-search = Bo lêgerînê binivîse
|
||||||
|
set-executable-and-launched = "{ $name }" wekî pelê xebitandinê hat sazkirin û xebitandin
|
||||||
|
mounted-drives = Ajokarên siwarkirî
|
||||||
|
sort-a-z = A-Z
|
||||||
|
mount-error = Nikare bigihêje ajokerê
|
||||||
|
extract-here = Derxîne
|
||||||
|
grid-view = Dîtina torê
|
||||||
|
add-to-sidebar = Tevlî benda kêlekê bike
|
||||||
|
dark = Tarî
|
||||||
|
notification-in-progress = Pêvajoyên pelê didome
|
||||||
|
home = Rûpela sereke
|
||||||
|
sort-z-a = Z-A
|
||||||
|
type-to-search-recursive = Di peldank û binpeldankên heyî de digere
|
||||||
|
history = Dîrok
|
||||||
|
progress-paused = { $percent }%, hate rawestandin
|
||||||
|
desktop-view-options = Vebijêrkên dîtina sermaseyê...
|
||||||
|
show-on-desktop = Li ser sermaseyê nîşan bide
|
||||||
|
cancelled = Têkbirî
|
||||||
|
new-folder = Peldanka nû...
|
||||||
|
match-desktop = Bi sermaseyê re li hev bîne
|
||||||
|
name-no-slashes = Nav nabe ku "/" tê de hebe
|
||||||
|
operations-running-finished =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] pêvajo
|
||||||
|
*[other] pêvajo
|
||||||
|
} dixebite ({ $percent }%), { $finished } qediya...
|
||||||
|
sort-by-name = Li gorî navê rêz bike
|
||||||
|
edit-history = Dîrokê biguhêrînê
|
||||||
|
show-hidden-files = Pelên veşartî nîşan bide
|
||||||
|
progress-failed = { $percent }%, têk çû
|
||||||
|
trash-folder-icon = Îkona peldanka jêbirdankê
|
||||||
|
extract-to-title = Derxîne bo peldankê
|
||||||
|
icon-size = Mezinahiya îkonê
|
||||||
|
open-with-title = Tu çawa dixwazî "{ $name }" vekî?
|
||||||
|
extracting =
|
||||||
|
{ $items } { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} tê derxistin ji "{ $from }" bo"{ $to }" ({ $progress })...
|
||||||
|
settings = Sazkarî
|
||||||
|
complete = Qediya
|
||||||
|
permanently-delete-question = Bi mayînde jê bibe?
|
||||||
|
extract-password-required = Borînpeyv pêdivî ye
|
||||||
|
pending = Li bendê ye
|
||||||
|
desktop-folder-content = Naveroka peldanka sermaseyê
|
||||||
|
no-history = Tu hêman di dîrok de tune ne.
|
||||||
|
open-folder = Peldankê veke
|
||||||
|
emptied-trash = { trash } hate valakirin
|
||||||
|
rename-file = Navê pelê biguherîne
|
||||||
|
sort-largest-to-smallest = Mezintir bo biçûktir
|
||||||
|
restore-from-trash = Ji jêbirdankê vegerîne
|
||||||
|
cut = Jê bike
|
||||||
|
moved =
|
||||||
|
{ $items } { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} hate livandin ji "{ $from }" bo "{ $to }"
|
||||||
|
progress-cancelled = { $percent }%, hat têkbirin
|
||||||
|
open-in-new-tab = Di rûgerê nû de veke
|
||||||
|
file = Pel
|
||||||
|
file-name = Navê pelê
|
||||||
|
save-file = Pelê tomar bike
|
||||||
|
created = "{ $name }" hate afirandin di "{ $parent }" de
|
||||||
|
delete-permanently = Bi mayînde jê bibe
|
||||||
|
display-settings = Sazkariyên dîmenderê...
|
||||||
|
new-tab = Rûgera nû
|
||||||
|
failed = Têk çû
|
||||||
|
name-invalid = Nav nabe ku \"{ $filename }\" be
|
||||||
|
desktop-appearance = Xuyanga sermaseyê...
|
||||||
|
comment = Rêveberê pelê ji bo sermaseya COSMIC
|
||||||
|
keywords = Rêveber;Peldank;
|
||||||
|
filesystem = Pergala pelê
|
||||||
|
networks = Tor
|
||||||
|
recents = Dawî
|
||||||
|
open = Veke
|
||||||
|
cancel = Têk bibe
|
||||||
|
trash = Jêbirdank
|
||||||
|
undo = Vegerîne
|
||||||
|
today = Îro
|
||||||
|
name = Nav
|
||||||
|
modified = Guhertin
|
||||||
|
size = Mezinahî
|
||||||
|
details = Hûrgilî
|
||||||
|
pause = Rawestîne
|
||||||
|
resume = Bidomîne
|
||||||
|
copy-to-title = Rêgeha jêgirtinê hilbijêre
|
||||||
|
copy-to-button-label = Jê bigire
|
||||||
|
move-to-title = Rêgeha livandinê hilbijêre
|
||||||
|
move-to-button-label = Bilivîne
|
||||||
|
create = Afirandin
|
||||||
|
save = Tomar bike
|
||||||
|
context-action = Çalakiya naverokê
|
||||||
|
context-action-confirm-title = "{ $name }" bixebtîne?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Ev ê li ser { $items } { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} bixebite.
|
||||||
|
run = Bixebitîne
|
||||||
|
selected-items = { $items } hêmanên hilbijartî
|
||||||
|
delete = Jê bibe
|
||||||
|
rename-confirm = Navê biguherîne
|
||||||
|
pasted-image = Wêneya pêvekirî
|
||||||
|
pasted-text = Nivîsa pêvekirî
|
||||||
|
pasted-video = Vîdiyoya pêvekirî
|
||||||
|
copy-path = Rêgehê jê bigire
|
||||||
|
show-recents = Peldanka dawî di benda kêlekê de
|
||||||
|
clear-recents-history = Dîroka dawî pak bike
|
||||||
|
compress = Biguvşîne...
|
||||||
|
copy-to = Jê bigire bo...
|
||||||
|
move-to = Bilivîne bo...
|
||||||
|
default-app = { $name } (berdest)
|
||||||
|
renamed = Nav hate guhertin ji "{ $from }" bo "{ $to }"
|
||||||
|
read-execute = Xwendin û xebitandin
|
||||||
|
item-modified = Guhertin: { $modified }
|
||||||
|
reload-folder = Peldankê ji nû ve bar bike
|
||||||
|
restoring =
|
||||||
|
{ $items } tê vegerandin { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} ji { trash } ({ $progress })...
|
||||||
|
icon-size-and-spacing = Mezinahiya îkonan û valahiya di navbera wan de
|
||||||
|
type-to-search-enter-path = Riya rêgehê yan jî pelê têdixîne
|
||||||
|
menu-open-with = Veke bi riya...
|
||||||
|
original-file = Pelê resen
|
||||||
|
read-write-execute = Xwendin, nivîsandin û xebitandin
|
||||||
|
calculating = Tê jimartin...
|
||||||
|
item-size = Mezinahî: { $size }
|
||||||
|
connecting = Tê girêdan...
|
||||||
|
read-write = Xwendin û nivîsandin
|
||||||
|
items = Hêman: { $items }
|
||||||
|
type = Cûre: { $mime }
|
||||||
|
replace-warning = Tu dixwazî bi ya ku tu niha tomar dikî biguhêrînî? Naveroka berê wê were jêbirin.
|
||||||
|
show-details = Hûrgiliyan nîşan bide
|
||||||
|
grid-spacing = Valahiya torê
|
||||||
|
sort-oldest-first = Pêşî ya herî kevn
|
||||||
|
repository = Embar
|
||||||
|
replace-warning-operation = Tu dixwazî biguherînî? Naveroka berê wê were jêbirin.
|
||||||
|
other = Ên din
|
||||||
|
read-only = Tenê xwendin
|
||||||
|
enter-server-address = Navnîşana rajekarê têxîne
|
||||||
|
connect-anonymously = Bi awayekî anonîm girê bide
|
||||||
|
group = Kom
|
||||||
|
apply-to-all = Bo hemûyan bisepîne
|
||||||
|
skip = Derbas bike
|
||||||
|
replace-with = Biguhêrîne bi
|
||||||
|
owner = Xwedî
|
||||||
|
set-and-launch = Saz bike û dest pê bike
|
||||||
|
removed-from-recents =
|
||||||
|
{ $items } hate rakirin { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} ji { recents }
|
||||||
|
item-created = Afirandin:{ $created }
|
||||||
|
network-drive-schemes =
|
||||||
|
Protokolên berdest,pêşgirê
|
||||||
|
AppleTalk,afp://
|
||||||
|
Protokola şandina pelê,ftp:// an ftps://
|
||||||
|
Pergala pelê torê,nfs://
|
||||||
|
Astengiya peyama rajekarê,smb://
|
||||||
|
SSH protokola şandina pelê,sftp:// an ssh://
|
||||||
|
WebDAV, dav:// an davs://
|
||||||
|
set-executable-and-launch = Wekî pelê xebitandinê saz bike û bixebitîne
|
||||||
|
restored =
|
||||||
|
{ $items } hate vegerandin { $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
} ji { trash }
|
||||||
|
domain = Navper
|
||||||
|
sort = Rêz bike
|
||||||
|
item-accessed = Gihîştin: { $accessed }
|
||||||
|
open-with = Veke bi riya
|
||||||
|
keep-both = Herduyan bihêle
|
||||||
|
permanently-deleted =
|
||||||
|
{ $items } bi mayînde hate jêbirin{ $items ->
|
||||||
|
[one] hêman
|
||||||
|
*[other] hêman
|
||||||
|
}
|
||||||
|
write-execute = Nivîsandin û xebitandin
|
||||||
|
renaming = Navê ji "{ $from }" biguhêrîne bo "{ $to }"
|
||||||
|
set-executable-and-launch-description = Tu dixwazî "{ $name }" wekî pelê xebitandinê saz bikî û bixebtînî?
|
||||||
|
replace = Biguhêrîne
|
||||||
|
replace-title = "{ $filename }" jixwe di vê cihê de heye
|
||||||
|
unknown-folder = Peldanka nenas
|
||||||
|
write-only = Tenê nivîsandin
|
||||||
|
trashed-on = Jêbirî
|
||||||
|
mixed = Tevlihev
|
||||||
|
type-to-search-select = Pel an peldanka yekem a lihevhatî hildibijêre
|
||||||
|
calculate = Bijmêre
|
||||||
|
error = Şaşetî
|
||||||
|
checksum = Tevahiya { $kind } kontrol bike
|
||||||
|
sidebar-label = Nîşanka benda kêlekê
|
||||||
|
search-application = Li gorî navê sepanê bigere
|
||||||
|
change-sidebar-label = Nîşanka benda kêlekê biguhêrîne
|
||||||
|
|
@ -110,7 +110,7 @@ dismiss = 메시지 무시
|
||||||
copy_noun = 복사
|
copy_noun = 복사
|
||||||
progress = { $percent }%
|
progress = { $percent }%
|
||||||
related-apps = 관련 앱
|
related-apps = 관련 앱
|
||||||
compress = 압축
|
compress = 압축...
|
||||||
network-drive-error = 네트워크 드라이브에 접근할 수 없음
|
network-drive-error = 네트워크 드라이브에 접근할 수 없음
|
||||||
icon-size-and-spacing = 아이콘 크기 및 간격
|
icon-size-and-spacing = 아이콘 크기 및 간격
|
||||||
password = 암호
|
password = 암호
|
||||||
|
|
@ -272,3 +272,18 @@ network-drive-schemes =
|
||||||
WebDAV,dav:// 또는 davs://
|
WebDAV,dav:// 또는 davs://
|
||||||
type-to-search-select = 일치하는 첫 번째 파일 또는 폴더를 선택합니다
|
type-to-search-select = 일치하는 첫 번째 파일 또는 폴더를 선택합니다
|
||||||
comment = COSMIC 데스크톱용 위한 파일 관리자
|
comment = COSMIC 데스크톱용 위한 파일 관리자
|
||||||
|
keywords = 폴더;관리자;
|
||||||
|
copy-to-button-label = 복사
|
||||||
|
move-to-button-label = 이동
|
||||||
|
clear-recents-history = 최근 기록 비우기
|
||||||
|
copy-path = 복사 경로
|
||||||
|
move-to-title = 이동 위치 선택
|
||||||
|
copy-to-title = 복사 경로 선택
|
||||||
|
run = 실행
|
||||||
|
rename-confirm = 이름 변경
|
||||||
|
mixed = 혼합됨
|
||||||
|
pasted-image = 붙여넣은 이미지
|
||||||
|
pasted-text = 붙여넣은 텍스트
|
||||||
|
pasted-video = 붙여넣은 영상
|
||||||
|
copy-to = 복사하기...
|
||||||
|
move-to = 이동하기...
|
||||||
|
|
|
||||||
0
i18n/lo/cosmic_files.ftl
Normal file
0
i18n/lo/cosmic_files.ftl
Normal file
|
|
@ -1,5 +1,5 @@
|
||||||
progress = { $percent }%
|
progress = { $percent }%
|
||||||
cosmic-files = Cosmic Files
|
cosmic-files = COSMIC Failai
|
||||||
empty-folder = Tuščias aplankas
|
empty-folder = Tuščias aplankas
|
||||||
empty-folder-hidden = Tuščias aplankas (turi paslėptų failų)
|
empty-folder-hidden = Tuščias aplankas (turi paslėptų failų)
|
||||||
no-results = Rezultatų nėra
|
no-results = Rezultatų nėra
|
||||||
|
|
@ -76,7 +76,7 @@ delete = Ištrinti
|
||||||
permanently-delete-warning = { $target } bus ištrintas visam laikui. Šis veiksmas yra negrįžtamas.
|
permanently-delete-warning = { $target } bus ištrintas visam laikui. Šis veiksmas yra negrįžtamas.
|
||||||
rename-file = Pervadinti failą
|
rename-file = Pervadinti failą
|
||||||
rename-folder = Pervadinti aplanką
|
rename-folder = Pervadinti aplanką
|
||||||
replace = Pakeisti
|
replace = Keisti
|
||||||
replace-title = „{ $filename }“ jau egzistuoja šioje vietoje
|
replace-title = „{ $filename }“ jau egzistuoja šioje vietoje
|
||||||
replace-warning = Ar norite pakeisti tai su tuo, kas yra įrašoma? Keičiant bus pakeistas turinys.
|
replace-warning = Ar norite pakeisti tai su tuo, kas yra įrašoma? Keičiant bus pakeistas turinys.
|
||||||
replace-warning-operation = Ar norite pakeisti tai? Pakeičiant bus keičiamas turinys.
|
replace-warning-operation = Ar norite pakeisti tai? Pakeičiant bus keičiamas turinys.
|
||||||
|
|
@ -244,7 +244,7 @@ item-created = Sukurtas: { $created }
|
||||||
item-modified = Modifikuota: { $modified }
|
item-modified = Modifikuota: { $modified }
|
||||||
item-accessed = Paskutinė prieiga: { $accessed }
|
item-accessed = Paskutinė prieiga: { $accessed }
|
||||||
calculating = Skaičiuojama...
|
calculating = Skaičiuojama...
|
||||||
settings = Nustatymai
|
settings = Nuostatai
|
||||||
single-click = Vieno paspaudimo atidarymas
|
single-click = Vieno paspaudimo atidarymas
|
||||||
appearance = Išvaizda
|
appearance = Išvaizda
|
||||||
match-desktop = Pagal darbalaukio temą
|
match-desktop = Pagal darbalaukio temą
|
||||||
|
|
@ -252,7 +252,7 @@ type-to-search = Norint ieškoti, pradėkite rašyti
|
||||||
type-to-search-recursive = Paieška dabartiniame aplanke ir jo poaplankiuose
|
type-to-search-recursive = Paieška dabartiniame aplanke ir jo poaplankiuose
|
||||||
type-to-search-enter-path = Įvedamas aplanko ar failo kelias
|
type-to-search-enter-path = Įvedamas aplanko ar failo kelias
|
||||||
add-to-sidebar = Pridėti į šonjuostę
|
add-to-sidebar = Pridėti į šonjuostę
|
||||||
compress = Suspausti
|
compress = Suspausti...
|
||||||
delete-permanently = Ištrinti visam laikui
|
delete-permanently = Ištrinti visam laikui
|
||||||
eject = Išstumti
|
eject = Išstumti
|
||||||
extract-here = Išskleisti
|
extract-here = Išskleisti
|
||||||
|
|
@ -276,22 +276,22 @@ new-window = Naujas langas
|
||||||
reload-folder = Perkrauti aplanką
|
reload-folder = Perkrauti aplanką
|
||||||
rename = Pervadinti...
|
rename = Pervadinti...
|
||||||
close-tab = Uždaryti skirtuką
|
close-tab = Uždaryti skirtuką
|
||||||
quit = Išeiti
|
quit = Išjungti
|
||||||
edit = Redaguoti
|
edit = Redaguoti
|
||||||
cut = Iškirpti
|
cut = Iškirpti
|
||||||
copy = Kopijuoti
|
copy = Kopijuoti
|
||||||
paste = Įklijuoti
|
paste = Įdėti
|
||||||
select-all = Pažymėti viską
|
select-all = Žymėti viską
|
||||||
zoom-in = Priartinti
|
zoom-in = Artinti
|
||||||
default-size = Numatytas dydis
|
default-size = Numatytas dydis
|
||||||
zoom-out = Nutolinti
|
zoom-out = Tolinti
|
||||||
view = Rodymas
|
view = Rodymas
|
||||||
grid-view = Tinklelio išdėstymas
|
grid-view = Tinklelio išdėstymas
|
||||||
list-view = Sąrašo išdėstymas
|
list-view = Sąrašo išdėstymas
|
||||||
show-hidden-files = Rodyti paslėptus failus
|
show-hidden-files = Rodyti paslėptus failus
|
||||||
list-directories-first = Pirmiau pateikti aplankus
|
list-directories-first = Pirmiau pateikti aplankus
|
||||||
gallery-preview = Galerijos peržiūra
|
gallery-preview = Galerijos peržiūra
|
||||||
menu-settings = Nustatymai...
|
menu-settings = Nuostatai...
|
||||||
menu-about = Apie COSMIC Files...
|
menu-about = Apie COSMIC Files...
|
||||||
sort = Rikiuoti
|
sort = Rikiuoti
|
||||||
sort-a-z = A-Ž
|
sort-a-z = A-Ž
|
||||||
|
|
@ -302,7 +302,7 @@ sort-smallest-to-largest = Nuo mažiausio iki didžiausio
|
||||||
sort-largest-to-smallest = Nuo didžiausio iki mažiausio
|
sort-largest-to-smallest = Nuo didžiausio iki mažiausio
|
||||||
dark = Tamsus
|
dark = Tamsus
|
||||||
light = Šviesus
|
light = Šviesus
|
||||||
comment = COSMIC desktop failų tvarkyklė
|
comment = COSMIC aplinkos failų tvarkyklė
|
||||||
keywords = Aplankas;Tvarkyklė;
|
keywords = Aplankas;Tvarkyklė;
|
||||||
copy-to-title = Pasirinkti kopijavimo vietą
|
copy-to-title = Pasirinkti kopijavimo vietą
|
||||||
copy-to-button-label = Kopijuoti
|
copy-to-button-label = Kopijuoti
|
||||||
|
|
@ -317,3 +317,10 @@ clear-recents-history = Išvalyti Neseniai naudotų istoriją
|
||||||
copy-to = Kopijuoti į...
|
copy-to = Kopijuoti į...
|
||||||
move-to = Perkeltiį į...
|
move-to = Perkeltiį į...
|
||||||
copy-path = Kopijuoti kelią
|
copy-path = Kopijuoti kelią
|
||||||
|
theme = Stilius
|
||||||
|
context-action = Konteksto veiksmas
|
||||||
|
context-action-confirm-title = Paleisti „{ $name }“?
|
||||||
|
run = Paleisti
|
||||||
|
rename-confirm = Pervadinti
|
||||||
|
calculate = Skaičiuoti
|
||||||
|
error = Klaida
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ permanently-delete-question = Slett permanent
|
||||||
permanently-delete-warning = Er du sukker på om du vil permanent slette { $target }? Dette kan ikke angres.
|
permanently-delete-warning = Er du sukker på om du vil permanent slette { $target }? Dette kan ikke angres.
|
||||||
rename-file = Endre filnavn
|
rename-file = Endre filnavn
|
||||||
rename-folder = Endre mappenavn
|
rename-folder = Endre mappenavn
|
||||||
replace-title = "{ $filename }" finnes allerede på denne plasseringen.
|
replace-title = «{ $filename }» finnes allerede på denne plasseringen
|
||||||
replace-warning = Vil du bytte den ut med filen du lagrer? Dette vil overskrive innholdet.
|
replace-warning = Vil du bytte den ut med filen du lagrer? Dette vil overskrive innholdet.
|
||||||
replace-warning-operation = Vil du erstatte den? Dette vil overskrive innholdet.
|
replace-warning-operation = Vil du erstatte den? Dette vil overskrive innholdet.
|
||||||
original-file = Orginalfil
|
original-file = Orginalfil
|
||||||
|
|
@ -122,8 +122,8 @@ read-write = Kun skrive- og lesbar
|
||||||
read-write-execute = Lesbar, skrivbar og kjørbar
|
read-write-execute = Lesbar, skrivbar og kjørbar
|
||||||
favorite-path-error = Feil ved åpning av mappen
|
favorite-path-error = Feil ved åpning av mappen
|
||||||
favorite-path-error-description =
|
favorite-path-error-description =
|
||||||
Kunne ikke åpne "{ $path }".
|
Klarte ikke åpne «{ $path }».
|
||||||
Den kan ikke eksistere eller så har du ikke tilgang til å åpne den.
|
«{ $path }» finnes ikke eller så har du ikke tilgang til å åpne den.
|
||||||
|
|
||||||
Vil du fjerne den fra sidepanelet?
|
Vil du fjerne den fra sidepanelet?
|
||||||
keep = Behold
|
keep = Behold
|
||||||
|
|
@ -140,7 +140,7 @@ zoom-out = Zoom ut
|
||||||
cut = Klipp ut
|
cut = Klipp ut
|
||||||
set-and-launch = Still inn og start
|
set-and-launch = Still inn og start
|
||||||
domain = Domene
|
domain = Domene
|
||||||
enter-server-address = Angi serveradresse
|
enter-server-address = Angi tjeneradresse
|
||||||
network-drive-description =
|
network-drive-description =
|
||||||
Serveradresser inkluderer ett protokollprefiks og en addresse.
|
Serveradresser inkluderer ett protokollprefiks og en addresse.
|
||||||
Examples: ssh://192.168.0.1, ftp://[2001:db8::1]
|
Examples: ssh://192.168.0.1, ftp://[2001:db8::1]
|
||||||
|
|
|
||||||
|
|
@ -425,7 +425,7 @@ removed-from-recents =
|
||||||
} uit { recents } verwijderd
|
} uit { recents } verwijderd
|
||||||
remove-from-recents = Uit recente verwijderen
|
remove-from-recents = Uit recente verwijderen
|
||||||
type-to-search-select = Dit selecteert het eerst overeenkomende bestand of map
|
type-to-search-select = Dit selecteert het eerst overeenkomende bestand of map
|
||||||
comment = Bestandsbeheerder voor COSMIC desktop
|
comment = Bestandsbeheerder voor COSMIC
|
||||||
copy-to-title = Kopieerbestemming aanwijzen
|
copy-to-title = Kopieerbestemming aanwijzen
|
||||||
copy-to-button-label = Kopiëren
|
copy-to-button-label = Kopiëren
|
||||||
move-to-button-label = Verplaatsen
|
move-to-button-label = Verplaatsen
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
appearance = Aparéncia
|
||||||
|
theme = Tèma
|
||||||
|
match-desktop = Assortir al burèu
|
||||||
|
dark = Escur
|
||||||
|
light = Clar
|
||||||
|
file = Fichièr
|
||||||
|
details = Detalhs
|
||||||
|
name = Nom
|
||||||
|
cancel = Anullar
|
||||||
|
repository = Depaus
|
||||||
|
settings = Configuracion
|
||||||
|
menu-settings = Configuracion...
|
||||||
|
quit = Quitar
|
||||||
|
cosmic-files = Fichièrs de COSMIC
|
||||||
|
comment = Gestionari de fichièrs pel burèu COSMIC
|
||||||
|
save-file = Enregistrar lo fichièr
|
||||||
|
open-with-title = Cossí volètz dobrir « { $name } » ?
|
||||||
|
other-apps = Autras aplicacions
|
||||||
|
context-action-confirm-title = Executar « { $name } » ?
|
||||||
|
run = Executar
|
||||||
|
delete = Suprimir
|
||||||
|
replace = Remplaçar
|
||||||
|
owner = Propietari
|
||||||
|
group = Grop
|
||||||
|
write-execute = Escritura e execucion
|
||||||
|
read-only = Lectura sola
|
||||||
|
read-execute = Lectura e execucion
|
||||||
|
read-write = Lectura e execucion
|
||||||
|
read-write-execute = Lectura, escritura e execucion
|
||||||
|
remove = Suprimir
|
||||||
|
connecting = Connexion...
|
||||||
|
domain = Domeni
|
||||||
|
password = Senhal
|
||||||
|
|
@ -32,7 +32,7 @@ pause = ਵਿਰਾਮ
|
||||||
resume = ਮੁੜ-ਚਾਲੂ
|
resume = ਮੁੜ-ਚਾਲੂ
|
||||||
create-archive = ਅਕਾਇਵ ਬਣਾਓ
|
create-archive = ਅਕਾਇਵ ਬਣਾਓ
|
||||||
extract-password-required = ਪਾਸਵਰਡ ਚਾਹੀਦਾ ਹੈ
|
extract-password-required = ਪਾਸਵਰਡ ਚਾਹੀਦਾ ਹੈ
|
||||||
extract-to = Extract To...
|
extract-to = ਖਿਲਾਰੋ...
|
||||||
extract-to-title = ਫੋਲਡਰ ਵਿੱਚ ਖਿਲਾਰੋ
|
extract-to-title = ਫੋਲਡਰ ਵਿੱਚ ਖਿਲਾਰੋ
|
||||||
empty-trash = ਰੱਦੀ ਨੂੰ ਖਾਲੀ ਕਰੋ
|
empty-trash = ਰੱਦੀ ਨੂੰ ਖਾਲੀ ਕਰੋ
|
||||||
empty-trash-title = ਰੱਦੀ ਨੂੰ ਖਾਲੀ ਕਰਨਾ ਹੈ?
|
empty-trash-title = ਰੱਦੀ ਨੂੰ ਖਾਲੀ ਕਰਨਾ ਹੈ?
|
||||||
|
|
@ -113,7 +113,7 @@ replace-title = "{ $filename }" ਪਹਿਲਾਂ ਹੀ ਇਸ ਟਿਕਾਣ
|
||||||
favorite-path-error = ਡਾਇਰੈਕਟਰੀ ਖੋਲ੍ਹਣ ਦੌਰਾਨ ਗਲਤੀ
|
favorite-path-error = ਡਾਇਰੈਕਟਰੀ ਖੋਲ੍ਹਣ ਦੌਰਾਨ ਗਲਤੀ
|
||||||
add-network-drive = ਨੈੱਟਵਰਕ ਡਰਾਇਵ ਜੋੜੋ
|
add-network-drive = ਨੈੱਟਵਰਕ ਡਰਾਇਵ ਜੋੜੋ
|
||||||
connect-anonymously = ਅਣਪਛਾਤੇ ਵਜੋਂ ਕਨੈਕਟ ਕਰੋ
|
connect-anonymously = ਅਣਪਛਾਤੇ ਵਜੋਂ ਕਨੈਕਟ ਕਰੋ
|
||||||
connecting = Connecting...
|
connecting = ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...
|
||||||
domain = ਡੋਮੇਨ
|
domain = ਡੋਮੇਨ
|
||||||
enter-server-address = ਸਰਵਰ ਦਾ ਸਿਰਨਾਵਾਂ ਦਿਓ
|
enter-server-address = ਸਰਵਰ ਦਾ ਸਿਰਨਾਵਾਂ ਦਿਓ
|
||||||
password = ਪਾਸਵਰਡ
|
password = ਪਾਸਵਰਡ
|
||||||
|
|
@ -138,15 +138,15 @@ sort-by-modified = ਸੋਧ ਨਾਲ ਲੜੀਬੱਧ
|
||||||
sort-by-size = ਆਕਾਰ ਨਾਲ ਲੜੀਬੱਧ
|
sort-by-size = ਆਕਾਰ ਨਾਲ ਲੜੀਬੱਧ
|
||||||
sort-by-trashed = ਹਟਾਉਣ ਸਮੇਂ ਨਾਲ ਲੜੀਬੱਧ
|
sort-by-trashed = ਹਟਾਉਣ ਸਮੇਂ ਨਾਲ ਲੜੀਬੱਧ
|
||||||
remove-from-recents = ਸੱਜਰਿਆਂ ਵਿੱਚੋਂ ਹਟਾਓ
|
remove-from-recents = ਸੱਜਰਿਆਂ ਵਿੱਚੋਂ ਹਟਾਓ
|
||||||
change-wallpaper = Change wallpaper...
|
change-wallpaper = ਵਾਲਪੇਪਰ ਨੂੰ ਬਦਲੋ...
|
||||||
desktop-appearance = Desktop appearance...
|
desktop-appearance = ਡੈਸਕਟਾਪ ਦੀ ਦਿੱਖ...
|
||||||
display-settings = Display settings...
|
display-settings = ਡਿਸਪਲੇਅ ਸੈਟਿੰਗਾਂ...
|
||||||
file = ਫ਼ਾਇਲ
|
file = ਫ਼ਾਇਲ
|
||||||
new-tab = ਨਵੀਂ ਟੈਬ
|
new-tab = ਨਵੀਂ ਟੈਬ
|
||||||
new-window = ਨਵੀਂ ਵਿੰਡੋ
|
new-window = ਨਵੀਂ ਵਿੰਡੋ
|
||||||
rename = ਨਾਂ ਨੂੰ ਬਦਲੋ...
|
rename = ਨਾਂ ਨੂੰ ਬਦਲੋ...
|
||||||
close-tab = ਟੈਬ ਨੂੰ ਬੰਦ ਕਰੋ
|
close-tab = ਟੈਬ ਨੂੰ ਬੰਦ ਕਰੋ
|
||||||
quit = ਬਾਹਰ
|
quit = ਸਮਾਪਤ
|
||||||
edit = ਸੋਧੋ
|
edit = ਸੋਧੋ
|
||||||
cut = ਕੱਟੋ
|
cut = ਕੱਟੋ
|
||||||
copy = ਕਾਪੀ ਕਰੋ
|
copy = ਕਾਪੀ ਕਰੋ
|
||||||
|
|
@ -188,18 +188,18 @@ item-accessed = ਪਹੁੰਚ: { $accessed }
|
||||||
calculating = ਗਿਣਤੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ...
|
calculating = ਗਿਣਤੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ...
|
||||||
single-click = ਇੱਕ ਵਾਰ ਕਲਿੱਕ ਕਰਕੇ ਖੋਲ੍ਹੋ
|
single-click = ਇੱਕ ਵਾਰ ਕਲਿੱਕ ਕਰਕੇ ਖੋਲ੍ਹੋ
|
||||||
show-recents = ਬਾਹੀ ਵਿੱਚ ਸੱਜਰੇ ਫੋਲਡਰ
|
show-recents = ਬਾਹੀ ਵਿੱਚ ਸੱਜਰੇ ਫੋਲਡਰ
|
||||||
match-desktop = ਡੈਸਕਟਾਪ ਨਾਲ ਮਿਲਦਾ
|
match-desktop = ਡੈਸਕਟਾਪ ਨਾਲ ਮਿਲਾਓ
|
||||||
dark = ਗੂੜ੍ਹਾ
|
dark = ਗੂੜ੍ਹਾ
|
||||||
light = ਹਲਕਾ
|
light = ਫਿੱਕਾ
|
||||||
type-to-search = ਖੋਜਣ ਲਈ ਲਿਖੋ
|
type-to-search = ਖੋਜਣ ਲਈ ਲਿਖੋ
|
||||||
type-to-search-recursive = ਮੌਜੂਦਾ ਫੋਲਡਰ ਅਤੇ ਸਭ ਅਧੀਨ-ਫੋਲਡਰਾਂ ਵਿੱਚ ਖੋਜੋ
|
type-to-search-recursive = ਮੌਜੂਦਾ ਫੋਲਡਰ ਅਤੇ ਸਭ ਅਧੀਨ-ਫੋਲਡਰਾਂ ਵਿੱਚ ਖੋਜੋ
|
||||||
add-to-sidebar = ਬਾਹੀ ਵਿੱਚ ਜੋੜੋ
|
add-to-sidebar = ਬਾਹੀ ਵਿੱਚ ਜੋੜੋ
|
||||||
compress = ਕੰਪਰੈਸ
|
compress = ਕੰਪਰੈਸ
|
||||||
copy-to = Copy to...
|
copy-to = ਇੱਥੇ ਕਾਪੀ ਕਰੋ...
|
||||||
delete-permanently = ਪੱਕੇ ਤੌਰ ਉੱਤੇ ਹਟਾਓ
|
delete-permanently = ਪੱਕੇ ਤੌਰ ਉੱਤੇ ਹਟਾਓ
|
||||||
eject = ਬਾਹਰ
|
eject = ਬਾਹਰ
|
||||||
extract-here = ਖਿਲਾਰੋ
|
extract-here = ਖਿਲਾਰੋ
|
||||||
move-to = Move to...
|
move-to = ਇੱਥੇ ਭੇਜੋ...
|
||||||
remove-from-sidebar = ਬਾਹੀ ਵਿੱਚੋਂ ਹਟਾਓ
|
remove-from-sidebar = ਬਾਹੀ ਵਿੱਚੋਂ ਹਟਾਓ
|
||||||
reload-folder = ਫੋਲਡਰ ਨੂੰ ਮੁੜ-ਲੋਡ ਕਰੋ
|
reload-folder = ਫੋਲਡਰ ਨੂੰ ਮੁੜ-ਲੋਡ ਕਰੋ
|
||||||
list-view = ਸੂਚੀ ਝਲਕ
|
list-view = ਸੂਚੀ ਝਲਕ
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
cosmic-files = Pliki COSMIC
|
cosmic-files = Pliki COSMIC
|
||||||
comment = Menedżer plików pulpitu COSMIC
|
comment = Menedżer plików dla środowiska COSMIC
|
||||||
keywords = Katalogi;Pliki;Menedżer;
|
keywords = Katalogi;Pliki;Menedżer;
|
||||||
empty-folder = Pusty katalog
|
empty-folder = Pusty katalog
|
||||||
empty-folder-hidden = Pusty katalog (z ukrytymi plikami)
|
empty-folder-hidden = Pusty katalog (z ukrytymi plikami)
|
||||||
|
|
@ -27,7 +27,7 @@ modified = Zmodyfikowano
|
||||||
trashed-on = Wyrzucono do kosza
|
trashed-on = Wyrzucono do kosza
|
||||||
size = Rozmiar
|
size = Rozmiar
|
||||||
# Progress footer
|
# Progress footer
|
||||||
details = Detale
|
details = Szczegóły
|
||||||
dismiss = Odrzuć wiadomość
|
dismiss = Odrzuć wiadomość
|
||||||
operations-running =
|
operations-running =
|
||||||
{ $running } bieżące { $running ->
|
{ $running } bieżące { $running ->
|
||||||
|
|
@ -390,7 +390,7 @@ new-window = Nowe okno
|
||||||
reload-folder = Odśwież katalog
|
reload-folder = Odśwież katalog
|
||||||
rename = Zmień nazwę…
|
rename = Zmień nazwę…
|
||||||
close-tab = Zamknij kartę
|
close-tab = Zamknij kartę
|
||||||
quit = Zamknij
|
quit = Zakończ
|
||||||
|
|
||||||
## Edit
|
## Edit
|
||||||
|
|
||||||
|
|
@ -437,3 +437,19 @@ move-to = Przenieś do…
|
||||||
show-recents = Ostatnie katalogi w panelu bocznym
|
show-recents = Ostatnie katalogi w panelu bocznym
|
||||||
clear-recents-history = Wyczyść bierzącą historię
|
clear-recents-history = Wyczyść bierzącą historię
|
||||||
copy-path = Skopiuj ścieżkę
|
copy-path = Skopiuj ścieżkę
|
||||||
|
mixed = Mieszane
|
||||||
|
context-action = Akcja sytuacyjna
|
||||||
|
context-action-confirm-title = Uruchomić "{ $name }"?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Zostanie uruchomionych { $items } { $items ->
|
||||||
|
[one] element
|
||||||
|
*[other] elementów
|
||||||
|
}.
|
||||||
|
run = Uruchom
|
||||||
|
rename-confirm = Zmień nazwę
|
||||||
|
checksum = { $kind } suma kontrolna
|
||||||
|
calculate = Oblicz
|
||||||
|
error = Błąd
|
||||||
|
sidebar-label = Etykieta panelu bocznego
|
||||||
|
search-application = Wyszukaj po nazwie aplikacji
|
||||||
|
change-sidebar-label = Zmień etykietę panelu bocznego
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,10 @@ permanently-delete-warning = Deseja realmente excluir permanentemente { $target
|
||||||
rename-file = Renomear arquivo
|
rename-file = Renomear arquivo
|
||||||
rename-folder = Renomear pasta
|
rename-folder = Renomear pasta
|
||||||
|
|
||||||
|
## Change Sidebar Label Dialog
|
||||||
|
|
||||||
|
sidebar-label = Legenda da barra lateral
|
||||||
|
|
||||||
## Replace Dialog
|
## Replace Dialog
|
||||||
|
|
||||||
replace = Substituir
|
replace = Substituir
|
||||||
|
|
@ -350,7 +354,7 @@ light = Estilo claro
|
||||||
|
|
||||||
### Type to Search
|
### Type to Search
|
||||||
|
|
||||||
type-to-search = Digite para pesquisar
|
type-to-search = Digitar para pesquisar
|
||||||
type-to-search-recursive = Pesquisa na pasta atual e em todas as subpastas
|
type-to-search-recursive = Pesquisa na pasta atual e em todas as subpastas
|
||||||
type-to-search-enter-path = Insere o caminho do diretório ou arquivo
|
type-to-search-enter-path = Insere o caminho do diretório ou arquivo
|
||||||
# Context menu
|
# Context menu
|
||||||
|
|
@ -365,6 +369,7 @@ open-in-terminal = Abrir no terminal
|
||||||
move-to-trash = Mover para a lixeira
|
move-to-trash = Mover para a lixeira
|
||||||
restore-from-trash = Restaurar da lixeira
|
restore-from-trash = Restaurar da lixeira
|
||||||
remove-from-sidebar = Remover da barra lateral
|
remove-from-sidebar = Remover da barra lateral
|
||||||
|
change-sidebar-label = Alterar legenda da barra lateral
|
||||||
sort-by-name = Ordenar por nome
|
sort-by-name = Ordenar por nome
|
||||||
sort-by-modified = Ordenar por data de modificação
|
sort-by-modified = Ordenar por data de modificação
|
||||||
sort-by-size = Ordenar por tamanho
|
sort-by-size = Ordenar por tamanho
|
||||||
|
|
@ -388,7 +393,7 @@ new-window = Nova janela
|
||||||
reload-folder = Recarregar pasta
|
reload-folder = Recarregar pasta
|
||||||
rename = Renomear...
|
rename = Renomear...
|
||||||
close-tab = Fechar aba
|
close-tab = Fechar aba
|
||||||
quit = Sair
|
quit = Encerrar
|
||||||
|
|
||||||
## Edit
|
## Edit
|
||||||
|
|
||||||
|
|
@ -424,8 +429,8 @@ sort-largest-to-smallest = Do maior para o menor
|
||||||
empty-trash-title = Esvaziar a lixeira?
|
empty-trash-title = Esvaziar a lixeira?
|
||||||
type-to-search-select = Seleciona o primeiro arquivo ou pasta correspondente
|
type-to-search-select = Seleciona o primeiro arquivo ou pasta correspondente
|
||||||
pasted-image = Imagem colada
|
pasted-image = Imagem colada
|
||||||
pasted-text = Texto copiado
|
pasted-text = Texto colado
|
||||||
pasted-video = Vídeo copiado
|
pasted-video = Vídeo colado
|
||||||
copy-to-title = Selecione o destino da cópia
|
copy-to-title = Selecione o destino da cópia
|
||||||
copy-to-button-label = Copiar
|
copy-to-button-label = Copiar
|
||||||
move-to-title = Selecione o destino da movimentação
|
move-to-title = Selecione o destino da movimentação
|
||||||
|
|
@ -433,6 +438,20 @@ move-to-button-label = Mover
|
||||||
copy-to = Copiar para...
|
copy-to = Copiar para...
|
||||||
move-to = Mover para...
|
move-to = Mover para...
|
||||||
keywords = Pasta;Gerenciador;
|
keywords = Pasta;Gerenciador;
|
||||||
show-recents = Pasta Recentes na barra lateral
|
show-recents = Pasta de recentes na barra lateral
|
||||||
clear-recents-history = Limpar histórico de recentes
|
clear-recents-history = Limpar histórico de recentes
|
||||||
copy-path = Copiar caminho
|
copy-path = Copiar caminho
|
||||||
|
mixed = Misto
|
||||||
|
context-action = Ação de contexto
|
||||||
|
context-action-confirm-title = Executar "{ $name }"?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Isso será executado em { $items } { $items ->
|
||||||
|
[one] item
|
||||||
|
*[other] itens
|
||||||
|
}.
|
||||||
|
run = Executar
|
||||||
|
rename-confirm = Renomear
|
||||||
|
checksum = Soma de verificação { $kind }
|
||||||
|
calculate = Calcular
|
||||||
|
error = Erro
|
||||||
|
search-application = Pesquisar por nome de aplicativo
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
cosmic-files = Ficheiros COSMIC
|
cosmic-files = Ficheiros COSMIC
|
||||||
comment = Gerenciador de arquivos do COSMIC
|
comment = Gerenciador de arquivos do COSMIC
|
||||||
keywords = Pastas;Gerenciador;Arquivos;Gestor;Explorer;
|
keywords = Pasta;Gestor;
|
||||||
empty-folder = Pasta vazia
|
empty-folder = Pasta vazia
|
||||||
empty-folder-hidden = Pasta vazia (tem ficheiros ocultos)
|
empty-folder-hidden = Pasta vazia (tem ficheiros ocultos)
|
||||||
no-results = Nenhum resultado encontrado
|
no-results = Nenhum resultado encontrado
|
||||||
filesystem = Sistema de ficheiros
|
filesystem = Sistema de ficheiros
|
||||||
home = Pasta Pessoal
|
home = Pasta Pessoal
|
||||||
notification-in-progress = Operações em curso.
|
notification-in-progress = Operações em curso
|
||||||
trash = Lixo
|
trash = Lixo
|
||||||
undo = Anular
|
undo = Anular
|
||||||
# List view
|
# List view
|
||||||
|
|
@ -204,7 +204,7 @@ desktop-view-options = Opções de visualização da área de trabalho...
|
||||||
show-on-desktop = Mostrar na área de trabalho
|
show-on-desktop = Mostrar na área de trabalho
|
||||||
desktop-folder-content = Conteúdo da pasta da área de trabalho
|
desktop-folder-content = Conteúdo da pasta da área de trabalho
|
||||||
mounted-drives = Dispositivos montados
|
mounted-drives = Dispositivos montados
|
||||||
trash-folder-icon = Ícone do lixo
|
trash-folder-icon = Ícone da pasta lixo
|
||||||
icon-size-and-spacing = Tamanho e espaçamento do ícone
|
icon-size-and-spacing = Tamanho e espaçamento do ícone
|
||||||
icon-size = Tamanho do ícone
|
icon-size = Tamanho do ícone
|
||||||
grid-spacing = Espaçamento entre ícones
|
grid-spacing = Espaçamento entre ícones
|
||||||
|
|
@ -352,3 +352,6 @@ sort-newest-first = Mais recentes primeiro
|
||||||
sort-oldest-first = Mais antigos primeiro
|
sort-oldest-first = Mais antigos primeiro
|
||||||
sort-smallest-to-largest = Do menor para o maior
|
sort-smallest-to-largest = Do menor para o maior
|
||||||
sort-largest-to-smallest = Do maior para o menor
|
sort-largest-to-smallest = Do maior para o menor
|
||||||
|
context-action-confirm-title = Executar "{ $name }"?
|
||||||
|
run = Executar
|
||||||
|
rename-confirm = Renomear
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,7 @@ unknown-folder = неизвестная папка
|
||||||
|
|
||||||
menu-open-with = Открыть с помощью…
|
menu-open-with = Открыть с помощью…
|
||||||
default-app = { $name } (по умолчанию)
|
default-app = { $name } (по умолчанию)
|
||||||
|
search-application = Поиск по названию приложения
|
||||||
|
|
||||||
## Show details
|
## Show details
|
||||||
|
|
||||||
|
|
@ -188,14 +189,14 @@ settings = Параметры
|
||||||
|
|
||||||
### Appearance
|
### Appearance
|
||||||
|
|
||||||
appearance = Оформление
|
appearance = Внешний вид
|
||||||
theme = Тема
|
theme = Тема
|
||||||
match-desktop = Как в системе
|
match-desktop = Как в системе
|
||||||
dark = Тёмная
|
dark = Тёмная
|
||||||
light = Светлая
|
light = Светлая
|
||||||
# Context menu
|
# Context menu
|
||||||
add-to-sidebar = Добавить на боковую панель
|
add-to-sidebar = Добавить на боковую панель
|
||||||
compress = Сжать
|
compress = Сжать...
|
||||||
extract-here = Распаковать
|
extract-here = Распаковать
|
||||||
new-file = Новый файл…
|
new-file = Новый файл…
|
||||||
new-folder = Новая папка…
|
new-folder = Новая папка…
|
||||||
|
|
@ -218,7 +219,7 @@ new-tab = Новая вкладка
|
||||||
new-window = Новое окно
|
new-window = Новое окно
|
||||||
rename = Переименовать…
|
rename = Переименовать…
|
||||||
close-tab = Закрыть вкладку
|
close-tab = Закрыть вкладку
|
||||||
quit = Выйти
|
quit = Завершить
|
||||||
|
|
||||||
## Edit
|
## Edit
|
||||||
|
|
||||||
|
|
@ -271,8 +272,8 @@ extract-to = Распаковать в…
|
||||||
extract-to-title = Распаковать в папку
|
extract-to-title = Распаковать в папку
|
||||||
mount-error = Не удалось получить доступ к диску
|
mount-error = Не удалось получить доступ к диску
|
||||||
create = Создать
|
create = Создать
|
||||||
open-with-title = Как вы хотите открыть «{ $name }»?
|
open-with-title = Как хотите открыть «{ $name }»?
|
||||||
browse-store = Искать в { $store }
|
browse-store = Перейти в { $store }
|
||||||
other-apps = Другие приложения
|
other-apps = Другие приложения
|
||||||
related-apps = Связанные приложения
|
related-apps = Связанные приложения
|
||||||
selected-items = { $items } выделенных элем.
|
selected-items = { $items } выделенных элем.
|
||||||
|
|
@ -320,7 +321,7 @@ type-to-search-enter-path = Ввод пути к каталогу или фай
|
||||||
delete-permanently = Удалить навсегда
|
delete-permanently = Удалить навсегда
|
||||||
eject = Извлечь
|
eject = Извлечь
|
||||||
remove-from-recents = Убрать из недавних
|
remove-from-recents = Убрать из недавних
|
||||||
change-wallpaper = Изменить обои…
|
change-wallpaper = Изменить фон...
|
||||||
desktop-appearance = Параметры оформления…
|
desktop-appearance = Параметры оформления…
|
||||||
display-settings = Параметры экрана…
|
display-settings = Параметры экрана…
|
||||||
reload-folder = Обновить папку
|
reload-folder = Обновить папку
|
||||||
|
|
@ -380,3 +381,18 @@ keywords = Папка;Менеджер;
|
||||||
show-recents = «Недавние документы» в бок. панели
|
show-recents = «Недавние документы» в бок. панели
|
||||||
clear-recents-history = Очистить историю недавних
|
clear-recents-history = Очистить историю недавних
|
||||||
copy-path = Копировать путь
|
copy-path = Копировать путь
|
||||||
|
mixed = Смешанные
|
||||||
|
context-action = Контекстная команда
|
||||||
|
context-action-confirm-title = Запустить «{ $name }»?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Команда затронет { $items } { $items ->
|
||||||
|
[one] элемент
|
||||||
|
*[other] элем.
|
||||||
|
}.
|
||||||
|
run = Запустить
|
||||||
|
rename-confirm = Переименовать
|
||||||
|
checksum = Хеш-сумма { $kind }
|
||||||
|
calculate = Вычислить
|
||||||
|
error = Ошибка
|
||||||
|
sidebar-label = Подпись в боковой панели
|
||||||
|
change-sidebar-label = Изменить подпись
|
||||||
|
|
|
||||||
3
i18n/sat/cosmic_files.ftl
Normal file
3
i18n/sat/cosmic_files.ftl
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
cancel = ᱵᱟᱹᱛᱤᱞ
|
||||||
|
name = ᱧᱩᱛᱩᱢ
|
||||||
|
save = ᱥᱟᱧᱪᱟᱣ
|
||||||
|
|
@ -0,0 +1,330 @@
|
||||||
|
open-file = Отвори датотеку
|
||||||
|
quit = Изађи
|
||||||
|
cancel = Откажи
|
||||||
|
open = Отвори
|
||||||
|
run = Покрени
|
||||||
|
connect = Повежи
|
||||||
|
save = Сачувај
|
||||||
|
password = Лозинка
|
||||||
|
remove = Уклони
|
||||||
|
appearance = Изглед
|
||||||
|
username = Корисничко име
|
||||||
|
light = Светла
|
||||||
|
dark = Тамна
|
||||||
|
settings = Подешавања
|
||||||
|
replace = Замени
|
||||||
|
size = Величина
|
||||||
|
sort-newest-first = Најновије прво
|
||||||
|
default-app = { $name } (подразумевано)
|
||||||
|
renamed = Преименована „{ $from }“ у „{ $to }“
|
||||||
|
read-execute = Читање и извршавање
|
||||||
|
deleted =
|
||||||
|
Обрисано је { $items } { $items ->
|
||||||
|
[one] ставка
|
||||||
|
*[other] ставки
|
||||||
|
} из { trash }
|
||||||
|
item-modified = Измењено: { $modified }
|
||||||
|
dismiss = Одбаци поруку
|
||||||
|
list-view = Преглед у виду списака
|
||||||
|
reload-folder = Поново учитај фасциклу
|
||||||
|
copy_noun = Копија
|
||||||
|
favorite-path-error = Грешка при отварању директоријума
|
||||||
|
progress = { $percent }%
|
||||||
|
remove-from-sidebar = Уклони из бочне траке
|
||||||
|
related-apps = Повезани програми
|
||||||
|
restoring =
|
||||||
|
Враћање { $items } { $items ->
|
||||||
|
[one] ставке
|
||||||
|
*[other] ставки
|
||||||
|
} из { trash } ({ $progress })...
|
||||||
|
network-drive-error = Немогуће приступити мрежном уређају
|
||||||
|
gallery-preview = Преглед галерије
|
||||||
|
sort-smallest-to-largest = Од најмање до највеће
|
||||||
|
zoom-in = Увећајте приказ
|
||||||
|
select-all = Означи све
|
||||||
|
icon-size-and-spacing = Величина и размак иконица
|
||||||
|
removing-from-recents =
|
||||||
|
Уклањање { $items } { $items ->
|
||||||
|
[one] ставке
|
||||||
|
*[other] ставки
|
||||||
|
} из { recents }
|
||||||
|
cosmic-files = Космик датотеке
|
||||||
|
type-to-search-enter-path = Уноси путању до директоријума или датотеке
|
||||||
|
trash = Смеће
|
||||||
|
emptying-trash = Пражњење { trash } ({ $progress })...
|
||||||
|
trashed-on = Премештено у смеће
|
||||||
|
new-window = Нови прозор
|
||||||
|
zoom-out = Умањите приказ
|
||||||
|
compressing =
|
||||||
|
Сажимање { $items } { $items ->
|
||||||
|
[one] ставке
|
||||||
|
*[other] ставки
|
||||||
|
} из „{ $from }“ у „{ $to }“ ({ $progress })...
|
||||||
|
move-to-trash = Премести у смеће
|
||||||
|
menu-about = О програму Космик Датотеке...
|
||||||
|
setting-executable-and-launching = Подешавање „{ $name }“ као извршне датотеке и покретање
|
||||||
|
open-multiple-files = Отвори више датотека
|
||||||
|
default-size = Подразумевана величина
|
||||||
|
menu-open-with = Отвори програмом...
|
||||||
|
extracted =
|
||||||
|
Извлачено { $items } { $items ->
|
||||||
|
[one] ставка
|
||||||
|
*[other] ставки
|
||||||
|
} из „{ $from }“ у „{ $to }“
|
||||||
|
create-new-folder = Направи нову фасциклу
|
||||||
|
original-file = Изворна датотека
|
||||||
|
create = Направи
|
||||||
|
create-archive = Направи архиву
|
||||||
|
read-write-execute = Читање, уписивање и извршавање
|
||||||
|
other-apps = Остали програми
|
||||||
|
set-permissions = Овлашћења за „{ $name }“ су постављена на { $mode }
|
||||||
|
pause = Паузирај
|
||||||
|
calculating = Израчунавам…
|
||||||
|
sort-by-size = Поређај по величини
|
||||||
|
rename = Преименуј...
|
||||||
|
empty-folder-hidden = Празна фасцикла (има сакривених ставки)
|
||||||
|
keep = Задржи
|
||||||
|
item-size = Величина: { $size }
|
||||||
|
permanently-deleting =
|
||||||
|
Трајно брисање { $items } { $items ->
|
||||||
|
[one] ставке
|
||||||
|
*[other] ставки
|
||||||
|
}
|
||||||
|
edit = Уреди
|
||||||
|
connecting = Повезујем се...
|
||||||
|
read-write = Читање и уписивање
|
||||||
|
copy = Умножи
|
||||||
|
none = Ништа
|
||||||
|
items = Ставки: { $items }
|
||||||
|
no-results = Нису пронађени резултати
|
||||||
|
theme = Тема
|
||||||
|
type = Врста: { $mime }
|
||||||
|
compressed =
|
||||||
|
Сажето { $items } { $items ->
|
||||||
|
[one] ставка
|
||||||
|
*[other] ставки
|
||||||
|
} из „{ $from }“ у „{ $to }“
|
||||||
|
replace-warning = Да ли желите да га замените оним који снимате? Замена ће преписати његов садржај.
|
||||||
|
rename-folder = Преименуј фасциклу
|
||||||
|
new-file = Нова датотека...
|
||||||
|
close-tab = Затвори језичак
|
||||||
|
name = Назив
|
||||||
|
open-in-terminal = Отвори у терминалу
|
||||||
|
resume = Настави
|
||||||
|
open-multiple-folders = Отвори више фасцикла
|
||||||
|
remember-password = Запамти лозинку
|
||||||
|
show-details = Прикажи детаље
|
||||||
|
grid-spacing = Размак мреже
|
||||||
|
extract-to = Распакуј у...
|
||||||
|
add-network-drive = Додај мрежни уређај
|
||||||
|
copying =
|
||||||
|
Умножавање { $items } { $items ->
|
||||||
|
[one] ставке
|
||||||
|
*[other] ставки
|
||||||
|
} из „{ $from }“ у „{ $to }“ ({ $progress })...
|
||||||
|
delete = Обриши
|
||||||
|
sort-oldest-first = Најстарије прво
|
||||||
|
repository = Ризница
|
||||||
|
create-new-file = Направи нову датотеку
|
||||||
|
sort-by-trashed = Поређај по времену брисања
|
||||||
|
replace-warning-operation = Да ли желите да га замените? Замена ће преписати његов садржај.
|
||||||
|
support = Подршка
|
||||||
|
try-again = Покушај поново
|
||||||
|
eject = Избаци
|
||||||
|
copied =
|
||||||
|
Умножено { $items } { $items ->
|
||||||
|
[one] ставке
|
||||||
|
*[other] ставки
|
||||||
|
} из „{ $from }“ у „{ $to }“
|
||||||
|
other = Друго
|
||||||
|
open-in-new-window = Отвори у новом прозору
|
||||||
|
empty-folder = Празна фасцикла
|
||||||
|
sort-by-modified = Поређај по датуму измене
|
||||||
|
list-directories-first = Прикажи директоријуме прво
|
||||||
|
read-only = Само за читање
|
||||||
|
folder-name = Назив фасцикле
|
||||||
|
browse-store = Разгледај { $store }
|
||||||
|
enter-server-address = Унесите адресу сервера
|
||||||
|
remove-from-recents = Уклони из недавних
|
||||||
|
connect-anonymously = Повежи се анонимно
|
||||||
|
group = Група
|
||||||
|
apply-to-all = Примени на све
|
||||||
|
skip = Прескочи
|
||||||
|
paste = Залепи
|
||||||
|
menu-settings = Подешавања...
|
||||||
|
moving =
|
||||||
|
Премештање { $items } { $items ->
|
||||||
|
[one] датотеке
|
||||||
|
*[other] датотека
|
||||||
|
} из „{ $from }“ у „{ $to }“ ({ $progress })...
|
||||||
|
replace-with = Замени са
|
||||||
|
recents = Недавно
|
||||||
|
change-wallpaper = Промени позадину...
|
||||||
|
network-drive-description =
|
||||||
|
Адресе сервера укључују префикс протокола и адресу.
|
||||||
|
Примери: ssh://192.168.0.1, ftp://[2001:db8::1]
|
||||||
|
deleting =
|
||||||
|
Брише се { $items } { $items ->
|
||||||
|
[one] ставка
|
||||||
|
*[other] ставки
|
||||||
|
} из { trash } ({ $progress })...
|
||||||
|
single-click = Један клик за отварање
|
||||||
|
view = Преглед
|
||||||
|
undo = Опозови
|
||||||
|
setting-permissions = Подешавање овлашћења за „{ $name }“ на { $mode }
|
||||||
|
owner = Власник
|
||||||
|
creating = Правим „{ $name }“ у „{ $parent }“
|
||||||
|
execute-only = Само извршавање
|
||||||
|
open-item-location = Отвори локацију ставке
|
||||||
|
details = Детаљи
|
||||||
|
set-executable-and-launched = Постављено „{ $name }“ као извршну датотеку и покренуто
|
||||||
|
mounted-drives = Прикључени дискови
|
||||||
|
sort-a-z = А-Ш
|
||||||
|
mount-error = Немогуће приступити уређају
|
||||||
|
extract-here = Извуци
|
||||||
|
grid-view = Преглед у виду мреже
|
||||||
|
filesystem = Систем датотека
|
||||||
|
set-and-launch = Подеси и покрени
|
||||||
|
removed-from-recents =
|
||||||
|
Уклоњено { $items } { $items ->
|
||||||
|
[one] ставке
|
||||||
|
*[other] ставки
|
||||||
|
} из { recents }
|
||||||
|
add-to-sidebar = Додај у страничник
|
||||||
|
item-created = Направљено: { $created }
|
||||||
|
network-drive-schemes =
|
||||||
|
Доступни протоколи,Префикс
|
||||||
|
ЕплТок,afp://
|
||||||
|
Протокол за пренос датотека,ftp:// или ftps://
|
||||||
|
Мрежни систем датотека,nfs://
|
||||||
|
Серверски блок порука,smb://
|
||||||
|
SSH протокол за пренос датотека,sftp:// или ssh://
|
||||||
|
ВебДАВ,dav:// или davs://
|
||||||
|
home = Лична
|
||||||
|
set-executable-and-launch = Постави као извршну и покрени
|
||||||
|
restored =
|
||||||
|
Враћено { $items } { $items ->
|
||||||
|
[one] ставка
|
||||||
|
*[other] ставки
|
||||||
|
} из { trash }
|
||||||
|
sort-z-a = Ш-А
|
||||||
|
type-to-search-recursive = Претражује тренутну фасциклу и све подфасцикле
|
||||||
|
history = Историјат
|
||||||
|
progress-paused = { $percent }%, паузирано
|
||||||
|
desktop-view-options = Могућности приказа радне површине...
|
||||||
|
show-on-desktop = Прикажи на радној површини
|
||||||
|
cancelled = Отказано
|
||||||
|
new-folder = Нова фасцикла...
|
||||||
|
match-desktop = Прати радну површину
|
||||||
|
domain = Домен
|
||||||
|
operations-running-finished =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] радња покренута
|
||||||
|
*[other] радње покренуте
|
||||||
|
} ({ $percent }%), { $finished } завршено...
|
||||||
|
sort-by-name = Поређај по називу
|
||||||
|
edit-history = Историјат уређивања
|
||||||
|
sort = Поређај
|
||||||
|
show-hidden-files = Прикажи скривене датотеке
|
||||||
|
progress-failed = { $percent }%, није успело
|
||||||
|
trash-folder-icon = Иконица фасцикле Смеће
|
||||||
|
item-accessed = Приступљено: { $accessed }
|
||||||
|
extract-to-title = Распакуј у фасциклу
|
||||||
|
open-with = Отвори помоћу
|
||||||
|
keep-both = Задржи оба
|
||||||
|
icon-size = Величина иконице
|
||||||
|
open-with-title = Како желите да отворите „{ $name }“?
|
||||||
|
extracting =
|
||||||
|
Извлачење { $items } { $items ->
|
||||||
|
[one] ставке
|
||||||
|
*[other] ставки
|
||||||
|
} из „{ $from }“ у „{ $to }“ ({ $progress })...
|
||||||
|
permanently-deleted =
|
||||||
|
Трајно обрисано { $items } { $items ->
|
||||||
|
[one] ставке
|
||||||
|
*[other] ставки
|
||||||
|
}
|
||||||
|
complete = Завршено
|
||||||
|
write-execute = Писање и извршавање
|
||||||
|
extract-password-required = Потребна је лозинка
|
||||||
|
pending = У току
|
||||||
|
desktop-folder-content = Садржај фасцикле радне површине
|
||||||
|
renaming = Преименовање „{ $from }“ у „{ $to }“
|
||||||
|
set-executable-and-launch-description = Да ли желите да поставите „{ $name }“ као извршну и покренете је?
|
||||||
|
no-history = Нема ставки у историјату.
|
||||||
|
open-folder = Отвори фасциклу
|
||||||
|
emptied-trash = Опражњено { trash }
|
||||||
|
rename-file = Преименуј датотеку
|
||||||
|
sort-largest-to-smallest = Од највеће до најмање
|
||||||
|
restore-from-trash = Врати из смећа
|
||||||
|
cut = Исеци
|
||||||
|
moved =
|
||||||
|
Премештено { $items } { $items ->
|
||||||
|
[one] датотеке
|
||||||
|
*[other] датотека
|
||||||
|
} из „{ $from }“ у „{ $to }“
|
||||||
|
progress-cancelled = { $percent }%, отказано
|
||||||
|
open-in-new-tab = Отвори у новом језичку
|
||||||
|
unknown-folder = непозната фасцикла
|
||||||
|
file = Датотека
|
||||||
|
file-name = Назив датотеке
|
||||||
|
save-file = Сачувај датотеку
|
||||||
|
created = Направљено „{ $name }“ у „{ $parent }“
|
||||||
|
delete-permanently = Трајно обриши
|
||||||
|
networks = Мреже
|
||||||
|
write-only = Само писање
|
||||||
|
today = Данас
|
||||||
|
display-settings = Подешавања екрана...
|
||||||
|
new-tab = Нови језичак
|
||||||
|
failed = Неуспешно
|
||||||
|
modified = Измењено
|
||||||
|
desktop-appearance = Изглед радне површине...
|
||||||
|
file-already-exists = Датотека са овим називом већ постоји
|
||||||
|
name-hidden = Називи који почињу тачком „.“ ће бити сакривени
|
||||||
|
folder-already-exists = Фасцикла са овим називом већ постоји
|
||||||
|
permanently-delete-warning = { $target } ће бити трајно обрисано. Ова радња се не може поништити.
|
||||||
|
favorite-path-error-description =
|
||||||
|
Не можемо да отворимо „{ $path }“
|
||||||
|
„{ $path }“ можда не постоји или немате дозволу за његово отварање
|
||||||
|
|
||||||
|
Желите ли да га уклоните из бочне површи?
|
||||||
|
empty-trash-warning = Ставке у смећу биће трајно обрисане
|
||||||
|
empty-trash = Испразни смеће
|
||||||
|
empty-trash-title = Испразнити смеће?
|
||||||
|
type-to-search = Куцајте за претрагу
|
||||||
|
notification-in-progress = Радње са датотекама су у току
|
||||||
|
name-no-slashes = Назив не може садржати косе црте
|
||||||
|
permanently-delete-question = Трајно обриши?
|
||||||
|
replace-title = „{ $filename }“ већ постоји на овој локацији
|
||||||
|
name-invalid = Назив не може бити „{ $filename }“
|
||||||
|
operations-running =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] радња покренута
|
||||||
|
*[other] радње покренуте
|
||||||
|
} ({ $percent }%)...
|
||||||
|
comment = Управник датотека за Космик радну површину
|
||||||
|
keywords = Folder;Manager;Фасцикла;Управник;fascikla;upravnik;
|
||||||
|
copy-to-title = Изабери одредиште умножавања
|
||||||
|
copy-to-button-label = Умножи
|
||||||
|
move-to-title = Изабери одредиште премештања
|
||||||
|
move-to-button-label = Помери
|
||||||
|
context-action = Контекстна радња
|
||||||
|
context-action-confirm-title = Покрени „{ $name }“?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Ово ће се извршити на { $items } { $items ->
|
||||||
|
[one] ставку
|
||||||
|
*[other] ставки
|
||||||
|
}.
|
||||||
|
selected-items = Изабраних { $items } ставки
|
||||||
|
mixed = Помешано
|
||||||
|
pasted-image = Убачена слика
|
||||||
|
pasted-text = Убачен текст
|
||||||
|
pasted-video = Убачен видео
|
||||||
|
show-recents = Недавна фасцикла у бочној површи
|
||||||
|
type-to-search-select = Обира прву подударајућу датотеку или фасциклу
|
||||||
|
clear-recents-history = Очисти историју недавних
|
||||||
|
compress = Сажми...
|
||||||
|
copy-to = Умножи у...
|
||||||
|
move-to = Помери у...
|
||||||
|
copy-path = Умножи путању
|
||||||
|
rename-confirm = Преименуј
|
||||||
|
|
@ -101,6 +101,7 @@ open-with = Öppna med
|
||||||
owner = Ägare
|
owner = Ägare
|
||||||
group = Grupp
|
group = Grupp
|
||||||
other = Andra
|
other = Andra
|
||||||
|
mixed = Blandade
|
||||||
# Listvy
|
# Listvy
|
||||||
name = Namn
|
name = Namn
|
||||||
modified = Ändrad
|
modified = Ändrad
|
||||||
|
|
@ -318,7 +319,7 @@ list-view = Listvy
|
||||||
show-hidden-files = Visa dolda filer
|
show-hidden-files = Visa dolda filer
|
||||||
list-directories-first = Lista mappar först
|
list-directories-first = Lista mappar först
|
||||||
gallery-preview = Galleri förhandsvisning
|
gallery-preview = Galleri förhandsvisning
|
||||||
menu-settings = Inställningar…
|
menu-settings = Inställningar...
|
||||||
menu-about = Om COSMIC Filer...
|
menu-about = Om COSMIC Filer...
|
||||||
|
|
||||||
## Sortera
|
## Sortera
|
||||||
|
|
@ -409,3 +410,18 @@ move-to = Flytta till...
|
||||||
show-recents = Mapp för senast använda filer i sidofältet
|
show-recents = Mapp för senast använda filer i sidofältet
|
||||||
clear-recents-history = Töm historik för Senaste
|
clear-recents-history = Töm historik för Senaste
|
||||||
copy-path = Kopiera sökväg
|
copy-path = Kopiera sökväg
|
||||||
|
context-action = Kontextåtgärd
|
||||||
|
context-action-confirm-title = Kör "{ $name }"?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Detta kommer att köras på { $items } { $items ->
|
||||||
|
[one] objekt
|
||||||
|
*[other] objekt
|
||||||
|
}.
|
||||||
|
run = Kör
|
||||||
|
rename-confirm = Byt namn
|
||||||
|
checksum = { $kind }-kontrollsumma
|
||||||
|
calculate = Beräkna
|
||||||
|
error = Fel
|
||||||
|
sidebar-label = Sidofältsetikett
|
||||||
|
search-application = Sök på programnamn
|
||||||
|
change-sidebar-label = Ändra sidofältsetikett
|
||||||
|
|
|
||||||
|
|
@ -202,17 +202,9 @@ moved =
|
||||||
*[other] ไฟล์
|
*[other] ไฟล์
|
||||||
} จาก "{ $from }" สู่ "{ $to }"
|
} จาก "{ $from }" สู่ "{ $to }"
|
||||||
renaming = กำลังเปลี่ยนชื่อจาก "{ $from }" เป็น "{ $to }"
|
renaming = กำลังเปลี่ยนชื่อจาก "{ $from }" เป็น "{ $to }"
|
||||||
renamed = Renamed "{ $from }" to "{ $to }"
|
renamed = เปลี่ยนชื่อจาก "{ $from }" เป็น "{ $to }" แล้ว
|
||||||
restoring =
|
restoring = กำลังกู้คืน { $items } รายการจาก { trash } ({ $progress })...
|
||||||
Restoring { $items } { $items ->
|
restored = กู้คืน { $items } รายการจาก { trash } แล้ว
|
||||||
[one] item
|
|
||||||
*[other] items
|
|
||||||
} from { trash } ({ $progress })...
|
|
||||||
restored =
|
|
||||||
Restored { $items } { $items ->
|
|
||||||
[one] item
|
|
||||||
*[other] items
|
|
||||||
} from { trash }
|
|
||||||
unknown-folder = แฟ้มที่ไม่รู้จัก
|
unknown-folder = แฟ้มที่ไม่รู้จัก
|
||||||
|
|
||||||
## Open with
|
## Open with
|
||||||
|
|
@ -240,8 +232,8 @@ settings = การตั้งค่า
|
||||||
appearance = ลักษณะ
|
appearance = ลักษณะ
|
||||||
theme = ธีม
|
theme = ธีม
|
||||||
match-desktop = ใช้ตามธีมหน้าจอหลัก
|
match-desktop = ใช้ตามธีมหน้าจอหลัก
|
||||||
dark = ธีมมืด
|
dark = มืด
|
||||||
light = ธีมสว่าง
|
light = สว่าง
|
||||||
# Context menu
|
# Context menu
|
||||||
add-to-sidebar = เพิ่มเข้าแถบด้านข้าง
|
add-to-sidebar = เพิ่มเข้าแถบด้านข้าง
|
||||||
compress = บีบอัด
|
compress = บีบอัด
|
||||||
|
|
@ -306,3 +298,4 @@ sort-newest-first = ไฟล์ใหม่ก่อน
|
||||||
sort-oldest-first = ไฟล์เก่าก่อน
|
sort-oldest-first = ไฟล์เก่าก่อน
|
||||||
sort-smallest-to-largest = ขนาดเล็กก่อน
|
sort-smallest-to-largest = ขนาดเล็กก่อน
|
||||||
sort-largest-to-smallest = ขนาดใหญ่ก่อน
|
sort-largest-to-smallest = ขนาดใหญ่ก่อน
|
||||||
|
remove = ลบ
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ filesystem = Файлова система
|
||||||
home = Домівка
|
home = Домівка
|
||||||
trash = Смітник
|
trash = Смітник
|
||||||
recents = Нещодавні
|
recents = Нещодавні
|
||||||
undo = Відмінити
|
undo = Скасувати
|
||||||
# List view
|
# List view
|
||||||
name = Назва
|
name = Назва
|
||||||
modified = Змінено
|
modified = Змінено
|
||||||
|
|
@ -82,10 +82,11 @@ copying =
|
||||||
Копіювання { $items } { $items ->
|
Копіювання { $items } { $items ->
|
||||||
[one] елемента
|
[one] елемента
|
||||||
*[other] елементів
|
*[other] елементів
|
||||||
} з «{ $from }» в «{ $to }» ({ $progress })...
|
} з «{ $from }» до «{ $to }» ({ $progress })...
|
||||||
copied =
|
copied =
|
||||||
Скопійовано { $items } { $items ->
|
Скопійовано { $items } { $items ->
|
||||||
[one] елемент
|
[one] елемент
|
||||||
|
[few] елементи
|
||||||
*[other] елеменів
|
*[other] елеменів
|
||||||
} з «{ $from }» в «{ $to }»
|
} з «{ $from }» в «{ $to }»
|
||||||
emptying-trash = Спорожнення { trash } ({ $progress })...
|
emptying-trash = Спорожнення { trash } ({ $progress })...
|
||||||
|
|
@ -94,11 +95,12 @@ moving =
|
||||||
Переміщення { $items } { $items ->
|
Переміщення { $items } { $items ->
|
||||||
[one] елемента
|
[one] елемента
|
||||||
*[other] елементів
|
*[other] елементів
|
||||||
} з «{ $from }» в «{ $to }» ({ $progress })...
|
} з «{ $from }» до «{ $to }» ({ $progress })...
|
||||||
moved =
|
moved =
|
||||||
Переміщено { $items } { $items ->
|
Переміщено { $items } { $items ->
|
||||||
[one] елемент
|
[one] елемент
|
||||||
*[other] елементи
|
[few] елементи
|
||||||
|
*[other] елементів
|
||||||
} з «{ $from }» в «{ $to }»
|
} з «{ $from }» в «{ $to }»
|
||||||
renaming = Перейменування «{ $from }» на «{ $to }»
|
renaming = Перейменування «{ $from }» на «{ $to }»
|
||||||
renamed = Перейменовано «{ $from }» на «{ $to }»
|
renamed = Перейменовано «{ $from }» на «{ $to }»
|
||||||
|
|
@ -110,7 +112,8 @@ restoring =
|
||||||
restored =
|
restored =
|
||||||
Відновлено { $items } { $items ->
|
Відновлено { $items } { $items ->
|
||||||
[one] елемент
|
[one] елемент
|
||||||
*[other] елементи
|
[few] елементи
|
||||||
|
*[other] елементів
|
||||||
} з { trash }
|
} з { trash }
|
||||||
unknown-folder = невідома тека
|
unknown-folder = невідома тека
|
||||||
|
|
||||||
|
|
@ -155,7 +158,7 @@ new-tab = Нова вкладка
|
||||||
new-window = Нове вікно
|
new-window = Нове вікно
|
||||||
rename = Перейменувати...
|
rename = Перейменувати...
|
||||||
close-tab = Закрити вкладку
|
close-tab = Закрити вкладку
|
||||||
quit = Вийти
|
quit = Завершити
|
||||||
|
|
||||||
## Edit
|
## Edit
|
||||||
|
|
||||||
|
|
@ -179,7 +182,7 @@ menu-settings = Налаштування...
|
||||||
menu-about = Про Файли COSMIC...
|
menu-about = Про Файли COSMIC...
|
||||||
repository = Сховище
|
repository = Сховище
|
||||||
support = Підтримка
|
support = Підтримка
|
||||||
details = Деталі
|
details = Подробиці
|
||||||
dismiss = Закрити повідомлення
|
dismiss = Закрити повідомлення
|
||||||
remove = Вилучити
|
remove = Вилучити
|
||||||
cancelled = Скасовані
|
cancelled = Скасовані
|
||||||
|
|
@ -223,7 +226,7 @@ permanently-delete-question = Остаточно видалити?
|
||||||
delete = Видалити
|
delete = Видалити
|
||||||
permanently-delete-warning = { $target } буде остаточно видалено. Цю дію не можна скасувати.
|
permanently-delete-warning = { $target } буде остаточно видалено. Цю дію не можна скасувати.
|
||||||
set-executable-and-launch = Зробити виконуваним і запустити
|
set-executable-and-launch = Зробити виконуваним і запустити
|
||||||
set-executable-and-launch-description = Бажаєте зробити "{ $name }" виконуваним і запустити його?
|
set-executable-and-launch-description = Бажаєте зробити «{ $name }» виконуваним і запустити його?
|
||||||
set-and-launch = Зробити і запустити
|
set-and-launch = Зробити і запустити
|
||||||
open-with = Відкрити за допомогою
|
open-with = Відкрити за допомогою
|
||||||
owner = Власник
|
owner = Власник
|
||||||
|
|
@ -245,7 +248,7 @@ favorite-path-error-description =
|
||||||
Вилучити з бічної панелі?
|
Вилучити з бічної панелі?
|
||||||
keep = Залишити
|
keep = Залишити
|
||||||
add-network-drive = Додати мережевий диск
|
add-network-drive = Додати мережевий диск
|
||||||
connect = З'єднати
|
connect = З’єднати
|
||||||
connect-anonymously = З'єднатись анонімно
|
connect-anonymously = З'єднатись анонімно
|
||||||
connecting = З'єднання…
|
connecting = З'єднання…
|
||||||
domain = Домен
|
domain = Домен
|
||||||
|
|
@ -274,12 +277,13 @@ compressing =
|
||||||
Стиснення { $items } { $items ->
|
Стиснення { $items } { $items ->
|
||||||
[one] елемента
|
[one] елемента
|
||||||
*[other] елементів
|
*[other] елементів
|
||||||
} з "{ $from }" до "{ $to }" ({ $progress })...
|
} з «{ $from }» до «{ $to }» ({ $progress })...
|
||||||
compressed =
|
compressed =
|
||||||
Стиснуто { $items } { $items ->
|
Стиснуто { $items } { $items ->
|
||||||
[one] елемент
|
[one] елемент
|
||||||
*[other] елементи
|
[few] елементи
|
||||||
} з "{ $from }" до "{ $to }"
|
*[other] елементів
|
||||||
|
} з «{ $from }» до «{ $to }»
|
||||||
deleting =
|
deleting =
|
||||||
Видалення { $items } { $items ->
|
Видалення { $items } { $items ->
|
||||||
[one] елемента
|
[one] елемента
|
||||||
|
|
@ -288,17 +292,19 @@ deleting =
|
||||||
deleted =
|
deleted =
|
||||||
Видалено { $items } { $items ->
|
Видалено { $items } { $items ->
|
||||||
[one] елемент
|
[one] елемент
|
||||||
|
[few] елементи
|
||||||
*[other] елементи
|
*[other] елементи
|
||||||
} з { trash }
|
} з { trash }
|
||||||
extracting =
|
extracting =
|
||||||
Видобування { $items } { $items ->
|
Видобування { $items } { $items ->
|
||||||
[one] елемента
|
[one] елемента
|
||||||
*[other] елементів
|
*[other] елементів
|
||||||
} з «{ $from }» в «{ $to }» ({ $progress })...
|
} з «{ $from }» до «{ $to }» ({ $progress })...
|
||||||
extracted =
|
extracted =
|
||||||
Видобуто { $items } { $items ->
|
Видобуто { $items } { $items ->
|
||||||
[one] елемент
|
[one] елемент
|
||||||
*[other] елементи
|
[few] елементи
|
||||||
|
*[other] елементів
|
||||||
} з «{ $from }» в «{ $to }»
|
} з «{ $from }» в «{ $to }»
|
||||||
setting-executable-and-launching = Надання «{ $name }» прав на виконання та запуск
|
setting-executable-and-launching = Надання «{ $name }» прав на виконання та запуск
|
||||||
set-executable-and-launched = «{ $name }» надано права на виконання і відкрито
|
set-executable-and-launched = «{ $name }» надано права на виконання і відкрито
|
||||||
|
|
@ -317,7 +323,7 @@ single-click = Відкривати одним клацанням
|
||||||
type-to-search = Введіть для пошуку
|
type-to-search = Введіть для пошуку
|
||||||
type-to-search-recursive = Шукає у поточній теці та всіх підтеках
|
type-to-search-recursive = Шукає у поточній теці та всіх підтеках
|
||||||
type-to-search-enter-path = Вводить шлях до каталогу або файлу
|
type-to-search-enter-path = Вводить шлях до каталогу або файлу
|
||||||
compress = Стиснути
|
compress = Стиснути...
|
||||||
delete-permanently = Остаточно видалити
|
delete-permanently = Остаточно видалити
|
||||||
eject = Безпечно вилучити
|
eject = Безпечно вилучити
|
||||||
extract-here = Видобути
|
extract-here = Видобути
|
||||||
|
|
@ -343,7 +349,8 @@ permanently-deleting =
|
||||||
permanently-deleted =
|
permanently-deleted =
|
||||||
Остаточно вилучено { $items } { $items ->
|
Остаточно вилучено { $items } { $items ->
|
||||||
[one] елемент
|
[one] елемент
|
||||||
*[other] елементи
|
[few] елементи
|
||||||
|
*[other] елементів
|
||||||
}
|
}
|
||||||
removing-from-recents =
|
removing-from-recents =
|
||||||
Вилучення { $items } { $items ->
|
Вилучення { $items } { $items ->
|
||||||
|
|
@ -353,13 +360,14 @@ removing-from-recents =
|
||||||
removed-from-recents =
|
removed-from-recents =
|
||||||
Вилучено { $items } { $items ->
|
Вилучено { $items } { $items ->
|
||||||
[one] елемент
|
[one] елемент
|
||||||
*[other] елементи
|
[few] елементи
|
||||||
|
*[other] елементів
|
||||||
} з { recents }
|
} з { recents }
|
||||||
empty-trash-title = Спорожити смітник?
|
empty-trash-title = Спорожити смітник?
|
||||||
type-to-search-select = Вибирає перший відповідний файл або папку
|
type-to-search-select = Вибирає перший відповідний файл або теку
|
||||||
pasted-image = Вставлене Зображення
|
pasted-image = Вставлене зображення
|
||||||
pasted-text = Вставлений Текст
|
pasted-text = Вставлений текст
|
||||||
pasted-video = Вставлене Видиво
|
pasted-video = Вставлене видиво
|
||||||
copy-to-button-label = Копіювати
|
copy-to-button-label = Копіювати
|
||||||
move-to-button-label = Перемістити
|
move-to-button-label = Перемістити
|
||||||
copy-to = Копіювати до…
|
copy-to = Копіювати до…
|
||||||
|
|
@ -371,3 +379,19 @@ keywords = Тека;Папка;Провідник;Менеджер;Катало
|
||||||
show-recents = Тека «Нещодавні» на бічній панелі
|
show-recents = Тека «Нещодавні» на бічній панелі
|
||||||
copy-path = Копіювати шлях
|
copy-path = Копіювати шлях
|
||||||
clear-recents-history = Очистити нещодавні
|
clear-recents-history = Очистити нещодавні
|
||||||
|
context-action-confirm-title = Запустити «{ $name }»?
|
||||||
|
run = Виконати
|
||||||
|
context-action-confirm-warning =
|
||||||
|
Запуститься для { $items } { $items ->
|
||||||
|
[one] елемента
|
||||||
|
*[other] елементів
|
||||||
|
}.
|
||||||
|
context-action = Контекстна дія
|
||||||
|
rename-confirm = Перейменувати
|
||||||
|
mixed = Змішано
|
||||||
|
checksum = контрольна сума { $kind }
|
||||||
|
calculate = Вирахувати
|
||||||
|
error = Помилка
|
||||||
|
sidebar-label = Назва бічної панелі
|
||||||
|
search-application = Пошук за назвою застосунку
|
||||||
|
change-sidebar-label = Змінити назву бічної панелі
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ recents = 最近访问
|
||||||
undo = 撤销
|
undo = 撤销
|
||||||
today = 今天
|
today = 今天
|
||||||
# Desktop view options
|
# Desktop view options
|
||||||
desktop-view-options = 桌面视图选项...
|
desktop-view-options = 桌面视图选项…
|
||||||
show-on-desktop = 在桌面显示
|
show-on-desktop = 在桌面显示
|
||||||
desktop-folder-content = 桌面文件夹内容
|
desktop-folder-content = 桌面文件夹内容
|
||||||
mounted-drives = 已装载驱动器
|
mounted-drives = 已装载驱动器
|
||||||
|
|
@ -25,13 +25,13 @@ modified = 修改时间
|
||||||
trashed-on = 删除时间
|
trashed-on = 删除时间
|
||||||
size = 大小
|
size = 大小
|
||||||
# Progress footer
|
# Progress footer
|
||||||
details = 详细信息
|
details = 详情
|
||||||
dismiss = 清除消息
|
dismiss = 清除消息
|
||||||
operations-running =
|
operations-running =
|
||||||
正在进行 { $running } { $running ->
|
正在进行 { $running } { $running ->
|
||||||
[one] 个操作
|
[one] 个操作
|
||||||
*[other] 个操作
|
*[other] 个操作
|
||||||
}({ $percent }%)...
|
}({ $percent }%)…
|
||||||
operations-running-finished =
|
operations-running-finished =
|
||||||
正在进行 { $running } { $running ->
|
正在进行 { $running } { $running ->
|
||||||
[one] 个操作
|
[one] 个操作
|
||||||
|
|
@ -50,7 +50,7 @@ create-archive = 创建压缩包
|
||||||
## Extract Dialog
|
## Extract Dialog
|
||||||
|
|
||||||
extract-password-required = 需要密码
|
extract-password-required = 需要密码
|
||||||
extract-to = 提取到...
|
extract-to = 提取到…
|
||||||
extract-to-title = 提取到文件夹
|
extract-to-title = 提取到文件夹
|
||||||
|
|
||||||
## Empty Trash Dialog
|
## Empty Trash Dialog
|
||||||
|
|
@ -167,10 +167,10 @@ read-write-execute = 读取、写入和执行
|
||||||
|
|
||||||
## Favorite Path Error Dialog
|
## Favorite Path Error Dialog
|
||||||
|
|
||||||
favorite-path-error = 打开路径时出错
|
favorite-path-error = 打开目录时出错
|
||||||
favorite-path-error-description =
|
favorite-path-error-description =
|
||||||
无法打开 "{ $path }" 。
|
无法打开“{ $path }” 。
|
||||||
"{ $path }" 可能不存在或您没有权限打开它。
|
“{ $path }”可能不存在或您没有权限打开它。
|
||||||
|
|
||||||
您想要从侧边栏中移除它吗?
|
您想要从侧边栏中移除它吗?
|
||||||
remove = 移除
|
remove = 移除
|
||||||
|
|
@ -267,10 +267,10 @@ extracted =
|
||||||
[one] 项目
|
[one] 项目
|
||||||
*[other] 项目
|
*[other] 项目
|
||||||
}到“{ $to }”
|
}到“{ $to }”
|
||||||
setting-executable-and-launching = 设置 "{ $name }" 为可执行文件并启动
|
setting-executable-and-launching = 设置 “{ $name }” 为可执行文件并启动
|
||||||
set-executable-and-launched = 已设 "{ $name }" 为可执行文件并且启动
|
set-executable-and-launched = 已设 “{ $name }” 为可执行文件并且启动
|
||||||
setting-permissions = 正在设置 "{ $name }" 的权限为 { $mode }
|
setting-permissions = 正在设置 “{ $name }” 的权限为 { $mode }
|
||||||
set-permissions = 设置 "{ $name }" 的权限为 { $mode }
|
set-permissions = 设置 “{ $name }” 的权限为 { $mode }
|
||||||
moving =
|
moving =
|
||||||
正在从“{ $from }”移动 { $items } { $items ->
|
正在从“{ $from }”移动 { $items } { $items ->
|
||||||
[one] 项目
|
[one] 项目
|
||||||
|
|
@ -302,7 +302,7 @@ removed-from-recents =
|
||||||
*[other] 项目
|
*[other] 项目
|
||||||
}
|
}
|
||||||
renaming = 正在将“{ $from }”重命名为“{ $to }”
|
renaming = 正在将“{ $from }”重命名为“{ $to }”
|
||||||
renamed = 已重命名"{ $from }"为"{ $to }"
|
renamed = 已重命名 “{ $from }” 为 “{ $to }”
|
||||||
restoring =
|
restoring =
|
||||||
正在从{ trash }中还原 { $items } { $items ->
|
正在从{ trash }中还原 { $items } { $items ->
|
||||||
[one] 项目
|
[one] 项目
|
||||||
|
|
@ -317,7 +317,7 @@ unknown-folder = 未知文件夹
|
||||||
|
|
||||||
## Open with
|
## Open with
|
||||||
|
|
||||||
menu-open-with = 打开方式...
|
menu-open-with = 打开方式…
|
||||||
default-app = { $name }(默认)
|
default-app = { $name }(默认)
|
||||||
|
|
||||||
## Show details
|
## Show details
|
||||||
|
|
@ -329,7 +329,7 @@ item-size = 文件大小:{ $size }
|
||||||
item-created = 创建于:{ $created }
|
item-created = 创建于:{ $created }
|
||||||
item-modified = 修改于:{ $modified }
|
item-modified = 修改于:{ $modified }
|
||||||
item-accessed = 访问于:{ $accessed }
|
item-accessed = 访问于:{ $accessed }
|
||||||
calculating = 计算中...
|
calculating = 计算中…
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
|
|
@ -348,15 +348,15 @@ light = 亮色模式
|
||||||
|
|
||||||
type-to-search = 输入即可搜索
|
type-to-search = 输入即可搜索
|
||||||
type-to-search-recursive = 搜索当前文件夹及其所有子文件夹
|
type-to-search-recursive = 搜索当前文件夹及其所有子文件夹
|
||||||
type-to-search-enter-path = 输入文件夹或文件路径
|
type-to-search-enter-path = 输入文件夹或文件目录
|
||||||
# Context menu
|
# Context menu
|
||||||
add-to-sidebar = 加入侧边栏
|
add-to-sidebar = 加入侧边栏
|
||||||
compress = 压缩…
|
compress = 压缩…
|
||||||
delete-permanently = 永久删除
|
delete-permanently = 永久删除
|
||||||
eject = 弹出
|
eject = 弹出
|
||||||
extract-here = 解压到此处
|
extract-here = 解压到此处
|
||||||
new-file = 新建文件...
|
new-file = 新建文件…
|
||||||
new-folder = 新建文件夹...
|
new-folder = 新建文件夹…
|
||||||
open-in-terminal = 在终端模拟器中打开
|
open-in-terminal = 在终端模拟器中打开
|
||||||
move-to-trash = 移动到回收站
|
move-to-trash = 移动到回收站
|
||||||
restore-from-trash = 从回收站中还原
|
restore-from-trash = 从回收站中还原
|
||||||
|
|
@ -369,9 +369,9 @@ remove-from-recents = 从最近访问中移除
|
||||||
|
|
||||||
## Desktop
|
## Desktop
|
||||||
|
|
||||||
change-wallpaper = 更改壁纸...
|
change-wallpaper = 更改壁纸…
|
||||||
desktop-appearance = 桌面外观...
|
desktop-appearance = 桌面外观…
|
||||||
display-settings = 显示设置...
|
display-settings = 显示设置…
|
||||||
|
|
||||||
# Menu
|
# Menu
|
||||||
|
|
||||||
|
|
@ -382,7 +382,7 @@ file = 文件
|
||||||
new-tab = 新建标签
|
new-tab = 新建标签
|
||||||
new-window = 新建窗口
|
new-window = 新建窗口
|
||||||
reload-folder = 刷新文件夹
|
reload-folder = 刷新文件夹
|
||||||
rename = 重命名...
|
rename = 重命名…
|
||||||
close-tab = 关闭标签
|
close-tab = 关闭标签
|
||||||
quit = 退出
|
quit = 退出
|
||||||
|
|
||||||
|
|
@ -405,7 +405,7 @@ list-view = 列表视图
|
||||||
show-hidden-files = 显示隐藏文件
|
show-hidden-files = 显示隐藏文件
|
||||||
list-directories-first = 优先列出目录
|
list-directories-first = 优先列出目录
|
||||||
gallery-preview = 图库预览
|
gallery-preview = 图库预览
|
||||||
menu-settings = 设置...
|
menu-settings = 设置…
|
||||||
menu-about = 关于 COSMIC 文件…
|
menu-about = 关于 COSMIC 文件…
|
||||||
|
|
||||||
## Sort
|
## Sort
|
||||||
|
|
@ -435,4 +435,20 @@ comment = COSMIC 桌面的文件管理器
|
||||||
keywords = 文件夹;管理器;
|
keywords = 文件夹;管理器;
|
||||||
clear-recents-history = 清除最近访问历史
|
clear-recents-history = 清除最近访问历史
|
||||||
copy-path = 复制文件路径
|
copy-path = 复制文件路径
|
||||||
show-recents = 侧边栏显示最近访问
|
show-recents = 侧边栏中的最近访问
|
||||||
|
mixed = 混合
|
||||||
|
context-action-confirm-title = 运行 “{ $name }” ?
|
||||||
|
run = 运行
|
||||||
|
context-action-confirm-warning =
|
||||||
|
该行动将会在 { $items } { $items ->
|
||||||
|
[one] 项目
|
||||||
|
*[other] 项目
|
||||||
|
} 上运行。
|
||||||
|
context-action = 环境行动
|
||||||
|
rename-confirm = 重命名
|
||||||
|
calculate = 计算
|
||||||
|
error = 错误
|
||||||
|
checksum = { $kind } 校验和
|
||||||
|
sidebar-label = 侧边栏标签
|
||||||
|
search-application = 使用应用名称搜索
|
||||||
|
change-sidebar-label = 更改侧边栏标签
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
cosmic-files = COSMIC 檔案總管
|
cosmic-files = COSMIC 檔案
|
||||||
empty-folder = 空資料夾
|
empty-folder = 空資料夾
|
||||||
empty-folder-hidden = 空資料夾(包含隱藏項目)
|
empty-folder-hidden = 空資料夾(包含隱藏項目)
|
||||||
no-results = 找不到結果
|
no-results = 找不到結果
|
||||||
filesystem = 檔案系統
|
filesystem = 檔案系統
|
||||||
home = 主目錄
|
home = 家目錄
|
||||||
networks = 網路
|
networks = 網路
|
||||||
notification-in-progress = 檔案操作正在進行中。
|
notification-in-progress = 檔案操作正在進行中
|
||||||
trash = 垃圾桶
|
trash = 垃圾桶
|
||||||
recents = 最近使用
|
recents = 最近使用
|
||||||
undo = 復原
|
undo = 復原
|
||||||
today = 今天
|
today = 今天
|
||||||
# List view
|
# List view
|
||||||
name = 名稱
|
name = 名稱
|
||||||
modified = 修改日期
|
modified = 修改時間
|
||||||
size = 大小
|
size = 大小
|
||||||
|
|
||||||
# Dialogs
|
# Dialogs
|
||||||
|
|
@ -25,7 +25,7 @@ create-archive = 建立壓縮檔案
|
||||||
## Empty Trash Dialog
|
## Empty Trash Dialog
|
||||||
|
|
||||||
empty-trash = 清空垃圾桶
|
empty-trash = 清空垃圾桶
|
||||||
empty-trash-warning = 你確定要永久刪除垃圾桶中的所有項目嗎?
|
empty-trash-warning = 垃圾桶中的項目將被永久刪除
|
||||||
|
|
||||||
## New File/Folder Dialog
|
## New File/Folder Dialog
|
||||||
|
|
||||||
|
|
@ -33,16 +33,16 @@ create-new-file = 建立新檔案
|
||||||
create-new-folder = 建立新資料夾
|
create-new-folder = 建立新資料夾
|
||||||
file-name = 檔案名稱
|
file-name = 檔案名稱
|
||||||
folder-name = 資料夾名稱
|
folder-name = 資料夾名稱
|
||||||
file-already-exists = 已存在同名檔案。
|
file-already-exists = 相同名稱的檔案已經存在
|
||||||
folder-already-exists = 已存在同名資料夾。
|
folder-already-exists = 相同名稱的資料夾已經存在
|
||||||
name-hidden = 以「.」開頭的名稱將會被隱藏。
|
name-hidden = 以「.」開頭的名稱將會被隱藏
|
||||||
name-invalid = 名稱不能是 「{ $filename }」。
|
name-invalid = 名稱不能是「{ $filename }」
|
||||||
name-no-slashes = 名稱不能包含斜線。
|
name-no-slashes = 名稱不能包含斜線
|
||||||
|
|
||||||
## Open/Save Dialog
|
## Open/Save Dialog
|
||||||
|
|
||||||
cancel = 取消
|
cancel = 取消
|
||||||
create = 建立
|
create = 創作
|
||||||
open = 開啟
|
open = 開啟
|
||||||
open-file = 開啟檔案
|
open-file = 開啟檔案
|
||||||
open-folder = 開啟資料夾
|
open-folder = 開啟資料夾
|
||||||
|
|
@ -62,12 +62,12 @@ rename-folder = 重新命名資料夾
|
||||||
## Replace Dialog
|
## Replace Dialog
|
||||||
|
|
||||||
replace = 取代
|
replace = 取代
|
||||||
replace-title = 檔案「{ $filename }」已存在於此位置。
|
replace-title = 「{ $filename }」已存在於此位置
|
||||||
replace-warning = 你要取代它嗎?取代將覆蓋其內容。
|
replace-warning = 你要取代它嗎?取代將覆蓋其內容。
|
||||||
replace-warning-operation = 你要取代它嗎?取代將覆蓋其內容。
|
replace-warning-operation = 你要取代它嗎?取代將覆蓋其內容。
|
||||||
original-file = 原始檔案
|
original-file = 原始檔案
|
||||||
replace-with = 取代為
|
replace-with = 取代為
|
||||||
apply-to-all = 套用至所有項目
|
apply-to-all = 套用至全部
|
||||||
keep-both = 保留兩者
|
keep-both = 保留兩者
|
||||||
skip = 跳過
|
skip = 跳過
|
||||||
|
|
||||||
|
|
@ -95,13 +95,13 @@ network-drive-description =
|
||||||
伺服器地址包括協定前綴和地址。
|
伺服器地址包括協定前綴和地址。
|
||||||
範例:ssh://192.168.0.1, ftp://[2001:db8::1]
|
範例:ssh://192.168.0.1, ftp://[2001:db8::1]
|
||||||
network-drive-schemes =
|
network-drive-schemes =
|
||||||
可用協定,前綴
|
可用協定,前綴
|
||||||
AppleTalk,afp://
|
AppleTalk,afp://
|
||||||
檔案傳輸協定,ftp:// 或 ftps://
|
檔案傳輸協定,ftp:// 或 ftps://
|
||||||
網路檔案系統,nfs://
|
網路檔案系統,nfs://
|
||||||
伺服器訊息區塊,smb://
|
伺服器訊息區塊,smb://
|
||||||
SSH 檔案傳輸協定,sftp:// 或 ssh://
|
SSH 檔案傳輸協定,sftp:// 或 ssh://
|
||||||
WebDav,dav:// 或 davs://
|
WebDav,dav:// 或 davs://
|
||||||
network-drive-error = 無法存取網路磁碟機
|
network-drive-error = 無法存取網路磁碟機
|
||||||
password = 密碼
|
password = 密碼
|
||||||
remember-password = 記住密碼
|
remember-password = 記住密碼
|
||||||
|
|
@ -112,7 +112,7 @@ username = 使用者名稱
|
||||||
|
|
||||||
edit-history = 編輯歷史
|
edit-history = 編輯歷史
|
||||||
history = 歷史紀錄
|
history = 歷史紀錄
|
||||||
no-history = 沒有歷史項目。
|
no-history = 無歷史記錄項目。
|
||||||
pending = 待處理
|
pending = 待處理
|
||||||
failed = 失敗
|
failed = 失敗
|
||||||
complete = 完成
|
complete = 完成
|
||||||
|
|
@ -133,56 +133,56 @@ copying =
|
||||||
正在複製 { $items } { $items ->
|
正在複製 { $items } { $items ->
|
||||||
[one] 項目
|
[one] 項目
|
||||||
*[other] 項目
|
*[other] 項目
|
||||||
} 從「{ $from }」到「{ $to }」({ $progress })...
|
}從「{ $from }」到「{ $to }」({ $progress })...
|
||||||
copied =
|
copied =
|
||||||
已複製 { $items } { $items ->
|
已複製 { $items } { $items ->
|
||||||
[one] 項目
|
[one] 項目
|
||||||
*[other] 項目
|
*[other] 項目
|
||||||
}從「{ $from }」到「{ $to }」
|
}從「{ $from }」到「{ $to }」
|
||||||
emptying-trash = 正在清空{ trash }({ $progress })…
|
emptying-trash = 正在清空 { trash }({ $progress })…
|
||||||
emptied-trash = 已清空{ trash }
|
emptied-trash = 已經清空 { trash }
|
||||||
extracting =
|
extracting =
|
||||||
正在解壓縮 { $items } 項目 { $items ->
|
正在解壓縮 { $items } 項目 { $items ->
|
||||||
[one] 項目
|
[one] 項目
|
||||||
*[other] 項目
|
*[other] 項目
|
||||||
} 從「{ $from }」到「{ $to }」({ $progress })...
|
}從「{ $from }」至「{ $to }」({ $progress })...
|
||||||
extracted =
|
extracted =
|
||||||
已解壓縮 { $items } 項目 { $items ->
|
已解壓縮 { $items } 項目 { $items ->
|
||||||
[one] 項目
|
[one] 項目
|
||||||
*[other] 項目
|
*[other] 項目
|
||||||
} 從 { $from } 到 { $to }
|
}從「{ $from }」到「{ $to }」
|
||||||
moving =
|
moving =
|
||||||
正在移動 { $items } { $items ->
|
正在移動 { $items } { $items ->
|
||||||
[one] 項目
|
[one] 項目
|
||||||
*[other] 項目
|
*[other] 項目
|
||||||
} 從「{ $from }」到「{ $to }」({ $progress })...
|
}從「{ $from }」到「{ $to }」({ $progress })...
|
||||||
moved =
|
moved =
|
||||||
已移動 { $items } { $items ->
|
已經移動 { $items } { $items ->
|
||||||
[one] 項目
|
[one] 項目
|
||||||
*[other] 項目
|
*[other] 項目
|
||||||
} 從「{ $from }」到「{ $to }」
|
} 從「{ $from }」至「{ $to }」
|
||||||
renaming = 正在重新命名「{ $from }」為「{ $to }」
|
renaming = 正在將「{ $from }」重新命名為「{ $to }」
|
||||||
renamed = 已重新命名 { $from } 為 { $to }
|
renamed = 已將「{ $from }」重新命名為「{ $to }」
|
||||||
restoring =
|
restoring =
|
||||||
正在還原 { $items } 項目 { $items ->
|
正在還原 { $items } 項目 { $items ->
|
||||||
[one] 項目
|
[one] 項目
|
||||||
*[other] 項目
|
*[other] 項目
|
||||||
} 從{ trash }({ $progress })...
|
}自 { trash } ({ $progress })...
|
||||||
restored =
|
restored =
|
||||||
已還原 { $items } 項目 { $items ->
|
已經還原 { $items } 項目 { $items ->
|
||||||
[one] 項目
|
[one] 項目
|
||||||
*[other] 項目
|
*[other] 項目
|
||||||
} 從{ trash }
|
}從 { trash }
|
||||||
unknown-folder = 未知資料夾
|
unknown-folder = 不明資料夾
|
||||||
|
|
||||||
## Open with
|
## Open with
|
||||||
|
|
||||||
menu-open-with = 開啟方式...
|
menu-open-with = 開啟檔案...
|
||||||
default-app = { $name } (預設)
|
default-app = { $name } (預設)
|
||||||
|
|
||||||
## Show details
|
## Show details
|
||||||
|
|
||||||
show-details = 顯示詳細資料
|
show-details = 顯示詳細資訊
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
|
|
@ -192,17 +192,17 @@ settings = 設定
|
||||||
|
|
||||||
appearance = 外觀
|
appearance = 外觀
|
||||||
theme = 主題
|
theme = 主題
|
||||||
match-desktop = 與桌面一致
|
match-desktop = 符合桌面
|
||||||
dark = 暗色模式
|
dark = 深色
|
||||||
light = 亮色模式
|
light = 淺色
|
||||||
# Context menu
|
# Context menu
|
||||||
add-to-sidebar = 加入側邊欄
|
add-to-sidebar = 加入側邊欄
|
||||||
compress = 壓縮
|
compress = 壓縮…
|
||||||
extract-here = 解壓縮至此
|
extract-here = 解壓縮
|
||||||
new-file = 新檔案...
|
new-file = 新增檔案...
|
||||||
new-folder = 新資料夾...
|
new-folder = 新增資料夾...
|
||||||
open-in-terminal = 在終端機中開啟
|
open-in-terminal = 在終端機中開啟
|
||||||
move-to-trash = 移動至垃圾桶
|
move-to-trash = 丟入垃圾桶
|
||||||
restore-from-trash = 從垃圾桶還原
|
restore-from-trash = 從垃圾桶還原
|
||||||
remove-from-sidebar = 從側邊欄移除
|
remove-from-sidebar = 從側邊欄移除
|
||||||
sort-by-name = 依名稱排序
|
sort-by-name = 依名稱排序
|
||||||
|
|
@ -215,11 +215,11 @@ sort-by-size = 依大小排序
|
||||||
## File
|
## File
|
||||||
|
|
||||||
file = 檔案
|
file = 檔案
|
||||||
new-tab = 新分頁
|
new-tab = 新增分頁
|
||||||
new-window = 新視窗
|
new-window = 新增視窗
|
||||||
rename = 重新命名...
|
rename = 重新命名...
|
||||||
close-tab = 關閉分頁
|
close-tab = 關閉分頁
|
||||||
quit = 退出
|
quit = 結束
|
||||||
|
|
||||||
## Edit
|
## Edit
|
||||||
|
|
||||||
|
|
@ -238,46 +238,164 @@ view = 檢視
|
||||||
grid-view = 網格檢視
|
grid-view = 網格檢視
|
||||||
list-view = 列表檢視
|
list-view = 列表檢視
|
||||||
show-hidden-files = 顯示隱藏檔案
|
show-hidden-files = 顯示隱藏檔案
|
||||||
list-directories-first = 優先列出目錄
|
list-directories-first = 目錄優先列出
|
||||||
menu-settings = 設定...
|
menu-settings = 設定...
|
||||||
menu-about = 關於 COSMIC 檔案總管...
|
menu-about = 關於 COSMIC 檔案...
|
||||||
|
|
||||||
## Sort
|
## Sort
|
||||||
|
|
||||||
sort = 排序
|
sort = 排序
|
||||||
sort-a-z = A-Z
|
sort-a-z = A-Z
|
||||||
sort-z-a = Z-A
|
sort-z-a = Z-A
|
||||||
sort-newest-first = 最新的在前
|
sort-newest-first = 最新優先
|
||||||
sort-oldest-first = 最舊的在前
|
sort-oldest-first = 最舊優先
|
||||||
sort-smallest-to-largest = 由小至大
|
sort-smallest-to-largest = 從小到大
|
||||||
sort-largest-to-smallest = 由大至小
|
sort-largest-to-smallest = 從大到小
|
||||||
deleted =
|
deleted =
|
||||||
已刪除 { $items } { $items ->
|
已經刪除 { $items } { $items ->
|
||||||
[one] 項目
|
[one] 項目
|
||||||
*[other] 項目
|
*[other] 項目
|
||||||
}從{ trash }
|
}從 { trash }
|
||||||
permanently-deleting =
|
permanently-deleting =
|
||||||
正在永久刪除 { $items } { $items ->
|
正在永久刪除 { $items } { $items ->
|
||||||
[one] 项目
|
[one] 項目
|
||||||
*[other] 项目
|
*[other] 項目
|
||||||
}
|
}
|
||||||
permanently-deleted =
|
permanently-deleted =
|
||||||
已永久刪除 { $items } { $items ->
|
已經永久刪除 { $items } { $items ->
|
||||||
[one] 项目
|
[one] 項目
|
||||||
*[other] 项目
|
*[other] 項目
|
||||||
}
|
}
|
||||||
removing-from-recents =
|
removing-from-recents =
|
||||||
正在從{ recents }中移除 { $items } { $items ->
|
正在從 { recents } 中移除 { $items } { $items ->
|
||||||
[one] 项目
|
[one] 項目
|
||||||
*[other] 项目
|
*[other] 項目
|
||||||
}
|
}
|
||||||
deleting =
|
deleting =
|
||||||
正在刪除 { $items } { $items ->
|
正在刪除 { $items } { $items ->
|
||||||
[one] 项目
|
[one] 项目
|
||||||
*[other] 项目
|
*[other] 项目
|
||||||
}從{ trash }({ $progress })…
|
}從 { trash }({ $progress })…
|
||||||
removed-from-recents =
|
removed-from-recents =
|
||||||
已從{ recents }中移除 { $items } { $items ->
|
已經從 { recents } 中移除 { $items } { $items ->
|
||||||
[one] 项目
|
[one] 項目
|
||||||
*[other] 项目
|
*[other] 項目
|
||||||
}
|
}
|
||||||
|
repository = 軟體庫
|
||||||
|
desktop-view-options = 桌面檢視選項...
|
||||||
|
show-on-desktop = 顯示在桌面
|
||||||
|
desktop-folder-content = 桌面資料夾內容
|
||||||
|
mounted-drives = 已經掛載的磁碟機
|
||||||
|
trash-folder-icon = 垃圾桶圖示
|
||||||
|
trashed-on = 丟入時間
|
||||||
|
icon-size-and-spacing = 圖示大小與間距
|
||||||
|
icon-size = 圖示大小
|
||||||
|
grid-spacing = 網格間距
|
||||||
|
details = 詳細資訊
|
||||||
|
dismiss = 關閉訊息
|
||||||
|
delete = 刪除
|
||||||
|
remove = 移除
|
||||||
|
support = 支援
|
||||||
|
cancelled = 已取消
|
||||||
|
keywords = 資料夾;管理器;
|
||||||
|
empty-trash-title = 清空垃圾桶?
|
||||||
|
pause = 暫停
|
||||||
|
resume = 繼續
|
||||||
|
extract-password-required = 需要密碼
|
||||||
|
extract-to = 解壓縮至...
|
||||||
|
extract-to-title = 解壓縮至資料夾
|
||||||
|
mount-error = 無法存取磁碟機
|
||||||
|
open-with-title = 您要如何開啟「{ $name }」?
|
||||||
|
browse-store = 瀏覽 { $store }
|
||||||
|
other-apps = 其他應用程式
|
||||||
|
related-apps = 相關應用程式
|
||||||
|
permanently-delete-question = 永久刪除?
|
||||||
|
set-executable-and-launch = 設定為可以執行並啟動
|
||||||
|
read-only = 唯讀
|
||||||
|
read-execute = 讀取和執行
|
||||||
|
read-write = 讀取和寫入
|
||||||
|
read-write-execute = 讀取、寫入和執行
|
||||||
|
favorite-path-error = 開啟目錄時發生錯誤
|
||||||
|
set-executable-and-launch-description = 您是否要將「{ $name }」設為可執行並啟動它?
|
||||||
|
set-and-launch = 設定並啟動
|
||||||
|
none = 無
|
||||||
|
execute-only = 僅執行
|
||||||
|
write-only = 僅寫入
|
||||||
|
write-execute = 寫入和執行
|
||||||
|
operations-running =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] 個操作
|
||||||
|
*[other] 個操作
|
||||||
|
}正在執行({ $percent }%)...
|
||||||
|
operations-running-finished =
|
||||||
|
{ $running } { $running ->
|
||||||
|
[one] 個操作
|
||||||
|
*[other] 個操作
|
||||||
|
}正在執行({ $percent }%), { $finished } 個已經完成...
|
||||||
|
permanently-delete-warning = 「{ $target }」將被永久刪除。此操作無法復原。
|
||||||
|
open-with = 開啟檔案
|
||||||
|
selected-items = 已經選定 { $items } 個項目
|
||||||
|
copy-to-title = 選擇複製目的地
|
||||||
|
copy-to-button-label = 複製
|
||||||
|
move-to-title = 選擇移動目的地
|
||||||
|
move-to-button-label = 移動
|
||||||
|
keep = 保留
|
||||||
|
progress = { $percent }%
|
||||||
|
progress-cancelled = { $percent }%,已經取消
|
||||||
|
progress-failed = { $percent }%,失敗
|
||||||
|
progress-paused = { $percent }%,已經暫停
|
||||||
|
favorite-path-error-description =
|
||||||
|
無法開啟「{ $path }」
|
||||||
|
「{ $path }」可能不存在,或您可能沒有權限開啟它。
|
||||||
|
|
||||||
|
您是否要將它從側邊欄移除?
|
||||||
|
comment = COSMIC 桌面檔案管理器
|
||||||
|
pasted-image = 已經貼上的圖片
|
||||||
|
pasted-text = 已經貼上的文字
|
||||||
|
pasted-video = 已經貼上的影片
|
||||||
|
sort-by-trashed = 依丟入時間排序
|
||||||
|
calculating = 計算中...
|
||||||
|
single-click = 點按以開啟
|
||||||
|
type-to-search = 輸入進行搜尋
|
||||||
|
type-to-search-recursive = 搜尋目前資料夾及全部子資料夾
|
||||||
|
type-to-search-enter-path = 輸入目錄或檔案的目錄
|
||||||
|
delete-permanently = 永久刪除
|
||||||
|
eject = 彈出
|
||||||
|
remove-from-recents = 從最近項目中移除
|
||||||
|
change-wallpaper = 變更桌布...
|
||||||
|
desktop-appearance = 桌面外觀...
|
||||||
|
display-settings = 顯示設定...
|
||||||
|
reload-folder = 重新載入資料夾
|
||||||
|
gallery-preview = 圖庫預覽
|
||||||
|
type = 類型:{ $mime }
|
||||||
|
items = 項目:{ $items }
|
||||||
|
item-size = 大小:{ $size }
|
||||||
|
item-created = 建立時間:{ $created }
|
||||||
|
item-modified = 修改時間:{ $modified }
|
||||||
|
item-accessed = 存取時間:{ $accessed }
|
||||||
|
type-to-search-select = 選取第一個符合條件的檔案或資料夾
|
||||||
|
copy-to = 複製至...
|
||||||
|
move-to = 移動至...
|
||||||
|
show-recents = 側邊欄中的最近使用資料夾
|
||||||
|
clear-recents-history = 清除最近使用歷史記錄
|
||||||
|
copy-path = 複製路徑
|
||||||
|
setting-executable-and-launching = 設定「{ $name }」為可以執行並進行啟動
|
||||||
|
set-executable-and-launched = 設定「{ $name }」為可以執行並已經啟動
|
||||||
|
setting-permissions = 設定「{ $name }」的權限至 { $mode }
|
||||||
|
set-permissions = 設定「{ $name }」的權限至 { $mode }
|
||||||
|
mixed = 混合
|
||||||
|
context-action = 環境行動
|
||||||
|
context-action-confirm-title = 執行「{ $name }」嗎?
|
||||||
|
context-action-confirm-warning =
|
||||||
|
該行動將會在 { $items } { $items ->
|
||||||
|
[one] 項目
|
||||||
|
*[other] 項目
|
||||||
|
} 上執行。
|
||||||
|
run = 執行
|
||||||
|
rename-confirm = 重新命名
|
||||||
|
checksum = { $kind } 核對和
|
||||||
|
calculate = 計算
|
||||||
|
error = 錯誤
|
||||||
|
sidebar-label = 側邊欄標籤
|
||||||
|
search-application = 依應用程式名稱搜尋
|
||||||
|
change-sidebar-label = 變更側邊欄標籤
|
||||||
|
|
|
||||||
19
justfile
19
justfile
|
|
@ -12,6 +12,10 @@ cargo-target-dir := env('CARGO_TARGET_DIR', 'target')
|
||||||
bin-src := cargo-target-dir / 'release' / name
|
bin-src := cargo-target-dir / 'release' / name
|
||||||
bin-dst := base-dir / 'bin' / name
|
bin-dst := base-dir / 'bin' / name
|
||||||
|
|
||||||
|
thumbnailer-bin-name := name + '-thumbnailer'
|
||||||
|
thumbnailer-bin-src := cargo-target-dir / 'release' / thumbnailer-bin-name
|
||||||
|
thumbnailer-bin-dst := base-dir / 'bin' / thumbnailer-bin-name
|
||||||
|
|
||||||
applet-name := name + '-applet'
|
applet-name := name + '-applet'
|
||||||
applet-src := cargo-target-dir / 'release' / applet-name
|
applet-src := cargo-target-dir / 'release' / applet-name
|
||||||
applet-dst := base-dir / 'bin' / applet-name
|
applet-dst := base-dir / 'bin' / applet-name
|
||||||
|
|
@ -24,6 +28,10 @@ metainfo := APPID + '.metainfo.xml'
|
||||||
metainfo-src := 'target/xdgen' / metainfo
|
metainfo-src := 'target/xdgen' / metainfo
|
||||||
metainfo-dst := clean(rootdir / prefix) / 'share' / 'metainfo' / metainfo
|
metainfo-dst := clean(rootdir / prefix) / 'share' / 'metainfo' / metainfo
|
||||||
|
|
||||||
|
thumbnailer := APPID + '.thumbnailer'
|
||||||
|
thumbnailer-src := 'res' / thumbnailer
|
||||||
|
thumbnailer-dst := clean(rootdir / prefix) / 'share' / 'thumbnailers' / thumbnailer
|
||||||
|
|
||||||
icons-src := 'res' / 'icons' / 'hicolor'
|
icons-src := 'res' / 'icons' / 'hicolor'
|
||||||
icons-dst := clean(rootdir / prefix) / 'share' / 'icons' / 'hicolor'
|
icons-dst := clean(rootdir / prefix) / 'share' / 'icons' / 'hicolor'
|
||||||
|
|
||||||
|
|
@ -45,13 +53,10 @@ clean-dist: clean clean-vendor
|
||||||
build-debug *args:
|
build-debug *args:
|
||||||
cargo build {{args}}
|
cargo build {{args}}
|
||||||
cargo build --package {{applet-name}} {{args}}
|
cargo build --package {{applet-name}} {{args}}
|
||||||
|
cargo build --package {{thumbnailer-bin-name}} {{args}}
|
||||||
|
|
||||||
# Compiles with release profile
|
# Compiles with release profile
|
||||||
build-release *args: (build-debug '--release' args)
|
build-release *args: (build-debug '--release' args)
|
||||||
|
|
||||||
# Compiles applet with release profile
|
|
||||||
build-release-applet *args:
|
|
||||||
cargo build --package {{applet-name}} --release {{args}}
|
|
||||||
|
|
||||||
# Compiles release profile with vendored dependencies
|
# Compiles release profile with vendored dependencies
|
||||||
build-vendored *args: vendor-extract (build-release '--frozen --offline' args)
|
build-vendored *args: vendor-extract (build-release '--frozen --offline' args)
|
||||||
|
|
@ -92,9 +97,11 @@ heaptrack *args:
|
||||||
# Installs files
|
# Installs files
|
||||||
install:
|
install:
|
||||||
install -Dm0755 {{bin-src}} {{bin-dst}}
|
install -Dm0755 {{bin-src}} {{bin-dst}}
|
||||||
|
install -Dm0755 {{thumbnailer-bin-src}} {{thumbnailer-bin-dst}}
|
||||||
install -Dm0755 {{applet-src}} {{applet-dst}}
|
install -Dm0755 {{applet-src}} {{applet-dst}}
|
||||||
install -Dm0644 {{desktop-src}} {{desktop-dst}}
|
install -Dm0644 {{desktop-src}} {{desktop-dst}}
|
||||||
install -Dm0644 {{metainfo-src}} {{metainfo-dst}}
|
install -Dm0644 {{metainfo-src}} {{metainfo-dst}}
|
||||||
|
install -Dm0644 {{thumbnailer-src}} {{thumbnailer-dst}}
|
||||||
for size in `ls {{icons-src}}`; do \
|
for size in `ls {{icons-src}}`; do \
|
||||||
install -Dm0644 "{{icons-src}}/$size/apps/{{APPID}}.svg" "{{icons-dst}}/$size/apps/{{APPID}}.svg"; \
|
install -Dm0644 "{{icons-src}}/$size/apps/{{APPID}}.svg" "{{icons-dst}}/$size/apps/{{APPID}}.svg"; \
|
||||||
done
|
done
|
||||||
|
|
@ -105,13 +112,13 @@ install-applet:
|
||||||
|
|
||||||
# Uninstalls installed files
|
# Uninstalls installed files
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -f {{bin-dst}} {{applet-dst}}
|
rm -f {{bin-dst}} {{thumbnailer-bin-dst}} {{applet-dst}} {{thumbnailer-dst}}
|
||||||
|
|
||||||
# Vendor dependencies locally
|
# Vendor dependencies locally
|
||||||
vendor:
|
vendor:
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
mkdir -p .cargo
|
mkdir -p .cargo
|
||||||
cargo vendor --sync Cargo.toml | head -n -1 > .cargo/config.toml
|
cargo vendor --locked --sync Cargo.toml | head -n -1 > .cargo/config.toml
|
||||||
echo 'directory = "vendor"' >> .cargo/config.toml
|
echo 'directory = "vendor"' >> .cargo/config.toml
|
||||||
echo >> .cargo/config.toml
|
echo >> .cargo/config.toml
|
||||||
echo '[env]' >> .cargo/config.toml
|
echo '[env]' >> .cargo/config.toml
|
||||||
|
|
|
||||||
4
res/com.system76.CosmicFiles.thumbnailer
Normal file
4
res/com.system76.CosmicFiles.thumbnailer
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
[Thumbnailer Entry]
|
||||||
|
TryExec=cosmic-files-thumbnailer
|
||||||
|
Exec=cosmic-files-thumbnailer %o --size %s %i
|
||||||
|
MimeType=application/vnd.microsoft.portable-executable;application/x-msdownload;
|
||||||
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "1.93.0"
|
||||||
|
components = ["clippy", "rustfmt"]
|
||||||
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
imports_granularity = "Module"
|
||||||
2090
src/app.rs
2090
src/app.rs
File diff suppressed because it is too large
Load diff
|
|
@ -1,17 +1,14 @@
|
||||||
use crate::{
|
use crate::mime_icon::mime_for_path;
|
||||||
mime_icon::mime_for_path,
|
use crate::operation::{Controller, OpReader, OperationError, OperationErrorType};
|
||||||
operation::{Controller, OpReader, OperationError, OperationErrorType, sync_to_disk},
|
|
||||||
};
|
|
||||||
use chrono::TimeZone;
|
|
||||||
use chrono::{Datelike, Timelike};
|
|
||||||
use cosmic::iced::futures;
|
use cosmic::iced::futures;
|
||||||
use std::{
|
use jiff::Zoned;
|
||||||
collections::HashSet,
|
use jiff::civil::DateTime;
|
||||||
fs,
|
use jiff::tz::TimeZone;
|
||||||
io::{self, Read, Write},
|
use std::collections::HashSet;
|
||||||
path::{Path, PathBuf},
|
use std::fs;
|
||||||
time::SystemTime,
|
use std::io::{self, Read, Write};
|
||||||
};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::time::SystemTime;
|
||||||
use zip::result::ZipError;
|
use zip::result::ZipError;
|
||||||
|
|
||||||
pub const SUPPORTED_ARCHIVE_TYPES: &[&str] = &[
|
pub const SUPPORTED_ARCHIVE_TYPES: &[&str] = &[
|
||||||
|
|
@ -48,7 +45,7 @@ pub fn extract(
|
||||||
new_dir: &Path,
|
new_dir: &Path,
|
||||||
password: &Option<String>,
|
password: &Option<String>,
|
||||||
controller: &Controller,
|
controller: &Controller,
|
||||||
) -> Result<(), OperationError> {
|
) -> Result<(Vec<PathBuf>, HashSet<PathBuf>), OperationError> {
|
||||||
let mime = mime_for_path(path, None, false);
|
let mime = mime_for_path(path, None, false);
|
||||||
let password = password.as_deref();
|
let password = password.as_deref();
|
||||||
match mime.essence_str() {
|
match mime.essence_str() {
|
||||||
|
|
@ -58,13 +55,15 @@ pub fn extract(
|
||||||
.map(flate2::read::GzDecoder::new)
|
.map(flate2::read::GzDecoder::new)
|
||||||
.map(tar::Archive::new)
|
.map(tar::Archive::new)
|
||||||
.and_then(|mut archive| archive.unpack(new_dir))
|
.and_then(|mut archive| archive.unpack(new_dir))
|
||||||
.map_err(|e| OperationError::from_err(e, controller))?;
|
.map_err(|e| OperationError::from_err(e, controller))
|
||||||
|
.map(|_| Default::default())
|
||||||
}
|
}
|
||||||
"application/x-tar" => OpReader::new(path, controller.clone())
|
"application/x-tar" => OpReader::new(path, controller.clone())
|
||||||
.map(io::BufReader::new)
|
.map(io::BufReader::new)
|
||||||
.map(tar::Archive::new)
|
.map(tar::Archive::new)
|
||||||
.and_then(|mut archive| archive.unpack(new_dir))
|
.and_then(|mut archive| archive.unpack(new_dir))
|
||||||
.map_err(|e| OperationError::from_err(e, controller))?,
|
.map_err(|e| OperationError::from_err(e, controller))
|
||||||
|
.map(|_| Default::default()),
|
||||||
"application/zip" => fs::File::open(path)
|
"application/zip" => fs::File::open(path)
|
||||||
.map(io::BufReader::new)
|
.map(io::BufReader::new)
|
||||||
.map(zip::ZipArchive::new)
|
.map(zip::ZipArchive::new)
|
||||||
|
|
@ -78,7 +77,7 @@ pub fn extract(
|
||||||
OperationError::from_kind(OperationErrorType::PasswordRequired, controller)
|
OperationError::from_kind(OperationErrorType::PasswordRequired, controller)
|
||||||
}
|
}
|
||||||
_ => OperationError::from_err(e, controller),
|
_ => OperationError::from_err(e, controller),
|
||||||
})?,
|
}),
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
"application/x-bzip"
|
"application/x-bzip"
|
||||||
| "application/x-bzip-compressed-tar"
|
| "application/x-bzip-compressed-tar"
|
||||||
|
|
@ -88,7 +87,8 @@ pub fn extract(
|
||||||
.map(bzip2::read::BzDecoder::new)
|
.map(bzip2::read::BzDecoder::new)
|
||||||
.map(tar::Archive::new)
|
.map(tar::Archive::new)
|
||||||
.and_then(|mut archive| archive.unpack(new_dir))
|
.and_then(|mut archive| archive.unpack(new_dir))
|
||||||
.map_err(|e| OperationError::from_err(e, controller))?,
|
.map_err(|e| OperationError::from_err(e, controller))
|
||||||
|
.map(|_| Default::default()),
|
||||||
#[cfg(feature = "lzma-rust2")]
|
#[cfg(feature = "lzma-rust2")]
|
||||||
"application/x-xz" | "application/x-xz-compressed-tar" => {
|
"application/x-xz" | "application/x-xz-compressed-tar" => {
|
||||||
OpReader::new(path, controller.clone())
|
OpReader::new(path, controller.clone())
|
||||||
|
|
@ -96,14 +96,14 @@ pub fn extract(
|
||||||
.map(|reader| lzma_rust2::XzReader::new(reader, true))
|
.map(|reader| lzma_rust2::XzReader::new(reader, true))
|
||||||
.map(tar::Archive::new)
|
.map(tar::Archive::new)
|
||||||
.and_then(|mut archive| archive.unpack(new_dir))
|
.and_then(|mut archive| archive.unpack(new_dir))
|
||||||
.map_err(|e| OperationError::from_err(e, controller))?;
|
.map_err(|e| OperationError::from_err(e, controller))
|
||||||
|
.map(|_| Default::default())
|
||||||
}
|
}
|
||||||
_ => Err(OperationError::from_err(
|
_ => Err(OperationError::from_err(
|
||||||
format!("unsupported mime type {mime:?}"),
|
format!("unsupported mime type {mime:?}"),
|
||||||
controller,
|
controller,
|
||||||
))?,
|
)),
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// From https://docs.rs/zip/latest/zip/read/struct.ZipArchive.html#method.extract, with cancellation and progress added
|
// From https://docs.rs/zip/latest/zip/read/struct.ZipArchive.html#method.extract, with cancellation and progress added
|
||||||
|
|
@ -112,8 +112,9 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
|
||||||
directory: P,
|
directory: P,
|
||||||
password: Option<&str>,
|
password: Option<&str>,
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
) -> zip::result::ZipResult<()> {
|
) -> zip::result::ZipResult<(Vec<PathBuf>, HashSet<PathBuf>)> {
|
||||||
use std::{ffi::OsString, fs};
|
use std::ffi::OsString;
|
||||||
|
use std::fs;
|
||||||
use zip::result::ZipError;
|
use zip::result::ZipError;
|
||||||
|
|
||||||
fn make_writable_dir_all<T: AsRef<Path>>(
|
fn make_writable_dir_all<T: AsRef<Path>>(
|
||||||
|
|
@ -190,6 +191,8 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
|
||||||
if file.is_symlink() && (cfg!(unix) || cfg!(windows)) {
|
if file.is_symlink() && (cfg!(unix) || cfg!(windows)) {
|
||||||
let mut target = Vec::with_capacity(file.size() as usize);
|
let mut target = Vec::with_capacity(file.size() as usize);
|
||||||
file.read_to_end(&mut target)?;
|
file.read_to_end(&mut target)?;
|
||||||
|
// File no longer needed, drop to allow reading target on windows
|
||||||
|
drop(file);
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
|
|
@ -200,11 +203,15 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
let Ok(target) = String::from_utf8(target) else {
|
let Ok(target) = String::from_utf8(target) else {
|
||||||
return Err(ZipError::InvalidArchive("Invalid UTF-8 as symlink target"));
|
return Err(ZipError::InvalidArchive(
|
||||||
|
"Invalid UTF-8 as symlink target".into(),
|
||||||
|
));
|
||||||
};
|
};
|
||||||
let target = target.into_boxed_str();
|
let target_is_dir_from_archive = match password {
|
||||||
let target_is_dir_from_archive =
|
None => archive.by_name(&target),
|
||||||
archive.shared.files.contains_key(&target) && is_dir(&target);
|
Some(pwd) => archive.by_name_decrypt(&target, pwd.as_bytes()),
|
||||||
|
}
|
||||||
|
.map_or(false, |x| x.is_dir());
|
||||||
let target_path = directory.as_ref().join(OsString::from(target.to_string()));
|
let target_path = directory.as_ref().join(OsString::from(target.to_string()));
|
||||||
let target_is_dir = if target_is_dir_from_archive {
|
let target_is_dir = if target_is_dir_from_archive {
|
||||||
true
|
true
|
||||||
|
|
@ -278,32 +285,29 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush files to disk
|
Ok((written_files, target_dirs))
|
||||||
futures::executor::block_on(async { sync_to_disk(written_files, target_dirs).await });
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zip_date_time_to_system_time(date_time: zip::DateTime) -> Option<SystemTime> {
|
fn zip_date_time_to_system_time(date_time: zip::DateTime) -> Option<SystemTime> {
|
||||||
let date = chrono::NaiveDate::from_ymd_opt(
|
let dt = DateTime::new(
|
||||||
date_time.year() as i32,
|
date_time.year() as i16,
|
||||||
date_time.month() as u32,
|
date_time.month() as i8,
|
||||||
date_time.day() as u32,
|
date_time.day() as i8,
|
||||||
)?;
|
date_time.hour() as i8,
|
||||||
let time = chrono::NaiveTime::from_hms_opt(
|
date_time.minute() as i8,
|
||||||
date_time.hour() as u32,
|
date_time.second() as i8,
|
||||||
date_time.minute() as u32,
|
0,
|
||||||
date_time.second() as u32,
|
)
|
||||||
)?;
|
.ok()?;
|
||||||
let naive = chrono::NaiveDateTime::new(date, time);
|
TimeZone::system()
|
||||||
chrono::Local
|
.to_ambiguous_zoned(dt)
|
||||||
.from_local_datetime(&naive)
|
.later()
|
||||||
.latest()
|
.ok()
|
||||||
.map(SystemTime::from)
|
.map(SystemTime::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn system_time_to_zip_date_time(system_time: SystemTime) -> Option<zip::DateTime> {
|
pub fn system_time_to_zip_date_time(system_time: SystemTime) -> Option<zip::DateTime> {
|
||||||
let date_time: chrono::DateTime<chrono::Local> = system_time.into();
|
let date_time = Zoned::try_from(system_time).ok()?;
|
||||||
|
|
||||||
zip::DateTime::from_date_and_time(
|
zip::DateTime::from_date_and_time(
|
||||||
date_time.year() as u16,
|
date_time.year() as u16,
|
||||||
|
|
|
||||||
73
src/channel.rs
Normal file
73
src/channel.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright 2025 System76 <info@system76.com>
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
/// Create a channel backed by `tokio::sync::Notify` and a sync mutex with a vec deque.
|
||||||
|
pub fn channel<Message>() -> (Sender<Message>, Receiver<Message>) {
|
||||||
|
let channel = Arc::new(Channel {
|
||||||
|
queue: Mutex::new(VecDeque::default()),
|
||||||
|
notify: tokio::sync::Notify::const_new(),
|
||||||
|
closed: AtomicBool::new(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
(Sender(channel.clone()), Receiver(channel))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A channel backed by `tokio::sync::Notify` and a sync mutex with a vec deque.
|
||||||
|
struct Channel<Message> {
|
||||||
|
pub(self) queue: Mutex<VecDeque<Message>>,
|
||||||
|
/// Set when a new message has been stored.
|
||||||
|
pub(self) notify: tokio::sync::Notify,
|
||||||
|
/// Set when the receiver is dropped.
|
||||||
|
pub(self) closed: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Sender<Message>(Arc<Channel<Message>>);
|
||||||
|
|
||||||
|
impl<Message> Sender<Message> {
|
||||||
|
pub fn send(&self, message: Message) {
|
||||||
|
self.0.queue.lock().unwrap().push_back(message);
|
||||||
|
self.0.notify.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message> Drop for Sender<Message> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.0.closed.store(true, Ordering::SeqCst);
|
||||||
|
self.0.notify.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Receiver<Message>(Arc<Channel<Message>>);
|
||||||
|
|
||||||
|
impl<Message> Receiver<Message> {
|
||||||
|
/// Returns a value until the sender is dropped.
|
||||||
|
pub async fn recv(&self) -> Option<Message> {
|
||||||
|
loop {
|
||||||
|
{
|
||||||
|
let mut queue = self.0.queue.lock().unwrap();
|
||||||
|
if let Some(value) = queue.pop_front() {
|
||||||
|
if queue.capacity() - queue.len() > 32 {
|
||||||
|
let capacity = queue.len().next_power_of_two();
|
||||||
|
queue.shrink_to(capacity);
|
||||||
|
}
|
||||||
|
drop(queue);
|
||||||
|
return Some(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.0.closed.load(Ordering::SeqCst) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.0.notify.notified().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_recv(&self) -> Option<Message> {
|
||||||
|
self.0.queue.lock().unwrap().pop_front()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,12 +2,10 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
|
use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
|
||||||
use std::{
|
use std::borrow::Cow;
|
||||||
borrow::Cow,
|
use std::error::Error;
|
||||||
error::Error,
|
use std::path::{Path, PathBuf};
|
||||||
path::{Path, PathBuf},
|
use std::str;
|
||||||
str,
|
|
||||||
};
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
@ -128,10 +126,15 @@ impl TryFrom<(Vec<u8>, String)> for ClipboardPaste {
|
||||||
// Assume the kind is Copy if not provided by the mime type
|
// Assume the kind is Copy if not provided by the mime type
|
||||||
let mut kind = ClipboardKind::Copy;
|
let mut kind = ClipboardKind::Copy;
|
||||||
let mut paths = Vec::new();
|
let mut paths = Vec::new();
|
||||||
|
|
||||||
match mime.as_str() {
|
match mime.as_str() {
|
||||||
"text/uri-list" => {
|
"text/uri-list" => {
|
||||||
let text = str::from_utf8(&data)?;
|
let text = str::from_utf8(&data)?;
|
||||||
for line in text.lines() {
|
|
||||||
|
for line in text.lines().filter(|line| {
|
||||||
|
let line = line.trim();
|
||||||
|
!line.is_empty() && !line.starts_with('#')
|
||||||
|
}) {
|
||||||
let url = Url::parse(line)?;
|
let url = Url::parse(line)?;
|
||||||
match url.to_file_path() {
|
match url.to_file_path() {
|
||||||
Ok(path) => paths.push(path),
|
Ok(path) => paths.push(path),
|
||||||
|
|
@ -141,6 +144,7 @@ impl TryFrom<(Vec<u8>, String)> for ClipboardPaste {
|
||||||
}
|
}
|
||||||
"x-special/gnome-copied-files" => {
|
"x-special/gnome-copied-files" => {
|
||||||
let text = str::from_utf8(&data)?;
|
let text = str::from_utf8(&data)?;
|
||||||
|
|
||||||
for (i, line) in text.lines().enumerate() {
|
for (i, line) in text.lines().enumerate() {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
kind = match line {
|
kind = match line {
|
||||||
|
|
|
||||||
219
src/config.rs
219
src/config.rs
|
|
@ -1,20 +1,20 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use std::{any::TypeId, num::NonZeroU16, path::PathBuf};
|
use std::any::TypeId;
|
||||||
|
use std::num::NonZeroU16;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::cosmic_config::cosmic_config_derive::CosmicConfigEntry;
|
||||||
Application,
|
use cosmic::cosmic_config::{self, CosmicConfigEntry};
|
||||||
cosmic_config::{self, CosmicConfigEntry, cosmic_config_derive::CosmicConfigEntry},
|
use cosmic::iced::Subscription;
|
||||||
iced::Subscription,
|
use cosmic::{Application, theme};
|
||||||
theme,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::FxOrderMap;
|
||||||
FxOrderMap,
|
use crate::app::App;
|
||||||
app::App,
|
use crate::tab::{HeadingOptions, Location, View};
|
||||||
tab::{HeadingOptions, Location, View},
|
|
||||||
};
|
pub use crate::context_action::{ContextActionPreset, ContextActionSelection};
|
||||||
|
|
||||||
pub const CONFIG_VERSION: u64 = 1;
|
pub const CONFIG_VERSION: u64 = 1;
|
||||||
|
|
||||||
|
|
@ -70,6 +70,11 @@ pub enum Favorite {
|
||||||
name: String,
|
name: String,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
},
|
},
|
||||||
|
/// A path with a custom name chosen by the user
|
||||||
|
Named {
|
||||||
|
path: PathBuf,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Favorite {
|
impl Favorite {
|
||||||
|
|
@ -98,6 +103,38 @@ impl Favorite {
|
||||||
Self::Videos => dirs::video_dir(),
|
Self::Videos => dirs::video_dir(),
|
||||||
Self::Path(path) => Some(path.clone()),
|
Self::Path(path) => Some(path.clone()),
|
||||||
Self::Network { path, .. } => Some(path.clone()),
|
Self::Network { path, .. } => Some(path.clone()),
|
||||||
|
Self::Named { path, .. } => Some(path.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Name shown in the sidebar, or `None` if the path has no usable file name
|
||||||
|
pub fn display_name(&self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
Self::Home => Some(crate::fl!("home")),
|
||||||
|
Self::Named { name, .. } | Self::Network { name, .. } => Some(name.clone()),
|
||||||
|
_ => self
|
||||||
|
.path_opt()?
|
||||||
|
.file_name()
|
||||||
|
.and_then(|x| x.to_str())
|
||||||
|
.map(ToString::to_string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return this favorite with a custom sidebar label chosen by the user
|
||||||
|
pub fn with_label(&self, name: &str) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Network { uri, path, .. } => Self::Network {
|
||||||
|
uri: uri.clone(),
|
||||||
|
name: name.to_string(),
|
||||||
|
path: path.clone(),
|
||||||
|
},
|
||||||
|
other => match other.path_opt() {
|
||||||
|
Some(path) => Self::Named {
|
||||||
|
path,
|
||||||
|
name: name.to_string(),
|
||||||
|
},
|
||||||
|
None => other.clone(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -113,6 +150,7 @@ pub enum TypeToSearch {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub sort_names: FxOrderMap<String, (HeadingOptions, bool)>,
|
pub sort_names: FxOrderMap<String, (HeadingOptions, bool)>,
|
||||||
|
pub dialog_sort_name: (HeadingOptions, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for State {
|
impl Default for State {
|
||||||
|
|
@ -124,6 +162,7 @@ impl Default for State {
|
||||||
(HeadingOptions::Modified, false),
|
(HeadingOptions::Modified, false),
|
||||||
)
|
)
|
||||||
})),
|
})),
|
||||||
|
dialog_sort_name: (HeadingOptions::Modified, false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -164,11 +203,17 @@ pub struct Config {
|
||||||
pub app_theme: AppTheme,
|
pub app_theme: AppTheme,
|
||||||
pub dialog: DialogConfig,
|
pub dialog: DialogConfig,
|
||||||
pub desktop: DesktopConfig,
|
pub desktop: DesktopConfig,
|
||||||
|
pub context_actions: Vec<ContextActionPreset>,
|
||||||
pub thumb_cfg: ThumbCfg,
|
pub thumb_cfg: ThumbCfg,
|
||||||
pub favorites: Vec<Favorite>,
|
pub favorites: Vec<Favorite>,
|
||||||
pub show_details: bool,
|
pub show_details: bool,
|
||||||
pub show_recents: bool,
|
pub show_recents: bool,
|
||||||
pub tab: TabConfig,
|
pub tab: TabConfig,
|
||||||
|
/// Yoda phase 3: Dolphin-style quick actions toolbar. An ordered list
|
||||||
|
/// of enabled buttons — position in the vec drives the toolbar order.
|
||||||
|
/// Reorder in Settings via drag-drop; items not in the vec are
|
||||||
|
/// hidden. Default = the minimal-6 set from phase 1.
|
||||||
|
pub toolbar: Vec<ToolbarAction>,
|
||||||
pub type_to_search: TypeToSearch,
|
pub type_to_search: TypeToSearch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,6 +265,7 @@ impl Default for Config {
|
||||||
app_theme: AppTheme::System,
|
app_theme: AppTheme::System,
|
||||||
desktop: DesktopConfig::default(),
|
desktop: DesktopConfig::default(),
|
||||||
dialog: DialogConfig::default(),
|
dialog: DialogConfig::default(),
|
||||||
|
context_actions: Vec::new(),
|
||||||
thumb_cfg: ThumbCfg::default(),
|
thumb_cfg: ThumbCfg::default(),
|
||||||
favorites: vec![
|
favorites: vec![
|
||||||
Favorite::Home,
|
Favorite::Home,
|
||||||
|
|
@ -232,11 +278,97 @@ impl Default for Config {
|
||||||
show_details: false,
|
show_details: false,
|
||||||
show_recents: true,
|
show_recents: true,
|
||||||
tab: TabConfig::default(),
|
tab: TabConfig::default(),
|
||||||
|
toolbar: default_toolbar(),
|
||||||
type_to_search: TypeToSearch::Recursive,
|
type_to_search: TypeToSearch::Recursive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Yoda phase 3: ordered enum of quick-action toolbar buttons.
|
||||||
|
/// The Config stores `Vec<ToolbarAction>` so the user can pick BOTH
|
||||||
|
/// visibility (just include/exclude the variant) AND order (position in
|
||||||
|
/// the vec). Drag-drop reorder in the Settings page moves items around.
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
pub enum ToolbarAction {
|
||||||
|
LocationUp,
|
||||||
|
Reload,
|
||||||
|
NewFolder,
|
||||||
|
NewFile,
|
||||||
|
Rename,
|
||||||
|
Delete,
|
||||||
|
Cut,
|
||||||
|
Copy,
|
||||||
|
Paste,
|
||||||
|
ToggleShowHidden,
|
||||||
|
OpenTerminal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToolbarAction {
|
||||||
|
/// Stable list of every supported action. Ordered roughly by logical
|
||||||
|
/// grouping (location → create/edit → clipboard → view/misc) so that
|
||||||
|
/// the default enabled set follows a sensible shape and the Settings
|
||||||
|
/// row for a not-yet-enabled action lands in a predictable spot.
|
||||||
|
pub const ALL: &'static [Self] = &[
|
||||||
|
Self::LocationUp,
|
||||||
|
Self::Reload,
|
||||||
|
Self::NewFolder,
|
||||||
|
Self::NewFile,
|
||||||
|
Self::Rename,
|
||||||
|
Self::Delete,
|
||||||
|
Self::Cut,
|
||||||
|
Self::Copy,
|
||||||
|
Self::Paste,
|
||||||
|
Self::ToggleShowHidden,
|
||||||
|
Self::OpenTerminal,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// u8 discriminant used to carry the action over a DnD mime payload.
|
||||||
|
pub const fn to_u8(self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Self::LocationUp => 0,
|
||||||
|
Self::Reload => 1,
|
||||||
|
Self::NewFolder => 2,
|
||||||
|
Self::NewFile => 3,
|
||||||
|
Self::Rename => 4,
|
||||||
|
Self::Delete => 5,
|
||||||
|
Self::Cut => 6,
|
||||||
|
Self::Copy => 7,
|
||||||
|
Self::Paste => 8,
|
||||||
|
Self::ToggleShowHidden => 9,
|
||||||
|
Self::OpenTerminal => 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn from_u8(v: u8) -> Option<Self> {
|
||||||
|
match v {
|
||||||
|
0 => Some(Self::LocationUp),
|
||||||
|
1 => Some(Self::Reload),
|
||||||
|
2 => Some(Self::NewFolder),
|
||||||
|
3 => Some(Self::NewFile),
|
||||||
|
4 => Some(Self::Rename),
|
||||||
|
5 => Some(Self::Delete),
|
||||||
|
6 => Some(Self::Cut),
|
||||||
|
7 => Some(Self::Copy),
|
||||||
|
8 => Some(Self::Paste),
|
||||||
|
9 => Some(Self::ToggleShowHidden),
|
||||||
|
10 => Some(Self::OpenTerminal),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default set shown on a fresh install — same "minimal 6" as phase 1/2.
|
||||||
|
pub fn default_toolbar() -> Vec<ToolbarAction> {
|
||||||
|
vec![
|
||||||
|
ToolbarAction::NewFolder,
|
||||||
|
ToolbarAction::Rename,
|
||||||
|
ToolbarAction::Delete,
|
||||||
|
ToolbarAction::Cut,
|
||||||
|
ToolbarAction::Copy,
|
||||||
|
ToolbarAction::Paste,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, CosmicConfigEntry, Deserialize, Serialize)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, CosmicConfigEntry, Deserialize, Serialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct DesktopConfig {
|
pub struct DesktopConfig {
|
||||||
|
|
@ -383,3 +515,66 @@ pub const TIME_CONFIG_ID: &str = "com.system76.CosmicAppletTime";
|
||||||
pub struct TimeConfig {
|
pub struct TimeConfig {
|
||||||
pub military_time: bool,
|
pub military_time: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn favorite_with_label_converts_path_to_named() {
|
||||||
|
let favorite = Favorite::Path(PathBuf::from("/some/dir"));
|
||||||
|
assert_eq!(
|
||||||
|
favorite.with_label("Custom"),
|
||||||
|
Favorite::Named {
|
||||||
|
path: PathBuf::from("/some/dir"),
|
||||||
|
name: "Custom".to_string(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn favorite_with_label_updates_network_in_place() {
|
||||||
|
let favorite = Favorite::Network {
|
||||||
|
uri: "sftp://example.com/".to_string(),
|
||||||
|
name: "example.com".to_string(),
|
||||||
|
path: PathBuf::from("/run/mount/example"),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
favorite.with_label("Custom"),
|
||||||
|
Favorite::Network {
|
||||||
|
uri: "sftp://example.com/".to_string(),
|
||||||
|
name: "Custom".to_string(),
|
||||||
|
path: PathBuf::from("/run/mount/example"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn favorite_with_label_converts_special_folder_to_named() {
|
||||||
|
let home = dirs::home_dir().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
Favorite::Home.with_label("Custom"),
|
||||||
|
Favorite::Named {
|
||||||
|
path: home,
|
||||||
|
name: "Custom".to_string(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn favorite_display_name() {
|
||||||
|
assert_eq!(
|
||||||
|
Favorite::Path(PathBuf::from("/some/dir")).display_name(),
|
||||||
|
Some("dir".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(Favorite::Path(PathBuf::from("/")).display_name(), None);
|
||||||
|
assert_eq!(
|
||||||
|
Favorite::Named {
|
||||||
|
path: PathBuf::from("/some/dir"),
|
||||||
|
name: "Custom".to_string(),
|
||||||
|
}
|
||||||
|
.display_name(),
|
||||||
|
Some("Custom".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
80
src/context_action.rs
Normal file
80
src/context_action.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::mime_app;
|
||||||
|
use crate::spawn_detached::spawn_detached;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
|
pub enum ContextActionSelection {
|
||||||
|
#[default]
|
||||||
|
#[serde(alias = "any")]
|
||||||
|
Any,
|
||||||
|
#[serde(alias = "files")]
|
||||||
|
Files,
|
||||||
|
#[serde(alias = "folders")]
|
||||||
|
Folders,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct ContextActionPreset {
|
||||||
|
pub name: String,
|
||||||
|
pub confirm: bool,
|
||||||
|
pub selection: ContextActionSelection,
|
||||||
|
pub steps: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextActionPreset {
|
||||||
|
pub fn matches_selection(&self, selected: usize, selected_dir: usize) -> bool {
|
||||||
|
if selected == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.selection {
|
||||||
|
ContextActionSelection::Any => true,
|
||||||
|
ContextActionSelection::Files => selected_dir == 0,
|
||||||
|
ContextActionSelection::Folders => selected_dir == selected,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&self, paths: &[PathBuf]) {
|
||||||
|
if self.steps.is_empty() {
|
||||||
|
log::warn!("context action {:?} has no steps", self.name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for step in &self.steps {
|
||||||
|
let Some(commands) = mime_app::exec_to_command(step, &self.name, None, paths) else {
|
||||||
|
log::warn!(
|
||||||
|
"failed to parse context action {:?}: invalid Exec {:?}",
|
||||||
|
self.name,
|
||||||
|
step
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for mut command in commands {
|
||||||
|
if let Err(err) = spawn_detached(&mut command) {
|
||||||
|
log::warn!(
|
||||||
|
"failed to run context action {:?} step {:?}: {}",
|
||||||
|
self.name,
|
||||||
|
step,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(actions: &[ContextActionPreset], action: usize, paths: &[PathBuf]) {
|
||||||
|
if let Some(preset) = actions.get(action) {
|
||||||
|
preset.run(paths);
|
||||||
|
} else {
|
||||||
|
log::warn!("invalid context action index `{action}`");
|
||||||
|
}
|
||||||
|
}
|
||||||
289
src/dialog.rs
289
src/dialog.rs
|
|
@ -1,58 +1,47 @@
|
||||||
// Copyright 2023 System76 <info@system76.com>
|
// Copyright 2023 System76 <info@system76.com>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::app::cosmic::Cosmic;
|
||||||
Application, ApplicationExt, Element,
|
use cosmic::app::{Core, Task, context_drawer};
|
||||||
app::{Core, Task, context_drawer, cosmic::Cosmic},
|
use cosmic::cosmic_config::ConfigSet;
|
||||||
cosmic_config, cosmic_theme, executor,
|
use cosmic::iced::core::SmolStr;
|
||||||
iced::{
|
use cosmic::iced::core::widget::operation;
|
||||||
self, Alignment, Event, Length, Size, Subscription,
|
use cosmic::iced::futures::{self, SinkExt};
|
||||||
core::SmolStr,
|
use cosmic::iced::keyboard::key::{Named, Physical};
|
||||||
event,
|
use cosmic::iced::keyboard::{Event as KeyEvent, Key, Modifiers};
|
||||||
futures::{self, SinkExt},
|
use cosmic::iced::platform_specific::shell::{self as iced_winit, SurfaceIdWrapper};
|
||||||
keyboard::{Event as KeyEvent, Key, Modifiers, key::Named},
|
use cosmic::iced::widget::scrollable;
|
||||||
mouse, stream,
|
use cosmic::iced::widget::scrollable::AbsoluteOffset;
|
||||||
widget::scrollable,
|
use cosmic::iced::{
|
||||||
window,
|
self, Alignment, Event, Length, Size, Subscription, event, mouse, stream, window,
|
||||||
},
|
|
||||||
iced_core::widget::operation,
|
|
||||||
iced_widget::scrollable::AbsoluteOffset,
|
|
||||||
iced_winit::{self, SurfaceIdWrapper},
|
|
||||||
theme,
|
|
||||||
widget::{
|
|
||||||
self, Operation,
|
|
||||||
menu::{Action as MenuAction, KeyBind, key_bind::Modifier},
|
|
||||||
segmented_button,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
use cosmic::widget::menu::key_bind::Modifier;
|
||||||
|
use cosmic::widget::menu::{Action as MenuAction, KeyBind};
|
||||||
|
use cosmic::widget::{self, Operation, segmented_button};
|
||||||
|
use cosmic::{Application, ApplicationExt, Element, cosmic_config, cosmic_theme, executor, theme};
|
||||||
use mime_guess::{Mime, mime};
|
use mime_guess::{Mime, mime};
|
||||||
use notify_debouncer_full::{
|
use notify_debouncer_full::notify::{self, RecommendedWatcher};
|
||||||
DebouncedEvent, Debouncer, RecommendedCache, new_debouncer,
|
use notify_debouncer_full::{DebouncedEvent, Debouncer, RecommendedCache, new_debouncer};
|
||||||
notify::{self, RecommendedWatcher},
|
|
||||||
};
|
|
||||||
use recently_used_xbel::update_recently_used;
|
use recently_used_xbel::update_recently_used;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use std::{
|
use std::any::TypeId;
|
||||||
any::TypeId,
|
use std::collections::{HashMap, VecDeque};
|
||||||
collections::{HashMap, VecDeque},
|
use std::path::PathBuf;
|
||||||
env, fmt, fs,
|
use std::time::{self, Instant};
|
||||||
path::PathBuf,
|
use std::{env, fmt, fs};
|
||||||
time::{self, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::app::{
|
||||||
app::{
|
Action, ContextPage, Message as AppMessage, PreviewItem, PreviewKind, REPLACE_BUTTON_ID,
|
||||||
Action, ContextPage, Message as AppMessage, PreviewItem, PreviewKind, REPLACE_BUTTON_ID,
|
|
||||||
},
|
|
||||||
config::{Config, DialogConfig, Favorite, TIME_CONFIG_ID, ThumbCfg, TimeConfig, TypeToSearch},
|
|
||||||
fl, home_dir,
|
|
||||||
key_bind::key_binds,
|
|
||||||
localize::LANGUAGE_SORTER,
|
|
||||||
menu,
|
|
||||||
mounter::{MOUNTERS, MounterItem, MounterItems, MounterKey, MounterMessage},
|
|
||||||
tab::{self, ItemMetadata, Location, SearchLocation, Tab},
|
|
||||||
zoom::{zoom_in_view, zoom_out_view, zoom_to_default},
|
|
||||||
};
|
};
|
||||||
|
use crate::config::{
|
||||||
|
Config, DialogConfig, State, TIME_CONFIG_ID, ThumbCfg, TimeConfig, TypeToSearch,
|
||||||
|
};
|
||||||
|
use crate::key_bind::key_binds;
|
||||||
|
use crate::localize::LANGUAGE_SORTER;
|
||||||
|
use crate::mounter::{MOUNTERS, MounterItem, MounterItems, MounterKey, MounterMessage};
|
||||||
|
use crate::tab::{self, HeadingOptions, ItemMetadata, Location, SearchLocation, Tab};
|
||||||
|
use crate::zoom::{zoom_in_view, zoom_out_view, zoom_to_default};
|
||||||
|
use crate::{fl, home_dir, menu, mime_icon};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DialogMessage(cosmic::Action<Message>);
|
pub struct DialogMessage(cosmic::Action<Message>);
|
||||||
|
|
@ -203,7 +192,7 @@ impl<T: AsRef<str>> From<T> for DialogLabel {
|
||||||
|
|
||||||
impl<'a, M: Clone + 'static> From<&'a DialogLabel> for Element<'a, M> {
|
impl<'a, M: Clone + 'static> From<&'a DialogLabel> for Element<'a, M> {
|
||||||
fn from(label: &'a DialogLabel) -> Self {
|
fn from(label: &'a DialogLabel) -> Self {
|
||||||
let mut iced_spans: Vec<cosmic::iced_core::text::Span<'_, ()>> =
|
let mut iced_spans: Vec<cosmic::iced::core::text::Span<'_, ()>> =
|
||||||
Vec::with_capacity(label.spans.len());
|
Vec::with_capacity(label.spans.len());
|
||||||
for span in &label.spans {
|
for span in &label.spans {
|
||||||
iced_spans.push(cosmic::iced::widget::span(&span.text).underline(span.underline));
|
iced_spans.push(cosmic::iced::widget::span(&span.text).underline(span.underline));
|
||||||
|
|
@ -265,6 +254,7 @@ impl<M: Send + 'static> Dialog<M> {
|
||||||
crate::localize::localize();
|
crate::localize::localize();
|
||||||
|
|
||||||
let (config_handler, config) = Config::load();
|
let (config_handler, config) = Config::load();
|
||||||
|
let (state_handler, state) = State::load();
|
||||||
|
|
||||||
let mut settings = window::Settings {
|
let mut settings = window::Settings {
|
||||||
decorations: false,
|
decorations: false,
|
||||||
|
|
@ -299,6 +289,8 @@ impl<M: Send + 'static> Dialog<M> {
|
||||||
window_id,
|
window_id,
|
||||||
config_handler,
|
config_handler,
|
||||||
config,
|
config,
|
||||||
|
state_handler,
|
||||||
|
state,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (cosmic, cosmic_command) = Cosmic::<App>::init((core, flags));
|
let (cosmic, cosmic_command) = Cosmic::<App>::init((core, flags));
|
||||||
|
|
@ -453,6 +445,8 @@ struct Flags {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
config_handler: Option<cosmic_config::Config>,
|
config_handler: Option<cosmic_config::Config>,
|
||||||
config: Config,
|
config: Config,
|
||||||
|
state_handler: Option<cosmic_config::Config>,
|
||||||
|
state: State,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Messages that are used specifically by our [`App`].
|
/// Messages that are used specifically by our [`App`].
|
||||||
|
|
@ -468,7 +462,7 @@ enum Message {
|
||||||
Escape,
|
Escape,
|
||||||
Filename(String),
|
Filename(String),
|
||||||
Filter(usize),
|
Filter(usize),
|
||||||
Key(Modifiers, Key, Option<SmolStr>),
|
Key(Modifiers, Key, Physical, Option<SmolStr>),
|
||||||
ModifiersChanged(Modifiers),
|
ModifiersChanged(Modifiers),
|
||||||
MounterItems(MounterKey, MounterItems),
|
MounterItems(MounterKey, MounterItems),
|
||||||
Mouse(window::Id, mouse::Button),
|
Mouse(window::Id, mouse::Button),
|
||||||
|
|
@ -482,12 +476,12 @@ enum Message {
|
||||||
SearchActivate,
|
SearchActivate,
|
||||||
SearchClear,
|
SearchClear,
|
||||||
SearchInput(String),
|
SearchInput(String),
|
||||||
Surface(cosmic::surface::Action),
|
Surface(cosmic::surface::Action<Message>),
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
TabMessage(tab::Message),
|
TabMessage(tab::Message),
|
||||||
TabRescan(
|
TabRescan(
|
||||||
Location,
|
Location,
|
||||||
Option<tab::Item>,
|
Option<Box<tab::Item>>,
|
||||||
Vec<tab::Item>,
|
Vec<tab::Item>,
|
||||||
Option<Vec<PathBuf>>,
|
Option<Vec<PathBuf>>,
|
||||||
),
|
),
|
||||||
|
|
@ -515,7 +509,7 @@ impl From<AppMessage> for Message {
|
||||||
AppMessage::ZoomIn(_entity_opt) => Self::ZoomIn,
|
AppMessage::ZoomIn(_entity_opt) => Self::ZoomIn,
|
||||||
AppMessage::ZoomOut(_entity_opt) => Self::ZoomOut,
|
AppMessage::ZoomOut(_entity_opt) => Self::ZoomOut,
|
||||||
AppMessage::NewItem(_entity_opt, true) => Self::NewFolder,
|
AppMessage::NewItem(_entity_opt, true) => Self::NewFolder,
|
||||||
AppMessage::Surface(action) => Self::Surface(action),
|
AppMessage::Surface(action) => Self::Surface(action.map(Self::from)),
|
||||||
unsupported => {
|
unsupported => {
|
||||||
log::warn!("{unsupported:?} not supported in dialog mode");
|
log::warn!("{unsupported:?} not supported in dialog mode");
|
||||||
Self::None
|
Self::None
|
||||||
|
|
@ -555,7 +549,6 @@ struct App {
|
||||||
title: String,
|
title: String,
|
||||||
accept_label: DialogLabel,
|
accept_label: DialogLabel,
|
||||||
choices: Vec<DialogChoice>,
|
choices: Vec<DialogChoice>,
|
||||||
context_menu_window: Option<window::Id>,
|
|
||||||
context_page: ContextPage,
|
context_page: ContextPage,
|
||||||
dialog_pages: VecDeque<DialogPage>,
|
dialog_pages: VecDeque<DialogPage>,
|
||||||
dialog_text_input: widget::Id,
|
dialog_text_input: widget::Id,
|
||||||
|
|
@ -587,7 +580,7 @@ impl App {
|
||||||
space_s,
|
space_s,
|
||||||
space_l,
|
space_l,
|
||||||
..
|
..
|
||||||
} = theme::active().cosmic().spacing;
|
} = theme::spacing();
|
||||||
let is_condensed = self.core().is_condensed();
|
let is_condensed = self.core().is_condensed();
|
||||||
|
|
||||||
let mut col = widget::column::with_capacity(2).spacing(space_xxs);
|
let mut col = widget::column::with_capacity(2).spacing(space_xxs);
|
||||||
|
|
@ -595,6 +588,7 @@ impl App {
|
||||||
col = col.push(
|
col = col.push(
|
||||||
widget::text_input("", filename)
|
widget::text_input("", filename)
|
||||||
.id(self.filename_id.clone())
|
.id(self.filename_id.clone())
|
||||||
|
.double_click_select_delimiter('.')
|
||||||
.on_input(Message::Filename)
|
.on_input(Message::Filename)
|
||||||
.on_submit(|_| Message::Save(false)),
|
.on_submit(|_| Message::Save(false)),
|
||||||
);
|
);
|
||||||
|
|
@ -717,7 +711,7 @@ impl App {
|
||||||
|
|
||||||
match (selected.next(), selected.next()) {
|
match (selected.next(), selected.next()) {
|
||||||
// At least two selected items
|
// At least two selected items
|
||||||
(Some(_), Some(_)) => Some(self.tab.multi_preview_view()),
|
(Some(_), Some(_)) => Some(self.tab.multi_preview_view(None)),
|
||||||
// Exactly one selected item
|
// Exactly one selected item
|
||||||
(Some(item), None) => Some(item.preview_view(None, military_time)),
|
(Some(item), None) => Some(item.preview_view(None, military_time)),
|
||||||
// No selected items
|
// No selected items
|
||||||
|
|
@ -743,11 +737,17 @@ impl App {
|
||||||
fn rescan_tab(&self, selection_paths: Option<Vec<PathBuf>>) -> Task<Message> {
|
fn rescan_tab(&self, selection_paths: Option<Vec<PathBuf>>) -> Task<Message> {
|
||||||
let location = self.tab.location.clone();
|
let location = self.tab.location.clone();
|
||||||
let icon_sizes = self.tab.config.icon_sizes;
|
let icon_sizes = self.tab.config.icon_sizes;
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
let mounter_items = self.mounter_items.clone();
|
let mounter_items = self.mounter_items.clone();
|
||||||
Task::future(async move {
|
Task::future(async move {
|
||||||
let location2 = location.clone();
|
let location2 = location.clone();
|
||||||
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
|
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
|
||||||
Ok((parent_item_opt, mut items)) => {
|
Ok((parent_item_opt, items)) => {
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
|
let mut items = items;
|
||||||
|
#[cfg(not(feature = "gvfs"))]
|
||||||
|
let items = items;
|
||||||
|
|
||||||
#[cfg(feature = "gvfs")]
|
#[cfg(feature = "gvfs")]
|
||||||
{
|
{
|
||||||
let mounter_paths: Box<[_]> = mounter_items
|
let mounter_paths: Box<[_]> = mounter_items
|
||||||
|
|
@ -798,7 +798,7 @@ impl App {
|
||||||
};
|
};
|
||||||
|
|
||||||
search_location.map(|search_location| {
|
search_location.map(|search_location| {
|
||||||
return (
|
(
|
||||||
Location::Search(
|
Location::Search(
|
||||||
search_location,
|
search_location,
|
||||||
term,
|
term,
|
||||||
|
|
@ -806,7 +806,7 @@ impl App {
|
||||||
Instant::now(),
|
Instant::now(),
|
||||||
),
|
),
|
||||||
true,
|
true,
|
||||||
);
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => match &self.tab.location {
|
None => match &self.tab.location {
|
||||||
|
|
@ -836,9 +836,10 @@ impl App {
|
||||||
|
|
||||||
fn update_config(&mut self) -> Task<Message> {
|
fn update_config(&mut self) -> Task<Message> {
|
||||||
self.core.window.show_context = self.flags.config.dialog.show_details;
|
self.core.window.show_context = self.flags.config.dialog.show_details;
|
||||||
self.tab.config = self.flags.config.dialog_tab();
|
let config = self.flags.config.dialog_tab();
|
||||||
|
self.tab.config.view = config.view;
|
||||||
self.update_nav_model();
|
self.update_nav_model();
|
||||||
self.update(Message::TabMessage(tab::Message::Config(self.tab.config)))
|
self.update(Message::TabMessage(tab::Message::Config(config)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_dialog_config<F: Fn(&mut DialogConfig)>(&mut self, f: F) -> Task<Message> {
|
fn with_dialog_config<F: Fn(&mut DialogConfig)>(&mut self, f: F) -> Task<Message> {
|
||||||
|
|
@ -877,31 +878,20 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_context_menus(&mut self) -> Task<Message> {
|
|
||||||
self.tab.location_context_menu_index = None;
|
|
||||||
if self.tab.context_menu.is_some() {
|
|
||||||
return self.update(Message::TabMessage(tab::Message::ContextMenu(None, None)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Task::none()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_nav_model(&mut self) {
|
fn update_nav_model(&mut self) {
|
||||||
let mut nav_model = segmented_button::ModelBuilder::default();
|
let mut nav_model = segmented_button::ModelBuilder::default();
|
||||||
|
|
||||||
nav_model = nav_model.insert(|b| {
|
if self.flags.config.show_recents {
|
||||||
b.text(fl!("recents"))
|
nav_model = nav_model.insert(|b| {
|
||||||
.icon(widget::icon::from_name("document-open-recent-symbolic"))
|
b.text(fl!("recents"))
|
||||||
.data(Location::Recents)
|
.icon(widget::icon::from_name("document-open-recent-symbolic"))
|
||||||
});
|
.data(Location::Recents)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for favorite in &self.flags.config.favorites {
|
for favorite in &self.flags.config.favorites {
|
||||||
if let Some(path) = favorite.path_opt() {
|
if let Some(path) = favorite.path_opt() {
|
||||||
let name = if matches!(favorite, Favorite::Home) {
|
let Some(name) = favorite.display_name() else {
|
||||||
fl!("home")
|
|
||||||
} else if let Some(file_name) = path.file_name().and_then(|x| x.to_str()) {
|
|
||||||
file_name.to_string()
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
nav_model = nav_model.insert(move |b| {
|
nav_model = nav_model.insert(move |b| {
|
||||||
|
|
@ -1051,8 +1041,7 @@ impl Application for App {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
tab.mode = tab::Mode::Dialog(flags.kind.clone());
|
tab.mode = tab::Mode::Dialog(flags.kind.clone());
|
||||||
tab.sort_name = tab::HeadingOptions::Modified;
|
(tab.sort_name, tab.sort_direction) = flags.state.dialog_sort_name;
|
||||||
tab.sort_direction = false;
|
|
||||||
|
|
||||||
let key_binds = key_binds(&tab.mode);
|
let key_binds = key_binds(&tab.mode);
|
||||||
|
|
||||||
|
|
@ -1062,7 +1051,6 @@ impl Application for App {
|
||||||
title,
|
title,
|
||||||
accept_label: DialogLabel::from(accept_label),
|
accept_label: DialogLabel::from(accept_label),
|
||||||
choices: Vec::new(),
|
choices: Vec::new(),
|
||||||
context_menu_window: None,
|
|
||||||
context_page: ContextPage::Preview(None, PreviewKind::Selected),
|
context_page: ContextPage::Preview(None, PreviewKind::Selected),
|
||||||
dialog_pages: VecDeque::new(),
|
dialog_pages: VecDeque::new(),
|
||||||
dialog_text_input: widget::Id::new("Dialog Text Input"),
|
dialog_text_input: widget::Id::new("Dialog Text Input"),
|
||||||
|
|
@ -1122,7 +1110,7 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dialog(&self) -> Option<Element<'_, Message>> {
|
fn dialog(&self) -> Option<Element<'_, Message>> {
|
||||||
let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing;
|
let cosmic_theme::Spacing { space_xxs, .. } = theme::spacing();
|
||||||
|
|
||||||
//TODO: should gallery view just be a dialog?
|
//TODO: should gallery view just be a dialog?
|
||||||
if self.tab.gallery {
|
if self.tab.gallery {
|
||||||
|
|
@ -1206,7 +1194,9 @@ impl Application for App {
|
||||||
.icon(widget::icon::from_name("dialog-question").size(64))
|
.icon(widget::icon::from_name("dialog-question").size(64))
|
||||||
.body(fl!("replace-warning"))
|
.body(fl!("replace-warning"))
|
||||||
.primary_action(
|
.primary_action(
|
||||||
widget::button::suggested(fl!("replace")).on_press(Message::DialogComplete),
|
widget::button::suggested(fl!("replace"))
|
||||||
|
.on_press(Message::DialogComplete)
|
||||||
|
.id(REPLACE_BUTTON_ID.clone()),
|
||||||
)
|
)
|
||||||
.secondary_action(
|
.secondary_action(
|
||||||
widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel),
|
widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel),
|
||||||
|
|
@ -1331,15 +1321,6 @@ impl Application for App {
|
||||||
return Task::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.tab.location_context_menu_index.is_some() {
|
|
||||||
self.tab.location_context_menu_index = None;
|
|
||||||
return Task::none();
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.tab.context_menu.is_some() {
|
|
||||||
return self.update(Message::TabMessage(tab::Message::ContextMenu(None, None)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.tab.edit_location.is_some() {
|
if self.tab.edit_location.is_some() {
|
||||||
// Close location editing if enabled
|
// Close location editing if enabled
|
||||||
self.tab.edit_location = None;
|
self.tab.edit_location = None;
|
||||||
|
|
@ -1398,7 +1379,10 @@ impl Application for App {
|
||||||
Message::Config(config) => {
|
Message::Config(config) => {
|
||||||
if config != self.flags.config {
|
if config != self.flags.config {
|
||||||
log::info!("update config");
|
log::info!("update config");
|
||||||
|
// Don't overwrite military time
|
||||||
|
let military_time = self.flags.config.tab.military_time;
|
||||||
self.flags.config = config;
|
self.flags.config = config;
|
||||||
|
self.flags.config.tab.military_time = military_time;
|
||||||
return self.update_config();
|
return self.update_config();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1451,16 +1435,16 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
return self.rescan_tab(None);
|
return self.rescan_tab(None);
|
||||||
}
|
}
|
||||||
Message::Key(modifiers, key, text) => {
|
Message::Key(modifiers, key, physical_key, text) => {
|
||||||
for (key_bind, action) in &self.key_binds {
|
for (key_bind, action) in &self.key_binds {
|
||||||
if key_bind.matches(modifiers, &key) {
|
if key_bind.matches(modifiers, &key, Some(&physical_key)) {
|
||||||
return self.update(Message::from(action.message()));
|
return self.update(Message::from(action.message()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check key binds from accept label
|
// Check key binds from accept label
|
||||||
if let Some(key_bind) = &self.accept_label.key_bind_opt
|
if let Some(key_bind) = &self.accept_label.key_bind_opt
|
||||||
&& key_bind.matches(modifiers, &key)
|
&& key_bind.matches(modifiers, &key, Some(&physical_key))
|
||||||
{
|
{
|
||||||
return self.update(if self.flags.kind.save() {
|
return self.update(if self.flags.kind.save() {
|
||||||
Message::Save(false)
|
Message::Save(false)
|
||||||
|
|
@ -1571,7 +1555,7 @@ impl Application for App {
|
||||||
Message::Mouse(window_id, _button) => {
|
Message::Mouse(window_id, _button) => {
|
||||||
// Close context menu when clicking outside.
|
// Close context menu when clicking outside.
|
||||||
if self.core.main_window_id() == Some(window_id) {
|
if self.core.main_window_id() == Some(window_id) {
|
||||||
return self.close_context_menus();
|
return Task::none();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::NewFolder => {
|
Message::NewFolder => {
|
||||||
|
|
@ -1731,7 +1715,7 @@ impl Application for App {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
Message::SearchActivate => {
|
Message::SearchActivate => {
|
||||||
let mut tasks = vec![self.close_context_menus()];
|
let mut tasks = vec![];
|
||||||
|
|
||||||
if self.search_get().is_none() {
|
if self.search_get().is_none() {
|
||||||
tasks.push(self.search_set(Some(String::new())));
|
tasks.push(self.search_set(Some(String::new())));
|
||||||
|
|
@ -1742,7 +1726,7 @@ impl Application for App {
|
||||||
return Task::batch(tasks);
|
return Task::batch(tasks);
|
||||||
}
|
}
|
||||||
Message::SearchClear => {
|
Message::SearchClear => {
|
||||||
return Task::batch([self.close_context_menus(), self.search_set(None)]);
|
return self.search_set(None);
|
||||||
}
|
}
|
||||||
Message::SearchInput(input) => {
|
Message::SearchInput(input) => {
|
||||||
return self.search_set(Some(input));
|
return self.search_set(Some(input));
|
||||||
|
|
@ -1778,76 +1762,9 @@ impl Application for App {
|
||||||
self.rescan_tab(selection_paths),
|
self.rescan_tab(selection_paths),
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
tab::Command::ContextMenu(point_opt, parent_id) => {
|
tab::Command::Surface(action) => {
|
||||||
#[cfg(feature = "wayland")]
|
let action = action.map(Message::TabMessage);
|
||||||
match point_opt {
|
commands.push(self.update(Message::Surface(action)));
|
||||||
Some(point) => {
|
|
||||||
if crate::is_wayland() {
|
|
||||||
// Open context menu
|
|
||||||
use cctk::wayland_protocols::xdg::shell::client::xdg_positioner::{
|
|
||||||
Anchor, Gravity,
|
|
||||||
};
|
|
||||||
use cosmic::iced_runtime::platform_specific::wayland::popup::{
|
|
||||||
SctkPopupSettings, SctkPositioner,
|
|
||||||
};
|
|
||||||
use cosmic::iced::Rectangle;
|
|
||||||
let window_id = window::Id::unique();
|
|
||||||
self.context_menu_window = Some(window_id);
|
|
||||||
let autosize_id = widget::Id::unique();
|
|
||||||
commands.push(self.update(Message::Surface(
|
|
||||||
cosmic::surface::action::app_popup(
|
|
||||||
move |app: &mut Self| -> SctkPopupSettings {
|
|
||||||
let anchor_rect = Rectangle {
|
|
||||||
x: point.x as i32,
|
|
||||||
y: point.y as i32,
|
|
||||||
width: 1,
|
|
||||||
height: 1,
|
|
||||||
};
|
|
||||||
let positioner = SctkPositioner {
|
|
||||||
size: None,
|
|
||||||
anchor_rect,
|
|
||||||
anchor: Anchor::None,
|
|
||||||
gravity: Gravity::BottomRight,
|
|
||||||
reactive: true,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
SctkPopupSettings {
|
|
||||||
parent: parent_id
|
|
||||||
.unwrap_or(app.flags.window_id),
|
|
||||||
id: window_id,
|
|
||||||
positioner,
|
|
||||||
parent_size: None,
|
|
||||||
grab: true,
|
|
||||||
close_with_children: false,
|
|
||||||
input_zone: None,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(Box::new(move |app: &Self| {
|
|
||||||
widget::autosize::autosize(
|
|
||||||
menu::context_menu(
|
|
||||||
&app.tab,
|
|
||||||
&app.key_binds,
|
|
||||||
&app.modifiers,
|
|
||||||
false, // Paste not used in dialogs
|
|
||||||
)
|
|
||||||
.map(Message::TabMessage)
|
|
||||||
.map(cosmic::Action::App),
|
|
||||||
autosize_id.clone(),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
if let Some(window_id) = self.context_menu_window.take() {
|
|
||||||
commands.push(self.update(Message::Surface(
|
|
||||||
cosmic::surface::action::destroy_popup(window_id),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tab::Command::Iced(iced_command) => {
|
tab::Command::Iced(iced_command) => {
|
||||||
commands.push(iced_command.0.map(|tab_message| {
|
commands.push(iced_command.0.map(|tab_message| {
|
||||||
|
|
@ -1882,6 +1799,17 @@ impl Application for App {
|
||||||
self.auto_scroll_speed = None;
|
self.auto_scroll_speed = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tab::Command::SetSort(_, heading_options, direction) => {
|
||||||
|
self.flags.state.dialog_sort_name = (heading_options, direction);
|
||||||
|
if let Some(state_handler) = self.flags.state_handler.as_ref()
|
||||||
|
&& let Err(err) = state_handler.set::<&(HeadingOptions, bool)>(
|
||||||
|
"dialog_sort_name",
|
||||||
|
&self.flags.state.dialog_sort_name,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
log::warn!("Failed to save dialog sort name: {err:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
unsupported => {
|
unsupported => {
|
||||||
log::warn!("{unsupported:?} not supported in dialog mode");
|
log::warn!("{unsupported:?} not supported in dialog mode");
|
||||||
}
|
}
|
||||||
|
|
@ -1919,11 +1847,13 @@ impl Application for App {
|
||||||
items.retain(|item| {
|
items.retain(|item| {
|
||||||
// Directories are always shown
|
// Directories are always shown
|
||||||
item.metadata.is_dir()
|
item.metadata.is_dir()
|
||||||
|
// Check for mime type match (first because it is faster)
|
||||||
|| mimes.iter().any(|filter_mime| {
|
|| mimes.iter().any(|filter_mime| {
|
||||||
if filter_mime.subtype() == mime::STAR {
|
if filter_mime.subtype() == mime::STAR {
|
||||||
filter_mime.type_() == item.mime.type_()
|
filter_mime.type_() == item.mime.type_()
|
||||||
} else {
|
} else {
|
||||||
*filter_mime == item.mime
|
*filter_mime == item.mime
|
||||||
|
|| mime_icon::is_mime_subclass_of(&item.mime, filter_mime)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Check for glob match (last because it is slower)
|
// Check for glob match (last because it is slower)
|
||||||
|
|
@ -1952,6 +1882,16 @@ impl Application for App {
|
||||||
if self.search_get().is_some() {
|
if self.search_get().is_some() {
|
||||||
return widget::text_input::focus(self.search_id.clone());
|
return widget::text_input::focus(self.search_id.clone());
|
||||||
}
|
}
|
||||||
|
if let DialogKind::SaveFile { filename } = &self.flags.kind {
|
||||||
|
return Task::batch([
|
||||||
|
widget::text_input::focus(self.filename_id.clone()),
|
||||||
|
widget::text_input::select_until_last(
|
||||||
|
self.filename_id.clone(),
|
||||||
|
filename,
|
||||||
|
'.',
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
return widget::text_input::focus(self.filename_id.clone());
|
return widget::text_input::focus(self.filename_id.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1990,9 +1930,7 @@ impl Application for App {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Message::Surface(action) => {
|
Message::Surface(action) => {
|
||||||
return cosmic::task::message(cosmic::Action::Cosmic(
|
return cosmic::task::message(cosmic::Action::Surface(action));
|
||||||
cosmic::app::Action::Surface(action),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2001,7 +1939,7 @@ impl Application for App {
|
||||||
|
|
||||||
/// Creates a view after each update.
|
/// Creates a view after each update.
|
||||||
fn view(&self) -> Element<'_, Message> {
|
fn view(&self) -> Element<'_, Message> {
|
||||||
let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing;
|
let cosmic_theme::Spacing { space_xxs, .. } = theme::spacing();
|
||||||
|
|
||||||
let mut col = widget::column::with_capacity(2);
|
let mut col = widget::column::with_capacity(2);
|
||||||
|
|
||||||
|
|
@ -2022,7 +1960,7 @@ impl Application for App {
|
||||||
|
|
||||||
col = col.push(
|
col = col.push(
|
||||||
self.tab
|
self.tab
|
||||||
.view(&self.key_binds, &self.modifiers, false)
|
.view(&self.key_binds, &self.modifiers, false, &[])
|
||||||
.map(Message::TabMessage),
|
.map(Message::TabMessage),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -2040,11 +1978,14 @@ impl Application for App {
|
||||||
},
|
},
|
||||||
Event::Keyboard(KeyEvent::KeyPressed {
|
Event::Keyboard(KeyEvent::KeyPressed {
|
||||||
key,
|
key,
|
||||||
|
physical_key,
|
||||||
modifiers,
|
modifiers,
|
||||||
text,
|
text,
|
||||||
..
|
..
|
||||||
}) => match status {
|
}) => match status {
|
||||||
event::Status::Ignored => Some(Message::Key(modifiers, key, text)),
|
event::Status::Ignored => {
|
||||||
|
Some(Message::Key(modifiers, key, physical_key, text))
|
||||||
|
}
|
||||||
event::Status::Captured => {
|
event::Status::Captured => {
|
||||||
if key == Key::Named(Named::Escape) {
|
if key == Key::Named(Named::Escape) {
|
||||||
Some(Message::Escape)
|
Some(Message::Escape)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
use cosmic::{
|
use cosmic::iced::core::keyboard::key::Named;
|
||||||
iced::keyboard::Key,
|
use cosmic::iced::keyboard::Key;
|
||||||
iced_core::keyboard::key::Named,
|
use cosmic::widget::menu::key_bind::{KeyBind, Modifier};
|
||||||
widget::menu::key_bind::{KeyBind, Modifier},
|
|
||||||
};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{app::Action, tab};
|
use crate::app::Action;
|
||||||
|
use crate::tab;
|
||||||
|
|
||||||
//TODO: load from config
|
//TODO: load from config
|
||||||
pub fn key_binds(mode: &tab::Mode) -> HashMap<KeyBind, Action> {
|
pub fn key_binds(mode: &tab::Mode) -> HashMap<KeyBind, Action> {
|
||||||
|
|
@ -31,12 +30,16 @@ pub fn key_binds(mode: &tab::Mode) -> HashMap<KeyBind, Action> {
|
||||||
bind!([], Key::Named(Named::F5), Reload);
|
bind!([], Key::Named(Named::F5), Reload);
|
||||||
bind!([], Key::Named(Named::Home), SelectFirst);
|
bind!([], Key::Named(Named::Home), SelectFirst);
|
||||||
bind!([], Key::Named(Named::End), SelectLast);
|
bind!([], Key::Named(Named::End), SelectLast);
|
||||||
|
bind!([], Key::Named(Named::PageDown), ItemPageDown);
|
||||||
|
bind!([], Key::Named(Named::PageUp), ItemPageUp);
|
||||||
bind!([Shift], Key::Named(Named::ArrowDown), ItemDown);
|
bind!([Shift], Key::Named(Named::ArrowDown), ItemDown);
|
||||||
bind!([Shift], Key::Named(Named::ArrowLeft), ItemLeft);
|
bind!([Shift], Key::Named(Named::ArrowLeft), ItemLeft);
|
||||||
bind!([Shift], Key::Named(Named::ArrowRight), ItemRight);
|
bind!([Shift], Key::Named(Named::ArrowRight), ItemRight);
|
||||||
bind!([Shift], Key::Named(Named::ArrowUp), ItemUp);
|
bind!([Shift], Key::Named(Named::ArrowUp), ItemUp);
|
||||||
bind!([Shift], Key::Named(Named::Home), SelectFirst);
|
bind!([Shift], Key::Named(Named::Home), SelectFirst);
|
||||||
bind!([Shift], Key::Named(Named::End), SelectLast);
|
bind!([Shift], Key::Named(Named::End), SelectLast);
|
||||||
|
bind!([Shift], Key::Named(Named::PageDown), ItemPageDown);
|
||||||
|
bind!([Shift], Key::Named(Named::PageUp), ItemPageUp);
|
||||||
bind!([Ctrl, Shift], Key::Character("n".into()), NewFolder);
|
bind!([Ctrl, Shift], Key::Character("n".into()), NewFolder);
|
||||||
bind!([], Key::Named(Named::Enter), Open);
|
bind!([], Key::Named(Named::Enter), Open);
|
||||||
bind!([Ctrl], Key::Character(" ".into()), Preview);
|
bind!([Ctrl], Key::Character(" ".into()), Preview);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
use cosmic::widget;
|
use cosmic::widget;
|
||||||
use image::ImageReader;
|
use image::ImageReader;
|
||||||
use std::{
|
use std::collections::{HashMap, HashSet};
|
||||||
collections::{HashMap, HashSet},
|
use std::path::{Path, PathBuf};
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Bytes per pixel in RGBA format (Red, Green, Blue, Alpha = 4 bytes)
|
/// Bytes per pixel in RGBA format (Red, Green, Blue, Alpha = 4 bytes)
|
||||||
pub const RGBA_BYTES_PER_PIXEL: u64 = 4;
|
pub const RGBA_BYTES_PER_PIXEL: u64 = 4;
|
||||||
|
|
|
||||||
58
src/lib.rs
58
src/lib.rs
|
|
@ -1,15 +1,23 @@
|
||||||
// Copyright 2023 System76 <info@system76.com>
|
// Copyright 2023 System76 <info@system76.com>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use cosmic::{app::Settings, iced::Limits};
|
use cosmic::app::Settings;
|
||||||
use std::{env, fs, path::PathBuf, process};
|
use cosmic::iced::Limits;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{env, fs, process};
|
||||||
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
|
|
||||||
|
use crate::app::{App, Flags};
|
||||||
|
use crate::config::{Config, State};
|
||||||
|
use crate::tab::Location;
|
||||||
|
|
||||||
use app::{App, Flags};
|
|
||||||
pub mod app;
|
pub mod app;
|
||||||
mod archive;
|
mod archive;
|
||||||
|
pub mod channel;
|
||||||
pub mod clipboard;
|
pub mod clipboard;
|
||||||
use config::Config;
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
mod context_action;
|
||||||
pub mod dialog;
|
pub mod dialog;
|
||||||
mod key_bind;
|
mod key_bind;
|
||||||
pub(crate) mod large_image;
|
pub(crate) mod large_image;
|
||||||
|
|
@ -22,16 +30,15 @@ mod mounter;
|
||||||
mod mouse_area;
|
mod mouse_area;
|
||||||
pub mod operation;
|
pub mod operation;
|
||||||
mod spawn_detached;
|
mod spawn_detached;
|
||||||
use tab::Location;
|
|
||||||
mod zoom;
|
|
||||||
|
|
||||||
use crate::config::State;
|
|
||||||
pub mod tab;
|
pub mod tab;
|
||||||
mod thumbnail_cacher;
|
mod thumbnail_cacher;
|
||||||
mod thumbnailer;
|
mod thumbnailer;
|
||||||
|
pub(crate) mod trash;
|
||||||
|
mod zoom;
|
||||||
|
|
||||||
pub(crate) type FxOrderMap<K, V> = ordermap::OrderMap<K, V, rustc_hash::FxBuildHasher>;
|
pub(crate) type FxOrderMap<K, V> = ordermap::OrderMap<K, V, rustc_hash::FxBuildHasher>;
|
||||||
|
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
pub(crate) fn err_str<T: ToString>(err: T) -> String {
|
pub(crate) fn err_str<T: ToString>(err: T) -> String {
|
||||||
err.to_string()
|
err.to_string()
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +79,22 @@ pub fn is_wayland() -> bool {
|
||||||
/// Runs application in desktop mode
|
/// Runs application in desktop mode
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub fn desktop() -> Result<(), Box<dyn std::error::Error>> {
|
pub fn desktop() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
|
let log_format = tracing_subscriber::fmt::format()
|
||||||
|
.pretty()
|
||||||
|
.without_time()
|
||||||
|
.with_line_number(true)
|
||||||
|
.with_file(true)
|
||||||
|
.with_target(false)
|
||||||
|
.with_thread_names(true);
|
||||||
|
|
||||||
|
let log_layer = tracing_subscriber::fmt::Layer::default()
|
||||||
|
.with_writer(std::io::stderr)
|
||||||
|
.event_format(log_format);
|
||||||
|
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(tracing_subscriber::EnvFilter::from_env("RUST_LOG"))
|
||||||
|
.with(log_layer)
|
||||||
|
.init();
|
||||||
|
|
||||||
localize::localize();
|
localize::localize();
|
||||||
|
|
||||||
|
|
@ -107,7 +129,21 @@ pub fn desktop() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// Runs application with these settings
|
/// Runs application with these settings
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
|
let log_format = tracing_subscriber::fmt::format()
|
||||||
|
.pretty()
|
||||||
|
.with_line_number(true)
|
||||||
|
.with_file(true)
|
||||||
|
.with_target(false)
|
||||||
|
.with_thread_names(true);
|
||||||
|
|
||||||
|
let log_layer = tracing_subscriber::fmt::Layer::default()
|
||||||
|
.with_writer(std::io::stderr)
|
||||||
|
.event_format(log_format);
|
||||||
|
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(tracing_subscriber::EnvFilter::from_default_env())
|
||||||
|
.with(log_layer)
|
||||||
|
.init();
|
||||||
|
|
||||||
localize::localize();
|
localize::localize();
|
||||||
|
|
||||||
|
|
@ -157,7 +193,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if daemonize {
|
if daemonize {
|
||||||
#[cfg(all(unix, not(target_os = "redox")))]
|
#[cfg(all(unix, not(any(target_os = "macos", target_os = "redox"))))]
|
||||||
match fork::daemon(true, true) {
|
match fork::daemon(true, true) {
|
||||||
Ok(fork::Fork::Child) => (),
|
Ok(fork::Fork::Child) => (),
|
||||||
Ok(fork::Fork::Parent(_child_pid)) => process::exit(0),
|
Ok(fork::Fork::Parent(_child_pid)) => process::exit(0),
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
use cosmic::{iced_core, iced_widget};
|
use cosmic::iced::{core as iced_core, widget as iced_widget};
|
||||||
use iced_core::event::Event;
|
use iced_core::event::Event;
|
||||||
use iced_core::layout;
|
|
||||||
use iced_core::mouse;
|
|
||||||
use iced_core::overlay;
|
|
||||||
use iced_core::renderer;
|
|
||||||
use iced_core::widget::{Operation, Tree};
|
use iced_core::widget::{Operation, Tree};
|
||||||
use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget};
|
use iced_core::{
|
||||||
|
Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget, layout, mouse, overlay,
|
||||||
|
renderer,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn loaded_image<'a, Message: 'static, Theme>(
|
pub fn loaded_image<'a, Message: 'static, Theme>(
|
||||||
handle: <cosmic::Renderer as iced_core::image::Renderer>::Handle,
|
handle: <cosmic::Renderer as iced_core::image::Renderer>::Handle,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use i18n_embed::{
|
use i18n_embed::fluent::{FluentLanguageLoader, fluent_language_loader};
|
||||||
DefaultLocalizer, LanguageLoader, Localizer,
|
use i18n_embed::{DefaultLocalizer, LanguageLoader, Localizer};
|
||||||
fluent::{FluentLanguageLoader, fluent_language_loader},
|
use icu::collator::options::CollatorOptions;
|
||||||
};
|
use icu::collator::preferences::CollationNumericOrdering;
|
||||||
use icu::collator::{
|
use icu::collator::{Collator, CollatorBorrowed, CollatorPreferences};
|
||||||
Collator, CollatorBorrowed, CollatorPreferences, options::CollatorOptions,
|
|
||||||
preferences::CollationNumericOrdering,
|
|
||||||
};
|
|
||||||
use icu::locale::Locale;
|
use icu::locale::Locale;
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,6 @@ use tikv_jemallocator::Jemalloc;
|
||||||
static GLOBAL: Jemalloc = Jemalloc;
|
static GLOBAL: Jemalloc = Jemalloc;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let _ = jxl_oxide::integration::register_image_decoding_hook();
|
||||||
cosmic_files::main()
|
cosmic_files::main()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
493
src/menu.rs
493
src/menu.rs
|
|
@ -1,48 +1,28 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{Element, theme};
|
||||||
Element,
|
use cosmic::app::Core;
|
||||||
app::Core,
|
use cosmic::iced::keyboard::Modifiers;
|
||||||
iced::{
|
use cosmic::widget::menu::action::MenuAction;
|
||||||
Alignment, Background, Border, Length, advanced::widget::text::Style as TextStyle,
|
use cosmic::widget::menu::key_bind::KeyBind;
|
||||||
keyboard::Modifiers,
|
use cosmic::widget::menu::{self, ItemHeight, ItemWidth, MenuBar};
|
||||||
},
|
use cosmic::widget::{self, responsive_menu_bar};
|
||||||
theme,
|
|
||||||
widget::{
|
|
||||||
self, Row, button, column, container, divider,
|
|
||||||
menu::{self, ItemHeight, ItemWidth, MenuBar, key_bind::KeyBind},
|
|
||||||
responsive_menu_bar, space, text,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use i18n_embed::LanguageLoader;
|
use i18n_embed::LanguageLoader;
|
||||||
use mime_guess::Mime;
|
use mime_guess::Mime;
|
||||||
use std::{collections::HashMap, sync::LazyLock};
|
use std::collections::HashMap;
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use crate::{
|
use crate::app::{Action, Message};
|
||||||
app::{Action, Message},
|
use crate::config::{Config, ContextActionPreset};
|
||||||
config::Config,
|
use crate::fl;
|
||||||
fl,
|
use crate::tab::{
|
||||||
tab::{self, HeadingOptions, Location, LocationMenuAction, SearchLocation, Tab},
|
self, HeadingOptions, ItemMetadata, Location, LocationMenuAction, SearchLocation, Tab,
|
||||||
};
|
};
|
||||||
|
use crate::trash::{Trash, TrashExt};
|
||||||
|
|
||||||
static MENU_ID: LazyLock<cosmic::widget::Id> =
|
static MENU_ID: LazyLock<cosmic::widget::Id> =
|
||||||
LazyLock::new(|| cosmic::widget::Id::new("responsive-menu"));
|
LazyLock::new(|| cosmic::widget::Id::new("responsive-menu"));
|
||||||
|
|
||||||
macro_rules! menu_button {
|
|
||||||
($($x:expr),+ $(,)?) => (
|
|
||||||
button::custom(
|
|
||||||
Row::with_children(
|
|
||||||
[$(Element::from($x)),+]
|
|
||||||
)
|
|
||||||
.height(Length::Fixed(24.0))
|
|
||||||
.align_y(Alignment::Center)
|
|
||||||
)
|
|
||||||
.padding([theme::active().cosmic().spacing.space_xxs, 16])
|
|
||||||
.width(Length::Fill)
|
|
||||||
.class(theme::Button::MenuItem)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn menu_button_optional(
|
const fn menu_button_optional(
|
||||||
label: String,
|
label: String,
|
||||||
action: Action,
|
action: Action,
|
||||||
|
|
@ -55,72 +35,46 @@ const fn menu_button_optional(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A menu action dispatched to the tab the menu was opened on.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct TabAction(pub Action);
|
||||||
|
|
||||||
|
impl MenuAction for TabAction {
|
||||||
|
type Message = tab::Message;
|
||||||
|
|
||||||
|
fn message(&self) -> tab::Message {
|
||||||
|
tab::Message::ContextAction(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn context_menu<'a>(
|
pub fn context_menu<'a>(
|
||||||
tab: &Tab,
|
tab: &Tab,
|
||||||
key_binds: &HashMap<KeyBind, Action>,
|
key_binds: &HashMap<KeyBind, Action>,
|
||||||
modifiers: &Modifiers,
|
modifiers: &Modifiers,
|
||||||
clipboard_paste_available: bool,
|
clipboard_paste_available: bool,
|
||||||
) -> Element<'a, tab::Message> {
|
context_actions: &[ContextActionPreset],
|
||||||
let find_key = |action: &Action| -> String {
|
) -> Vec<menu::Tree<tab::Message>> {
|
||||||
for (key_bind, key_action) in key_binds {
|
let menu_item =
|
||||||
if action == key_action {
|
|label: String, action: Action| menu::Item::Button(label, None, TabAction(action));
|
||||||
return key_bind.to_string();
|
let menu_item_disabled =
|
||||||
}
|
|label: String, action: Action| menu::Item::ButtonDisabled(label, None, TabAction(action));
|
||||||
}
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
fn key_style(theme: &cosmic::Theme) -> TextStyle {
|
|
||||||
let mut color = theme.cosmic().background.component.on;
|
|
||||||
color.alpha *= 0.75;
|
|
||||||
TextStyle {
|
|
||||||
color: Some(color.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn disabled_style(theme: &cosmic::Theme) -> TextStyle {
|
|
||||||
let mut color = theme.cosmic().background.component.on;
|
|
||||||
color.alpha *= 0.5;
|
|
||||||
TextStyle {
|
|
||||||
color: Some(color.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let menu_item = |label, action| {
|
|
||||||
let key = find_key(&action);
|
|
||||||
menu_button!(
|
|
||||||
text::body(label),
|
|
||||||
space::horizontal(),
|
|
||||||
text::body(key).class(theme::Text::Custom(key_style))
|
|
||||||
)
|
|
||||||
.on_press(tab::Message::ContextAction(action))
|
|
||||||
};
|
|
||||||
|
|
||||||
let menu_item_disabled = |label, action: Action| {
|
|
||||||
let key = find_key(&action);
|
|
||||||
menu_button!(
|
|
||||||
text::body(label).class(theme::Text::Custom(disabled_style)),
|
|
||||||
space::horizontal(),
|
|
||||||
text::body(key).class(theme::Text::Custom(disabled_style))
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allow paste when clipboard has data and we're in a location that supports it
|
// Allow paste when clipboard has data and we're in a location that supports it
|
||||||
let can_paste = clipboard_paste_available && tab.location.supports_paste();
|
let can_paste = clipboard_paste_available && tab.location.supports_paste();
|
||||||
|
|
||||||
let (sort_name, sort_direction, _) = tab.sort_options();
|
let (sort_name, sort_direction, _) = tab.sort_options();
|
||||||
let sort_item = |label, variant| {
|
let sort_item = |label: String, variant| {
|
||||||
menu_item(
|
let entry = menu::Entry::new(label, TabAction(Action::ToggleSort(variant)));
|
||||||
format!(
|
menu::Item::Entry(if sort_name == variant {
|
||||||
"{} {}",
|
let icon_name = if sort_direction {
|
||||||
label,
|
"view-sort-ascending-symbolic"
|
||||||
match (sort_name == variant, sort_direction) {
|
} else {
|
||||||
(true, true) => "\u{2B07}",
|
"view-sort-descending-symbolic"
|
||||||
(true, false) => "\u{2B06}",
|
};
|
||||||
_ => "",
|
entry.icon(widget::icon::from_name(icon_name).size(14).handle())
|
||||||
}
|
} else {
|
||||||
),
|
entry.reserve_icon()
|
||||||
Action::ToggleSort(variant),
|
})
|
||||||
)
|
|
||||||
.into()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut selected_dir = 0;
|
let mut selected_dir = 0;
|
||||||
|
|
@ -129,6 +83,7 @@ pub fn context_menu<'a>(
|
||||||
let mut selected_desktop_entry = None;
|
let mut selected_desktop_entry = None;
|
||||||
let mut selected_types: Vec<Mime> = vec![];
|
let mut selected_types: Vec<Mime> = vec![];
|
||||||
let mut selected_mount_point = 0;
|
let mut selected_mount_point = 0;
|
||||||
|
let mut any_trash_item = false;
|
||||||
if let Some(items) = tab.items_opt() {
|
if let Some(items) = tab.items_opt() {
|
||||||
for item in items {
|
for item in items {
|
||||||
if item.selected {
|
if item.selected {
|
||||||
|
|
@ -141,15 +96,17 @@ pub fn context_menu<'a>(
|
||||||
Some(Location::Trash) | Some(Location::Search(SearchLocation::Trash, ..)) => {
|
Some(Location::Trash) | Some(Location::Search(SearchLocation::Trash, ..)) => {
|
||||||
selected_trash_only = true
|
selected_trash_only = true
|
||||||
}
|
}
|
||||||
Some(Location::Path(path)) => {
|
Some(Location::Path(path))
|
||||||
if selected == 1
|
if selected == 1
|
||||||
&& path.extension().and_then(|s| s.to_str()) == Some("desktop")
|
&& path.extension().and_then(|s| s.to_str()) == Some("desktop") =>
|
||||||
{
|
{
|
||||||
selected_desktop_entry = Some(&**path);
|
selected_desktop_entry = Some(&**path);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
if matches!(&item.metadata, ItemMetadata::Trash { .. }) {
|
||||||
|
any_trash_item = true;
|
||||||
|
}
|
||||||
selected_types.push(item.mime.clone());
|
selected_types.push(item.mime.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -157,6 +114,14 @@ pub fn context_menu<'a>(
|
||||||
selected_types.sort_unstable();
|
selected_types.sort_unstable();
|
||||||
selected_types.dedup();
|
selected_types.dedup();
|
||||||
selected_trash_only = selected_trash_only && selected == 1;
|
selected_trash_only = selected_trash_only && selected == 1;
|
||||||
|
let context_action_items = |selected: usize, selected_dir: usize| {
|
||||||
|
context_actions
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, action)| action.matches_selection(selected, selected_dir))
|
||||||
|
.map(|(i, action)| menu_item(action.name.clone(), Action::RunContextAction(i)))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
// Parse the desktop entry if it is the only selection
|
// Parse the desktop entry if it is the only selection
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
let selected_desktop_entry = selected_desktop_entry.and_then(|path| {
|
let selected_desktop_entry = selected_desktop_entry.and_then(|path| {
|
||||||
|
|
@ -170,7 +135,7 @@ pub fn context_menu<'a>(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut children: Vec<Element<_>> = Vec::new();
|
let mut children: Vec<menu::Item<TabAction, String>> = Vec::new();
|
||||||
match (&tab.mode, &tab.location) {
|
match (&tab.mode, &tab.location) {
|
||||||
(
|
(
|
||||||
tab::Mode::App | tab::Mode::Desktop,
|
tab::Mode::App | tab::Mode::Desktop,
|
||||||
|
|
@ -182,142 +147,175 @@ pub fn context_menu<'a>(
|
||||||
| Location::Network(_, _, Some(_)),
|
| Location::Network(_, _, Some(_)),
|
||||||
) => {
|
) => {
|
||||||
if selected_trash_only {
|
if selected_trash_only {
|
||||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
children.push(menu_item(fl!("open"), Action::Open));
|
||||||
if !trash::os_limited::is_empty().unwrap_or(true) {
|
if !Trash::is_empty() {
|
||||||
children.push(menu_item(fl!("empty-trash"), Action::EmptyTrash).into());
|
children.push(menu_item(fl!("empty-trash"), Action::EmptyTrash));
|
||||||
}
|
}
|
||||||
} else if let Some(entry) = selected_desktop_entry {
|
} else if let Some(entry) = selected_desktop_entry {
|
||||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
children.push(menu_item(fl!("open"), Action::Open));
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
{
|
{
|
||||||
children.extend(entry.desktop_actions.into_iter().enumerate().map(
|
children.extend(
|
||||||
|(i, action)| menu_item(action.name, Action::ExecEntryAction(i)).into(),
|
entry
|
||||||
));
|
.desktop_actions
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, action)| menu_item(action.name, Action::ExecEntryAction(i))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu_item(fl!("edit-desktop-entry"), Action::EditDesktopEntry));
|
||||||
children.push(menu_item(fl!("rename"), Action::Rename).into());
|
children.push(menu::Item::Divider);
|
||||||
children.push(menu_item(fl!("cut"), Action::Cut).into());
|
children.push(menu_item(fl!("rename"), Action::Rename));
|
||||||
|
children.push(menu_item(fl!("cut"), Action::Cut));
|
||||||
if modifiers.shift() && !modifiers.control() {
|
if modifiers.shift() && !modifiers.control() {
|
||||||
children.push(menu_item(fl!("copy-path"), Action::CopyPath).into());
|
children.push(menu_item(fl!("copy-path"), Action::CopyPath));
|
||||||
} else {
|
} else {
|
||||||
children.push(menu_item(fl!("copy"), Action::Copy).into());
|
children.push(menu_item(fl!("copy"), Action::Copy));
|
||||||
}
|
}
|
||||||
// Should this simply bypass trash and remove the shortcut?
|
// Should this simply bypass trash and remove the shortcut?
|
||||||
children.push(menu_item(fl!("move-to-trash"), Action::Delete).into());
|
children.push(menu_item(fl!("move-to-trash"), Action::Delete));
|
||||||
|
let action_items = context_action_items(selected, selected_dir);
|
||||||
|
if !action_items.is_empty() {
|
||||||
|
children.push(menu::Item::Divider);
|
||||||
|
children.extend(action_items);
|
||||||
|
}
|
||||||
} else if selected > 0 {
|
} else if selected > 0 {
|
||||||
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
||||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
children.push(menu_item(fl!("open"), Action::Open));
|
||||||
}
|
}
|
||||||
if selected == 1 {
|
if selected == 1 {
|
||||||
children.push(menu_item(fl!("menu-open-with"), Action::OpenWith).into());
|
children.push(menu_item(fl!("menu-open-with"), Action::OpenWith));
|
||||||
if selected_dir == 1 {
|
if selected_dir == 1 {
|
||||||
children
|
children.push(menu_item(fl!("open-in-terminal"), Action::OpenTerminal));
|
||||||
.push(menu_item(fl!("open-in-terminal"), Action::OpenTerminal).into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tab.location.is_recents() {
|
if tab.location.is_recents() || matches!(tab.location, Location::Search(..)) {
|
||||||
children.push(
|
children.push(menu_item(
|
||||||
menu_item(fl!("open-item-location"), Action::OpenItemLocation).into(),
|
fl!("open-item-location"),
|
||||||
);
|
Action::OpenItemLocation,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
// All selected items are directories
|
// All selected items are directories
|
||||||
if selected == selected_dir && matches!(tab.mode, tab::Mode::App) {
|
if selected == selected_dir && matches!(tab.mode, tab::Mode::App) {
|
||||||
children.push(menu_item(fl!("open-in-new-tab"), Action::OpenInNewTab).into());
|
children.push(menu_item(fl!("open-in-new-tab"), Action::OpenInNewTab));
|
||||||
children
|
children.push(menu_item(
|
||||||
.push(menu_item(fl!("open-in-new-window"), Action::OpenInNewWindow).into());
|
fl!("open-in-new-window"),
|
||||||
|
Action::OpenInNewWindow,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
children.push(divider::horizontal::light().into());
|
let action_items = context_action_items(selected, selected_dir);
|
||||||
|
if !action_items.is_empty() {
|
||||||
|
children.push(menu::Item::Divider);
|
||||||
|
children.extend(action_items);
|
||||||
|
}
|
||||||
|
children.push(menu::Item::Divider);
|
||||||
if selected_mount_point == 0 {
|
if selected_mount_point == 0 {
|
||||||
children.push(menu_item(fl!("rename"), Action::Rename).into());
|
children.push(menu_item(fl!("rename"), Action::Rename));
|
||||||
children.push(menu_item(fl!("cut"), Action::Cut).into());
|
children.push(menu_item(fl!("cut"), Action::Cut));
|
||||||
}
|
}
|
||||||
if modifiers.shift() && !modifiers.control() {
|
if modifiers.shift() && !modifiers.control() {
|
||||||
children.push(menu_item(fl!("copy-path"), Action::CopyPath).into());
|
children.push(menu_item(fl!("copy-path"), Action::CopyPath));
|
||||||
} else {
|
} else {
|
||||||
children.push(menu_item(fl!("copy"), Action::Copy).into());
|
children.push(menu_item(fl!("copy"), Action::Copy));
|
||||||
}
|
}
|
||||||
if selected_mount_point == 0 {
|
if selected_mount_point == 0 {
|
||||||
children.push(menu_item(fl!("move-to"), Action::MoveTo).into());
|
children.push(menu_item(fl!("move-to"), Action::MoveTo));
|
||||||
}
|
}
|
||||||
children.push(menu_item(fl!("copy-to"), Action::CopyTo).into());
|
children.push(menu_item(fl!("copy-to"), Action::CopyTo));
|
||||||
|
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
let supported_archive_types = crate::archive::SUPPORTED_ARCHIVE_TYPES;
|
let supported_archive_types = crate::archive::SUPPORTED_ARCHIVE_TYPES;
|
||||||
selected_types.retain(|t| supported_archive_types.iter().copied().all(|m| *t != m));
|
selected_types.retain(|t| supported_archive_types.iter().copied().all(|m| *t != m));
|
||||||
if selected_types.is_empty() {
|
if selected_types.is_empty() {
|
||||||
children.push(menu_item(fl!("extract-here"), Action::ExtractHere).into());
|
children.push(menu_item(fl!("extract-here"), Action::ExtractHere));
|
||||||
children.push(menu_item(fl!("extract-to"), Action::ExtractTo).into());
|
children.push(menu_item(fl!("extract-to"), Action::ExtractTo));
|
||||||
}
|
}
|
||||||
children.push(menu_item(fl!("compress"), Action::Compress).into());
|
children.push(menu_item(fl!("compress"), Action::Compress));
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
|
|
||||||
//TODO: Print?
|
//TODO: Print?
|
||||||
children.push(menu_item(fl!("show-details"), Action::Preview).into());
|
children.push(menu_item(fl!("show-details"), Action::Preview));
|
||||||
if matches!(tab.mode, tab::Mode::App) {
|
if any_trash_item {
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
children.push(menu_item(fl!("add-to-sidebar"), Action::AddToSidebar).into());
|
children.push(menu_item(
|
||||||
}
|
fl!("restore-from-trash"),
|
||||||
children.push(divider::horizontal::light().into());
|
Action::RestoreFromTrash,
|
||||||
if tab.location.is_recents() {
|
));
|
||||||
children.push(
|
children.push(menu::Item::Divider);
|
||||||
menu_item(fl!("remove-from-recents"), Action::RemoveFromRecents).into(),
|
children.push(menu_item(fl!("delete-permanently"), Action::Delete));
|
||||||
);
|
} else {
|
||||||
children.push(divider::horizontal::light().into());
|
if matches!(tab.mode, tab::Mode::App) {
|
||||||
}
|
children.push(menu::Item::Divider);
|
||||||
if selected_mount_point == 0 {
|
children.push(menu_item(fl!("add-to-sidebar"), Action::AddToSidebar));
|
||||||
if modifiers.shift() && !modifiers.control() {
|
}
|
||||||
children.push(
|
children.push(menu::Item::Divider);
|
||||||
menu_item(fl!("delete-permanently"), Action::PermanentlyDelete).into(),
|
if tab.location.is_recents() {
|
||||||
);
|
children.push(menu_item(
|
||||||
} else {
|
fl!("remove-from-recents"),
|
||||||
children.push(menu_item(fl!("move-to-trash"), Action::Delete).into());
|
Action::RemoveFromRecents,
|
||||||
|
));
|
||||||
|
children.push(menu::Item::Divider);
|
||||||
|
}
|
||||||
|
if selected_mount_point == 0 {
|
||||||
|
if modifiers.shift() && !modifiers.control() {
|
||||||
|
children.push(
|
||||||
|
menu_item(fl!("delete-permanently"), Action::PermanentlyDelete)
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
children.push(menu_item(fl!("move-to-trash"), Action::Delete));
|
||||||
|
}
|
||||||
|
} else if selected == 1 {
|
||||||
|
children.push(menu_item(fl!("eject"), Action::Eject));
|
||||||
}
|
}
|
||||||
} else if selected == 1 {
|
|
||||||
children.push(menu_item(fl!("eject"), Action::Eject).into());
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//TODO: need better designs for menu with no selection
|
//TODO: need better designs for menu with no selection
|
||||||
//TODO: have things like properties but they apply to the folder?
|
//TODO: have things like properties but they apply to the folder?
|
||||||
if tab.location != Location::Recents {
|
if tab.location != Location::Recents {
|
||||||
children.push(menu_item(fl!("new-folder"), Action::NewFolder).into());
|
children.push(menu_item(fl!("new-folder"), Action::NewFolder));
|
||||||
children.push(menu_item(fl!("new-file"), Action::NewFile).into());
|
children.push(menu_item(fl!("new-file"), Action::NewFile));
|
||||||
children.push(menu_item(fl!("open-in-terminal"), Action::OpenTerminal).into());
|
children.push(menu_item(fl!("open-in-terminal"), Action::OpenTerminal));
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
}
|
}
|
||||||
|
|
||||||
if tab.mode.multiple() {
|
if tab.mode.multiple() {
|
||||||
children.push(menu_item(fl!("select-all"), Action::SelectAll).into());
|
children.push(menu_item(fl!("select-all"), Action::SelectAll));
|
||||||
}
|
}
|
||||||
if can_paste {
|
if can_paste {
|
||||||
children.push(menu_item(fl!("paste"), Action::Paste).into());
|
children.push(menu_item(fl!("paste"), Action::Paste));
|
||||||
} else {
|
} else {
|
||||||
children.push(menu_item_disabled(fl!("paste"), Action::Paste).into());
|
children.push(menu_item_disabled(fl!("paste"), Action::Paste));
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: only show if cosmic-settings is found?
|
//TODO: only show if cosmic-settings is found?
|
||||||
if matches!(tab.mode, tab::Mode::Desktop) {
|
if matches!(tab.mode, tab::Mode::Desktop) {
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
children.push(
|
children.push(menu_item(
|
||||||
menu_item(fl!("change-wallpaper"), Action::CosmicSettingsWallpaper).into(),
|
fl!("change-wallpaper"),
|
||||||
);
|
Action::CosmicSettingsWallpaper,
|
||||||
children.push(
|
));
|
||||||
menu_item(fl!("desktop-appearance"), Action::CosmicSettingsDesktop).into(),
|
children.push(menu_item(
|
||||||
);
|
fl!("desktop-appearance"),
|
||||||
children.push(
|
Action::CosmicSettingsDesktop,
|
||||||
menu_item(fl!("display-settings"), Action::CosmicSettingsDisplays).into(),
|
));
|
||||||
);
|
children.push(menu_item(
|
||||||
|
fl!("display-settings"),
|
||||||
|
Action::CosmicSettingsDisplays,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
// TODO: Nested menu
|
// TODO: Nested menu
|
||||||
children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name));
|
children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name));
|
||||||
children.push(sort_item(fl!("sort-by-modified"), HeadingOptions::Modified));
|
children.push(sort_item(fl!("sort-by-modified"), HeadingOptions::Modified));
|
||||||
children.push(sort_item(fl!("sort-by-size"), HeadingOptions::Size));
|
children.push(sort_item(fl!("sort-by-size"), HeadingOptions::Size));
|
||||||
if matches!(tab.location, Location::Desktop(..)) {
|
if matches!(tab.location, Location::Desktop(..)) {
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
children.push(
|
children.push(menu_item(
|
||||||
menu_item(fl!("desktop-view-options"), Action::DesktopViewOptions).into(),
|
fl!("desktop-view-options"),
|
||||||
);
|
Action::DesktopViewOptions,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -332,24 +330,25 @@ pub fn context_menu<'a>(
|
||||||
) => {
|
) => {
|
||||||
if selected > 0 {
|
if selected > 0 {
|
||||||
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
||||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
children.push(menu_item(fl!("open"), Action::Open));
|
||||||
}
|
}
|
||||||
if matches!(tab.location, Location::Search(..)) || tab.location.is_recents() {
|
if matches!(tab.location, Location::Search(..)) || tab.location.is_recents() {
|
||||||
children.push(
|
children.push(menu_item(
|
||||||
menu_item(fl!("open-item-location"), Action::OpenItemLocation).into(),
|
fl!("open-item-location"),
|
||||||
);
|
Action::OpenItemLocation,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
children.push(menu_item(fl!("show-details"), Action::Preview).into());
|
children.push(menu_item(fl!("show-details"), Action::Preview));
|
||||||
} else {
|
} else {
|
||||||
if dialog_kind.save() {
|
if dialog_kind.save() {
|
||||||
children.push(menu_item(fl!("new-folder"), Action::NewFolder).into());
|
children.push(menu_item(fl!("new-folder"), Action::NewFolder));
|
||||||
}
|
}
|
||||||
if tab.mode.multiple() {
|
if tab.mode.multiple() {
|
||||||
children.push(menu_item(fl!("select-all"), Action::SelectAll).into());
|
children.push(menu_item(fl!("select-all"), Action::SelectAll));
|
||||||
}
|
}
|
||||||
if !children.is_empty() {
|
if !children.is_empty() {
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
}
|
}
|
||||||
children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name));
|
children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name));
|
||||||
children.push(sort_item(fl!("sort-by-modified"), HeadingOptions::Modified));
|
children.push(sort_item(fl!("sort-by-modified"), HeadingOptions::Modified));
|
||||||
|
|
@ -359,14 +358,14 @@ pub fn context_menu<'a>(
|
||||||
(_, Location::Network(..)) => {
|
(_, Location::Network(..)) => {
|
||||||
if selected > 0 {
|
if selected > 0 {
|
||||||
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
||||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
children.push(menu_item(fl!("open"), Action::Open));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if tab.mode.multiple() {
|
if tab.mode.multiple() {
|
||||||
children.push(menu_item(fl!("select-all"), Action::SelectAll).into());
|
children.push(menu_item(fl!("select-all"), Action::SelectAll));
|
||||||
}
|
}
|
||||||
if !children.is_empty() {
|
if !children.is_empty() {
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
}
|
}
|
||||||
children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name));
|
children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name));
|
||||||
children.push(sort_item(fl!("sort-by-modified"), HeadingOptions::Modified));
|
children.push(sort_item(fl!("sort-by-modified"), HeadingOptions::Modified));
|
||||||
|
|
@ -375,18 +374,26 @@ pub fn context_menu<'a>(
|
||||||
}
|
}
|
||||||
(_, Location::Trash | Location::Search(SearchLocation::Trash, ..)) => {
|
(_, Location::Trash | Location::Search(SearchLocation::Trash, ..)) => {
|
||||||
if tab.mode.multiple() {
|
if tab.mode.multiple() {
|
||||||
children.push(menu_item(fl!("select-all"), Action::SelectAll).into());
|
children.push(menu_item(fl!("select-all"), Action::SelectAll));
|
||||||
|
}
|
||||||
|
if !Trash::is_empty() {
|
||||||
|
if !children.is_empty() {
|
||||||
|
children.push(menu::Item::Divider);
|
||||||
|
}
|
||||||
|
children.push(menu_item(fl!("empty-trash"), Action::EmptyTrash));
|
||||||
}
|
}
|
||||||
if !children.is_empty() {
|
if !children.is_empty() {
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
}
|
}
|
||||||
if selected > 0 {
|
if selected > 0 {
|
||||||
children.push(menu_item(fl!("show-details"), Action::Preview).into());
|
children.push(menu_item(fl!("show-details"), Action::Preview));
|
||||||
children.push(divider::horizontal::light().into());
|
children.push(menu::Item::Divider);
|
||||||
children
|
children.push(menu_item(
|
||||||
.push(menu_item(fl!("restore-from-trash"), Action::RestoreFromTrash).into());
|
fl!("restore-from-trash"),
|
||||||
children.push(divider::horizontal::light().into());
|
Action::RestoreFromTrash,
|
||||||
children.push(menu_item(fl!("delete-permanently"), Action::Delete).into());
|
));
|
||||||
|
children.push(menu::Item::Divider);
|
||||||
|
children.push(menu_item(fl!("delete-permanently"), Action::Delete));
|
||||||
} else {
|
} else {
|
||||||
// TODO: Nested menu
|
// TODO: Nested menu
|
||||||
children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name));
|
children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name));
|
||||||
|
|
@ -396,26 +403,11 @@ pub fn context_menu<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
container(column::with_children(children))
|
let key_binds: HashMap<KeyBind, TabAction> = key_binds
|
||||||
.padding(1)
|
.iter()
|
||||||
//TODO: move style to libcosmic
|
.map(|(key_bind, action)| (key_bind.clone(), TabAction(*action)))
|
||||||
.style(|theme| {
|
.collect();
|
||||||
let cosmic = theme.cosmic();
|
menu::items(&key_binds, children)
|
||||||
let component = &cosmic.background.component;
|
|
||||||
container::Style {
|
|
||||||
icon_color: Some(component.on.into()),
|
|
||||||
text_color: Some(component.on.into()),
|
|
||||||
background: Some(Background::Color(component.base.into())),
|
|
||||||
border: Border {
|
|
||||||
radius: cosmic.radius_s().map(|x| x + 1.0).into(),
|
|
||||||
width: 1.0,
|
|
||||||
color: component.divider.into(),
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.width(Length::Fixed(360.0))
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dialog_menu(
|
pub fn dialog_menu(
|
||||||
|
|
@ -559,7 +551,7 @@ pub fn dialog_menu(
|
||||||
])
|
])
|
||||||
.item_height(ItemHeight::Dynamic(40))
|
.item_height(ItemHeight::Dynamic(40))
|
||||||
.item_width(ItemWidth::Uniform(360))
|
.item_width(ItemWidth::Uniform(360))
|
||||||
.spacing(theme::active().cosmic().spacing.space_xxxs.into())
|
.spacing(theme::spacing().space_xxxs.into())
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -614,7 +606,7 @@ pub fn menu_bar<'a>(
|
||||||
responsive_menu_bar()
|
responsive_menu_bar()
|
||||||
.item_height(ItemHeight::Dynamic(40))
|
.item_height(ItemHeight::Dynamic(40))
|
||||||
.item_width(ItemWidth::Uniform(360))
|
.item_width(ItemWidth::Uniform(360))
|
||||||
.spacing(theme::active().cosmic().spacing.space_xxxs.into())
|
.spacing(theme::spacing().space_xxxs.into())
|
||||||
.into_element(
|
.into_element(
|
||||||
core,
|
core,
|
||||||
key_binds,
|
key_binds,
|
||||||
|
|
@ -764,50 +756,33 @@ pub fn menu_bar<'a>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn location_context_menu<'a>(ancestor_index: usize) -> Element<'a, tab::Message> {
|
pub fn location_context_menu(ancestor_index: usize) -> Vec<menu::Tree<tab::Message>> {
|
||||||
//TODO: only add some of these when in App mode
|
//TODO: only add some of these when in App mode
|
||||||
let children = [
|
menu::items(
|
||||||
menu_button!(text::body(fl!("open-in-new-tab")))
|
&HashMap::new(),
|
||||||
.on_press(tab::Message::LocationMenuAction(
|
vec![
|
||||||
|
menu::Item::Button(
|
||||||
|
fl!("open-in-new-tab"),
|
||||||
|
None,
|
||||||
LocationMenuAction::OpenInNewTab(ancestor_index),
|
LocationMenuAction::OpenInNewTab(ancestor_index),
|
||||||
))
|
),
|
||||||
.into(),
|
menu::Item::Button(
|
||||||
menu_button!(text::body(fl!("open-in-new-window")))
|
fl!("open-in-new-window"),
|
||||||
.on_press(tab::Message::LocationMenuAction(
|
None,
|
||||||
LocationMenuAction::OpenInNewWindow(ancestor_index),
|
LocationMenuAction::OpenInNewWindow(ancestor_index),
|
||||||
))
|
),
|
||||||
.into(),
|
menu::Item::Divider,
|
||||||
divider::horizontal::light().into(),
|
menu::Item::Button(
|
||||||
menu_button!(text::body(fl!("show-details")))
|
fl!("show-details"),
|
||||||
.on_press(tab::Message::LocationMenuAction(
|
None,
|
||||||
LocationMenuAction::Preview(ancestor_index),
|
LocationMenuAction::Preview(ancestor_index),
|
||||||
))
|
),
|
||||||
.into(),
|
menu::Item::Divider,
|
||||||
divider::horizontal::light().into(),
|
menu::Item::Button(
|
||||||
menu_button!(text::body(fl!("add-to-sidebar")))
|
fl!("add-to-sidebar"),
|
||||||
.on_press(tab::Message::LocationMenuAction(
|
None,
|
||||||
LocationMenuAction::AddToSidebar(ancestor_index),
|
LocationMenuAction::AddToSidebar(ancestor_index),
|
||||||
))
|
),
|
||||||
.into(),
|
],
|
||||||
];
|
)
|
||||||
|
|
||||||
container(column::with_children(children))
|
|
||||||
.padding(1)
|
|
||||||
.style(|theme| {
|
|
||||||
let cosmic = theme.cosmic();
|
|
||||||
let component = &cosmic.background.component;
|
|
||||||
container::Style {
|
|
||||||
icon_color: Some(component.on.into()),
|
|
||||||
text_color: Some(component.on.into()),
|
|
||||||
background: Some(Background::Color(component.base.into())),
|
|
||||||
border: Border {
|
|
||||||
radius: cosmic.radius_s().map(|x| x + 1.0).into(),
|
|
||||||
width: 1.0,
|
|
||||||
color: component.divider.into(),
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.width(Length::Fixed(360.0))
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
760
src/mime_app.rs
760
src/mime_app.rs
|
|
@ -1,155 +1,171 @@
|
||||||
// Copyright 2023 System76 <info@system76.com>
|
// Copyright 2023 System76 <info@system76.com>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
#[cfg(feature = "desktop")]
|
use bstr::{BString, ByteSlice, ByteVec};
|
||||||
use cosmic::desktop;
|
|
||||||
use cosmic::widget;
|
use cosmic::widget;
|
||||||
pub use mime_guess::Mime;
|
pub use mime_guess::Mime;
|
||||||
use rustc_hash::FxHashMap;
|
#[cfg(feature = "desktop")]
|
||||||
use std::{
|
use notify_debouncer_full::notify;
|
||||||
cmp::Ordering,
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
ffi::OsStr,
|
use std::ffi::OsStr;
|
||||||
fs, io,
|
use std::os::unix::ffi::OsStrExt;
|
||||||
path::{Path, PathBuf},
|
use std::path::{Path, PathBuf};
|
||||||
process,
|
use std::sync::atomic::AtomicBool;
|
||||||
time::Instant,
|
use std::sync::{Arc, RwLock, atomic};
|
||||||
};
|
use std::time::{self, Instant};
|
||||||
|
use std::{fs, io, process};
|
||||||
|
|
||||||
// Supported exec key field codes
|
#[cfg(feature = "desktop")]
|
||||||
const EXEC_HANDLERS: [&str; 4] = ["%f", "%F", "%u", "%U"];
|
pub async fn watch(mut emitter: impl FnMut() + 'static + Send) {
|
||||||
// Deprecated field codes. The spec advises to ignore these handlers.
|
let watcher_result = notify_debouncer_full::new_debouncer(
|
||||||
const DEPRECATED_HANDLERS: [&str; 6] = ["%d", "%D", "%n", "%N", "%v", "%m"];
|
time::Duration::from_millis(250),
|
||||||
|
Some(time::Duration::from_millis(250)),
|
||||||
|
move |event_res: notify_debouncer_full::DebounceEventResult| {
|
||||||
|
let Ok(events) = event_res else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if events.iter().any(|event| {
|
||||||
|
event.kind.is_create() || event.kind.is_modify() || event.kind.is_remove()
|
||||||
|
}) {
|
||||||
|
emitter();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Ok(mut watcher) = watcher_result {
|
||||||
|
let system_paths = cosmic_mime_apps::list_paths();
|
||||||
|
let local_paths = (|| {
|
||||||
|
let base_dirs = xdg::BaseDirectories::new();
|
||||||
|
let Some(home) = base_dirs.get_config_home() else {
|
||||||
|
return Err(std::io::Error::other("XDG config home not set"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(desktop) = std::env::var("XDG_CURRENT_DESKTOP") else {
|
||||||
|
return Err(std::io::Error::other("XDG_CURRENT_DESKTOP unset"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let default_mimeapps = home.join("mimeapps.list");
|
||||||
|
let desktop_mimeapps =
|
||||||
|
home.join([&desktop.to_ascii_lowercase(), "-mimeapps.list"].concat());
|
||||||
|
|
||||||
|
Ok([desktop_mimeapps, default_mimeapps])
|
||||||
|
})()
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
for path in system_paths
|
||||||
|
.iter()
|
||||||
|
.chain(local_paths.as_ref().into_iter().flatten())
|
||||||
|
{
|
||||||
|
_ = watcher.watch(path.as_path(), notify::RecursiveMode::NonRecursive);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future::pending().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn exec_to_command(
|
pub fn exec_to_command(
|
||||||
exec: &str,
|
exec: &str,
|
||||||
|
entry_name: &str,
|
||||||
|
entry_path: Option<&Path>,
|
||||||
path_opt: &[impl AsRef<OsStr>],
|
path_opt: &[impl AsRef<OsStr>],
|
||||||
) -> Option<Vec<process::Command>> {
|
) -> Option<Vec<process::Command>> {
|
||||||
let args_vec = shlex::split(exec)?;
|
let arguments = shlex::split(exec)?;
|
||||||
let program = args_vec.first()?;
|
|
||||||
// Skip program to make indexing easier
|
|
||||||
let args_vec = &args_vec[1..];
|
|
||||||
|
|
||||||
// Base Command instance(s)
|
if arguments.is_empty() {
|
||||||
// 1. We may need to launch multiple of the same process.
|
tracing::error!("command does not contain any arguments");
|
||||||
// 2. Each of those processes will need to be passed args from exec.
|
return None;
|
||||||
// 3. Each of those args may appear in any order.
|
}
|
||||||
// 4. Arg order should be preserved.
|
|
||||||
//
|
let mut commands = Vec::new();
|
||||||
// So, we'll go through exec in two passes. The first pass handles paths (%f etc) and args up
|
|
||||||
// to the field code followed by the second which passes extra, non-% args to each processes.
|
let paths = path_opt
|
||||||
//
|
|
||||||
// While it'd be marginally faster to process everything in one pass, that's problematic:
|
|
||||||
// 1. path_opt may need to be cloned because it may be moved on each iteration (borrowck
|
|
||||||
// doesn't know we'll only use it once)
|
|
||||||
// 2. We have to keep track of which modifier (%f etc) we've used/seen already
|
|
||||||
// 3. We have to keep track of which processes received non-modifier args which gets messy fast
|
|
||||||
// 4. `exec` is likely small so looping over it twice is not a big deal
|
|
||||||
let field_code_pos = args_vec
|
|
||||||
.iter()
|
.iter()
|
||||||
.position(|arg| EXEC_HANDLERS.contains(&arg.as_str()));
|
.map(AsRef::as_ref)
|
||||||
let args_handler = field_code_pos.and_then(|i| args_vec.get(i));
|
.map(Some)
|
||||||
// msrv
|
// Add a single `None` if no path was given.
|
||||||
// .inspect(|handler| log::trace!("Found paths handler: {handler} for exec: {exec}"));
|
.chain(std::iter::repeat_n(
|
||||||
// Number of args before the field code.
|
None,
|
||||||
// This won't be an off by one err below because take is not zero indexed.
|
if path_opt.is_empty() { 1 } else { 0 },
|
||||||
let field_code_pos = field_code_pos.unwrap_or_default();
|
));
|
||||||
let mut processes = match args_handler.map(String::as_str) {
|
|
||||||
Some("%f") => {
|
|
||||||
let mut processes = Vec::with_capacity(path_opt.len());
|
|
||||||
|
|
||||||
for path in path_opt.iter().map(AsRef::as_ref) {
|
for path in paths {
|
||||||
// TODO: %f and %F need to handle non-file URLs (see spec)
|
let mut batch_process = false;
|
||||||
if from_file_or_dir(path).is_none() {
|
let mut args = Vec::with_capacity(arguments.len());
|
||||||
log::warn!("Desktop file expects a file path instead of a URL: {path:?}");
|
let mut field_code_used = false;
|
||||||
|
|
||||||
|
for argument in arguments.iter().skip(1) {
|
||||||
|
let mut new_argument = BString::new(Vec::with_capacity(argument.capacity()));
|
||||||
|
let mut chars = argument.chars();
|
||||||
|
while let Some(char) = chars.next() {
|
||||||
|
// https://specifications.freedesktop.org/desktop-entry/latest/exec-variables.html
|
||||||
|
if char == '%' {
|
||||||
|
match chars.next() {
|
||||||
|
Some('%') => new_argument.push_char(char),
|
||||||
|
Some('c') => new_argument.push_str(entry_name),
|
||||||
|
Some('k') => {
|
||||||
|
if let Some(path) = entry_path {
|
||||||
|
new_argument.push_str(path.as_os_str().as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// %f and %u behave the same in a file manager.
|
||||||
|
Some('f' | 'u') => {
|
||||||
|
if let Some(path) = path
|
||||||
|
&& !field_code_used
|
||||||
|
{
|
||||||
|
// TODO: files on remote file systems should be copied to a temporary local file.
|
||||||
|
batch_process = true;
|
||||||
|
field_code_used = true;
|
||||||
|
new_argument.push_str(path.as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// %F and %U behave the same in a file manager.
|
||||||
|
Some('F') | Some('U') => {
|
||||||
|
if !field_code_used && new_argument.is_empty() {
|
||||||
|
field_code_used = true;
|
||||||
|
for path in path_opt.iter().map(AsRef::as_ref) {
|
||||||
|
args.push(BString::new(path.as_bytes().to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
new_argument.push_char(char);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Passing multiple paths to %f should open an instance per path
|
|
||||||
let mut process = process::Command::new(program);
|
|
||||||
process.args(
|
|
||||||
args_vec
|
|
||||||
.iter()
|
|
||||||
.map(AsRef::as_ref)
|
|
||||||
.take(field_code_pos)
|
|
||||||
.chain(std::iter::once(path)),
|
|
||||||
);
|
|
||||||
processes.push(process);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processes
|
if !new_argument.is_empty() {
|
||||||
}
|
args.push(new_argument);
|
||||||
Some("%F") => {
|
|
||||||
// TODO: %f and %F need to handle non-file URLs (see spec)
|
|
||||||
for invalid in path_opt
|
|
||||||
.iter()
|
|
||||||
.map(AsRef::as_ref)
|
|
||||||
.filter(|&path| from_file_or_dir(path).is_none())
|
|
||||||
{
|
|
||||||
log::warn!("Desktop file expects a file path instead of a URL: {invalid:?}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Launch one instance with all args
|
|
||||||
let mut process = process::Command::new(program);
|
|
||||||
process.args(
|
|
||||||
args_vec
|
|
||||||
.iter()
|
|
||||||
.map(OsStr::new)
|
|
||||||
.take(field_code_pos)
|
|
||||||
.chain(path_opt.iter().map(AsRef::as_ref)),
|
|
||||||
);
|
|
||||||
|
|
||||||
vec![process]
|
|
||||||
}
|
}
|
||||||
Some("%u") => path_opt
|
|
||||||
.iter()
|
|
||||||
.map(|path| {
|
|
||||||
let mut process = process::Command::new(program);
|
|
||||||
process.args(
|
|
||||||
args_vec
|
|
||||||
.iter()
|
|
||||||
.map(OsStr::new)
|
|
||||||
.take(field_code_pos)
|
|
||||||
.chain(std::iter::once(path.as_ref())),
|
|
||||||
);
|
|
||||||
process
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
Some("%U") => {
|
|
||||||
let mut process = process::Command::new(program);
|
|
||||||
process.args(
|
|
||||||
args_vec
|
|
||||||
.iter()
|
|
||||||
.map(OsStr::new)
|
|
||||||
.take(field_code_pos)
|
|
||||||
.chain(path_opt.iter().map(AsRef::as_ref)),
|
|
||||||
);
|
|
||||||
vec![process]
|
|
||||||
}
|
|
||||||
Some(invalid) => unreachable!("All valid variants were checked; got: {invalid}"),
|
|
||||||
None => vec![process::Command::new(program)],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Pass 2: Add remaining arguments that are not % to each process
|
let mut command = process::Command::new(&arguments[0]);
|
||||||
for arg in args_vec.iter().skip(field_code_pos) {
|
|
||||||
match arg.as_str() {
|
for arg in args {
|
||||||
// Consume path field codes or fail on codes we don't handle yet
|
match arg.to_os_str() {
|
||||||
field_code if arg.starts_with('%') => {
|
Ok(arg) => {
|
||||||
if !EXEC_HANDLERS.contains(&field_code)
|
command.arg(arg);
|
||||||
&& !DEPRECATED_HANDLERS.contains(&field_code)
|
}
|
||||||
{
|
Err(_) => {
|
||||||
log::warn!("unsupported Exec code {field_code:?} in {exec:?}");
|
tracing::error!("invalid string encoding in command");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
arg => {
|
}
|
||||||
for process in &mut processes {
|
|
||||||
process.arg(arg);
|
commands.push(command);
|
||||||
}
|
|
||||||
}
|
if !batch_process {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
for command in &processes {
|
for command in &commands {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Parsed program {} with args: {:?}",
|
"Parsed program {} with args: {:?}",
|
||||||
command.get_program().to_string_lossy(),
|
command.get_program().to_string_lossy(),
|
||||||
|
|
@ -157,13 +173,14 @@ pub fn exec_to_command(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(processes)
|
Some(commands)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_file_or_dir(path: impl AsRef<Path>) -> Option<url::Url> {
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
url::Url::from_file_path(&path)
|
pub enum MimeAppMatch {
|
||||||
.ok()
|
Exact,
|
||||||
.or_else(|| url::Url::from_directory_path(&path).ok())
|
Related,
|
||||||
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -172,14 +189,42 @@ pub struct MimeApp {
|
||||||
pub path: Option<PathBuf>,
|
pub path: Option<PathBuf>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub exec: Option<String>,
|
pub exec: Option<String>,
|
||||||
pub icon: widget::icon::Handle,
|
icon_name: Box<str>,
|
||||||
pub is_default: bool,
|
icon: std::sync::OnceLock<widget::icon::Handle>,
|
||||||
|
is_default: Arc<RwLock<FxHashSet<Box<str>>>>,
|
||||||
|
no_display: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MimeApp {
|
impl MimeApp {
|
||||||
//TODO: move to libcosmic, support multiple files
|
//TODO: move to libcosmic, support multiple files
|
||||||
pub fn command<O: AsRef<OsStr>>(&self, path_opt: &[O]) -> Option<Vec<process::Command>> {
|
pub fn command<O: AsRef<OsStr>>(&self, path_opt: &[O]) -> Option<Vec<process::Command>> {
|
||||||
exec_to_command(self.exec.as_deref()?, path_opt)
|
exec_to_command(
|
||||||
|
self.exec.as_deref()?,
|
||||||
|
&self.name,
|
||||||
|
self.path.as_deref(),
|
||||||
|
path_opt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_default(&self, mime: &Mime) -> bool {
|
||||||
|
self.is_default.read().unwrap().contains(mime.essence_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn no_display(&self) -> bool {
|
||||||
|
self.no_display.load(atomic::Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn icon(&self) -> widget::icon::Handle {
|
||||||
|
self.icon
|
||||||
|
.get_or_init(|| {
|
||||||
|
let name = &*self.icon_name;
|
||||||
|
if name.starts_with('/') {
|
||||||
|
cosmic::widget::icon::from_path(PathBuf::from(name))
|
||||||
|
} else {
|
||||||
|
cosmic::widget::icon::from_name(name).size(32).handle()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,38 +235,10 @@ impl AsRef<str> for MimeApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "desktop")]
|
|
||||||
impl From<&desktop::DesktopEntryData> for MimeApp {
|
|
||||||
fn from(app: &desktop::DesktopEntryData) -> Self {
|
|
||||||
Self {
|
|
||||||
id: app.id.clone(),
|
|
||||||
path: app.path.clone(),
|
|
||||||
name: app.name.clone(),
|
|
||||||
exec: app.exec.clone(),
|
|
||||||
icon: match &app.icon {
|
|
||||||
desktop::fde::IconSource::Name(name) => {
|
|
||||||
widget::icon::from_name(name.as_str()).size(32).handle()
|
|
||||||
}
|
|
||||||
desktop::fde::IconSource::Path(path) => widget::icon::from_path(path.clone()),
|
|
||||||
},
|
|
||||||
is_default: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "desktop")]
|
|
||||||
fn filename_eq(path_opt: &Option<PathBuf>, filename: &str) -> bool {
|
|
||||||
path_opt
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|path| path.file_name())
|
|
||||||
.is_some_and(|x| x == filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MimeAppCache {
|
pub struct MimeAppCache {
|
||||||
apps: Vec<MimeApp>,
|
apps: Vec<Arc<MimeApp>>,
|
||||||
cache: FxHashMap<Mime, Vec<MimeApp>>,
|
cache: FxHashMap<Mime, Vec<Arc<MimeApp>>>,
|
||||||
icons: FxHashMap<Mime, Box<[widget::icon::Handle]>>,
|
terminals: Vec<Arc<MimeApp>>,
|
||||||
terminals: Vec<MimeApp>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MimeAppCache {
|
impl MimeAppCache {
|
||||||
|
|
@ -229,154 +246,273 @@ impl MimeAppCache {
|
||||||
let mut mime_app_cache = Self {
|
let mut mime_app_cache = Self {
|
||||||
apps: Vec::new(),
|
apps: Vec::new(),
|
||||||
cache: FxHashMap::default(),
|
cache: FxHashMap::default(),
|
||||||
icons: FxHashMap::default(),
|
|
||||||
terminals: Vec::new(),
|
terminals: Vec::new(),
|
||||||
};
|
};
|
||||||
mime_app_cache.reload();
|
mime_app_cache.reload();
|
||||||
mime_app_cache
|
mime_app_cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_apps_for_mime(
|
||||||
|
&self,
|
||||||
|
mime_type: &Mime,
|
||||||
|
include_other: bool,
|
||||||
|
) -> Vec<(&Arc<MimeApp>, MimeAppMatch)> {
|
||||||
|
let mut results = Vec::new();
|
||||||
|
let mut dedupe = FxHashSet::default();
|
||||||
|
|
||||||
|
// start with exact matches
|
||||||
|
results.extend(
|
||||||
|
self.get(mime_type)
|
||||||
|
.iter()
|
||||||
|
.filter(|&mime_app| dedupe.insert(&mime_app.id))
|
||||||
|
.map(|mime_app| (mime_app, MimeAppMatch::Exact)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let include_mime = match mime_type.type_().as_str() {
|
||||||
|
"audio" => Some("video/mp4".parse::<Mime>().expect("video/mp4 mime")),
|
||||||
|
"text" => Some(mime_guess::mime::TEXT_PLAIN),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(mime) = include_mime {
|
||||||
|
results.extend(
|
||||||
|
self.get(&mime)
|
||||||
|
.iter()
|
||||||
|
.filter(|&mime_app| dedupe.insert(&mime_app.id))
|
||||||
|
.map(|mime_app| (mime_app, MimeAppMatch::Exact)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab matches based off of subclass / parent mime type
|
||||||
|
if let Some(parent_types) = crate::mime_icon::parent_mime_types(mime_type) {
|
||||||
|
for parent_type in parent_types {
|
||||||
|
results.extend(
|
||||||
|
self.get(&parent_type)
|
||||||
|
.iter()
|
||||||
|
.filter(|&mime_app| dedupe.insert(&mime_app.id))
|
||||||
|
.map(|mime_app| (mime_app, MimeAppMatch::Related)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if include_other {
|
||||||
|
results.extend({
|
||||||
|
let mut apps = self
|
||||||
|
.apps()
|
||||||
|
.iter()
|
||||||
|
.filter(|mime_app| !mime_app.no_display())
|
||||||
|
.filter(|&mime_app| dedupe.insert(&mime_app.id))
|
||||||
|
.map(|mime_app| (mime_app, MimeAppMatch::Other))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
apps.sort_by(|(a, _), (b, _)| {
|
||||||
|
crate::localize::LANGUAGE_SORTER.compare(&a.name, &b.name)
|
||||||
|
});
|
||||||
|
apps
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
results
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "desktop"))]
|
#[cfg(not(feature = "desktop"))]
|
||||||
pub fn reload(&mut self) {}
|
pub fn reload(&mut self) {}
|
||||||
|
|
||||||
// Only available when using desktop feature of libcosmic, which only works on Unix-likes
|
/// Reload mime types and their known app associations and defaults.
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
pub fn reload(&mut self) {
|
pub fn reload(&mut self) {
|
||||||
use crate::localize::LANGUAGE_SORTER;
|
use crate::localize::LANGUAGE_SORTER;
|
||||||
|
use crate::mime_icon;
|
||||||
|
use cosmic::desktop::fde;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
self.apps.clear();
|
self.apps.clear();
|
||||||
self.cache.clear();
|
self.cache.clear();
|
||||||
self.icons.clear();
|
|
||||||
self.terminals.clear();
|
self.terminals.clear();
|
||||||
|
|
||||||
//TODO: get proper locale?
|
|
||||||
let locale = &[];
|
|
||||||
|
|
||||||
// Load desktop applications by supported mime types
|
|
||||||
//TODO: hashmap for all apps by id?
|
|
||||||
let all_apps: Box<[_]> = desktop::load_applications(locale, false, None).collect();
|
|
||||||
for app in &all_apps {
|
|
||||||
//TODO: just collect apps that can be executed with a file argument?
|
|
||||||
if !app.mime_types.is_empty() {
|
|
||||||
self.apps.push(MimeApp::from(app));
|
|
||||||
}
|
|
||||||
for mime in &app.mime_types {
|
|
||||||
let apps = self
|
|
||||||
.cache
|
|
||||||
.entry(mime.clone())
|
|
||||||
.or_insert_with(|| Vec::with_capacity(1));
|
|
||||||
if !apps.iter().any(|x| x.id == app.id) {
|
|
||||||
apps.push(MimeApp::from(app));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for category in &app.categories {
|
|
||||||
if category == "TerminalEmulator" {
|
|
||||||
self.terminals.push(MimeApp::from(app));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut list = cosmic_mime_apps::List::default();
|
let mut list = cosmic_mime_apps::List::default();
|
||||||
|
|
||||||
let paths = cosmic_mime_apps::list_paths();
|
let paths = cosmic_mime_apps::list_paths();
|
||||||
list.load_from_paths(&paths);
|
list.load_from_paths(&paths);
|
||||||
|
let locales = fde::get_languages_from_env();
|
||||||
|
let desktop_entries = fde::Iter::new(fde::default_paths()).entries(Some(&locales));
|
||||||
|
let mime_icon_cache = mime_icon::MIME_ICON_CACHE.lock().unwrap();
|
||||||
|
let shared_mime_info = &mime_icon_cache.shared_mime_info;
|
||||||
|
let mut aliased_mimes = FxHashMap::default();
|
||||||
|
|
||||||
for (mime, filenames) in list
|
for desktop_entry in desktop_entries {
|
||||||
.added_associations
|
let name = desktop_entry
|
||||||
.iter()
|
.name(&locales)
|
||||||
.chain(list.default_apps.iter())
|
.unwrap_or_else(|| Cow::Borrowed(desktop_entry.id()));
|
||||||
{
|
|
||||||
for filename in filenames {
|
let app = Arc::new(MimeApp {
|
||||||
log::trace!("add {mime}={filename}");
|
id: desktop_entry.appid.clone(),
|
||||||
let apps = self
|
path: Some(desktop_entry.path.clone()),
|
||||||
.cache
|
name: name.into(),
|
||||||
.entry(mime.clone())
|
exec: desktop_entry.exec().map(String::from),
|
||||||
.or_insert_with(|| Vec::with_capacity(1));
|
icon_name: desktop_entry.icon().unwrap_or_default().into(),
|
||||||
if !apps.iter().any(|x| filename_eq(&x.path, filename)) {
|
icon: std::sync::OnceLock::new(),
|
||||||
if let Some(app) = all_apps.iter().find(|&x| filename_eq(&x.path, filename)) {
|
is_default: Arc::new(RwLock::default()),
|
||||||
apps.push(MimeApp::from(app));
|
no_display: Arc::new(AtomicBool::new(false)),
|
||||||
} else {
|
});
|
||||||
log::info!(
|
|
||||||
"failed to add association for {mime:?}: application {filename:?} not found"
|
tracing::info!(target: "mime-apps", id = app.id, "detected desktop entry");
|
||||||
);
|
|
||||||
|
self.apps.push(app.clone());
|
||||||
|
|
||||||
|
if desktop_entry
|
||||||
|
.categories()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.any(|c| c == "TerminalEmulator")
|
||||||
|
{
|
||||||
|
self.terminals.push(app.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache associations defined by the desktop entry.
|
||||||
|
let mime_types = desktop_entry.mime_type().unwrap_or_else(Vec::new);
|
||||||
|
let associated_mime_types = mime_types.iter().filter_map(|m| {
|
||||||
|
m.parse::<Mime>().ok().map(|mime| {
|
||||||
|
if let Some(unaliased) = shared_mime_info.unalias_mime_type(&mime) {
|
||||||
|
aliased_mimes.insert(unaliased.clone(), mime);
|
||||||
|
return unaliased;
|
||||||
|
}
|
||||||
|
|
||||||
|
mime
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
for mime in associated_mime_types {
|
||||||
|
let apps = self.cache.entry(mime.clone()).or_default();
|
||||||
|
if apps.iter().all(|cached_app| cached_app.id != app.id) {
|
||||||
|
apps.push(app.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache added associations from mimeapps lists.
|
||||||
|
for (mut added_mime, added_apps) in &list.added_associations {
|
||||||
|
let _unaliased;
|
||||||
|
if let Some(unaliased) = shared_mime_info.unalias_mime_type(added_mime) {
|
||||||
|
aliased_mimes.insert(unaliased.clone(), added_mime.clone());
|
||||||
|
_unaliased = unaliased;
|
||||||
|
added_mime = &_unaliased;
|
||||||
|
}
|
||||||
|
|
||||||
|
for added_app in added_apps {
|
||||||
|
if let Some(app) = self
|
||||||
|
.apps
|
||||||
|
.iter()
|
||||||
|
.find(|cached| cached.id.as_str() == added_app.as_ref())
|
||||||
|
{
|
||||||
|
let apps = self.cache.entry(added_mime.clone()).or_default();
|
||||||
|
if apps.iter().all(|cached_app| cached_app.id != app.id) {
|
||||||
|
apps.push(app.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (mime, filenames) in list.removed_associations.iter() {
|
// Remove associations
|
||||||
for filename in filenames {
|
for (mut removed_mime, removed_apps) in &list.removed_associations {
|
||||||
log::trace!("remove {mime}={filename}");
|
let _unaliased;
|
||||||
if let Some(apps) = self.cache.get_mut(mime) {
|
if let Some(unaliased) = shared_mime_info.unalias_mime_type(removed_mime) {
|
||||||
apps.retain(|x| !filename_eq(&x.path, filename));
|
aliased_mimes.insert(unaliased.clone(), removed_mime.clone());
|
||||||
|
_unaliased = unaliased;
|
||||||
|
removed_mime = &_unaliased;
|
||||||
|
}
|
||||||
|
|
||||||
|
for removed_app in removed_apps {
|
||||||
|
if let Some(app) = self
|
||||||
|
.apps
|
||||||
|
.iter()
|
||||||
|
.find(|cached| cached.id.as_str() == removed_app.as_ref())
|
||||||
|
&& let Some(apps) = self.cache.get_mut(removed_mime)
|
||||||
|
{
|
||||||
|
apps.retain(|cached_app| cached_app.id != app.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (mime, filenames) in list.default_apps.iter() {
|
// Fetch defaults and sort apps by their default precedence.
|
||||||
for filename in filenames {
|
for (mime, mut apps) in std::mem::take(&mut self.cache).into_iter() {
|
||||||
log::trace!("default {mime}={filename}");
|
let defaults = list.default_app_for(&mime);
|
||||||
if let Some(apps) = self.cache.get_mut(mime) {
|
let aliased_defaults = aliased_mimes
|
||||||
let mut found = false;
|
.get(&mime)
|
||||||
for app in apps.iter_mut() {
|
.and_then(|mime| list.default_app_for(mime));
|
||||||
if filename_eq(&app.path, filename) {
|
|
||||||
app.is_default = true;
|
let cache = self
|
||||||
found = true;
|
.cache
|
||||||
} else {
|
.entry(mime.clone())
|
||||||
app.is_default = false;
|
.or_insert_with(|| Vec::with_capacity(apps.len()));
|
||||||
}
|
|
||||||
}
|
// Sort cached apps for this mime by default precedence.
|
||||||
|
for default in defaults
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.chain(aliased_defaults.into_iter().flatten())
|
||||||
|
{
|
||||||
|
let default = default.strip_suffix(".desktop").unwrap_or(default.as_ref());
|
||||||
|
let mut found_any = false;
|
||||||
|
apps.retain(|app| {
|
||||||
|
let found = app.id.as_str() == default;
|
||||||
if found {
|
if found {
|
||||||
break;
|
app.is_default
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.insert(mime.essence_str().into());
|
||||||
|
cache.push(app.clone());
|
||||||
|
found_any = true;
|
||||||
}
|
}
|
||||||
log::debug!(
|
|
||||||
"failed to set default for {mime:?}: application {filename:?} not found"
|
!found
|
||||||
);
|
});
|
||||||
|
|
||||||
|
if !found_any && let Some(app) = self.apps.iter().find(|app| app.id == default) {
|
||||||
|
app.is_default
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.insert(mime.essence_str().into());
|
||||||
|
cache.push(app.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort remaining apps by name
|
||||||
|
apps.sort_by(|a, b| LANGUAGE_SORTER.compare(&a.name, &b.name));
|
||||||
|
cache.extend_from_slice(&apps);
|
||||||
|
|
||||||
|
tracing::debug!(target: "mime-apps", mime = mime.essence_str(), apps = ?(cache.iter().map(|app| &*app.id).collect::<Vec<&str>>()), "mime defaults found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort apps by name
|
let associated: rustc_hash::FxHashSet<&str> = self
|
||||||
self.apps
|
.cache
|
||||||
.sort_by(|a, b| match (a.is_default, b.is_default) {
|
.values()
|
||||||
(true, false) => Ordering::Less,
|
.flatten()
|
||||||
(false, true) => Ordering::Greater,
|
.map(|app| app.id.as_str())
|
||||||
_ => LANGUAGE_SORTER.compare(&a.name, &b.name),
|
.collect();
|
||||||
});
|
for app in &self.apps {
|
||||||
for apps in self.cache.values_mut() {
|
app.no_display.store(
|
||||||
apps.sort_by(|a, b| match (a.is_default, b.is_default) {
|
!associated.contains(app.id.as_str()),
|
||||||
(true, false) => Ordering::Less,
|
atomic::Ordering::Relaxed,
|
||||||
(false, true) => Ordering::Greater,
|
);
|
||||||
_ => LANGUAGE_SORTER.compare(&a.name, &b.name),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy icons to special cache
|
|
||||||
//TODO: adjust dropdown API so this is no longer needed
|
|
||||||
self.icons.extend(self.cache.iter().map(|(mime, apps)| {
|
|
||||||
(
|
|
||||||
mime.clone(),
|
|
||||||
apps.iter().map(|app| app.icon.clone()).collect(),
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
|
|
||||||
let elapsed = start.elapsed();
|
let elapsed = start.elapsed();
|
||||||
log::info!("loaded mime app cache in {elapsed:?}");
|
tracing::info!(target: "mime-apps", "loaded mime app cache in {elapsed:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apps(&self) -> &[MimeApp] {
|
pub fn apps(&self) -> &[Arc<MimeApp>] {
|
||||||
&self.apps
|
&self.apps
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, key: &Mime) -> &[MimeApp] {
|
pub fn get(&self, key: &Mime) -> &[Arc<MimeApp>] {
|
||||||
self.cache.get(key).map_or(&[], Vec::as_slice)
|
self.cache.get(key).map_or(&[], Vec::as_slice)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn icons(&self, key: &Mime) -> &[widget::icon::Handle] {
|
pub fn icons(&self, key: &Mime) -> Vec<widget::icon::Handle> {
|
||||||
self.icons.get(key).map_or(&[], Box::as_ref)
|
self.cache
|
||||||
|
.get(key)
|
||||||
|
.map_or_else(Vec::new, |apps| apps.iter().map(|app| app.icon()).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_default_terminal(&self) -> Option<String> {
|
fn get_default_terminal(&self) -> Option<String> {
|
||||||
|
|
@ -394,14 +530,19 @@ impl MimeAppCache {
|
||||||
.map(|string| string.trim().replace(".desktop", ""))
|
.map(|string| string.trim().replace(".desktop", ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terminal(&self) -> Option<&MimeApp> {
|
pub fn terminal(&self) -> Option<&Arc<MimeApp>> {
|
||||||
//TODO: consider rules in https://github.com/Vladimir-csp/xdg-terminal-exec
|
//TODO: consider rules in https://github.com/Vladimir-csp/xdg-terminal-exec
|
||||||
// The current approach works but might not adhere to the spec (yet)
|
// The current approach works but might not adhere to the spec (yet)
|
||||||
|
|
||||||
// Look for and return preferred terminals
|
// Look for and return preferred terminals
|
||||||
//TODO: fallback order beyond cosmic-term?
|
//TODO: fallback order beyond cosmic-term?
|
||||||
|
|
||||||
let mut preference_order = vec!["com.system76.CosmicTerm".to_string()];
|
// Yoda: cosmic-yoterm (our fork) wins over upstream cosmic-term if both
|
||||||
|
// are installed — useful when xdg-mime default is not set.
|
||||||
|
let mut preference_order = vec![
|
||||||
|
"com.aditua.CosmicYoterm".to_string(),
|
||||||
|
"com.system76.CosmicTerm".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
if let Some(id) = self.get_default_terminal() {
|
if let Some(id) = self.get_default_terminal() {
|
||||||
preference_order.insert(0, id);
|
preference_order.insert(0, id);
|
||||||
|
|
@ -475,11 +616,43 @@ impl Default for MimeAppCache {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::exec_to_command;
|
use super::exec_to_command;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keys_within_words() {
|
||||||
|
let exec = "/usr/bin/foo --option=%f";
|
||||||
|
let paths = ["file1"];
|
||||||
|
let commands = exec_to_command(exec, "keys_within_words", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
|
assert_eq!(1, commands.len());
|
||||||
|
let command = commands.first().unwrap();
|
||||||
|
|
||||||
|
assert_eq!("/usr/bin/foo", command.get_program().to_str().unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
"--option=file1",
|
||||||
|
command.get_args().next().unwrap().to_str().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_path_f_field_code() {
|
||||||
|
let exec = "/usr/bin/foo %f";
|
||||||
|
let paths: [&str; 0] = [];
|
||||||
|
let commands = exec_to_command(exec, "no_path_f_field_code", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
|
assert_eq!(1, commands.len());
|
||||||
|
let command = commands.first().unwrap();
|
||||||
|
|
||||||
|
assert_eq!("/usr/bin/foo", command.get_program().to_str().unwrap());
|
||||||
|
assert_eq!(0, command.get_args().len());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_path_f_field_code() {
|
fn one_path_f_field_code() {
|
||||||
let exec = "/usr/bin/foo %f";
|
let exec = "/usr/bin/foo %f";
|
||||||
let paths = ["file1"];
|
let paths = ["file1"];
|
||||||
let commands = exec_to_command(exec, &paths).expect("Should parse valid exec");
|
let commands = exec_to_command(exec, "one_path_f_field_code", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
assert_eq!(1, commands.len());
|
assert_eq!(1, commands.len());
|
||||||
let command = commands.first().unwrap();
|
let command = commands.first().unwrap();
|
||||||
|
|
@ -494,31 +667,40 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn one_path_F_field_code() {
|
fn one_path_F_field_code() {
|
||||||
let exec = "/usr/bin/bar %F";
|
let exec = "/usr/bin/cosmic-term -w %F";
|
||||||
let paths = ["cat"];
|
let paths = ["/home/user"];
|
||||||
let commands = exec_to_command(exec, &paths).expect("Should parse valid exec");
|
let commands = exec_to_command(exec, "one_path_F_field_code", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
assert_eq!(1, commands.len());
|
assert_eq!(1, commands.len());
|
||||||
let command = commands.first().unwrap();
|
let command = commands.first().unwrap();
|
||||||
|
let mut args = command.get_args();
|
||||||
|
|
||||||
assert_eq!("/usr/bin/bar", command.get_program().to_str().unwrap());
|
assert_eq!(
|
||||||
assert_eq!("cat", command.get_args().next().unwrap().to_str().unwrap());
|
"/usr/bin/cosmic-term",
|
||||||
|
command.get_program().to_str().unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!("-w", args.next().unwrap().to_str().unwrap());
|
||||||
|
assert_eq!(paths[0], args.next().unwrap().to_str().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_path_u_field_code() {
|
fn one_path_u_field_code() {
|
||||||
let exec = "/usr/bin/foobar %u";
|
let exec = "/usr/bin/cosmic-term -w %u";
|
||||||
let paths = ["/home/josh/krumpli"];
|
let paths = ["/home/user"];
|
||||||
let commands = exec_to_command(exec, &paths).expect("Should parse valid exec");
|
let commands = exec_to_command(exec, "one_path_u_field_code", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
assert_eq!(1, commands.len());
|
assert_eq!(1, commands.len());
|
||||||
let command = commands.first().unwrap();
|
let command = commands.first().unwrap();
|
||||||
|
let mut args = command.get_args();
|
||||||
|
|
||||||
assert_eq!("/usr/bin/foobar", command.get_program().to_str().unwrap());
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*paths.first().unwrap(),
|
"/usr/bin/cosmic-term",
|
||||||
command.get_args().next().unwrap().to_str().unwrap()
|
command.get_program().to_str().unwrap()
|
||||||
);
|
);
|
||||||
|
assert_eq!("-w", args.next().unwrap().to_str().unwrap());
|
||||||
|
assert_eq!(paths[0], args.next().unwrap().to_str().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -526,7 +708,8 @@ mod tests {
|
||||||
fn one_path_U_field_code() {
|
fn one_path_U_field_code() {
|
||||||
let exec = "/usr/bin/rmrfbye %U";
|
let exec = "/usr/bin/rmrfbye %U";
|
||||||
let paths = ["/"];
|
let paths = ["/"];
|
||||||
let commands = exec_to_command(exec, &paths).expect("Should parse valid exec");
|
let commands = exec_to_command(exec, "one_path_U_field_code", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
assert_eq!(1, commands.len());
|
assert_eq!(1, commands.len());
|
||||||
let command = commands.first().unwrap();
|
let command = commands.first().unwrap();
|
||||||
|
|
@ -542,7 +725,8 @@ mod tests {
|
||||||
"/usr/share/games/psp/miku.iso",
|
"/usr/share/games/psp/miku.iso",
|
||||||
"/usr/share/games/psp/eternia.iso",
|
"/usr/share/games/psp/eternia.iso",
|
||||||
];
|
];
|
||||||
let commands = exec_to_command(exec, &paths).expect("Should parse valid exec");
|
let commands = exec_to_command(exec, "mult_path_f_field_code", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
assert_eq!(paths.len(), commands.len());
|
assert_eq!(paths.len(), commands.len());
|
||||||
for (command, path) in commands.into_iter().zip(paths.iter()) {
|
for (command, path) in commands.into_iter().zip(paths.iter()) {
|
||||||
|
|
@ -562,7 +746,8 @@ mod tests {
|
||||||
"/usr/share/games/doom2/hr.wad",
|
"/usr/share/games/doom2/hr.wad",
|
||||||
"/usr/share/games/doom2/hrmus.wad",
|
"/usr/share/games/doom2/hrmus.wad",
|
||||||
];
|
];
|
||||||
let commands = exec_to_command(exec, &paths).expect("Should parse valid exec");
|
let commands = exec_to_command(exec, "mult_path_F_field_code", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
assert_eq!(1, commands.len());
|
assert_eq!(1, commands.len());
|
||||||
let command = commands.first().unwrap();
|
let command = commands.first().unwrap();
|
||||||
|
|
@ -584,7 +769,8 @@ mod tests {
|
||||||
"https://redox-os.org/",
|
"https://redox-os.org/",
|
||||||
"https://system76.com/",
|
"https://system76.com/",
|
||||||
];
|
];
|
||||||
let commands = exec_to_command(exec, &paths).expect("Should parse valid exec");
|
let commands = exec_to_command(exec, "mult_path_u_field_code", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
assert_eq!(paths.len(), commands.len());
|
assert_eq!(paths.len(), commands.len());
|
||||||
for (command, path) in commands.into_iter().zip(paths.iter()) {
|
for (command, path) in commands.into_iter().zip(paths.iter()) {
|
||||||
|
|
@ -607,7 +793,8 @@ mod tests {
|
||||||
"frieren01.mkv",
|
"frieren01.mkv",
|
||||||
"rtmp://example.org/this/video/doesnt/exist.avi",
|
"rtmp://example.org/this/video/doesnt/exist.avi",
|
||||||
];
|
];
|
||||||
let commands = exec_to_command(exec, &paths).expect("Should parse valid exec");
|
let commands = exec_to_command(exec, "mult_path_U_field_code", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
assert_eq!(1, commands.len());
|
assert_eq!(1, commands.len());
|
||||||
let command = commands.first().unwrap();
|
let command = commands.first().unwrap();
|
||||||
|
|
@ -635,7 +822,8 @@ mod tests {
|
||||||
"@@u",
|
"@@u",
|
||||||
];
|
];
|
||||||
let paths = ["file1.rs", "file2.rs"];
|
let paths = ["file1.rs", "file2.rs"];
|
||||||
let commands = exec_to_command(exec, &paths).expect("Should parse valid exec");
|
let commands = exec_to_command(exec, "flatpak_style_exec", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
assert_eq!(1, commands.len());
|
assert_eq!(1, commands.len());
|
||||||
let command = commands.first().unwrap();
|
let command = commands.first().unwrap();
|
||||||
|
|
@ -658,7 +846,8 @@ mod tests {
|
||||||
"file:///usr/share/games/roguelike/mods/mod1",
|
"file:///usr/share/games/roguelike/mods/mod1",
|
||||||
"file:///usr/share/games/roguelike/mods/mod2",
|
"file:///usr/share/games/roguelike/mods/mod2",
|
||||||
];
|
];
|
||||||
let commands = exec_to_command(exec, &paths).expect("Should parse valid exec");
|
let commands = exec_to_command(exec, "multiple_field_codes", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
assert_eq!(1, commands.len());
|
assert_eq!(1, commands.len());
|
||||||
let command = commands.first().unwrap();
|
let command = commands.first().unwrap();
|
||||||
|
|
@ -691,7 +880,8 @@ mod tests {
|
||||||
];
|
];
|
||||||
let paths = ["rust_game_dev.pdf", "superhero_ferris.epub"];
|
let paths = ["rust_game_dev.pdf", "superhero_ferris.epub"];
|
||||||
let args_trailing = ["@@"];
|
let args_trailing = ["@@"];
|
||||||
let commands = exec_to_command(exec, &paths).expect("Should parse valid exec");
|
let commands = exec_to_command(exec, "sandwiched_field_code", None, &paths)
|
||||||
|
.expect("Should parse valid exec");
|
||||||
|
|
||||||
assert_eq!(1, commands.len());
|
assert_eq!(1, commands.len());
|
||||||
let command = commands.first().unwrap();
|
let command = commands.first().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,9 @@
|
||||||
use cosmic::widget::icon;
|
use cosmic::widget::icon;
|
||||||
use mime_guess::Mime;
|
use mime_guess::Mime;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use std::{
|
use std::fs;
|
||||||
fs,
|
use std::path::Path;
|
||||||
path::Path,
|
use std::sync::{LazyLock, Mutex};
|
||||||
sync::{LazyLock, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const FALLBACK_MIME_ICON: &str = "text-x-generic";
|
pub const FALLBACK_MIME_ICON: &str = "text-x-generic";
|
||||||
|
|
||||||
|
|
@ -17,20 +15,21 @@ struct MimeIconKey {
|
||||||
size: u16,
|
size: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MimeIconCache {
|
#[derive(Default)]
|
||||||
|
pub struct MimeIconCache {
|
||||||
cache: FxHashMap<MimeIconKey, Option<icon::Handle>>,
|
cache: FxHashMap<MimeIconKey, Option<icon::Handle>>,
|
||||||
shared_mime_info: xdg_mime::SharedMimeInfo,
|
#[cfg(unix)]
|
||||||
|
pub shared_mime_info: xdg_mime::SharedMimeInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MimeIconCache {
|
impl MimeIconCache {
|
||||||
pub fn new() -> Self {
|
#[cfg(not(unix))]
|
||||||
Self {
|
pub fn get(&mut self, _key: MimeIconKey) -> Option<icon::Handle> {
|
||||||
cache: FxHashMap::default(),
|
None
|
||||||
shared_mime_info: xdg_mime::SharedMimeInfo::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&mut self, key: MimeIconKey) -> Option<icon::Handle> {
|
#[cfg(unix)]
|
||||||
|
fn get(&mut self, key: MimeIconKey) -> Option<icon::Handle> {
|
||||||
self.cache
|
self.cache
|
||||||
.entry(key)
|
.entry(key)
|
||||||
.or_insert_with_key(|key| {
|
.or_insert_with_key(|key| {
|
||||||
|
|
@ -39,7 +38,7 @@ impl MimeIconCache {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let icon_name = icon_names.remove(0);
|
let icon_name = icon_names.remove(0);
|
||||||
let mut named = icon::from_name(icon_name).size(key.size);
|
let mut named = icon::from_name(icon_name).prefer_svg(true).size(key.size);
|
||||||
if !icon_names.is_empty() {
|
if !icon_names.is_empty() {
|
||||||
let fallback_names =
|
let fallback_names =
|
||||||
icon_names.into_iter().map(std::borrow::Cow::from).collect();
|
icon_names.into_iter().map(std::borrow::Cow::from).collect();
|
||||||
|
|
@ -50,9 +49,20 @@ impl MimeIconCache {
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static MIME_ICON_CACHE: LazyLock<Mutex<MimeIconCache>> =
|
|
||||||
LazyLock::new(|| Mutex::new(MimeIconCache::new()));
|
|
||||||
|
|
||||||
|
pub static MIME_ICON_CACHE: LazyLock<Mutex<MimeIconCache>> =
|
||||||
|
LazyLock::new(|| Mutex::new(MimeIconCache::default()));
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
pub fn mime_for_path(
|
||||||
|
path: impl AsRef<Path>,
|
||||||
|
metadata_opt: Option<&fs::Metadata>,
|
||||||
|
remote: bool,
|
||||||
|
) -> Mime {
|
||||||
|
mime_guess::from_path(path).first_or_octet_stream()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn mime_for_path(
|
pub fn mime_for_path(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
metadata_opt: Option<&fs::Metadata>,
|
metadata_opt: Option<&fs::Metadata>,
|
||||||
|
|
@ -62,6 +72,7 @@ pub fn mime_for_path(
|
||||||
let mime_icon_cache = MIME_ICON_CACHE.lock().unwrap();
|
let mime_icon_cache = MIME_ICON_CACHE.lock().unwrap();
|
||||||
// Try the shared mime info cache first
|
// Try the shared mime info cache first
|
||||||
let mut gb = mime_icon_cache.shared_mime_info.guess_mime_type();
|
let mut gb = mime_icon_cache.shared_mime_info.guess_mime_type();
|
||||||
|
gb.zero_size(false);
|
||||||
if remote {
|
if remote {
|
||||||
if let Some(file_name) = path.file_name().and_then(std::ffi::OsStr::to_str) {
|
if let Some(file_name) = path.file_name().and_then(std::ffi::OsStr::to_str) {
|
||||||
gb.file_name(file_name);
|
gb.file_name(file_name);
|
||||||
|
|
@ -96,12 +107,28 @@ pub fn mime_icon(mime: Mime, size: u16) -> icon::Handle {
|
||||||
let mut mime_icon_cache = MIME_ICON_CACHE.lock().unwrap();
|
let mut mime_icon_cache = MIME_ICON_CACHE.lock().unwrap();
|
||||||
match mime_icon_cache.get(MimeIconKey { mime, size }) {
|
match mime_icon_cache.get(MimeIconKey { mime, size }) {
|
||||||
Some(handle) => handle,
|
Some(handle) => handle,
|
||||||
None => icon::from_name(FALLBACK_MIME_ICON).size(size).handle(),
|
None => icon::from_name(FALLBACK_MIME_ICON)
|
||||||
|
.prefer_svg(true)
|
||||||
|
.size(size)
|
||||||
|
.handle(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
pub fn parent_mime_types(_mime: &Mime) -> Option<Vec<Mime>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn parent_mime_types(mime: &Mime) -> Option<Vec<Mime>> {
|
pub fn parent_mime_types(mime: &Mime) -> Option<Vec<Mime>> {
|
||||||
let mime_icon_cache = MIME_ICON_CACHE.lock().unwrap();
|
let mime_icon_cache = MIME_ICON_CACHE.lock().unwrap();
|
||||||
|
|
||||||
mime_icon_cache.shared_mime_info.get_parents_aliased(mime)
|
mime_icon_cache.shared_mime_info.get_parents_aliased(mime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_mime_subclass_of(mime_type: &Mime, base: &Mime) -> bool {
|
||||||
|
let mime_icon_cache = MIME_ICON_CACHE.lock().unwrap();
|
||||||
|
|
||||||
|
mime_icon_cache
|
||||||
|
.shared_mime_info
|
||||||
|
.mime_type_subclass(mime_type, base)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,32 @@
|
||||||
use cosmic::{
|
use cosmic::iced::futures::SinkExt;
|
||||||
Task,
|
use cosmic::iced::{Subscription, stream};
|
||||||
iced::{Subscription, futures::SinkExt, stream},
|
use cosmic::{Task, widget};
|
||||||
widget,
|
use gio::glib;
|
||||||
};
|
use gio::prelude::*;
|
||||||
use gio::{glib, prelude::*};
|
use std::any::TypeId;
|
||||||
use std::{any::TypeId, cell::Cell, future::pending, hash::Hash, path::PathBuf, sync::Arc};
|
use std::cell::Cell;
|
||||||
use tokio::sync::{Mutex, mpsc};
|
use std::future::pending;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use super::{Mounter, MounterAuth, MounterItem, MounterItems, MounterMessage};
|
use super::{Mounter, MounterAuth, MounterItem, MounterItems, MounterMessage};
|
||||||
use crate::{
|
use crate::config::IconSizes;
|
||||||
config::IconSizes,
|
use crate::err_str;
|
||||||
err_str,
|
use crate::tab::{self, ChecksumState, DirSize, ItemMetadata, ItemThumbnail, Location};
|
||||||
tab::{self, DirSize, ItemMetadata, ItemThumbnail, Location},
|
|
||||||
};
|
|
||||||
|
|
||||||
const TARGET_URI_ATTRIBUTE: &str = "standard::target-uri";
|
const TARGET_URI_ATTRIBUTE: &str = "standard::target-uri";
|
||||||
|
|
||||||
|
// Attributes requested when listing a network directory.
|
||||||
|
const SCAN_ATTRIBUTES: &str = "standard::name,\
|
||||||
|
standard::display-name,\
|
||||||
|
standard::type,\
|
||||||
|
standard::size,\
|
||||||
|
standard::icon,\
|
||||||
|
standard::is-hidden,\
|
||||||
|
time::modified";
|
||||||
|
|
||||||
fn resolve_uri(uri: &str) -> (String, gio::File) {
|
fn resolve_uri(uri: &str) -> (String, gio::File) {
|
||||||
let file = gio::File::for_uri(uri);
|
let file = gio::File::for_uri(uri);
|
||||||
// Resolve the target-uri if it exists
|
// Resolve the target-uri if it exists
|
||||||
|
|
@ -117,9 +128,27 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
||||||
Box::from([])
|
Box::from([])
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// `filesystem::remote` belongs to the filesystem namespace, which `enumerate_children`
|
||||||
|
// never fills in, so it has to be queried once for the directory being listed.
|
||||||
|
let remote = file
|
||||||
|
.query_filesystem_info(
|
||||||
|
gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE,
|
||||||
|
gio::Cancellable::NONE,
|
||||||
|
)
|
||||||
|
.map(|info| info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE))
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
log::warn!("failed to get GIO filesystem info for {uri}: {err}");
|
||||||
|
// Assume remote, so per-entry work is skipped rather than retried
|
||||||
|
true
|
||||||
|
});
|
||||||
|
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
for info_res in file
|
for info_res in file
|
||||||
.enumerate_children("*", gio::FileQueryInfoFlags::NONE, gio::Cancellable::NONE)
|
.enumerate_children(
|
||||||
|
SCAN_ATTRIBUTES,
|
||||||
|
gio::FileQueryInfoFlags::NONE,
|
||||||
|
gio::Cancellable::NONE,
|
||||||
|
)
|
||||||
.map_err(err_str)?
|
.map_err(err_str)?
|
||||||
{
|
{
|
||||||
let info = info_res.map_err(err_str)?;
|
let info = info_res.map_err(err_str)?;
|
||||||
|
|
@ -131,13 +160,17 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
||||||
//TODO: what is the best way to resolve shortcuts?
|
//TODO: what is the best way to resolve shortcuts?
|
||||||
let location = Location::Network(uri, display_name.clone(), file.child(&name).path());
|
let location = Location::Network(uri, display_name.clone(), file.child(&name).path());
|
||||||
|
|
||||||
let metadata = if !force_dir && !info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE) {
|
let metadata = if force_dir {
|
||||||
|
ItemMetadata::SimpleDir { entries: 0 }
|
||||||
|
} else {
|
||||||
let mtime = info.attribute_uint64(gio::FILE_ATTRIBUTE_TIME_MODIFIED);
|
let mtime = info.attribute_uint64(gio::FILE_ATTRIBUTE_TIME_MODIFIED);
|
||||||
let is_dir = matches!(info.file_type(), gio::FileType::Directory);
|
let is_dir = matches!(info.file_type(), gio::FileType::Directory);
|
||||||
let size_opt = (!is_dir).then_some(info.size() as u64);
|
let size_opt = (!is_dir).then_some(info.size() as u64);
|
||||||
let mut children_opt = None;
|
let mut children_opt = None;
|
||||||
|
|
||||||
if is_dir {
|
// Counting children costs a directory listing per entry, which is far too
|
||||||
|
// expensive on a remote filesystem
|
||||||
|
if is_dir && !remote {
|
||||||
if let Some(path) = file.child(&name).path() {
|
if let Some(path) = file.child(&name).path() {
|
||||||
//TODO: calculate children in the background (and make it cancellable?)
|
//TODO: calculate children in the background (and make it cancellable?)
|
||||||
match std::fs::read_dir(&path) {
|
match std::fs::read_dir(&path) {
|
||||||
|
|
@ -157,9 +190,8 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
||||||
mtime,
|
mtime,
|
||||||
size_opt,
|
size_opt,
|
||||||
children_opt,
|
children_opt,
|
||||||
|
is_dir,
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ItemMetadata::SimpleDir { entries: 0 }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mime, icon_handle_grid, icon_handle_list, icon_handle_list_condensed) = {
|
let (mime, icon_handle_grid, icon_handle_list, icon_handle_list_condensed) = {
|
||||||
|
|
@ -199,6 +231,7 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
||||||
metadata,
|
metadata,
|
||||||
hidden,
|
hidden,
|
||||||
location_opt: Some(location),
|
location_opt: Some(location),
|
||||||
|
image_dimensions: None,
|
||||||
mime,
|
mime,
|
||||||
icon_handle_grid,
|
icon_handle_grid,
|
||||||
icon_handle_list,
|
icon_handle_list,
|
||||||
|
|
@ -213,6 +246,7 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
||||||
//TODO: scan directory size on gvfs mounts?
|
//TODO: scan directory size on gvfs mounts?
|
||||||
dir_size: DirSize::NotDirectory,
|
dir_size: DirSize::NotDirectory,
|
||||||
cut: false,
|
cut: false,
|
||||||
|
checksums: ChecksumState::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(items)
|
Ok(items)
|
||||||
|
|
@ -229,7 +263,10 @@ fn dir_info(uri: &str) -> Result<(String, String, Option<PathBuf>), glib::Error>
|
||||||
Ok((resolved_uri, info.display_name().into(), file.path()))
|
Ok((resolved_uri, info.display_name().into(), file.path()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mount_op(uri: String, event_tx: mpsc::UnboundedSender<Event>) -> gio::MountOperation {
|
fn mount_op(
|
||||||
|
uri: String,
|
||||||
|
event_tx: std::sync::Weak<crate::channel::Sender<Event>>,
|
||||||
|
) -> gio::MountOperation {
|
||||||
let mount_op = gio::MountOperation::new();
|
let mount_op = gio::MountOperation::new();
|
||||||
mount_op.connect_ask_password(
|
mount_op.connect_ask_password(
|
||||||
move |mount_op, message, default_user, default_domain, flags| {
|
move |mount_op, message, default_user, default_domain, flags| {
|
||||||
|
|
@ -252,9 +289,9 @@ fn mount_op(uri: String, event_tx: mpsc::UnboundedSender<Event>) -> gio::MountOp
|
||||||
.then_some(false),
|
.then_some(false),
|
||||||
};
|
};
|
||||||
let (auth_tx, mut auth_rx) = mpsc::channel(1);
|
let (auth_tx, mut auth_rx) = mpsc::channel(1);
|
||||||
event_tx
|
if let Some(event_tx) = event_tx.upgrade() {
|
||||||
.send(Event::NetworkAuth(uri.clone(), auth, auth_tx))
|
event_tx.send(Event::NetworkAuth(uri.clone(), auth, auth_tx));
|
||||||
.unwrap();
|
}
|
||||||
//TODO: async recv?
|
//TODO: async recv?
|
||||||
if let Some(auth) = auth_rx.blocking_recv() {
|
if let Some(auth) = auth_rx.blocking_recv() {
|
||||||
if auth.anonymous_opt == Some(true) {
|
if auth.anonymous_opt == Some(true) {
|
||||||
|
|
@ -357,37 +394,45 @@ impl Item {
|
||||||
|
|
||||||
pub struct Gvfs {
|
pub struct Gvfs {
|
||||||
command_tx: mpsc::UnboundedSender<Cmd>,
|
command_tx: mpsc::UnboundedSender<Cmd>,
|
||||||
event_rx: Arc<Mutex<mpsc::UnboundedReceiver<Event>>>,
|
event_rx: Arc<crate::channel::Receiver<Event>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gvfs {
|
impl Gvfs {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
//TODO: switch to using gvfs-zbus which will better integrate with async rust
|
//TODO: switch to using gvfs-zbus which will better integrate with async rust
|
||||||
let (command_tx, mut command_rx) = mpsc::unbounded_channel();
|
let (command_tx, mut command_rx) = mpsc::unbounded_channel();
|
||||||
let (event_tx, event_rx) = mpsc::unbounded_channel();
|
let (event_tx, event_rx) = crate::channel::channel();
|
||||||
|
let event_tx = Arc::new(event_tx);
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let main_loop = glib::MainLoop::new(None, false);
|
let main_loop = glib::MainLoop::new(None, false);
|
||||||
main_loop.context().spawn_local(async move {
|
main_loop.context().spawn_local(async move {
|
||||||
|
let event_tx = Arc::downgrade(&event_tx);
|
||||||
let monitor = gio::VolumeMonitor::get();
|
let monitor = gio::VolumeMonitor::get();
|
||||||
{
|
{
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
monitor.connect_mount_changed(move |_monitor, mount| {
|
monitor.connect_mount_changed(move |_monitor, mount| {
|
||||||
log::info!("mount changed {}", MountExt::name(mount));
|
log::info!("mount changed {}", MountExt::name(mount));
|
||||||
event_tx.send(Event::Changed).unwrap();
|
if let Some(event_tx) = event_tx.upgrade() {
|
||||||
|
event_tx.send(Event::Changed);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
monitor.connect_mount_added(move |_monitor, mount| {
|
monitor.connect_mount_added(move |_monitor, mount| {
|
||||||
log::info!("mount added {}", MountExt::name(mount));
|
log::info!("mount added {}", MountExt::name(mount));
|
||||||
event_tx.send(Event::Changed).unwrap();
|
if let Some(event_tx) = event_tx.upgrade() {
|
||||||
|
event_tx.send(Event::Changed);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
monitor.connect_mount_removed(move |_monitor, mount| {
|
monitor.connect_mount_removed(move |_monitor, mount| {
|
||||||
log::info!("mount removed {}", MountExt::name(mount));
|
log::info!("mount removed {}", MountExt::name(mount));
|
||||||
event_tx.send(Event::Changed).unwrap();
|
if let Some(event_tx) = event_tx.upgrade() {
|
||||||
|
event_tx.send(Event::Changed);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -395,21 +440,27 @@ impl Gvfs {
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
monitor.connect_volume_changed(move |_monitor, volume| {
|
monitor.connect_volume_changed(move |_monitor, volume| {
|
||||||
log::info!("volume changed {}", VolumeExt::name(volume));
|
log::info!("volume changed {}", VolumeExt::name(volume));
|
||||||
event_tx.send(Event::Changed).unwrap();
|
if let Some(event_tx) = event_tx.upgrade() {
|
||||||
|
event_tx.send(Event::Changed);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
monitor.connect_volume_added(move |_monitor, volume| {
|
monitor.connect_volume_added(move |_monitor, volume| {
|
||||||
log::info!("volume added {}", VolumeExt::name(volume));
|
log::info!("volume added {}", VolumeExt::name(volume));
|
||||||
event_tx.send(Event::Changed).unwrap();
|
if let Some(event_tx) = event_tx.upgrade() {
|
||||||
|
event_tx.send(Event::Changed);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
monitor.connect_volume_removed(move |_monitor, volume| {
|
monitor.connect_volume_removed(move |_monitor, volume| {
|
||||||
log::info!("volume removed {}", VolumeExt::name(volume));
|
log::info!("volume removed {}", VolumeExt::name(volume));
|
||||||
event_tx.send(Event::Changed).unwrap();
|
if let Some(event_tx) = event_tx.upgrade() {
|
||||||
|
event_tx.send(Event::Changed);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -419,7 +470,11 @@ impl Gvfs {
|
||||||
items_tx.send(items(&monitor, sizes)).await.unwrap();
|
items_tx.send(items(&monitor, sizes)).await.unwrap();
|
||||||
}
|
}
|
||||||
Cmd::Rescan => {
|
Cmd::Rescan => {
|
||||||
event_tx.send(Event::Items(items(&monitor, IconSizes::default()))).unwrap();
|
let Some(event_tx) = event_tx.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
event_tx.send(Event::Items(items(&monitor, IconSizes::default())));
|
||||||
}
|
}
|
||||||
Cmd::Mount(mounter_item, complete_tx) => {
|
Cmd::Mount(mounter_item, complete_tx) => {
|
||||||
let MounterItem::Gvfs(ref item) = mounter_item else {
|
let MounterItem::Gvfs(ref item) = mounter_item else {
|
||||||
|
|
@ -471,6 +526,9 @@ impl Gvfs {
|
||||||
.ok().map(|info| info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE))
|
.ok().map(|info| info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE))
|
||||||
.unwrap_or(true);
|
.unwrap_or(true);
|
||||||
}
|
}
|
||||||
|
let Some(event_tx) = event_tx.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
event_tx.send(Event::MountResult(updated_item, match res {
|
event_tx.send(Event::MountResult(updated_item, match res {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
_ = complete_tx.send(Ok(()));
|
_ = complete_tx.send(Ok(()));
|
||||||
|
|
@ -482,7 +540,7 @@ impl Gvfs {
|
||||||
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
||||||
_ => Err(format!("{err}"))
|
_ => Err(format!("{err}"))
|
||||||
}}
|
}}
|
||||||
})).unwrap();
|
}));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
@ -498,6 +556,9 @@ impl Gvfs {
|
||||||
gio::Cancellable::NONE,
|
gio::Cancellable::NONE,
|
||||||
move |res| {
|
move |res| {
|
||||||
log::info!("network drive {uri}: result {res:?}");
|
log::info!("network drive {uri}: result {res:?}");
|
||||||
|
let Some(event_tx) = event_tx.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
event_tx.send(Event::NetworkResult(uri, match res {
|
event_tx.send(Event::NetworkResult(uri, match res {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
_ = result_tx.send(Ok(()));
|
_ = result_tx.send(Ok(()));
|
||||||
|
|
@ -508,7 +569,7 @@ impl Gvfs {
|
||||||
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
||||||
_ => Err(format!("{err}"))
|
_ => Err(format!("{err}"))
|
||||||
}}
|
}}
|
||||||
})).unwrap();
|
}));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -532,6 +593,9 @@ impl Gvfs {
|
||||||
// FIXME sometimes a uri can be mounted and then not recognized as mounted...
|
// FIXME sometimes a uri can be mounted and then not recognized as mounted...
|
||||||
// seems to be related to uri with a path
|
// seems to be related to uri with a path
|
||||||
items_tx.blocking_send(network_scan(&uri, sizes)).unwrap();
|
items_tx.blocking_send(network_scan(&uri, sizes)).unwrap();
|
||||||
|
let Some(event_tx) = event_tx.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
event_tx.send(Event::NetworkResult(resolved_uri, match res {
|
event_tx.send(Event::NetworkResult(resolved_uri, match res {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
|
@ -540,7 +604,7 @@ impl Gvfs {
|
||||||
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
||||||
_ => Err(format!("{err}"))
|
_ => Err(format!("{err}"))
|
||||||
}
|
}
|
||||||
})).unwrap();
|
}));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -596,7 +660,7 @@ impl Gvfs {
|
||||||
});
|
});
|
||||||
Self {
|
Self {
|
||||||
command_tx,
|
command_tx,
|
||||||
event_rx: Arc::new(Mutex::new(event_rx)),
|
event_rx: Arc::new(event_rx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -625,7 +689,7 @@ impl Mounter for Gvfs {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn network_drive(&self, uri: String) -> Task<()> {
|
fn network_drive(&self, uri: String) -> Task<bool> {
|
||||||
let command_tx = self.command_tx.clone();
|
let command_tx = self.command_tx.clone();
|
||||||
Task::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
|
|
@ -634,9 +698,15 @@ impl Mounter for Gvfs {
|
||||||
command_tx.send(Cmd::NetworkDrive(uri, res_tx)).unwrap();
|
command_tx.send(Cmd::NetworkDrive(uri, res_tx)).unwrap();
|
||||||
res_rx.await
|
res_rx.await
|
||||||
},
|
},
|
||||||
|x| {
|
|result| match result {
|
||||||
if let Err(err) = x {
|
Ok(Ok(())) => true,
|
||||||
|
Ok(Err(err)) => {
|
||||||
log::error!("{err:?}");
|
log::error!("{err:?}");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("{err:?}");
|
||||||
|
false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -670,7 +740,7 @@ impl Mounter for Gvfs {
|
||||||
let event_rx = self.event_rx.clone();
|
let event_rx = self.event_rx.clone();
|
||||||
struct Wrapper {
|
struct Wrapper {
|
||||||
command_tx: mpsc::UnboundedSender<Cmd>,
|
command_tx: mpsc::UnboundedSender<Cmd>,
|
||||||
event_rx: Arc<Mutex<mpsc::UnboundedReceiver<Event>>>,
|
event_rx: Arc<crate::channel::Receiver<Event>>,
|
||||||
}
|
}
|
||||||
impl Hash for Wrapper {
|
impl Hash for Wrapper {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
|
@ -694,7 +764,7 @@ impl Mounter for Gvfs {
|
||||||
MounterMessage,
|
MounterMessage,
|
||||||
>| async move {
|
>| async move {
|
||||||
command_tx.send(Cmd::Rescan).unwrap();
|
command_tx.send(Cmd::Rescan).unwrap();
|
||||||
while let Some(event) = event_rx.lock().await.recv().await {
|
while let Some(event) = event_rx.recv().await {
|
||||||
match event {
|
match event {
|
||||||
Event::Changed => command_tx.send(Cmd::Rescan).unwrap(),
|
Event::Changed => command_tx.send(Cmd::Rescan).unwrap(),
|
||||||
Event::Items(items) => {
|
Event::Items(items) => {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use cosmic::{Task, iced::Subscription, widget};
|
use cosmic::iced::Subscription;
|
||||||
use std::{
|
use cosmic::{Task, widget};
|
||||||
collections::BTreeMap,
|
use std::collections::BTreeMap;
|
||||||
fmt,
|
use std::fmt;
|
||||||
path::PathBuf,
|
use std::path::PathBuf;
|
||||||
sync::{Arc, LazyLock},
|
use std::sync::{Arc, LazyLock};
|
||||||
};
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::{config::IconSizes, tab};
|
use crate::config::IconSizes;
|
||||||
|
use crate::tab;
|
||||||
|
|
||||||
#[cfg(feature = "gvfs")]
|
#[cfg(feature = "gvfs")]
|
||||||
mod gvfs;
|
mod gvfs;
|
||||||
|
|
@ -75,10 +75,10 @@ impl MounterItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn icon(&self, symbolic: bool) -> Option<widget::icon::Handle> {
|
pub fn icon(&self, _symbolic: bool) -> Option<widget::icon::Handle> {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "gvfs")]
|
#[cfg(feature = "gvfs")]
|
||||||
Self::Gvfs(item) => item.icon(symbolic),
|
Self::Gvfs(item) => item.icon(_symbolic),
|
||||||
Self::None => unreachable!(),
|
Self::None => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -103,6 +103,7 @@ impl MounterItem {
|
||||||
pub type MounterItems = Vec<MounterItem>;
|
pub type MounterItems = Vec<MounterItem>;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub enum MounterMessage {
|
pub enum MounterMessage {
|
||||||
Items(MounterItems),
|
Items(MounterItems),
|
||||||
MountResult(MounterItem, Result<bool, String>),
|
MountResult(MounterItem, Result<bool, String>),
|
||||||
|
|
@ -114,7 +115,7 @@ pub trait Mounter: Send + Sync {
|
||||||
fn items(&self, sizes: IconSizes) -> Option<MounterItems>;
|
fn items(&self, sizes: IconSizes) -> Option<MounterItems>;
|
||||||
//TODO: send result
|
//TODO: send result
|
||||||
fn mount(&self, item: MounterItem) -> Task<()>;
|
fn mount(&self, item: MounterItem) -> Task<()>;
|
||||||
fn network_drive(&self, uri: String) -> Task<()>;
|
fn network_drive(&self, uri: String) -> Task<bool>;
|
||||||
fn network_scan(&self, uri: &str, sizes: IconSizes) -> Option<Result<Vec<tab::Item>, String>>;
|
fn network_scan(&self, uri: &str, sizes: IconSizes) -> Option<Result<Vec<tab::Item>, String>>;
|
||||||
fn dir_info(&self, uri: &str) -> Option<(String, String, Option<PathBuf>)>;
|
fn dir_info(&self, uri: &str) -> Option<(String, String, Option<PathBuf>)>;
|
||||||
fn unmount(&self, item: MounterItem) -> Task<()>;
|
fn unmount(&self, item: MounterItem) -> Task<()>;
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,17 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use crate::tab::DOUBLE_CLICK_DURATION;
|
use crate::tab::DOUBLE_CLICK_DURATION;
|
||||||
use cosmic::{
|
use cosmic::iced::core::border::Border;
|
||||||
Element, Renderer, Theme,
|
use cosmic::iced::core::event::Event;
|
||||||
iced_core::{
|
use cosmic::iced::core::mouse::{self, click};
|
||||||
Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget,
|
use cosmic::iced::core::renderer::{self, Quad, Renderer as _};
|
||||||
border::Border,
|
use cosmic::iced::core::widget::{Operation, Tree, tree};
|
||||||
event::Event,
|
use cosmic::iced::core::{
|
||||||
layout,
|
Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget, layout,
|
||||||
mouse::{self, click},
|
overlay, touch,
|
||||||
overlay,
|
|
||||||
renderer::{self, Quad, Renderer as _},
|
|
||||||
touch,
|
|
||||||
widget::{Operation, Tree, tree},
|
|
||||||
},
|
|
||||||
widget::Id,
|
|
||||||
};
|
};
|
||||||
|
use cosmic::widget::Id;
|
||||||
|
use cosmic::{Element, Renderer, Theme};
|
||||||
|
|
||||||
/// Emit messages on mouse events.
|
/// Emit messages on mouse events.
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
|
|
@ -218,8 +214,9 @@ impl<'a, Message, F> OnAutoScroll<'a, Message> for F where F: Fn(Option<f32>) ->
|
||||||
pub trait OnMouseButton<'a, Message>: Fn(Option<Point>) -> Message + 'a {}
|
pub trait OnMouseButton<'a, Message>: Fn(Option<Point>) -> Message + 'a {}
|
||||||
impl<'a, Message, F> OnMouseButton<'a, Message> for F where F: Fn(Option<Point>) -> Message + 'a {}
|
impl<'a, Message, F> OnMouseButton<'a, Message> for F where F: Fn(Option<Point>) -> Message + 'a {}
|
||||||
|
|
||||||
pub trait OnDrag<'a, Message>: Fn(Option<Rectangle>) -> Message + 'a {}
|
pub trait OnDrag<'a, Message>: Fn(Option<Rectangle>) -> Option<Message> + 'a {}
|
||||||
impl<'a, Message, F> OnDrag<'a, Message> for F where F: Fn(Option<Rectangle>) -> Message + 'a {}
|
impl<'a, Message, F> OnDrag<'a, Message> for F where F: Fn(Option<Rectangle>) -> Option<Message> + 'a
|
||||||
|
{}
|
||||||
|
|
||||||
pub trait OnResize<'a, Message>: Fn(Rectangle) -> Message + 'a {}
|
pub trait OnResize<'a, Message>: Fn(Rectangle) -> Message + 'a {}
|
||||||
impl<'a, Message, F> OnResize<'a, Message> for F where F: Fn(Rectangle) -> Message + 'a {}
|
impl<'a, Message, F> OnResize<'a, Message> for F where F: Fn(Rectangle) -> Message + 'a {}
|
||||||
|
|
@ -395,7 +392,7 @@ where
|
||||||
|
|
||||||
update(
|
update(
|
||||||
self,
|
self,
|
||||||
&event,
|
event,
|
||||||
layout,
|
layout,
|
||||||
cursor,
|
cursor,
|
||||||
shell,
|
shell,
|
||||||
|
|
@ -488,7 +485,7 @@ where
|
||||||
state: &Tree,
|
state: &Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
dnd_rectangles: &mut cosmic::iced_core::clipboard::DndDestinationRectangles,
|
dnd_rectangles: &mut cosmic::iced::core::clipboard::DndDestinationRectangles,
|
||||||
) {
|
) {
|
||||||
self.content.as_widget().drag_destinations(
|
self.content.as_widget().drag_destinations(
|
||||||
&state.children[0],
|
&state.children[0],
|
||||||
|
|
@ -567,6 +564,16 @@ fn update<Message: Clone>(
|
||||||
state.last_position = position_in;
|
state.last_position = position_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if state.drag_initiated.is_some()
|
||||||
|
&& matches!(
|
||||||
|
event,
|
||||||
|
Event::Mouse(mouse::Event::CursorMoved { .. })
|
||||||
|
| Event::Touch(touch::Event::FingerMoved { .. })
|
||||||
|
)
|
||||||
|
{
|
||||||
|
shell.request_redraw();
|
||||||
|
}
|
||||||
|
|
||||||
if let Event::Mouse(mouse::Event::CursorMoved { position }) = event {
|
if let Event::Mouse(mouse::Event::CursorMoved { position }) = event {
|
||||||
let virtual_position = Point::new(
|
let virtual_position = Point::new(
|
||||||
viewport.x - layout_bounds.x + position.x,
|
viewport.x - layout_bounds.x + position.x,
|
||||||
|
|
@ -643,6 +650,7 @@ fn update<Message: Clone>(
|
||||||
{
|
{
|
||||||
state.drag_initiated = None;
|
state.drag_initiated = None;
|
||||||
state.prev_click = None;
|
state.prev_click = None;
|
||||||
|
shell.request_redraw();
|
||||||
if let Some(message) = widget.on_drag_end.as_ref() {
|
if let Some(message) = widget.on_drag_end.as_ref() {
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||||
}
|
}
|
||||||
|
|
@ -662,7 +670,9 @@ fn update<Message: Clone>(
|
||||||
state.prev_click = None;
|
state.prev_click = None;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.drag_initiated = None;
|
if state.drag_initiated.take().is_some() {
|
||||||
|
shell.request_redraw();
|
||||||
|
}
|
||||||
if let Some(message) = widget.on_release.as_ref() {
|
if let Some(message) = widget.on_release.as_ref() {
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||||
|
|
||||||
|
|
@ -789,12 +799,13 @@ fn update<Message: Clone>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((message, drag_rect)) = widget.on_drag.as_ref().zip(state.drag_rect(cursor)) {
|
if let Some((message, drag_rect)) = widget.on_drag.as_ref().zip(state.drag_rect(cursor)) {
|
||||||
shell.publish(message(drag_rect.intersection(&layout_bounds).map(
|
let rect_opt = drag_rect.intersection(&layout_bounds).map(|mut rect| {
|
||||||
|mut rect| {
|
rect.x -= layout_bounds.x;
|
||||||
rect.x -= layout_bounds.x;
|
rect.y -= layout_bounds.y;
|
||||||
rect.y -= layout_bounds.y;
|
rect
|
||||||
rect
|
});
|
||||||
},
|
if let Some(message) = message(rect_opt) {
|
||||||
)));
|
shell.publish(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
use std::sync::{Arc, Mutex};
|
use atomic_float::AtomicF32;
|
||||||
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{self, AtomicU16};
|
||||||
use tokio::sync::Notify;
|
use tokio::sync::Notify;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)]
|
||||||
|
#[repr(u16)]
|
||||||
pub enum ControllerState {
|
pub enum ControllerState {
|
||||||
Cancelled,
|
Cancelled,
|
||||||
Failed,
|
Failed,
|
||||||
|
|
@ -11,8 +15,8 @@ pub enum ControllerState {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ControllerInner {
|
struct ControllerInner {
|
||||||
state: Mutex<ControllerState>,
|
state: AtomicU16,
|
||||||
progress: Mutex<f32>,
|
progress: AtomicF32,
|
||||||
notify: Notify,
|
notify: Notify,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,8 +31,8 @@ impl Default for Controller {
|
||||||
Self {
|
Self {
|
||||||
primary: true,
|
primary: true,
|
||||||
inner: Arc::new(ControllerInner {
|
inner: Arc::new(ControllerInner {
|
||||||
state: Mutex::new(ControllerState::Running),
|
state: AtomicU16::new(ControllerState::Running.into()),
|
||||||
progress: Mutex::new(0.0),
|
progress: AtomicF32::new(0.0),
|
||||||
notify: Notify::new(),
|
notify: Notify::new(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
@ -50,19 +54,24 @@ impl Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn progress(&self) -> f32 {
|
pub fn progress(&self) -> f32 {
|
||||||
*self.inner.progress.lock().unwrap()
|
self.inner.progress.load(atomic::Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_progress(&self, progress: f32) {
|
pub fn set_progress(&self, progress: f32) {
|
||||||
*self.inner.progress.lock().unwrap() = progress;
|
self.inner
|
||||||
|
.progress
|
||||||
|
.swap(progress, atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state(&self) -> ControllerState {
|
pub fn state(&self) -> ControllerState {
|
||||||
*self.inner.state.lock().unwrap()
|
ControllerState::try_from(self.inner.state.load(atomic::Ordering::Relaxed))
|
||||||
|
.unwrap_or(ControllerState::Failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_state(&self, state: ControllerState) {
|
pub fn set_state(&self, state: ControllerState) {
|
||||||
*self.inner.state.lock().unwrap() = state;
|
self.inner
|
||||||
|
.state
|
||||||
|
.store(state.into(), atomic::Ordering::Relaxed);
|
||||||
self.inner.notify.notify_waiters();
|
self.inner.notify.notify_waiters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,6 +95,35 @@ impl Controller {
|
||||||
self.set_state(ControllerState::Paused);
|
self.set_state(ControllerState::Paused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns when the state is paused.
|
||||||
|
///
|
||||||
|
/// Use this to pause futures.
|
||||||
|
pub async fn until_paused(&self) {
|
||||||
|
loop {
|
||||||
|
if matches!(self.state(), ControllerState::Paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner.notify.notified().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns when state is neither paused, cancelled, nor failed.
|
||||||
|
///
|
||||||
|
/// Use this to resume futures.
|
||||||
|
pub async fn until_unpaused(&self) {
|
||||||
|
loop {
|
||||||
|
if !matches!(
|
||||||
|
self.state(),
|
||||||
|
ControllerState::Paused | ControllerState::Cancelled | ControllerState::Failed
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner.notify.notified().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn unpause(&self) {
|
pub fn unpause(&self) {
|
||||||
if !self.is_cancelled() | !self.is_failed() {
|
if !self.is_cancelled() | !self.is_failed() {
|
||||||
self.set_state(ControllerState::Running);
|
self.set_state(ControllerState::Running);
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,15 @@
|
||||||
use crate::{
|
use crate::app::{ArchiveType, DialogPage, Message, REPLACE_BUTTON_ID};
|
||||||
app::{ArchiveType, DialogPage, Message, REPLACE_BUTTON_ID},
|
use crate::config::IconSizes;
|
||||||
archive,
|
use crate::spawn_detached::spawn_detached;
|
||||||
config::IconSizes,
|
use crate::{archive, fl, tab};
|
||||||
fl,
|
use cosmic::iced::futures::channel::mpsc::Sender;
|
||||||
spawn_detached::spawn_detached,
|
use cosmic::iced::futures::{self, SinkExt, StreamExt, stream};
|
||||||
tab,
|
use std::borrow::Cow;
|
||||||
};
|
use std::fmt::Formatter;
|
||||||
use cosmic::iced::futures::{self, SinkExt, StreamExt, channel::mpsc::Sender, stream};
|
use std::fs;
|
||||||
use std::{
|
use std::io::{self, Read, Write};
|
||||||
borrow::Cow,
|
use std::path::{Path, PathBuf};
|
||||||
fmt::Formatter,
|
use std::sync::Arc;
|
||||||
fs,
|
|
||||||
io::{self, Read, Write},
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
use tokio::sync::{Mutex as TokioMutex, mpsc};
|
use tokio::sync::{Mutex as TokioMutex, mpsc};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
use zip::AesMode::Aes256;
|
use zip::AesMode::Aes256;
|
||||||
|
|
@ -22,6 +17,9 @@ use zip::AesMode::Aes256;
|
||||||
pub use self::controller::{Controller, ControllerState};
|
pub use self::controller::{Controller, ControllerState};
|
||||||
pub mod controller;
|
pub mod controller;
|
||||||
|
|
||||||
|
pub use notifiers::*;
|
||||||
|
mod notifiers;
|
||||||
|
|
||||||
pub use self::reader::OpReader;
|
pub use self::reader::OpReader;
|
||||||
pub mod reader;
|
pub mod reader;
|
||||||
|
|
||||||
|
|
@ -36,7 +34,7 @@ async fn handle_replace(
|
||||||
conflict_count: usize,
|
conflict_count: usize,
|
||||||
) -> ReplaceResult {
|
) -> ReplaceResult {
|
||||||
let item_from = match tab::item_from_path(file_from, IconSizes::default()) {
|
let item_from = match tab::item_from_path(file_from, IconSizes::default()) {
|
||||||
Ok(ok) => ok,
|
Ok(ok) => Box::new(ok),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::warn!("{err}");
|
log::warn!("{err}");
|
||||||
return ReplaceResult::Cancel;
|
return ReplaceResult::Cancel;
|
||||||
|
|
@ -44,7 +42,7 @@ async fn handle_replace(
|
||||||
};
|
};
|
||||||
|
|
||||||
let item_to = match tab::item_from_path(file_to, IconSizes::default()) {
|
let item_to = match tab::item_from_path(file_to, IconSizes::default()) {
|
||||||
Ok(ok) => ok,
|
Ok(ok) => Box::new(ok),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::warn!("{err}");
|
log::warn!("{err}");
|
||||||
return ReplaceResult::Cancel;
|
return ReplaceResult::Cancel;
|
||||||
|
|
@ -111,7 +109,7 @@ async fn copy_or_move(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle duplicate file names by renaming paths
|
// Handle duplicate file names by renaming paths
|
||||||
let mut from_to_pairs: Vec<(PathBuf, PathBuf)> = paths
|
let from_to_pairs_iter = paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(std::iter::repeat(to.as_path()))
|
.zip(std::iter::repeat(to.as_path()))
|
||||||
.filter_map(|(from, to)| {
|
.filter_map(|(from, to)| {
|
||||||
|
|
@ -129,36 +127,46 @@ async fn copy_or_move(
|
||||||
//TODO: how to handle from missing file name?
|
//TODO: how to handle from missing file name?
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Attempt quick and simple renames
|
// Attempt quick and simple renames
|
||||||
//TODO: allow rename to be used for directories in recursive context?
|
//TODO: allow rename to be used for directories in recursive context?
|
||||||
if matches!(method, Method::Move { .. }) {
|
|
||||||
from_to_pairs.retain(|(from, to)| {
|
|
||||||
//TODO: show replace dialog here?
|
|
||||||
if to.exists() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: use compio::fs::rename?
|
let from_to_pairs: Vec<(PathBuf, PathBuf)> = if matches!(method, Method::Move { .. }) {
|
||||||
match fs::rename(from, to) {
|
from_to_pairs_iter
|
||||||
Ok(()) => {
|
.map(|(from, to)| async move {
|
||||||
log::info!("renamed {} to {}", from.display(), to.display());
|
//TODO: show replace dialog here?
|
||||||
false
|
if to.exists() {
|
||||||
|
return Some((from, to));
|
||||||
}
|
}
|
||||||
Err(err) => {
|
|
||||||
log::info!(
|
match compio::fs::rename(&from, &to).await {
|
||||||
"failed to rename {} to {}, fallback to recursive move: {}",
|
Ok(()) => {
|
||||||
from.display(),
|
log::info!("renamed {} to {}", from.display(), to.display());
|
||||||
to.display(),
|
None
|
||||||
err
|
}
|
||||||
);
|
Err(err) => {
|
||||||
true
|
log::info!(
|
||||||
|
"failed to rename {} to {}, fallback to recursive move: {}",
|
||||||
|
from.display(),
|
||||||
|
to.display(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
Some((from, to))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
.collect::<cosmic::iced::futures::stream::FuturesOrdered<_>>()
|
||||||
}
|
.fold(Vec::new(), |mut pairs, pair| async move {
|
||||||
|
if let Some(pair) = pair {
|
||||||
|
pairs.push(pair);
|
||||||
|
}
|
||||||
|
pairs
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
from_to_pairs_iter.collect()
|
||||||
|
};
|
||||||
|
|
||||||
let mut context = Context::new(controller.clone());
|
let mut context = Context::new(controller.clone());
|
||||||
|
|
||||||
|
|
@ -216,7 +224,7 @@ pub async fn sync_to_disk(
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.buffer_unordered(32)
|
.buffer_unordered(32)
|
||||||
.collect::<Vec<_>>()
|
.collect::<()>()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Sync directories to disk
|
// Sync directories to disk
|
||||||
|
|
@ -226,7 +234,7 @@ pub async fn sync_to_disk(
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.buffer_unordered(16)
|
.buffer_unordered(16)
|
||||||
.collect::<Vec<_>>()
|
.collect::<()>()
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -762,13 +770,12 @@ impl Operation {
|
||||||
OperationError::from_err(e, &controller)
|
OperationError::from_err(e, &controller)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if let Ok(modified) = metadata.modified() {
|
if let Ok(modified) = metadata.modified()
|
||||||
if let Some(last_modified) =
|
&& let Some(last_modified) =
|
||||||
archive::system_time_to_zip_date_time(modified)
|
archive::system_time_to_zip_date_time(modified)
|
||||||
{
|
{
|
||||||
zip_options =
|
zip_options =
|
||||||
zip_options.last_modified_time(last_modified);
|
zip_options.last_modified_time(last_modified);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
|
@ -960,11 +967,13 @@ impl Operation {
|
||||||
password,
|
password,
|
||||||
} => {
|
} => {
|
||||||
let controller_clone = controller.clone();
|
let controller_clone = controller.clone();
|
||||||
compio::runtime::spawn_blocking(
|
compio::runtime::spawn(async move {
|
||||||
move || -> Result<OperationSelection, OperationError> {
|
let extracted = compio::runtime::spawn_blocking(move || {
|
||||||
let controller = controller_clone;
|
let controller = controller_clone;
|
||||||
let total_paths = paths.len();
|
let total_paths = paths.len();
|
||||||
let mut op_sel = OperationSelection::default();
|
let mut op_sel = OperationSelection::default();
|
||||||
|
let mut written_files = Vec::new();
|
||||||
|
let mut target_dirs = std::collections::HashSet::new();
|
||||||
for (i, path) in paths.iter().enumerate() {
|
for (i, path) in paths.iter().enumerate() {
|
||||||
futures::executor::block_on(async {
|
futures::executor::block_on(async {
|
||||||
controller
|
controller
|
||||||
|
|
@ -988,16 +997,32 @@ impl Operation {
|
||||||
op_sel.ignored.push(path.clone());
|
op_sel.ignored.push(path.clone());
|
||||||
op_sel.selected.push(new_dir.clone());
|
op_sel.selected.push(new_dir.clone());
|
||||||
|
|
||||||
crate::archive::extract(path, &new_dir, &password, &controller)?;
|
let (files, dirs) = crate::archive::extract(
|
||||||
|
path,
|
||||||
|
&new_dir,
|
||||||
|
&password,
|
||||||
|
&controller,
|
||||||
|
)?;
|
||||||
|
written_files.extend(files);
|
||||||
|
target_dirs.extend(dirs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(op_sel)
|
Ok::<_, OperationError>((op_sel, written_files, target_dirs))
|
||||||
},
|
})
|
||||||
)
|
.await
|
||||||
|
.map_err(wrap_compio_spawn_error)??;
|
||||||
|
|
||||||
|
let (op_sel, written_files, target_dirs) = extracted;
|
||||||
|
if !written_files.is_empty() || !target_dirs.is_empty() {
|
||||||
|
sync_to_disk(written_files, target_dirs).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok::<_, OperationError>(op_sel)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(wrap_compio_spawn_error)?
|
||||||
}
|
}
|
||||||
.await
|
|
||||||
.map_err(wrap_compio_spawn_error)?,
|
|
||||||
Self::Move {
|
Self::Move {
|
||||||
paths,
|
paths,
|
||||||
to,
|
to,
|
||||||
|
|
@ -1113,7 +1138,9 @@ impl Operation {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
Self::Restore { .. } => {
|
Self::Restore { .. } => {
|
||||||
// TODO: add support for macos
|
// TODO: add support for macos
|
||||||
return OperationError::from_msg("Restoring from trash is not supported on macos");
|
return Err(OperationError::from_msg(
|
||||||
|
"Restoring from trash is not supported on macos",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
Self::Restore { items } => {
|
Self::Restore { items } => {
|
||||||
|
|
@ -1129,10 +1156,26 @@ impl Operation {
|
||||||
|
|
||||||
paths.push(item.original_path());
|
paths.push(item.original_path());
|
||||||
|
|
||||||
compio::runtime::spawn_blocking(|| trash::os_limited::restore_all([item]))
|
// Items with .trashinfo id use standard restore; sub-items use manual move
|
||||||
.await
|
if item
|
||||||
.map_err(wrap_compio_spawn_error)?
|
.id
|
||||||
.map_err(|e| OperationError::from_err(e, &controller))?;
|
.to_str()
|
||||||
|
.map_or(false, |s| s.ends_with(".trashinfo"))
|
||||||
|
{
|
||||||
|
compio::runtime::spawn_blocking(|| trash::os_limited::restore_all([item]))
|
||||||
|
.await
|
||||||
|
.map_err(wrap_compio_spawn_error)?
|
||||||
|
.map_err(|e| OperationError::from_err(e, &controller))?;
|
||||||
|
} else {
|
||||||
|
let from = PathBuf::from(&item.id);
|
||||||
|
let to = item.original_path();
|
||||||
|
if let Some(parent) = to.parent() {
|
||||||
|
std::fs::create_dir_all(parent)
|
||||||
|
.map_err(|e| OperationError::from_err(e, &controller))?;
|
||||||
|
}
|
||||||
|
std::fs::rename(&from, &to)
|
||||||
|
.map_err(|e| OperationError::from_err(e, &controller))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(OperationSelection {
|
Ok(OperationSelection {
|
||||||
ignored: Vec::new(),
|
ignored: Vec::new(),
|
||||||
|
|
@ -1181,8 +1224,10 @@ impl Operation {
|
||||||
.map_err(|s| OperationError::from_state(s, &controller))?;
|
.map_err(|s| OperationError::from_state(s, &controller))?;
|
||||||
|
|
||||||
let controller_clone = controller.clone();
|
let controller_clone = controller.clone();
|
||||||
|
let path_clone = path.clone();
|
||||||
compio::runtime::spawn_blocking(move || -> Result<(), OperationError> {
|
compio::runtime::spawn_blocking(move || -> Result<(), OperationError> {
|
||||||
let controller = controller_clone;
|
let controller = controller_clone;
|
||||||
|
let path = path_clone;
|
||||||
//TODO: what to do on non-Unix systems?
|
//TODO: what to do on non-Unix systems?
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
|
|
@ -1197,7 +1242,10 @@ impl Operation {
|
||||||
.await
|
.await
|
||||||
.map_err(wrap_compio_spawn_error)?
|
.map_err(wrap_compio_spawn_error)?
|
||||||
.map_err(|e| OperationError::from_err(e, &controller))?;
|
.map_err(|e| OperationError::from_err(e, &controller))?;
|
||||||
Ok(OperationSelection::default())
|
Ok(OperationSelection {
|
||||||
|
ignored: Vec::new(),
|
||||||
|
selected: vec![path],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1224,28 +1272,23 @@ fn wrap_compio_spawn_error(err: Box<dyn std::any::Any + Send>) -> OperationError
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{
|
use std::fs::{self, File};
|
||||||
fs::{self, File},
|
use std::io;
|
||||||
io,
|
use std::path::PathBuf;
|
||||||
path::PathBuf,
|
|
||||||
};
|
|
||||||
|
|
||||||
use cosmic::iced::futures::{StreamExt, channel::mpsc, future};
|
use cosmic::iced::futures::channel::mpsc;
|
||||||
|
use cosmic::iced::futures::{StreamExt, future};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use test_log::test;
|
use test_log::test;
|
||||||
use tokio::sync;
|
use tokio::sync;
|
||||||
|
|
||||||
use super::{Controller, Operation, OperationError, OperationSelection, ReplaceResult};
|
use super::{Controller, Operation, OperationError, OperationSelection, ReplaceResult};
|
||||||
use crate::{
|
use crate::app::test_utils::{
|
||||||
app::{
|
NAME_LEN, NUM_DIRS, NUM_FILES, NUM_HIDDEN, NUM_NESTED, empty_fs, filter_dirs, filter_files,
|
||||||
DialogPage, Message,
|
simple_fs,
|
||||||
test_utils::{
|
|
||||||
NAME_LEN, NUM_DIRS, NUM_FILES, NUM_HIDDEN, NUM_NESTED, empty_fs, filter_dirs,
|
|
||||||
filter_files, simple_fs,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fl,
|
|
||||||
};
|
};
|
||||||
|
use crate::app::{DialogPage, Message};
|
||||||
|
use crate::fl;
|
||||||
|
|
||||||
/// Simple wrapper around `[Operation::Copy]`
|
/// Simple wrapper around `[Operation::Copy]`
|
||||||
pub async fn operation_copy(
|
pub async fn operation_copy(
|
||||||
|
|
|
||||||
58
src/operation/notifiers.rs
Normal file
58
src/operation/notifiers.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright 2026 System76 <info@system76.com>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::{Arc, LazyLock, Mutex};
|
||||||
|
use tokio::sync::Notify;
|
||||||
|
|
||||||
|
/// Monitor files which are being written to.
|
||||||
|
pub struct FileWritingNotifier {
|
||||||
|
data: Vec<PathBuf>,
|
||||||
|
notify: Arc<Notify>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static ACTIVELY_WRITING: LazyLock<Mutex<FileWritingNotifier>> = LazyLock::new(|| {
|
||||||
|
Mutex::new(FileWritingNotifier {
|
||||||
|
data: Vec::new(),
|
||||||
|
notify: Arc::new(Notify::new()),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Append path that is being written to.
|
||||||
|
pub fn actively_writing_add(path: PathBuf) {
|
||||||
|
ACTIVELY_WRITING.lock().unwrap().data.push(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove path to file that has finished writing and notify waiters.
|
||||||
|
pub fn actively_writing_remove(path: &Path) {
|
||||||
|
let mut guard = ACTIVELY_WRITING.lock().unwrap();
|
||||||
|
guard.data.retain(|p| p != path);
|
||||||
|
guard.notify.notify_waiters();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait until the actively-writing queue is empty or a file has been removed.
|
||||||
|
pub async fn actively_writing_tick() {
|
||||||
|
let notify = (|| {
|
||||||
|
let guard = ACTIVELY_WRITING.lock().unwrap();
|
||||||
|
|
||||||
|
if !guard.data.is_empty() {
|
||||||
|
return Some(guard.notify.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
})();
|
||||||
|
|
||||||
|
if let Some(notify) = notify {
|
||||||
|
notify.notified().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a file is being written to. Avoid thumbnail generation until after it is finished.
|
||||||
|
pub fn is_actively_writing_to(path: &Path) -> bool {
|
||||||
|
ACTIVELY_WRITING
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.data
|
||||||
|
.iter()
|
||||||
|
.any(|p| p == path)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use std::{fs, io, path::Path};
|
use std::path::Path;
|
||||||
|
use std::{fs, io};
|
||||||
|
|
||||||
use crate::operation::OperationError;
|
use crate::operation::OperationError;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,39 @@
|
||||||
use compio::BufResult;
|
// Copyright 2023 System76 <info@system76.com>
|
||||||
use compio::buf::{IntoInner, IoBuf};
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
use compio::io::{AsyncReadAt, AsyncWriteAt};
|
|
||||||
use std::future::Future;
|
|
||||||
use std::pin::Pin;
|
|
||||||
use std::time::Instant;
|
|
||||||
use std::{cell::Cell, error::Error, fs, ops::ControlFlow, path::PathBuf, rc::Rc};
|
|
||||||
use walkdir::WalkDir;
|
|
||||||
|
|
||||||
use crate::operation::{OperationError, sync_to_disk};
|
|
||||||
|
|
||||||
use super::{Controller, OperationSelection, ReplaceResult, copy_unique_path};
|
use super::{Controller, OperationSelection, ReplaceResult, copy_unique_path};
|
||||||
|
use crate::operation::{OperationError, sync_to_disk};
|
||||||
|
use anyhow::Context as AnyhowContext;
|
||||||
|
use compio::BufResult;
|
||||||
|
use compio::buf::{IntoInner, IoBuf};
|
||||||
|
use compio::driver::ToSharedFd;
|
||||||
|
use compio::driver::op::AsyncifyFd;
|
||||||
|
use compio::io::{AsyncReadAt, AsyncWriteAt};
|
||||||
|
use cosmic::iced::futures;
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
|
use futures::{FutureExt, StreamExt};
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::ops::ControlFlow;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::time::Instant;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
|
use gio::prelude::FileExtManual;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum GioCopyError {
|
||||||
|
#[error("controller state")]
|
||||||
|
Controller(OperationError),
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
|
#[error("gio copy failed")]
|
||||||
|
GLib(#[from] glib::Error),
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Method {
|
pub enum Method {
|
||||||
Copy,
|
Copy,
|
||||||
|
|
@ -313,136 +337,28 @@ impl Op {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&mut self, ctx: &mut Context, progress: Progress) -> Result<bool, Box<dyn Error>> {
|
||||||
&mut self,
|
|
||||||
ctx: &mut Context,
|
|
||||||
mut progress: Progress,
|
|
||||||
) -> Result<bool, Box<dyn Error>> {
|
|
||||||
if self.skipped.normal.get() || (self.is_cleanup && self.skipped.cleanup.get()) {
|
if self.skipped.normal.get() || (self.is_cleanup && self.skipped.cleanup.get()) {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
match self.kind {
|
match self.kind {
|
||||||
OpKind::Copy => {
|
OpKind::Copy => {
|
||||||
// Remove `to` if overwriting and it is an existing file
|
crate::operation::actively_writing_add(self.to.clone());
|
||||||
if self.to.is_file() {
|
let result = self.copy(ctx, progress).await;
|
||||||
match ctx.replace(self).await? {
|
|
||||||
ControlFlow::Continue(to) => {
|
if result.is_err() {
|
||||||
self.to = to;
|
_ = compio::fs::remove_file(&self.to).await;
|
||||||
}
|
|
||||||
ControlFlow::Break(ret) => {
|
|
||||||
return Ok(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (from_file, metadata, mut to_file) = cosmic::iced::futures::try_join!(
|
crate::operation::actively_writing_remove(&self.to);
|
||||||
async {
|
return result;
|
||||||
compio::fs::OpenOptions::new()
|
|
||||||
.read(true)
|
|
||||||
.open(&self.from)
|
|
||||||
.await
|
|
||||||
},
|
|
||||||
compio::fs::metadata(&self.from),
|
|
||||||
// This is atomic and ensures `to` is not created by any other process
|
|
||||||
async {
|
|
||||||
compio::fs::OpenOptions::new()
|
|
||||||
.create_new(true)
|
|
||||||
.write(true)
|
|
||||||
.open(&self.to)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
)?;
|
|
||||||
|
|
||||||
progress.total_bytes = Some(metadata.len());
|
|
||||||
(ctx.on_progress)(self, &progress);
|
|
||||||
if let Err(err) = to_file.set_permissions(metadata.permissions()).await {
|
|
||||||
// This error is not propagated upwards as some filesystems do not support setting permissions
|
|
||||||
log::warn!(
|
|
||||||
"failed to set permissions for {}: {}",
|
|
||||||
self.to.display(),
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent spamming the progress callbacks.
|
|
||||||
let mut last_progress_update = Instant::now();
|
|
||||||
// io_uring/IOCP requires transferring ownership of the buffer to the kernel.
|
|
||||||
let mut buf_in = std::mem::take(&mut ctx.buf);
|
|
||||||
// Track where the current read/write position is at.
|
|
||||||
let mut pos = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let BufResult(result, buf_out) = from_file.read_at(buf_in, pos).await;
|
|
||||||
|
|
||||||
let count = match result {
|
|
||||||
Ok(0) => {
|
|
||||||
ctx.buf = buf_out;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ok(count) => count,
|
|
||||||
Err(why) => {
|
|
||||||
ctx.buf = buf_out;
|
|
||||||
return Err(why.into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let BufResult(result, buf_out_slice) =
|
|
||||||
to_file.write_at(buf_out.slice(..count), pos).await;
|
|
||||||
let buf_out = buf_out_slice.into_inner();
|
|
||||||
|
|
||||||
if let Err(why) = result {
|
|
||||||
ctx.buf = buf_out;
|
|
||||||
return Err(why.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.current_bytes += count as u64;
|
|
||||||
pos += count as u64;
|
|
||||||
|
|
||||||
// Avoid spamming progress messages too early.
|
|
||||||
let current = Instant::now();
|
|
||||||
if current.duration_since(last_progress_update).as_millis() > 49 {
|
|
||||||
last_progress_update = current;
|
|
||||||
(ctx.on_progress)(self, &progress);
|
|
||||||
|
|
||||||
// Also check if the progress was cancelled.
|
|
||||||
if let Err(state) = ctx.controller.check().await {
|
|
||||||
ctx.buf = buf_out;
|
|
||||||
return Err(OperationError::from_state(state, &ctx.controller).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf_in = buf_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut times = fs::FileTimes::new();
|
|
||||||
{
|
|
||||||
use std::os::unix::prelude::MetadataExt;
|
|
||||||
log::info!("{}", metadata.mtime());
|
|
||||||
}
|
|
||||||
if let Ok(time) = dbg!(metadata.modified()) {
|
|
||||||
times = times.set_modified(time);
|
|
||||||
}
|
|
||||||
if let Ok(time) = dbg!(metadata.accessed()) {
|
|
||||||
times = times.set_accessed(time);
|
|
||||||
}
|
|
||||||
//TODO: upstream set_times implementation to compio?
|
|
||||||
{
|
|
||||||
use compio::driver::{ToSharedFd, op::AsyncifyFd};
|
|
||||||
let op =
|
|
||||||
AsyncifyFd::new(to_file.to_shared_fd(), move |file: &std::fs::File| {
|
|
||||||
BufResult(file.set_times(times).map(|_| 0), ())
|
|
||||||
});
|
|
||||||
match compio::runtime::submit(op).await.0.map(|_| ()) {
|
|
||||||
Ok(()) => {
|
|
||||||
log::info!("set times for {} to {:?}", self.to.display(), times);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
log::warn!("failed to set times for {}: {}", self.to.display(), err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
OpKind::Move { cross_device_copy } => {
|
OpKind::Move { cross_device_copy } => {
|
||||||
|
// Do not clean up if cross_device_copy is set
|
||||||
|
if cross_device_copy {
|
||||||
|
self.skipped.cleanup.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove `to` if overwriting and it is an existing file
|
// Remove `to` if overwriting and it is an existing file
|
||||||
if self.to.is_file() {
|
if self.to.is_file() {
|
||||||
match ctx.replace(self).await? {
|
match ctx.replace(self).await? {
|
||||||
|
|
@ -520,4 +436,286 @@ impl Op {
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn copy(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut Context,
|
||||||
|
mut progress: Progress,
|
||||||
|
) -> Result<bool, Box<dyn Error>> {
|
||||||
|
// Remove `to` if overwriting and it is an existing file
|
||||||
|
if self.to.is_file() {
|
||||||
|
match ctx.replace(self).await? {
|
||||||
|
ControlFlow::Continue(to) => {
|
||||||
|
self.to = to;
|
||||||
|
}
|
||||||
|
ControlFlow::Break(ret) => {
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (from_file_open_result, metadata, to_file_open_result) = cosmic::iced::futures::join!(
|
||||||
|
async {
|
||||||
|
compio::fs::OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&self.from)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("failed to open {} for reading", self.from.display(),))
|
||||||
|
},
|
||||||
|
async { compio::fs::metadata(&self.from).await.ok() },
|
||||||
|
// This is atomic and ensures `to` is not created by any other process
|
||||||
|
async {
|
||||||
|
compio::fs::OpenOptions::new()
|
||||||
|
.create_new(true)
|
||||||
|
.write(true)
|
||||||
|
.open(&self.to)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let from_file = from_file_open_result?;
|
||||||
|
|
||||||
|
let mut to_file = match to_file_open_result {
|
||||||
|
Ok(file) => file,
|
||||||
|
#[cfg(not(feature = "gvfs"))]
|
||||||
|
Err(why) => {
|
||||||
|
_ = from_file.close().await;
|
||||||
|
return Err(why)
|
||||||
|
.with_context(|| format!("failed to open {} for writing", self.to.display()))
|
||||||
|
.map_err(Into::into);
|
||||||
|
}
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
|
Err(_why) => {
|
||||||
|
_ = from_file.close().await;
|
||||||
|
return self
|
||||||
|
.gio_file_copy(ctx, progress)
|
||||||
|
.await
|
||||||
|
.map(|()| true)
|
||||||
|
.map_err(Into::into);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
progress.total_bytes = metadata.as_ref().map(|m| m.len());
|
||||||
|
(ctx.on_progress)(self, &progress);
|
||||||
|
|
||||||
|
if let Some(metadata) = metadata.as_ref()
|
||||||
|
&& let Err(why) = to_file.set_permissions(metadata.permissions()).await
|
||||||
|
{
|
||||||
|
// This error is not propagated upwards as some filesystems do not support setting permissions
|
||||||
|
if !matches!(why.kind(), std::io::ErrorKind::Unsupported) {
|
||||||
|
tracing::warn!(?why, "failed to set permissions for {}", self.to.display(),);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent spamming the progress callbacks.
|
||||||
|
let mut last_progress_update = Instant::now();
|
||||||
|
// io_uring/IOCP requires transferring ownership of the buffer to the kernel.
|
||||||
|
let mut buf_in = std::mem::take(&mut ctx.buf);
|
||||||
|
// Track where the current read/write position is at.
|
||||||
|
let mut pos = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let BufResult(result, buf_out) = from_file.read_at(buf_in, pos).await;
|
||||||
|
|
||||||
|
let count = match result {
|
||||||
|
Ok(0) => {
|
||||||
|
buf_in = buf_out;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(count) => count,
|
||||||
|
Err(why) => {
|
||||||
|
ctx.buf = buf_out;
|
||||||
|
tracing::error!("failed to read: {:?}", why);
|
||||||
|
_ = futures::future::join(from_file.close(), to_file.close()).await;
|
||||||
|
return Err(why).context("failed to read")?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let BufResult(result, buf_out_slice) =
|
||||||
|
to_file.write_at(buf_out.slice(..count), pos).await;
|
||||||
|
let buf_out = buf_out_slice.into_inner();
|
||||||
|
|
||||||
|
if let Err(why) = result {
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
|
if let std::io::ErrorKind::Unsupported = why.kind() {
|
||||||
|
ctx.buf = buf_out;
|
||||||
|
_ = futures::future::join(from_file.close(), to_file.close()).await;
|
||||||
|
return self
|
||||||
|
.gio_file_copy(ctx, progress)
|
||||||
|
.await
|
||||||
|
.map(|_| true)
|
||||||
|
.map_err(Into::into);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::error!("failed to write: {:?}", why);
|
||||||
|
ctx.buf = buf_out;
|
||||||
|
_ = futures::future::join(from_file.close(), to_file.close()).await;
|
||||||
|
return Err(why).context("failed to write")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.current_bytes += count as u64;
|
||||||
|
pos += count as u64;
|
||||||
|
|
||||||
|
// Avoid spamming progress messages too early.
|
||||||
|
let current = Instant::now();
|
||||||
|
if current.duration_since(last_progress_update).as_millis() > 49 {
|
||||||
|
last_progress_update = current;
|
||||||
|
(ctx.on_progress)(self, &progress);
|
||||||
|
|
||||||
|
// Also check if the progress was cancelled.
|
||||||
|
if let Err(state) = ctx.controller.check().await {
|
||||||
|
ctx.buf = buf_out;
|
||||||
|
tracing::warn!(
|
||||||
|
"operation to copy from {:?} to {:?} cancelled",
|
||||||
|
self.from,
|
||||||
|
self.to
|
||||||
|
);
|
||||||
|
_ = futures::future::join(from_file.close(), to_file.close()).await;
|
||||||
|
return Err(OperationError::from_state(state, &ctx.controller).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_in = buf_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.buf = buf_in;
|
||||||
|
|
||||||
|
if let Some(metadata) = metadata.as_ref() {
|
||||||
|
let mut times = fs::FileTimes::new();
|
||||||
|
if let Ok(time) = metadata.modified() {
|
||||||
|
times = times.set_modified(time);
|
||||||
|
}
|
||||||
|
if let Ok(time) = metadata.accessed() {
|
||||||
|
times = times.set_accessed(time);
|
||||||
|
}
|
||||||
|
//TODO: upstream set_times implementation to compio?
|
||||||
|
let op = AsyncifyFd::new(to_file.to_shared_fd(), move |file: &std::fs::File| {
|
||||||
|
BufResult(file.set_times(times).map(|_| 0), ())
|
||||||
|
});
|
||||||
|
match compio::runtime::submit(op).await.0.map(|_| ()) {
|
||||||
|
Ok(()) => {
|
||||||
|
tracing::info!("set times for {} to {:?}", self.to.display(), times);
|
||||||
|
}
|
||||||
|
Err(why) => {
|
||||||
|
if !matches!(why.kind(), std::io::ErrorKind::Unsupported) {
|
||||||
|
tracing::error!(?why, "failed to set times for {}", self.to.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = to_file.close().await;
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fallback mechanism in the event that unsupported I/O error errors occur.
|
||||||
|
/// Fixes unsupported errors when copying large files over MTP.
|
||||||
|
/// TODO: Find what Gio.File does to work around this.
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
|
async fn gio_file_copy(
|
||||||
|
&self,
|
||||||
|
ctx: &mut Context,
|
||||||
|
mut progress: Progress,
|
||||||
|
) -> Result<(), GioCopyError> {
|
||||||
|
_ = compio::fs::remove_file(&self.to).await;
|
||||||
|
|
||||||
|
let from = gio::File::for_path(&self.from);
|
||||||
|
let to = gio::File::for_path(&self.to);
|
||||||
|
let (progress_tx, mut progress_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||||
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||||
|
let (pause_tx, mut pause_rx) = tokio::sync::watch::channel(false);
|
||||||
|
|
||||||
|
let task = compio::runtime::spawn_blocking(move || {
|
||||||
|
let glib_context = glib::MainContext::new();
|
||||||
|
let glib_loop = glib::MainLoop::new(Some(&glib_context), false);
|
||||||
|
glib_context.with_thread_default(move || {
|
||||||
|
let glib_loop2 = glib_loop.clone();
|
||||||
|
glib::MainContext::ref_thread_default().spawn_local(async move {
|
||||||
|
// Create a future for copying the file with `gio::File`. This also creates a progress stream.
|
||||||
|
let (gio_copy_fut, mut progress_stream) = from.copy_future(
|
||||||
|
&to,
|
||||||
|
gio::FileCopyFlags::OVERWRITE | gio::FileCopyFlags::ALL_METADATA,
|
||||||
|
glib::Priority::LOW,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut copy_fut = gio_copy_fut
|
||||||
|
.map(|result| result.map_err(GioCopyError::GLib))
|
||||||
|
.fuse();
|
||||||
|
|
||||||
|
let progress_fut = std::pin::pin!(async {
|
||||||
|
while let Some((current_bytes, _)) = progress_stream.next().await {
|
||||||
|
_ = progress_tx.send(current_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(progress_tx);
|
||||||
|
futures::future::pending::<()>().await;
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut progress_fut = progress_fut.fuse();
|
||||||
|
let mut pause_rx2 = pause_rx.clone();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let until_paused = std::pin::pin!(pause_rx.wait_for(|paused| *paused));
|
||||||
|
futures::select! {
|
||||||
|
_ = &mut progress_fut => {},
|
||||||
|
|
||||||
|
result = &mut copy_fut => {
|
||||||
|
_ = tx.send(result.map(|_| ()));
|
||||||
|
glib_loop2.quit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = until_paused.fuse() => {
|
||||||
|
_ = pause_rx2.wait_for(|paused| !*paused).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
glib_loop.run();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut last_progress_update = Instant::now();
|
||||||
|
let mut task = task.fuse();
|
||||||
|
let mut rx = rx.fuse();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let until_paused = std::pin::pin!(ctx.controller.until_paused());
|
||||||
|
futures::select! {
|
||||||
|
value = progress_rx.recv().fuse() => {
|
||||||
|
if let Some(current_bytes) = value {
|
||||||
|
progress.current_bytes = current_bytes as u64;
|
||||||
|
let current = Instant::now();
|
||||||
|
if current.duration_since(last_progress_update).as_millis() > 49 {
|
||||||
|
last_progress_update = current;
|
||||||
|
(ctx.on_progress)(self, &progress);
|
||||||
|
// Also check if the progress was cancelled.
|
||||||
|
if let Err(state) = ctx.controller.check().await {
|
||||||
|
tracing::warn!(
|
||||||
|
"operation to copy from {:?} to {:?} cancelled",
|
||||||
|
self.from,
|
||||||
|
self.to
|
||||||
|
);
|
||||||
|
return Err::<(), GioCopyError>(GioCopyError::Controller(
|
||||||
|
OperationError::from_state(state, &ctx.controller),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = rx => return result.unwrap(),
|
||||||
|
|
||||||
|
_ = task => (),
|
||||||
|
|
||||||
|
_ = until_paused.fuse() => {
|
||||||
|
// Pauses an active copy while the controller state is paused.
|
||||||
|
_ = pause_tx.send(true);
|
||||||
|
ctx.controller.until_unpaused().await;
|
||||||
|
_ = pause_tx.send(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1922
src/tab.rs
1922
src/tab.rs
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +1,14 @@
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
use md5::{Digest, Md5};
|
use md5::{Digest, Md5};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use std::{
|
use std::error::Error;
|
||||||
error::Error,
|
use std::fs::{self, File};
|
||||||
fs::{self, File},
|
use std::io::{self, BufReader, BufWriter};
|
||||||
io::{self, BufReader, BufWriter},
|
#[cfg(unix)]
|
||||||
os::unix::fs::PermissionsExt,
|
use std::os::unix::fs::PermissionsExt;
|
||||||
path::{Path, PathBuf},
|
use std::path::{Path, PathBuf};
|
||||||
sync::LazyLock,
|
use std::sync::LazyLock;
|
||||||
time::UNIX_EPOCH,
|
use std::time::UNIX_EPOCH;
|
||||||
};
|
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
@ -93,6 +92,7 @@ impl ThumbnailCacher {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_with_temp_file(&self, temp_file: NamedTempFile) -> Result<&Path, Box<dyn Error>> {
|
pub fn update_with_temp_file(&self, temp_file: NamedTempFile) -> Result<&Path, Box<dyn Error>> {
|
||||||
|
#[cfg(unix)]
|
||||||
fs::set_permissions(temp_file.path(), fs::Permissions::from_mode(0o600))?;
|
fs::set_permissions(temp_file.path(), fs::Permissions::from_mode(0o600))?;
|
||||||
self.update_thumbnail_text_metadata(temp_file.path())?;
|
self.update_thumbnail_text_metadata(temp_file.path())?;
|
||||||
fs::rename(temp_file.path(), &self.thumbnail_path)?;
|
fs::rename(temp_file.path(), &self.thumbnail_path)?;
|
||||||
|
|
@ -127,6 +127,7 @@ impl ThumbnailCacher {
|
||||||
pub fn create_fail_marker(&self) -> Result<(), Box<dyn Error>> {
|
pub fn create_fail_marker(&self) -> Result<(), Box<dyn Error>> {
|
||||||
if let Some(dir) = self.thumbnail_fail_marker_path.parent() {
|
if let Some(dir) = self.thumbnail_fail_marker_path.parent() {
|
||||||
fs::create_dir_all(dir)?;
|
fs::create_dir_all(dir)?;
|
||||||
|
#[cfg(unix)]
|
||||||
fs::set_permissions(dir, fs::Permissions::from_mode(0o700))?;
|
fs::set_permissions(dir, fs::Permissions::from_mode(0o700))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
// Copyright 2023 System76 <info@system76.com>
|
// Copyright 2023 System76 <info@system76.com>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
use cosmic::desktop::fde::GenericEntry;
|
use cosmic::desktop::fde::GenericEntry;
|
||||||
use mime_guess::Mime;
|
use mime_guess::Mime;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
use std::{fs, time::Instant};
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
|
||||||
path::Path,
|
path::Path,
|
||||||
process,
|
process,
|
||||||
sync::{LazyLock, Mutex},
|
sync::{LazyLock, Mutex},
|
||||||
time::Instant,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -174,3 +175,25 @@ pub fn thumbnailer(mime: &Mime) -> Vec<Thumbnailer> {
|
||||||
let thumbnailer_cache = THUMBNAILER_CACHE.lock().unwrap();
|
let thumbnailer_cache = THUMBNAILER_CACHE.lock().unwrap();
|
||||||
thumbnailer_cache.get(mime)
|
thumbnailer_cache.get(mime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, feature = "desktop"))]
|
||||||
|
mod tests {
|
||||||
|
use super::Thumbnailer;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_expands_thumbnailer_arguments() {
|
||||||
|
let thumbnailer = Thumbnailer {
|
||||||
|
exec: "cosmic-files-thumbnailer %o --size %s %i".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let command = thumbnailer
|
||||||
|
.command(Path::new("input.exe"), Path::new("output.png"), 128)
|
||||||
|
.expect("valid thumbnailer command");
|
||||||
|
assert_eq!(command.get_program(), "cosmic-files-thumbnailer");
|
||||||
|
assert_eq!(
|
||||||
|
command.get_args().collect::<Vec<_>>(),
|
||||||
|
["output.png", "--size", "128", "input.exe"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
234
src/trash.rs
Normal file
234
src/trash.rs
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
use cosmic::widget;
|
||||||
|
use regex::Regex;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
use crate::config::IconSizes;
|
||||||
|
use crate::tab::{Item, SearchItem};
|
||||||
|
|
||||||
|
fn percent_decode(s: &str) -> Option<String> {
|
||||||
|
let (mut r, mut b) = (String::with_capacity(s.len()), s.bytes());
|
||||||
|
while let Some(c) = b.next() {
|
||||||
|
if c == b'%' {
|
||||||
|
let (hi, lo) = (b.next()?, b.next()?);
|
||||||
|
let h = |x: u8| match x {
|
||||||
|
b'0'..=b'9' => Some(x - b'0'),
|
||||||
|
b'a'..=b'f' => Some(x - b'a' + 10),
|
||||||
|
b'A'..=b'F' => Some(x - b'A' + 10),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
r.push((h(hi)? << 4 | h(lo)?) as char);
|
||||||
|
} else {
|
||||||
|
r.push(c as char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TrashExt {
|
||||||
|
fn is_empty() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entries() -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn folders() -> Result<HashSet<PathBuf>, trash::Error> {
|
||||||
|
Err(trash::Error::Unknown {
|
||||||
|
description: "reading trash folders not supported on this platform".into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan(_sizes: IconSizes) -> Vec<Item> {
|
||||||
|
log::warn!("viewing trash not supported on this platform");
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan_search<F: Fn(SearchItem) -> bool + Sync>(_callback: F, _regex: &Regex) {}
|
||||||
|
|
||||||
|
fn icon(icon_size: u16) -> widget::icon::Handle {
|
||||||
|
widget::icon::from_name(if Self::is_empty() {
|
||||||
|
"user-trash"
|
||||||
|
} else {
|
||||||
|
"user-trash-full"
|
||||||
|
})
|
||||||
|
.size(icon_size)
|
||||||
|
.handle()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn icon_symbolic(icon_size: u16) -> widget::icon::Handle {
|
||||||
|
widget::icon::from_name(if Self::is_empty() {
|
||||||
|
"user-trash-symbolic"
|
||||||
|
} else {
|
||||||
|
"user-trash-full-symbolic"
|
||||||
|
})
|
||||||
|
.size(icon_size)
|
||||||
|
.handle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Derive the actual filesystem path of a trashed item from its .trashinfo path
|
||||||
|
/// (or return the id directly if it's already a filesystem path).
|
||||||
|
pub fn trash_item_path(item: &trash::TrashItem) -> Option<PathBuf> {
|
||||||
|
let id_path = Path::new(&item.id);
|
||||||
|
if id_path.extension().map_or(false, |e| e == "trashinfo") {
|
||||||
|
let trash_root = id_path.parent()?.parent()?;
|
||||||
|
let file_name = id_path.file_stem()?;
|
||||||
|
Some(trash_root.join("files").join(file_name))
|
||||||
|
} else {
|
||||||
|
Some(PathBuf::from(&item.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For a path inside a trash `files/` directory, reconstruct the original path
|
||||||
|
/// by reading the parent `.trashinfo` file.
|
||||||
|
///
|
||||||
|
/// Given `~/.local/share/Trash/files/folder/sub/file.txt`:
|
||||||
|
/// - The top-level trashed item is `folder`
|
||||||
|
/// - Read `~/.local/share/Trash/info/folder.trashinfo` to get the original path
|
||||||
|
/// - Compute: `<original_path>/sub/file.txt`
|
||||||
|
pub fn original_path_for_trash_child(p: &Path) -> Option<PathBuf> {
|
||||||
|
let files = trash_files_dir(p)?;
|
||||||
|
let root = files.parent()?;
|
||||||
|
let top = p.strip_prefix(files).ok()?.components().next()?;
|
||||||
|
let mut trashinfo_name = top.as_os_str().to_os_string();
|
||||||
|
trashinfo_name.push(".trashinfo");
|
||||||
|
let info = std::fs::read_to_string(root.join("info").join(trashinfo_name)).ok()?;
|
||||||
|
let orig = percent_decode(info.lines().find_map(|l| l.strip_prefix("Path="))?.trim())?;
|
||||||
|
let rel = p.strip_prefix(files.join(top)).ok()?;
|
||||||
|
let mut result = PathBuf::from(&orig);
|
||||||
|
if !rel.as_os_str().is_empty() {
|
||||||
|
result.push(rel);
|
||||||
|
}
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
static TRASH_FOLDERS: LazyLock<HashSet<PathBuf>> = LazyLock::new(|| {
|
||||||
|
Trash::folders().unwrap_or_else(|e| {
|
||||||
|
log::warn!("failed to list trash folders: {}", e);
|
||||||
|
HashSet::new()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
fn is_trash_root(root: &Path) -> bool {
|
||||||
|
if TRASH_FOLDERS.is_empty() {
|
||||||
|
root.join("info").is_dir()
|
||||||
|
} else {
|
||||||
|
TRASH_FOLDERS.contains(root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trash_files_dir(path: &Path) -> Option<&Path> {
|
||||||
|
path.ancestors()
|
||||||
|
.find(|a| a.ends_with("files") && a.parent().is_some_and(is_trash_root))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether a path is inside any trash `files/` directory.
|
||||||
|
pub fn is_trash_path(path: &Path) -> bool {
|
||||||
|
trash_files_dir(path).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Trash;
|
||||||
|
|
||||||
|
// This config statement is from trash::os_limited
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "windows",
|
||||||
|
all(
|
||||||
|
unix,
|
||||||
|
not(target_os = "macos"),
|
||||||
|
not(target_os = "ios"),
|
||||||
|
not(target_os = "android")
|
||||||
|
)
|
||||||
|
))]
|
||||||
|
impl TrashExt for Trash {
|
||||||
|
fn is_empty() -> bool {
|
||||||
|
trash::os_limited::is_empty().unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entries() -> usize {
|
||||||
|
match trash::os_limited::list() {
|
||||||
|
Ok(entries) => entries.len(),
|
||||||
|
Err(_err) => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not available on Windows only
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn folders() -> Result<HashSet<PathBuf>, trash::Error> {
|
||||||
|
trash::os_limited::trash_folders()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan(sizes: IconSizes) -> Vec<Item> {
|
||||||
|
use crate::localize::LANGUAGE_SORTER;
|
||||||
|
use crate::tab::item_from_trash_entry;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
let entries = match trash::os_limited::list() {
|
||||||
|
Ok(entry) => entry,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("failed to read trash items: {err}");
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut items: Vec<_> = entries
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|entry| {
|
||||||
|
let metadata = trash::os_limited::metadata(&entry)
|
||||||
|
.inspect_err(|err| {
|
||||||
|
log::warn!("failed to get metadata for trash item {entry:?}: {err}")
|
||||||
|
})
|
||||||
|
.ok()?;
|
||||||
|
Some(item_from_trash_entry(entry, metadata, sizes))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
items.sort_by(|a, b| match (a.metadata.is_dir(), b.metadata.is_dir()) {
|
||||||
|
(true, false) => Ordering::Less,
|
||||||
|
(false, true) => Ordering::Greater,
|
||||||
|
_ => LANGUAGE_SORTER.compare(&a.display_name, &b.display_name),
|
||||||
|
});
|
||||||
|
items
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan_search<F: Fn(SearchItem) -> bool + Sync>(callback: F, regex: &Regex) {
|
||||||
|
let entries = match trash::os_limited::list() {
|
||||||
|
Ok(entries) => entries,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("failed to read trash items: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for entry in entries {
|
||||||
|
if let Ok(metadata) = trash::os_limited::metadata(&entry).inspect_err(|err| {
|
||||||
|
log::warn!("failed to get metadata for trash item {entry:?}: {err}")
|
||||||
|
}) {
|
||||||
|
let name = entry.name.to_string_lossy();
|
||||||
|
if regex.is_match(&name) && !callback(SearchItem::Trash(entry, metadata)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This config statement is from trash::os_limited, inverted
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_os = "windows",
|
||||||
|
all(
|
||||||
|
unix,
|
||||||
|
not(target_os = "macos"),
|
||||||
|
not(target_os = "ios"),
|
||||||
|
not(target_os = "android")
|
||||||
|
)
|
||||||
|
)))]
|
||||||
|
impl TrashExt for Trash {
|
||||||
|
fn scan_search<F: Fn(SearchItem) -> bool + Sync>(callback: F, regex: &Regex) {
|
||||||
|
log::warn!(
|
||||||
|
"searching trash not supported on this platform for pattern {:?}",
|
||||||
|
regex.as_str()
|
||||||
|
);
|
||||||
|
drop(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use std::num::NonZeroU16;
|
use std::num::NonZeroU16;
|
||||||
|
|
||||||
use crate::{config::IconSizes, tab::View};
|
use crate::config::IconSizes;
|
||||||
|
use crate::tab::View;
|
||||||
|
|
||||||
static DEFAULT_ZOOM: NonZeroU16 = NonZeroU16::new(100).unwrap();
|
static DEFAULT_ZOOM: NonZeroU16 = NonZeroU16::new(100).unwrap();
|
||||||
static MIN_ZOOM: NonZeroU16 = NonZeroU16::new(50).unwrap();
|
static MIN_ZOOM: NonZeroU16 = NonZeroU16::new(50).unwrap();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue