From 4c64eb5ae5be9542c8f0a13079debd7e8e6bfe98 Mon Sep 17 00:00:00 2001 From: crschnick Date: Tue, 9 Jul 2024 14:03:56 +0000 Subject: [PATCH] [stage] --- .../app/browser/BrowserSelectionListComp.java | 11 +- .../app/browser/BrowserTransferComp.java | 91 ++++------ .../app/browser/BrowserTransferModel.java | 156 ++++++++---------- .../app/browser/BrowserTransferProgress.java | 3 +- .../file/BrowserFileTransferOperation.java | 3 + .../io/xpipe/app/fxcomps/impl/FilterComp.java | 9 +- .../java/io/xpipe/app/util/DesktopHelper.java | 17 ++ .../io/xpipe/app/resources/style/browser.css | 8 +- .../xpipe/app/resources/style/filter-comp.css | 2 +- lang/app/strings/translations_da.properties | 4 +- lang/app/strings/translations_de.properties | 4 +- lang/app/strings/translations_en.properties | 5 +- lang/app/strings/translations_es.properties | 4 +- lang/app/strings/translations_fr.properties | 4 +- lang/app/strings/translations_it.properties | 4 +- lang/app/strings/translations_ja.properties | 4 +- lang/app/strings/translations_nl.properties | 4 +- lang/app/strings/translations_pt.properties | 2 +- lang/app/strings/translations_ru.properties | 4 +- lang/app/strings/translations_tr.properties | 4 +- lang/app/strings/translations_zh.properties | 4 +- version | 2 +- 22 files changed, 166 insertions(+), 183 deletions(-) diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserSelectionListComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserSelectionListComp.java index 93ae14cec..624d9f618 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserSelectionListComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserSelectionListComp.java @@ -7,6 +7,7 @@ import io.xpipe.app.core.window.AppWindowHelper; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.impl.PrettyImageHelper; +import io.xpipe.app.fxcomps.util.BindingsHelper; import io.xpipe.app.fxcomps.util.PlatformThread; import javafx.beans.binding.Bindings; @@ -58,9 +59,15 @@ public class BrowserSelectionListComp extends SimpleComp { return Comp.of(() -> { var image = PrettyImageHelper.ofFixedSizeSquare(entry.getIcon(), 24) .createRegion(); - var l = new Label(null, image); + var t = nameTransformation.apply(entry); + var l = new Label(t.getValue(), image); l.setTextOverrun(OverrunStyle.CENTER_ELLIPSIS); - l.textProperty().bind(PlatformThread.sync(nameTransformation.apply(entry))); + t.addListener((observable, oldValue, newValue) -> { + PlatformThread.runLaterIfNeeded(() -> { + l.setText(newValue); + }); + }); + BindingsHelper.preserve(l, t); return l; }); }, diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserTransferComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserTransferComp.java index e4bfb2a2d..0ea541c1e 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserTransferComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserTransferComp.java @@ -1,8 +1,6 @@ package io.xpipe.app.browser; -import io.xpipe.app.browser.file.BrowserFileTransferMode; import io.xpipe.app.browser.fs.OpenFileSystemModel; -import io.xpipe.app.comp.base.LoadingOverlayComp; import io.xpipe.app.core.AppFont; import io.xpipe.app.core.AppI18n; import io.xpipe.app.fxcomps.Comp; @@ -11,11 +9,14 @@ import io.xpipe.app.fxcomps.augment.DragOverPseudoClassAugment; import io.xpipe.app.fxcomps.impl.*; import io.xpipe.app.fxcomps.util.DerivedObservableList; import io.xpipe.app.fxcomps.util.PlatformThread; +import io.xpipe.app.util.ThreadHelper; import javafx.beans.binding.Bindings; +import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.css.PseudoClass; import javafx.geometry.Insets; import javafx.scene.image.Image; +import javafx.scene.input.ClipboardContent; import javafx.scene.input.Dragboard; import javafx.scene.input.TransferMode; import javafx.scene.layout.Region; @@ -38,8 +39,6 @@ public class BrowserTransferComp extends SimpleComp { @Override protected Region createSimple() { var syncItems = PlatformThread.sync(model.getItems()); - var syncDownloaded = PlatformThread.sync(model.getDownloading()); - var syncAllDownloaded = PlatformThread.sync(model.getAllDownloaded()); var background = new LabelComp(AppI18n.observable("transferDescription")) .apply(struc -> struc.get().setGraphic(new FontIcon("mdi2d-download-outline"))) @@ -53,46 +52,42 @@ public class BrowserTransferComp extends SimpleComp { .getList(); var list = new BrowserSelectionListComp( binding, - entry -> Bindings.createStringBinding( - () -> { - var sourceItem = syncItems.stream() - .filter(item -> item.getBrowserEntry() == entry) - .findAny(); - if (sourceItem.isEmpty()) { - return "?"; - } - var name = entry.getModel() == null - || sourceItem - .get() - .downloadFinished() - .get() - ? "Local" - : entry.getModel() - .getFileSystemModel() - .getName(); - return entry.getFileName() + " (" + name + ")"; - }, - syncAllDownloaded)) + entry -> { + var sourceItem = syncItems.stream() + .filter(item -> item.getBrowserEntry() == entry) + .findAny(); + if (sourceItem.isEmpty()) { + return new SimpleStringProperty("?"); + } + return Bindings.createStringBinding(() -> { + var p = sourceItem.get().getProgress().getValue(); + var progressSuffix = sourceItem.get().downloadFinished().get() ? "" : " " + (p.getTransferred() * 100 / p.getTotal()) + "%"; + return entry.getFileName() + progressSuffix; + }, sourceItem.get().getProgress()); + }) .grow(false, true); - var dragNotice = new LabelComp(syncAllDownloaded.flatMap( - aBoolean -> aBoolean ? AppI18n.observable("dragLocalFiles") : AppI18n.observable("dragFiles"))) + var dragNotice = new LabelComp(AppI18n.observable("dragLocalFiles")) .apply(struc -> struc.get().setGraphic(new FontIcon("mdi2h-hand-left"))) .apply(struc -> AppFont.medium(struc.get())) .apply(struc -> struc.get().setWrapText(true)) .hide(Bindings.isEmpty(syncItems)); - var downloadButton = new IconButtonComp("mdi2d-download", () -> { - model.download(); - }) - .hide(Bindings.isEmpty(syncItems)) - .disable(syncAllDownloaded) - .tooltipKey("downloadStageDescription"); var clearButton = new IconButtonComp("mdi2c-close", () -> { - model.clear(true); + ThreadHelper.runAsync(() -> { + model.clear(true); + }); }) .hide(Bindings.isEmpty(syncItems)) .tooltipKey("clearTransferDescription"); + var downloadButton = new IconButtonComp("mdi2f-folder-move-outline", () -> { + ThreadHelper.runFailableAsync(() -> { + model.transferToDownloads(); + }); + }) + .hide(Bindings.isEmpty(syncItems)) + .tooltipKey("downloadStageDescription"); + var bottom = new HorizontalComp(List.of(Comp.hspacer(), dragNotice, Comp.hspacer(), downloadButton, Comp.hspacer(4), clearButton)); var listBox = new VerticalComp(List.of(list, bottom)) @@ -100,8 +95,7 @@ public class BrowserTransferComp extends SimpleComp { .padding(new Insets(10, 10, 5, 10)) .apply(struc -> struc.get().setMinHeight(200)) .apply(struc -> struc.get().setMaxHeight(200)); - var stack = LoadingOverlayComp.noProgress( - new StackComp(List.of(backgroundStack, listBox)) + var stack = new StackComp(List.of(backgroundStack, listBox)) .apply(DragOverPseudoClassAugment.create()) .apply(struc -> { struc.get().setOnDragOver(event -> { @@ -110,13 +104,6 @@ public class BrowserTransferComp extends SimpleComp { event.acceptTransferModes(TransferMode.ANY); event.consume(); } - - // Accept drops from outside the app window - if (event.getGestureSource() == null - && !event.getDragboard().getFiles().isEmpty()) { - event.acceptTransferModes(TransferMode.ANY); - event.consume(); - } }); struc.get().setOnDragDropped(event -> { // Accept drops from inside the app window @@ -138,29 +125,14 @@ public class BrowserTransferComp extends SimpleComp { event.setDropCompleted(true); event.consume(); } - - // Accept drops from outside the app window - if (event.getGestureSource() == null) { - model.dropLocal(event.getDragboard().getFiles()); - event.setDropCompleted(true); - event.consume(); - } }); struc.get().setOnDragDetected(event -> { - if (syncDownloaded.getValue()) { - return; - } - var selected = syncItems.stream() .map(item -> item.getBrowserEntry()) .toList(); Dragboard db = struc.get().startDragAndDrop(TransferMode.COPY); - var cc = BrowserClipboard.startDrag(null, selected, BrowserFileTransferMode.NORMAL); - if (cc == null) { - return; - } - + var cc = new ClipboardContent(); var files = syncItems.stream() .filter(item -> item.downloadFinished().get()) .map(item -> { @@ -197,8 +169,7 @@ public class BrowserTransferComp extends SimpleComp { model.clear(false); event.consume(); }); - }), - syncDownloaded); + }); stack.apply(struc -> { model.getBrowserSessionModel().getDraggingFiles().addListener((observable, oldValue, newValue) -> { diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserTransferModel.java b/app/src/main/java/io/xpipe/app/browser/BrowserTransferModel.java index bc19c109b..b15e38672 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserTransferModel.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserTransferModel.java @@ -7,45 +7,49 @@ import io.xpipe.app.browser.file.LocalFileSystem; import io.xpipe.app.browser.fs.OpenFileSystemModel; import io.xpipe.app.browser.session.BrowserSessionModel; import io.xpipe.app.issue.ErrorEvent; -import io.xpipe.app.util.BooleanScope; +import io.xpipe.app.util.DesktopHelper; import io.xpipe.app.util.ShellTemp; - +import io.xpipe.app.util.ThreadHelper; import javafx.beans.binding.Bindings; -import javafx.beans.property.BooleanProperty; import javafx.beans.property.Property; -import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableBooleanValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; - import lombok.Value; import org.apache.commons.io.FileUtils; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; +import java.nio.file.StandardCopyOption; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.Optional; @Value public class BrowserTransferModel { private static final Path TEMP = ShellTemp.getLocalTempDataDirectory("download"); - ExecutorService executor = Executors.newSingleThreadExecutor(r -> { - Thread t = Executors.defaultThreadFactory().newThread(r); - t.setDaemon(true); - t.setName("file downloader"); - return t; - }); BrowserSessionModel browserSessionModel; ObservableList items = FXCollections.observableArrayList(); - BooleanProperty downloading = new SimpleBooleanProperty(); - BooleanProperty allDownloaded = new SimpleBooleanProperty(); + + public BrowserTransferModel(BrowserSessionModel browserSessionModel) { + this.browserSessionModel = browserSessionModel; + var thread = ThreadHelper.createPlatformThread("file downloader", true,() -> { + while (true) { + Optional toDownload; + synchronized (items) { + toDownload = items.stream().filter(item -> !item.downloadFinished().get()).findFirst(); + } + if (toDownload.isPresent()) { + downloadSingle(toDownload.get()); + } + ThreadHelper.sleep(20); + } + }); + thread.start(); + } private void cleanDirectory() { if (!Files.isDirectory(TEMP)) { @@ -63,95 +67,77 @@ public class BrowserTransferModel { } public void clear(boolean delete) { - items.clear(); + synchronized (items) { + items.clear(); + } if (delete) { - executor.submit(() -> { - cleanDirectory(); - }); + cleanDirectory(); } } public void drop(OpenFileSystemModel model, List entries) { - entries.forEach(entry -> { - var name = entry.getFileName(); - if (items.stream().anyMatch(item -> item.getName().equals(name))) { - return; - } - - Path file = TEMP.resolve(name); - var item = new Item(model, name, entry, file); - items.add(item); - allDownloaded.set(false); - }); - } - - public void dropLocal(List entries) { - if (entries.isEmpty()) { - return; - } - - var empty = items.isEmpty(); - try { - var paths = entries.stream().map(File::toPath).filter(Files::exists).toList(); - for (Path path : paths) { - var entry = LocalFileSystem.getLocalBrowserEntry(path); + synchronized (items) { + entries.forEach(entry -> { var name = entry.getFileName(); if (items.stream().anyMatch(item -> item.getName().equals(name))) { return; } - var item = new Item(null, name, entry, path); - item.progress.setValue(BrowserTransferProgress.finished( - entry.getFileName(), entry.getRawFileEntry().getSize())); + Path file = TEMP.resolve(name); + var item = new Item(model, name, entry, file); items.add(item); - } - } catch (Exception ex) { - ErrorEvent.fromThrowable(ex).handle(); - } - if (empty) { - allDownloaded.set(true); + }); } } - public void download() { - executor.submit(() -> { - try { - FileUtils.forceMkdir(TEMP.toFile()); - } catch (IOException e) { - ErrorEvent.fromThrowable(e).handle(); + public void downloadSingle(Item item) { + try { + FileUtils.forceMkdir(TEMP.toFile()); + } catch (IOException e) { + ErrorEvent.fromThrowable(e).handle(); + return; + } + + if (item.downloadFinished().get()) { return; } - for (Item item : new ArrayList<>(items)) { - if (item.downloadFinished().get()) { - continue; - } + if (item.getOpenFileSystemModel() != null + && item.getOpenFileSystemModel().isClosed()) { + return; + } - if (item.getOpenFileSystemModel() != null - && item.getOpenFileSystemModel().isClosed()) { - continue; - } - - try { - try (var ignored = new BooleanScope(downloading).start()) { - var op = new BrowserFileTransferOperation( - LocalFileSystem.getLocalFileEntry(TEMP), - List.of(item.getBrowserEntry().getRawFileEntry()), - BrowserFileTransferMode.COPY, - false, - progress -> { - item.getProgress().setValue(progress); - item.getOpenFileSystemModel().getProgress().setValue(progress); - }); - op.execute(); - } - } catch (Throwable t) { - ErrorEvent.fromThrowable(t).handle(); + try { + var op = new BrowserFileTransferOperation( + LocalFileSystem.getLocalFileEntry(TEMP), + List.of(item.getBrowserEntry().getRawFileEntry()), + BrowserFileTransferMode.COPY, + false, + progress -> { + item.getProgress().setValue(progress); + item.getOpenFileSystemModel().getProgress().setValue(progress); + }); + op.execute(); + } catch (Throwable t) { + ErrorEvent.fromThrowable(t).handle(); + synchronized (items) { items.remove(item); } } - allDownloaded.set(true); - }); + } + + public void transferToDownloads() throws Exception { + if (items.isEmpty()) { + return; + } + + var files = items.stream().map(item -> item.getLocalFile()).toList(); + var downloads = DesktopHelper.getDownloadsDirectory(); + for (Path file : files) { + Files.move(file, downloads.resolve(file.getFileName()), StandardCopyOption.REPLACE_EXISTING); + } + clear(true); + DesktopHelper.browseFileInDirectory(downloads.resolve(files.getFirst().getFileName())); } @Value diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserTransferProgress.java b/app/src/main/java/io/xpipe/app/browser/BrowserTransferProgress.java index 5deb42634..bb1e16ec7 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserTransferProgress.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserTransferProgress.java @@ -41,6 +41,7 @@ public class BrowserTransferProgress { var share = (double) transferred / total; var rest = (1.0 - share) / share; var restMillis = (long) (elapsed.toMillis() * rest); - return Duration.of(restMillis, ChronoUnit.MILLIS); + var startupAdjustment = (long) (restMillis / (1.0 + Math.max(10000 - elapsed.toMillis(), 0) / 10000.0)); + return Duration.of(restMillis + startupAdjustment, ChronoUnit.MILLIS); } } diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileTransferOperation.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileTransferOperation.java index 9271e41d6..5a2f42ea5 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileTransferOperation.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileTransferOperation.java @@ -229,6 +229,9 @@ public class BrowserFileTransferOperation { OutputStream outputStream = null; try { var fileSize = sourceFile.getFileSystem().getFileSize(sourceFile.getPath()); + + // Read the first few bytes to figure out possible command failure early + // before creating the output stream inputStream = new BufferedInputStream(sourceFile.getFileSystem().openInput(sourceFile.getPath()), 1024); inputStream.mark(1024); var streamStart = new byte[1024]; diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java index 4aa431338..f10f1de61 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java @@ -9,7 +9,6 @@ import io.xpipe.app.fxcomps.SimpleCompStructure; import io.xpipe.app.fxcomps.util.PlatformThread; import javafx.beans.binding.Bindings; import javafx.beans.property.Property; -import javafx.geometry.Pos; import javafx.scene.Cursor; import javafx.scene.input.MouseButton; import org.kordamp.ikonli.javafx.FontIcon; @@ -36,14 +35,12 @@ public class FilterComp extends Comp> { } }); var filter = new CustomTextField(); - filter.alignmentProperty().bind(Bindings.createObjectBinding(() -> { - return filter.isFocused() || (filter.getText() != null && !filter.getText().isEmpty()) ? Pos.CENTER_LEFT : Pos.CENTER; - }, filter.textProperty(), filter.focusedProperty())); filter.setMaxHeight(2000); filter.getStyleClass().add("filter-comp"); filter.promptTextProperty().bind(AppI18n.observable("searchFilter")); - filter.setLeft(fi); - filter.setRight(clear); + filter.rightProperty().bind(Bindings.createObjectBinding(() -> { + return filter.isFocused() ? clear : fi; + }, filter.focusedProperty())); filter.setAccessibleText("Filter"); filterText.subscribe(val -> { diff --git a/app/src/main/java/io/xpipe/app/util/DesktopHelper.java b/app/src/main/java/io/xpipe/app/util/DesktopHelper.java index d3adaad33..66ba5266e 100644 --- a/app/src/main/java/io/xpipe/app/util/DesktopHelper.java +++ b/app/src/main/java/io/xpipe/app/util/DesktopHelper.java @@ -28,6 +28,23 @@ public class DesktopHelper { return Path.of(System.getProperty("user.home") + "/Desktop"); } + public static Path getDownloadsDirectory() throws Exception { + if (OsType.getLocal() == OsType.WINDOWS) { + return Path.of(LocalShell.getLocalPowershell() + .executeSimpleStringCommand("(New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path")); + } else if (OsType.getLocal() == OsType.LINUX) { + try (var cmd = LocalShell.getShell().command("xdg-user-dir DOWNLOAD").start()) { + var read = cmd.readStdoutDiscardErr(); + var exit = cmd.getExitCode(); + if (exit == 0) { + return Path.of(read); + } + } + } + + return Path.of(System.getProperty("user.home") + "/Downloads"); + } + public static void browsePathRemote(ShellControl sc, String path, FileKind kind) throws Exception { var d = sc.getShellDialect(); switch (sc.getOsType()) { diff --git a/app/src/main/resources/io/xpipe/app/resources/style/browser.css b/app/src/main/resources/io/xpipe/app/resources/style/browser.css index 6fd8e80b1..26b6e512d 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/browser.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/browser.css @@ -15,7 +15,7 @@ -fx-padding: 0 6 8 8; } -.transfer > * { +.transfer > .download-background { -fx-border-radius: 4; -fx-background-radius: 4; -fx-border-color: -color-border-default; @@ -23,7 +23,7 @@ -fx-background-color: -color-bg-subtle; } -.transfer:highlighted > * { +.transfer:highlighted > .download-background { -fx-border-color: -color-accent-emphasis; -fx-background-color: derive(-color-bg-subtle, 5%); } @@ -228,8 +228,8 @@ .browser .browser-content { -fx-padding: 6 0 0 0; - -fx-border-radius: 10 10 4 4; - -fx-background-radius: 10 10 4 4; + -fx-border-radius: 4; + -fx-background-radius: 4; -fx-background-color: -color-bg-subtle, -color-bg-default; -fx-background-insets: 0, 7 0 0 0; -fx-border-width: 1; diff --git a/app/src/main/resources/io/xpipe/app/resources/style/filter-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/filter-comp.css index 331fd8c3c..f9e10e0d9 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/filter-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/filter-comp.css @@ -1,4 +1,4 @@ .filter-comp { - -fx-padding: 0.15em 0.3em 0.15em 0.3em; + -fx-padding: 0.15em 0.3em 0.15em 0.7em; -fx-background-color: transparent; } diff --git a/lang/app/strings/translations_da.properties b/lang/app/strings/translations_da.properties index 6ec8894c6..29b3d05f7 100644 --- a/lang/app/strings/translations_da.properties +++ b/lang/app/strings/translations_da.properties @@ -80,7 +80,7 @@ lockCreationAlertHeader=Indstil din nye master-adgangssætning #custom finish=Afslut error=Der opstod en fejl -downloadStageDescription=Downloader filer til din lokale maskine, så du kan trække og slippe dem i dit oprindelige skrivebordsmiljø. +downloadStageDescription=Flytter downloadede filer til dit systems download-bibliotek og åbner det. ok=Ok search=Søg efter newFile=Ny fil @@ -108,7 +108,7 @@ deleteAlertHeader=Vil du slette de ($COUNT$) valgte elementer? selectedElements=Udvalgte elementer: mustNotBeEmpty=$VALUE$ må ikke være tom valueMustNotBeEmpty=Værdien må ikke være tom -transferDescription=Drop filer til overførsel +transferDescription=Drop filer til download dragFiles=Træk filer i browseren dragLocalFiles=Træk lokale filer herfra null=$VALUE$ må ikke være nul diff --git a/lang/app/strings/translations_de.properties b/lang/app/strings/translations_de.properties index b839873c5..2cbd38e3b 100644 --- a/lang/app/strings/translations_de.properties +++ b/lang/app/strings/translations_de.properties @@ -82,7 +82,7 @@ lockCreationAlertHeader=Lege deine neue Master-Passphrase fest #custom finish=Fertigstellen error=Ein Fehler ist aufgetreten -downloadStageDescription=Lädt Dateien auf deinen lokalen Rechner herunter, damit du sie per Drag & Drop in deine native Desktopumgebung ziehen kannst. +downloadStageDescription=Verschiebt heruntergeladene Dateien in das Download-Verzeichnis deines Systems und öffnet sie. ok=Ok search=Suche newFile=Neue Datei @@ -107,7 +107,7 @@ deleteAlertHeader=Willst du die ($COUNT$) ausgewählten Elemente löschen? selectedElements=Ausgewählte Elemente: mustNotBeEmpty=$VALUE$ darf nicht leer sein valueMustNotBeEmpty=Der Wert darf nicht leer sein -transferDescription=Dateien zum Übertragen ablegen +transferDescription=Dateien zum Herunterladen ablegen dragFiles=Dateien im Browser ziehen dragLocalFiles=Lokale Dateien von hier ziehen null=$VALUE$ muss nicht null sein diff --git a/lang/app/strings/translations_en.properties b/lang/app/strings/translations_en.properties index d963a8371..6c15f5395 100644 --- a/lang/app/strings/translations_en.properties +++ b/lang/app/strings/translations_en.properties @@ -80,7 +80,8 @@ lockCreationAlertHeader=Set your new master passphrase #context: verb, exit finish=Finish error=An error occurred -downloadStageDescription=Downloads files to your local machine, so you can drag and drop them into your native desktop environment. +#force +downloadStageDescription=Moves downloaded files into your system downloads directory and opens it. ok=Ok search=Search newFile=New file @@ -105,7 +106,7 @@ deleteAlertHeader=Do you want to delete the ($COUNT$) selected elements? selectedElements=Selected elements: mustNotBeEmpty=$VALUE$ must not be empty valueMustNotBeEmpty=Value must not be empty -transferDescription=Drop files to transfer +transferDescription=Drop files to download dragFiles=Drag files within browser dragLocalFiles=Drag local files from here null=$VALUE$ must be not null diff --git a/lang/app/strings/translations_es.properties b/lang/app/strings/translations_es.properties index f40b2540c..cf9a45f31 100644 --- a/lang/app/strings/translations_es.properties +++ b/lang/app/strings/translations_es.properties @@ -77,7 +77,7 @@ lockCreationAlertTitle=Establecer frase de contraseña lockCreationAlertHeader=Establece tu nueva frase de contraseña maestra finish=Terminar error=Se ha producido un error -downloadStageDescription=Descarga archivos a tu máquina local, para que puedas arrastrarlos y soltarlos en tu entorno de escritorio nativo. +downloadStageDescription=Mueve los archivos descargados al directorio de descargas de tu sistema y ábrelo. ok=Ok search=Busca en newFile=Nuevo archivo @@ -102,7 +102,7 @@ deleteAlertHeader=¿Quieres borrar los ($COUNT$) elementos seleccionados? selectedElements=Elementos seleccionados: mustNotBeEmpty=$VALUE$ no debe estar vacío valueMustNotBeEmpty=El valor no debe estar vacío -transferDescription=Soltar archivos para transferir +transferDescription=Soltar archivos para descargar dragFiles=Arrastrar archivos dentro del navegador dragLocalFiles=Arrastra archivos locales desde aquí null=$VALUE$ debe ser no nulo diff --git a/lang/app/strings/translations_fr.properties b/lang/app/strings/translations_fr.properties index 973d8722b..1d75f20d0 100644 --- a/lang/app/strings/translations_fr.properties +++ b/lang/app/strings/translations_fr.properties @@ -77,7 +77,7 @@ lockCreationAlertTitle=Définir une phrase de passe lockCreationAlertHeader=Définis ta nouvelle phrase de passe principale finish=Finir error=Une erreur s'est produite -downloadStageDescription=Télécharge les fichiers sur ta machine locale, afin que tu puisses les faire glisser et les déposer dans ton environnement de bureau natif. +downloadStageDescription=Déplace les fichiers téléchargés dans le répertoire des téléchargements de ton système et l'ouvre. ok=Ok search=Rechercher newFile=Nouveau fichier @@ -102,7 +102,7 @@ deleteAlertHeader=Veux-tu supprimer les ($COUNT$) éléments sélectionnés ? selectedElements=Éléments sélectionnés : mustNotBeEmpty=$VALUE$ ne doit pas être vide valueMustNotBeEmpty=La valeur ne doit pas être vide -transferDescription=Dépose des fichiers à transférer +transferDescription=Dépose des fichiers à télécharger dragFiles=Faire glisser des fichiers dans le navigateur dragLocalFiles=Fais glisser des fichiers locaux à partir d'ici null=$VALUE$ doit être non nul diff --git a/lang/app/strings/translations_it.properties b/lang/app/strings/translations_it.properties index 89712f1ff..94212063c 100644 --- a/lang/app/strings/translations_it.properties +++ b/lang/app/strings/translations_it.properties @@ -77,7 +77,7 @@ lockCreationAlertTitle=Imposta una passphrase lockCreationAlertHeader=Imposta la tua nuova passphrase principale finish=Terminare error=Si è verificato un errore -downloadStageDescription=Scarica i file sul tuo computer locale, in modo che tu possa trascinarli e rilasciarli nel tuo ambiente desktop nativo. +downloadStageDescription=Sposta i file scaricati nella directory dei download del sistema e li apre. ok=Ok search=Ricerca newFile=Nuovo file @@ -102,7 +102,7 @@ deleteAlertHeader=Vuoi cancellare gli elementi ($COUNT$) selezionati? selectedElements=Elementi selezionati: mustNotBeEmpty=$VALUE$ non deve essere vuoto valueMustNotBeEmpty=Il valore non deve essere vuoto -transferDescription=Rilasciare i file da trasferire +transferDescription=Scaricare i file dragFiles=Trascinare i file nel browser dragLocalFiles=Trascina i file locali da qui null=$VALUE$ deve essere non nullo diff --git a/lang/app/strings/translations_ja.properties b/lang/app/strings/translations_ja.properties index 88581f5e6..75e23cdbc 100644 --- a/lang/app/strings/translations_ja.properties +++ b/lang/app/strings/translations_ja.properties @@ -77,7 +77,7 @@ lockCreationAlertTitle=パスフレーズを設定する lockCreationAlertHeader=新しいマスターパスフレーズを設定する finish=終了する error=エラーが発生した -downloadStageDescription=ファイルをローカルマシンにダウンロードし、ネイティブのデスクトップ環境にドラッグ&ドロップできるようにする。 +downloadStageDescription=ダウンロードしたファイルをシステムのダウンロード・ディレクトリに移動し、開く。 ok=OK search=検索 newFile=新規ファイル @@ -102,7 +102,7 @@ deleteAlertHeader=選択した ($COUNT$) 要素を削除するか? selectedElements=選択された要素: mustNotBeEmpty=$VALUE$ は空であってはならない valueMustNotBeEmpty=値は空であってはならない -transferDescription=ファイルをドロップして転送する +transferDescription=ファイルをドロップしてダウンロードする dragFiles=ブラウザ内でファイルをドラッグする dragLocalFiles=ここからローカルファイルをドラッグする null=$VALUE$ はnullであってはならない。 diff --git a/lang/app/strings/translations_nl.properties b/lang/app/strings/translations_nl.properties index c3cea1d4c..350d3a941 100644 --- a/lang/app/strings/translations_nl.properties +++ b/lang/app/strings/translations_nl.properties @@ -77,7 +77,7 @@ lockCreationAlertTitle=Passphrase instellen lockCreationAlertHeader=Stel je nieuwe hoofdwachtzin in finish=Beëindigen error=Er is een fout opgetreden -downloadStageDescription=Downloadt bestanden naar je lokale computer, zodat je ze naar je eigen desktopomgeving kunt slepen. +downloadStageDescription=Verplaatst gedownloade bestanden naar de downloadmap van je systeem en opent deze. ok=Ok search=Zoeken newFile=Nieuw bestand @@ -102,7 +102,7 @@ deleteAlertHeader=Wil je de ($COUNT$) geselecteerde elementen verwijderen? selectedElements=Geselecteerde elementen: mustNotBeEmpty=$VALUE$ mag niet leeg zijn valueMustNotBeEmpty=Waarde mag niet leeg zijn -transferDescription=Bestanden laten vallen om over te dragen +transferDescription=Bestanden laten vallen om te downloaden dragFiles=Bestanden slepen binnen browser dragLocalFiles=Lokale bestanden van hier slepen null=$VALUE$ moet not null zijn diff --git a/lang/app/strings/translations_pt.properties b/lang/app/strings/translations_pt.properties index 9ef35244f..c642a1874 100644 --- a/lang/app/strings/translations_pt.properties +++ b/lang/app/strings/translations_pt.properties @@ -77,7 +77,7 @@ lockCreationAlertTitle=Define a frase-chave lockCreationAlertHeader=Define a tua nova frase-chave principal finish=Termina error=Ocorreu um erro -downloadStageDescription=Descarrega ficheiros para a sua máquina local, para que possa arrastá-los e largá-los no seu ambiente de trabalho nativo. +downloadStageDescription=Move os ficheiros transferidos para o diretório de transferências do sistema e abre-o. ok=Ok search=Procura newFile=Novo ficheiro diff --git a/lang/app/strings/translations_ru.properties b/lang/app/strings/translations_ru.properties index 088805f7c..738d83e95 100644 --- a/lang/app/strings/translations_ru.properties +++ b/lang/app/strings/translations_ru.properties @@ -77,7 +77,7 @@ lockCreationAlertTitle=Установите парольную фразу lockCreationAlertHeader=Установите новую главную кодовую фразу finish=Закончи error=Произошла ошибка -downloadStageDescription=Загрузи файлы на локальную машину, чтобы ты мог перетащить их в родное окружение рабочего стола. +downloadStageDescription=Перемести скачанные файлы в системный каталог загрузок и открой его. ok=Ок search=Поиск newFile=Новый файл @@ -102,7 +102,7 @@ deleteAlertHeader=Хочешь удалить ($COUNT$) выбранные эл selectedElements=Выбранные элементы: mustNotBeEmpty=$VALUE$ не должен быть пустым valueMustNotBeEmpty=Значение не должно быть пустым -transferDescription=Сбрасывать файлы для передачи +transferDescription=Сбрасывать файлы для загрузки dragFiles=Перетаскивание файлов в браузере dragLocalFiles=Перетащите локальные файлы отсюда null=$VALUE$ должен быть не нулевым diff --git a/lang/app/strings/translations_tr.properties b/lang/app/strings/translations_tr.properties index da0916c98..67c1c6116 100644 --- a/lang/app/strings/translations_tr.properties +++ b/lang/app/strings/translations_tr.properties @@ -77,7 +77,7 @@ lockCreationAlertTitle=Parolayı ayarla lockCreationAlertHeader=Yeni ana parolanızı ayarlayın finish=Bitirmek error=Bir hata oluştu -downloadStageDescription=Dosyaları yerel makinenize indirir, böylece bunları yerel masaüstü ortamınıza sürükleyip bırakabilirsiniz. +downloadStageDescription=İndirilen dosyaları sisteminizin indirilenler dizinine taşır ve açar. ok=Tamam search=Arama newFile=Yeni dosya @@ -102,7 +102,7 @@ deleteAlertHeader=($COUNT$) seçili öğeleri silmek istiyor musunuz? selectedElements=Seçilen unsurlar: mustNotBeEmpty=$VALUE$ boş olmamalıdır valueMustNotBeEmpty=Değer boş olmamalıdır -transferDescription=Aktarılacak dosyaları bırakın +transferDescription=İndirilecek dosyaları bırakın dragFiles=Dosyaları tarayıcı içinde sürükleyin dragLocalFiles=Yerel dosyaları buradan sürükleyin null=$VALUE$ null olmamalıdır diff --git a/lang/app/strings/translations_zh.properties b/lang/app/strings/translations_zh.properties index 3379beb8b..c97aa15c4 100644 --- a/lang/app/strings/translations_zh.properties +++ b/lang/app/strings/translations_zh.properties @@ -77,7 +77,7 @@ lockCreationAlertTitle=设置口令 lockCreationAlertHeader=设置新的主密码 finish=完成 error=发生错误 -downloadStageDescription=将文件下载到本地计算机,以便拖放到本地桌面环境中。 +downloadStageDescription=将下载的文件移动到系统下载目录并打开。 ok=好的 search=搜索 newFile=新文件 @@ -102,7 +102,7 @@ deleteAlertHeader=您想删除 ($COUNT$) 选定的元素吗? selectedElements=选定要素: mustNotBeEmpty=$VALUE$ 不得为空 valueMustNotBeEmpty=值不得为空 -transferDescription=下拉传输文件 +transferDescription=下载文件 dragFiles=在浏览器中拖动文件 dragLocalFiles=从此处拖动本地文件 null=$VALUE$ 必须为非空 diff --git a/version b/version index 924c68fa4..9339bea98 100644 --- a/version +++ b/version @@ -1 +1 @@ -10.1-6 +10.1-7