mirror of
https://github.com/veeso/termscp.git
synced 2025-12-06 17:15:35 -08:00
feat: 132 queuing transfers (#332)
the logic of selecting files has been extended! From now on selecting file will put the files into a transfer queue, which is shown on the bottom panel. When a file is selected the file is added to the queue with a destination path, which is the **current other explorer path at the moment of selection. It is possible to navigate to the transfer queue by using `P` and pressing `ENTER` on a file will remove it from the transfer queue.Other commands will work as well on the transfer queue, like `COPY`, `MOVE`, `DELETE`, `RENAME`. closes #132
This commit is contained in:
committed by
GitHub
parent
368570592f
commit
ec75ae1486
@@ -44,6 +44,12 @@
|
|||||||
|
|
||||||
Released on ??
|
Released on ??
|
||||||
|
|
||||||
|
- **Queuing transfers**:
|
||||||
|
- the logic of selecting files has been extended!
|
||||||
|
- From now on selecting file will put the files into a **transfer queue**, which is shown on the bottom panel.
|
||||||
|
- When a file is selected the file is added to the queue with a destination path, which is the **current other explorer path at the moment of selection.**
|
||||||
|
- It is possible to navigate to the transfer queue by using `P` and pressing `ENTER` or `DELETE` on a file will remove it from the transfer queue.
|
||||||
|
- Other commands will work as well on the transfer queue, like `COPY`, `MOVE`, `DELETE`, `RENAME`.
|
||||||
- [issue 308](https://github.com/veeso/termscp/issues/308): added `--wno-keyring` flag to disable keyring
|
- [issue 308](https://github.com/veeso/termscp/issues/308): added `--wno-keyring` flag to disable keyring
|
||||||
- [issue 316](https://github.com/veeso/termscp/issues/316): Local directory path is not switching to what's specified in the bookmark. Now the local directory path is correctly set following this hierarchy:
|
- [issue 316](https://github.com/veeso/termscp/issues/316): Local directory path is not switching to what's specified in the bookmark. Now the local directory path is correctly set following this hierarchy:
|
||||||
1. Local directory path specified for the host bridge
|
1. Local directory path specified for the host bridge
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="/docs/ptbr/README.md"
|
href="/docs/pt-BR/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="/assets/images/flags/br.png"
|
src="/assets/images/flags/br.png"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/veeso/termscp/blob/main/docs/ptbr/README.md"
|
href="https://github.com/veeso/termscp/blob/main/docs/pt-BR/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="/assets/images/flags/br.png"
|
src="/assets/images/flags/br.png"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
- [Dateiexplorer 📂](#dateiexplorer-)
|
- [Dateiexplorer 📂](#dateiexplorer-)
|
||||||
- [Tastenkombinationen ⌨](#tastenkombinationen-)
|
- [Tastenkombinationen ⌨](#tastenkombinationen-)
|
||||||
- [Mit mehreren Dateien arbeiten 🥷](#mit-mehreren-dateien-arbeiten-)
|
- [Mit mehreren Dateien arbeiten 🥷](#mit-mehreren-dateien-arbeiten-)
|
||||||
|
- [Beispiel](#beispiel)
|
||||||
- [Synchronisiertes Durchsuchen ⏲️](#synchronisiertes-durchsuchen-️)
|
- [Synchronisiertes Durchsuchen ⏲️](#synchronisiertes-durchsuchen-️)
|
||||||
- [Öffnen und Öffnen mit 🚪](#öffnen-und-öffnen-mit-)
|
- [Öffnen und Öffnen mit 🚪](#öffnen-und-öffnen-mit-)
|
||||||
- [Lesezeichen ⭐](#lesezeichen-)
|
- [Lesezeichen ⭐](#lesezeichen-)
|
||||||
@@ -297,19 +298,34 @@ Diese Panels sind im Wesentlichen 3 (ja, tatsächlich drei):
|
|||||||
| <CTRL+C> | Dateiübertragungsvorgang abbrechen | |
|
| <CTRL+C> | Dateiübertragungsvorgang abbrechen | |
|
||||||
| <CTRL+T> | Alle synchronisierten Pfade anzeigen | Track |
|
| <CTRL+T> | Alle synchronisierten Pfade anzeigen | Track |
|
||||||
|
|
||||||
### Mit mehreren Dateien arbeiten 🥷
|
### Mit mehreren Dateien arbeiten 🥷
|
||||||
|
|
||||||
Sie können mit mehreren Dateien arbeiten, indem Sie `<M>` drücken, um die aktuelle Datei auszuwählen, oder `<CTRL+A>`, um alle Dateien im Arbeitsverzeichnis auszuwählen.
|
Du kannst mit mehreren Dateien gleichzeitig arbeiten, mit diesen einfachen Tastenkombinationen:
|
||||||
Sobald eine Datei zur Auswahl markiert ist, wird sie mit einem `*` auf der linken Seite angezeigt.
|
|
||||||
Bei der Arbeit mit der Auswahl werden nur die ausgewählten Dateien für Aktionen verarbeitet, während der aktuell hervorgehobene Eintrag ignoriert wird.
|
|
||||||
Es ist auch möglich, mit mehreren Dateien im Suchergebnis-Panel zu arbeiten.
|
|
||||||
Alle Aktionen sind verfügbar, wenn Sie mit mehreren Dateien arbeiten, aber beachten Sie, dass einige Aktionen etwas anders funktionieren. Schauen wir uns das genauer an:
|
|
||||||
|
|
||||||
- _Kopieren_: Wann immer Sie eine Datei kopieren, werden Sie aufgefordert, den Zielnamen einzugeben. Bei der Arbeit mit mehreren Dateien bezieht sich dieser Name auf das Zielverzeichnis, in dem alle diese Dateien kopiert werden.
|
- `<M>`: Datei zur Auswahl markieren
|
||||||
|
- `<CTRL+A>`: alle Dateien im aktuellen Verzeichnis auswählenas
|
||||||
|
- `<ALT+A>`: Auswahl aller Dateien aufheben
|
||||||
|
|
||||||
- _Umbenennen_: Dasselbe wie Kopieren, aber die Dateien werden dorthin verschoben.
|
Markierte Dateien werden **mit hervorgehobenem Hintergrund** angezeigt.
|
||||||
|
Bei Auswahlaktionen werden nur die markierten Dateien verarbeitet, das aktuell hervorgehobene Element wird ignoriert.
|
||||||
|
|
||||||
- _Speichern unter_: Dasselbe wie Kopieren, aber die Dateien werden dorthin geschrieben.
|
Auch im Suchergebnis-Panel ist die Mehrfachauswahl möglich.
|
||||||
|
|
||||||
|
Alle Aktionen sind bei mehreren Dateien verfügbar, einige funktionieren jedoch leicht anders:
|
||||||
|
|
||||||
|
- *Kopieren*: du wirst nach einem Zielnamen gefragt. Bei mehreren Dateien ist das das Zielverzeichnis.
|
||||||
|
- *Umbenennen*: wie Kopieren, aber verschiebt die Dateien.
|
||||||
|
- *Speichern unter*: wie Kopieren, aber schreibt die Dateien dorthin.
|
||||||
|
|
||||||
|
Wenn du eine Datei in einem Verzeichnis (z. B. `/home`) auswählst und dann das Verzeichnis wechselst, bleibt sie ausgewählt und erscheint in der **Transfer-Warteschlange** im unteren Panel.
|
||||||
|
Beim Markieren einer Datei wird das aktuelle *Remote*-Verzeichnis gespeichert; bei einem Transfer wird sie in dieses Verzeichnis übertragen.
|
||||||
|
|
||||||
|
#### Beispiel
|
||||||
|
|
||||||
|
Wenn wir `/home/a.txt` lokal auswählen und im Remote-Panel in `/tmp` sind, dann zu `/var` wechseln, `/var/b.txt` auswählen und im Remote-Panel in `/home` sind, ergibt der Transfer:
|
||||||
|
|
||||||
|
- `/home/a.txt` → `/tmp/a.txt`
|
||||||
|
- `/var/b.txt` → `/home/b.txt`
|
||||||
|
|
||||||
### Synchronisiertes Durchsuchen ⏲️
|
### Synchronisiertes Durchsuchen ⏲️
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/veeso/termscp/blob/main/docs/ptbr/README.md"
|
href="https://github.com/veeso/termscp/blob/main/docs/pt-BR/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="/assets/images/flags/br.png"
|
src="/assets/images/flags/br.png"
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
- [Credenciales de S3 🦊](#credenciales-de-s3-)
|
- [Credenciales de S3 🦊](#credenciales-de-s3-)
|
||||||
- [Explorador de archivos 📂](#explorador-de-archivos-)
|
- [Explorador de archivos 📂](#explorador-de-archivos-)
|
||||||
- [Keybindings ⌨](#keybindings-)
|
- [Keybindings ⌨](#keybindings-)
|
||||||
- [Trabaja en varios archivos 🥷](#trabaja-en-varios-archivos-)
|
- [Trabajar con múltiples archivos 🥷](#trabajar-con-múltiples-archivos-)
|
||||||
|
- [Ejemplo](#ejemplo)
|
||||||
- [Navegación sincronizada ⏲️](#navegación-sincronizada-️)
|
- [Navegación sincronizada ⏲️](#navegación-sincronizada-️)
|
||||||
- [Abierta y abierta con 🚪](#abierta-y-abierta-con-)
|
- [Abierta y abierta con 🚪](#abierta-y-abierta-con-)
|
||||||
- [Marcadores ⭐](#marcadores-)
|
- [Marcadores ⭐](#marcadores-)
|
||||||
@@ -259,17 +260,34 @@ Para cambiar de panel, debe escribir `<LEFT>` para mover el panel del explorador
|
|||||||
| `<CTRL+C>` | Abortar el proceso de transferencia de archivos | |
|
| `<CTRL+C>` | Abortar el proceso de transferencia de archivos | |
|
||||||
| `<CTRL+T>` | Mostrar todas las rutas sincronizadas | Track |
|
| `<CTRL+T>` | Mostrar todas las rutas sincronizadas | Track |
|
||||||
|
|
||||||
### Trabaja en varios archivos 🥷
|
### Trabajar con múltiples archivos 🥷
|
||||||
|
|
||||||
Puede optar por trabajar en varios archivos, seleccionándolos presionando `<M>`, para seleccionar el archivo actual, o presionando `<CTRL + A>`, que seleccionará todos los archivos en el directorio de trabajo.
|
Puedes optar por trabajar con varios archivos, usando estos controles:
|
||||||
Una vez que un archivo está marcado para su selección, se mostrará con un `*` a la izquierda.
|
|
||||||
Al trabajar en la selección, solo se procesará el archivo seleccionado para las acciones, mientras que el elemento resaltado actual se ignorará.
|
|
||||||
También es posible trabajar en varios archivos desde el panel de resultados de búsqueda.
|
|
||||||
Todas las acciones están disponibles cuando se trabaja con varios archivos, pero tenga en cuenta que algunas acciones funcionan de forma ligeramente diferente. Vamos a sumergirnos en:
|
|
||||||
|
|
||||||
- *Copy*: cada vez que copie un archivo, se le pedirá que inserte el nombre de destino. Cuando se trabaja con varios archivos, este nombre se refiere al directorio de destino donde se copiarán todos estos archivos.
|
- `<M>`: marcar un archivo para selección
|
||||||
- *Rename*: igual que copiar, pero moverá archivos allí.
|
- `<CTRL+A>`: seleccionar todos los archivos del directorio actual
|
||||||
- *Save as*: igual que copiar, pero los escribirá allí.
|
- `<ALT+A>`: deseleccionar todos los archivos
|
||||||
|
|
||||||
|
Una vez marcado, el archivo será **mostrado con un fondo resaltado** .
|
||||||
|
Cuando se trabaja con una selección, solo los archivos seleccionados serán procesados; el archivo resaltado actual será ignorado.
|
||||||
|
|
||||||
|
También se puede trabajar con múltiples archivos desde el panel de resultados de búsqueda.
|
||||||
|
|
||||||
|
Todas las acciones están disponibles con archivos múltiples, pero algunas funcionan de forma algo distinta. Veamos:
|
||||||
|
|
||||||
|
- *Copiar*: al copiar, se pedirá el nombre de destino. Para varios archivos, es el directorio donde se copiarán.
|
||||||
|
- *Renombrar*: igual que copiar, pero mueve los archivos.
|
||||||
|
- *Guardar como*: igual que copiar, pero escribe los archivos allí.
|
||||||
|
|
||||||
|
Si seleccionas un archivo en un directorio (ej. `/home`) y cambias de directorio, seguirá seleccionado y se mostrará en la **cola de transferencia** en el panel inferior.
|
||||||
|
Cuando se selecciona un archivo, se asocia la carpeta *remota* actual con él; si se transfiere, será a esa carpeta.
|
||||||
|
|
||||||
|
#### Ejemplo
|
||||||
|
|
||||||
|
Si seleccionamos `/home/a.txt` localmente y estamos en `/tmp` en remoto, luego cambiamos a `/var`, seleccionamos `/var/b.txt` y estamos en `/home` en el panel remoto, el resultado de transferir será:
|
||||||
|
|
||||||
|
- `/home/a.txt` transferido a `/tmp/a.txt`
|
||||||
|
- `/var/b.txt` transferido a `/home/b.txt`
|
||||||
|
|
||||||
### Navegación sincronizada ⏲️
|
### Navegación sincronizada ⏲️
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/veeso/termscp/blob/main/docs/ptbr/README.md"
|
href="https://github.com/veeso/termscp/blob/main/docs/pt-BR/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="/assets/images/flags/br.png"
|
src="/assets/images/flags/br.png"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
- [Explorateur de fichiers 📂](#explorateur-de-fichiers-)
|
- [Explorateur de fichiers 📂](#explorateur-de-fichiers-)
|
||||||
- [Raccourcis clavier ⌨](#raccourcis-clavier-)
|
- [Raccourcis clavier ⌨](#raccourcis-clavier-)
|
||||||
- [Travailler sur plusieurs fichiers 🥷](#travailler-sur-plusieurs-fichiers-)
|
- [Travailler sur plusieurs fichiers 🥷](#travailler-sur-plusieurs-fichiers-)
|
||||||
|
- [Exemple](#exemple)
|
||||||
- [Navigation synchronisée ⏲️](#navigation-synchronisée-️)
|
- [Navigation synchronisée ⏲️](#navigation-synchronisée-️)
|
||||||
- [Ouvrir et ouvrir avec 🚪](#ouvrir-et-ouvrir-avec-)
|
- [Ouvrir et ouvrir avec 🚪](#ouvrir-et-ouvrir-avec-)
|
||||||
- [Signets ⭐](#signets-)
|
- [Signets ⭐](#signets-)
|
||||||
@@ -258,17 +259,34 @@ Pour changer de panneau, vous devez taper `<LEFT>` pour déplacer le panneau de
|
|||||||
| `<CTRL+C>` | Abandonner le processus de transfert de fichiers | |
|
| `<CTRL+C>` | Abandonner le processus de transfert de fichiers | |
|
||||||
| `<CTRL+T>` | Afficher tous les chemins synchronisés | Track |
|
| `<CTRL+T>` | Afficher tous les chemins synchronisés | Track |
|
||||||
|
|
||||||
### Travailler sur plusieurs fichiers 🥷
|
### Travailler sur plusieurs fichiers 🥷
|
||||||
|
|
||||||
Vous pouvez choisir de travailler sur plusieurs fichiers, en les sélectionnant en appuyant sur `<M>`, afin de sélectionner le fichier actuel, ou en appuyant sur `<CTRL+A>`, ce qui sélectionnera tous les fichiers dans le répertoire de travail.
|
Vous pouvez choisir de travailler sur plusieurs fichiers avec ces simples commandes :
|
||||||
Une fois qu'un fichier est marqué pour la sélection, il sera affiché avec un `*` sur la gauche.
|
|
||||||
Lorsque vous travaillez sur la sélection, seul le fichier sélectionné sera traité pour les actions, tandis que l'élément en surbrillance actuel sera ignoré.
|
|
||||||
Il est également possible de travailler sur plusieurs fichiers dans le panneau des résultats de recherche.
|
|
||||||
Toutes les actions sont disponibles lorsque vous travaillez avec plusieurs fichiers, mais sachez que certaines actions fonctionnent de manière légèrement différente. Plongeons dans:
|
|
||||||
|
|
||||||
- *Copy*: chaque fois que vous copiez un fichier, vous serez invité à insérer le nom de destination. Lorsque vous travaillez avec plusieurs fichiers, ce nom fait référence au répertoire de destination où tous ces fichiers seront copiés.
|
- `<M>` : marquer un fichier à sélectionner
|
||||||
- *Rename*: identique à la copie, mais y déplacera les fichiers.
|
- `<CTRL+A>` : sélectionner tous les fichiers du répertoire actuel
|
||||||
- *Save as*: identique à la copie, mais les y écrira.
|
- `<ALT+A>` : désélectionner tous les fichiers
|
||||||
|
|
||||||
|
Une fois sélectionné, un fichier sera **affiché avec un fond en surbrillance** .
|
||||||
|
Lorsqu’on travaille avec des sélections, seules les fichiers sélectionnés seront affectés par les actions, tandis que l'élément actuellement surligné sera ignoré.
|
||||||
|
|
||||||
|
Il est également possible de travailler avec plusieurs fichiers depuis le panneau des résultats de recherche.
|
||||||
|
|
||||||
|
Toutes les actions sont disponibles avec des fichiers multiples, mais certaines peuvent se comporter différemment. Détails :
|
||||||
|
|
||||||
|
- *Copier* : lors de la copie, il vous sera demandé un nom de destination. Avec plusieurs fichiers, cela correspond au dossier de destination.
|
||||||
|
- *Renommer* : identique à la copie, mais déplace les fichiers.
|
||||||
|
- *Enregistrer sous* : identique à la copie, mais enregistre les fichiers à cet emplacement.
|
||||||
|
|
||||||
|
Si vous sélectionnez un fichier dans un dossier (ex. `/home`) puis changez de répertoire, il restera sélectionné et sera affiché dans la **file d’attente de transfert** en bas.
|
||||||
|
Lorsqu’un fichier est sélectionné, le dossier *distant* courant lui est associé ; en cas de transfert, il sera envoyé vers ce dossier.
|
||||||
|
|
||||||
|
#### Exemple
|
||||||
|
|
||||||
|
Si on sélectionne `/home/a.txt` localement et que le panneau distant est sur `/tmp`, puis on passe à `/var`, on sélectionne `/var/b.txt` et que le panneau distant est sur `/home`, le transfert donnera :
|
||||||
|
|
||||||
|
- `/home/a.txt` transféré vers `/tmp/a.txt`
|
||||||
|
- `/var/b.txt` transféré vers `/home/b.txt`
|
||||||
|
|
||||||
### Navigation synchronisée ⏲️
|
### Navigation synchronisée ⏲️
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/veeso/termscp/blob/main/docs/ptbr/README.md"
|
href="https://github.com/veeso/termscp/blob/main/docs/pt-BR/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="/assets/images/flags/br.png"
|
src="/assets/images/flags/br.png"
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
- [Credenziali S3 🦊](#credenziali-s3-)
|
- [Credenziali S3 🦊](#credenziali-s3-)
|
||||||
- [File explorer 📂](#file-explorer-)
|
- [File explorer 📂](#file-explorer-)
|
||||||
- [Abbinamento tasti ⌨](#abbinamento-tasti-)
|
- [Abbinamento tasti ⌨](#abbinamento-tasti-)
|
||||||
- [Lavora su più file 🥷](#lavora-su-più-file-)
|
- [Lavora con più file 🥷](#lavora-con-più-file-)
|
||||||
|
- [Esempio](#esempio)
|
||||||
- [Synchronized browsing ⏲️](#synchronized-browsing-️)
|
- [Synchronized browsing ⏲️](#synchronized-browsing-️)
|
||||||
- [Apri e apri con 🚪](#apri-e-apri-con-)
|
- [Apri e apri con 🚪](#apri-e-apri-con-)
|
||||||
- [Segnalibri ⭐](#segnalibri-)
|
- [Segnalibri ⭐](#segnalibri-)
|
||||||
@@ -254,17 +255,34 @@ Per cambiare pannello ti puoi muovere con le frecce, `<LEFT>` per andare sul pan
|
|||||||
| `<CTRL+C>` | Annulla trasferimento file | |
|
| `<CTRL+C>` | Annulla trasferimento file | |
|
||||||
| `<CTRL+T>` | Visualizza tutti i percorsi sincronizzati | Track |
|
| `<CTRL+T>` | Visualizza tutti i percorsi sincronizzati | Track |
|
||||||
|
|
||||||
### Lavora su più file 🥷
|
### Lavora con più file 🥷
|
||||||
|
|
||||||
Puoi lavorare su una selezione di file, marcandoli come selezionati tramite `<M>`, per selezionare il file corrente o con `<CTRL+A` per selezionarli tutti.
|
Puoi scegliere di lavorare con più file, usando questi semplici comandi:
|
||||||
Una volta che un file è marcato, sarà visualizzato con un `*` prima del nome.
|
|
||||||
Quando lavori con una selezioni, solo i file selezionati saranno presi in considerazione (l'eventuale file evidenziato sarà ignorato).
|
|
||||||
È possibile operare su più file anche nel pannello di ricerca.
|
|
||||||
Tutte le azioni sono disponibili quando si lavora sulle selezioni, ma occhio, che alcune azioni si comporteranno in maniera leggermente differente. Vediamo quali e come:
|
|
||||||
|
|
||||||
- *Copia*: Se copi un file, ti verrà richiesto di inserire il nome delle destinazione, ma quando lavori con la selezione, il nome si riferisce alla directory di destinazione, mentre il nome del file rimarrà inviariato.
|
- `<M>`: marca un file per la selezione
|
||||||
- *Rinomina*: Come il copia, ma li sposterà.
|
- `<CTRL+A>`: seleziona tutti i file nella directory corrente
|
||||||
- *Salva con nome*: Come il copia, ma li trasferirà.
|
- `<ALT+A>`: deseleziona tutti i file
|
||||||
|
|
||||||
|
Una volta che un file è stato selezionato, verrà **evidenziato con uno sfondo colorato** .
|
||||||
|
Quando lavori su una selezione, solo i file selezionati verranno processati per le azioni, mentre l'elemento attualmente evidenziato sarà ignorato.
|
||||||
|
|
||||||
|
È possibile lavorare con più file anche dal pannello dei risultati di ricerca.
|
||||||
|
|
||||||
|
Tutte le azioni sono disponibili anche quando si lavora con più file, ma alcune funzionano in modo leggermente diverso. Ecco i dettagli:
|
||||||
|
|
||||||
|
- *Copia*: quando copi un file, ti verrà chiesto di inserire il nome di destinazione. Con più file selezionati, questo nome rappresenta la cartella di destinazione dove verranno copiati.
|
||||||
|
- *Rinomina*: come la copia, ma i file verranno spostati lì.
|
||||||
|
- *Salva come*: come la copia, ma i file verranno salvati lì.
|
||||||
|
|
||||||
|
Se selezioni un file in una directory (es. `/home`) e poi cambi directory, il file rimarrà selezionato e sarà visibile nella **coda di trasferimento** nel pannello inferiore.
|
||||||
|
Quando un file viene selezionato, la directory *remota* corrente viene associata all’elemento; quindi, se il file viene trasferito, verrà trasferito nella directory associata.
|
||||||
|
|
||||||
|
#### Esempio
|
||||||
|
|
||||||
|
Se selezioniamo un file locale `/home/a.txt`, siamo su `/tmp` nel pannello remoto, poi ci spostiamo su `/var`, selezioniamo `/var/b.txt`, e sul pannello remoto siamo su `/home`, eseguendo il trasferimento otterremo:
|
||||||
|
|
||||||
|
- `/home/a.txt` trasferito su `/tmp/a.txt`
|
||||||
|
- `/var/b.txt` trasferito su `/home/b.txt`
|
||||||
|
|
||||||
### Synchronized browsing ⏲️
|
### Synchronized browsing ⏲️
|
||||||
|
|
||||||
|
|||||||
23
docs/man.md
23
docs/man.md
@@ -16,6 +16,7 @@
|
|||||||
- [File explorer 📂](#file-explorer-)
|
- [File explorer 📂](#file-explorer-)
|
||||||
- [Keybindings ⌨](#keybindings-)
|
- [Keybindings ⌨](#keybindings-)
|
||||||
- [Work on multiple files 🥷](#work-on-multiple-files-)
|
- [Work on multiple files 🥷](#work-on-multiple-files-)
|
||||||
|
- [Example](#example)
|
||||||
- [Synchronized browsing ⏲️](#synchronized-browsing-️)
|
- [Synchronized browsing ⏲️](#synchronized-browsing-️)
|
||||||
- [Open and Open With 🚪](#open-and-open-with-)
|
- [Open and Open With 🚪](#open-and-open-with-)
|
||||||
- [Bookmarks ⭐](#bookmarks-)
|
- [Bookmarks ⭐](#bookmarks-)
|
||||||
@@ -274,16 +275,34 @@ In order to change panel you need to type `<LEFT>` to move the remote explorer p
|
|||||||
|
|
||||||
### Work on multiple files 🥷
|
### Work on multiple files 🥷
|
||||||
|
|
||||||
You can opt to work on multiple files, selecting them pressing `<M>`, in order to select the current file, or pressing `<CTRL+A>`, which will select all the files in the working directory.
|
You can opt to work on multiple files, with these simple controls:
|
||||||
Once a file is marked for selection, it will be displayed with a `*` on the left.
|
|
||||||
|
- `<M>`: mark a file for selection
|
||||||
|
- `<CTRL+A>`: select all files in the current directory
|
||||||
|
- `<ALT+A>`: deselect all files
|
||||||
|
|
||||||
|
Once a file is marked for selection, it will be **displayed with an highlighted background**.
|
||||||
|
|
||||||
When working on selection, only selected file will be processed for actions, while the current highlighted item will be ignored.
|
When working on selection, only selected file will be processed for actions, while the current highlighted item will be ignored.
|
||||||
It is possible to work on multiple files also when in the find result panel.
|
It is possible to work on multiple files also when in the find result panel.
|
||||||
|
|
||||||
All the actions are available when working with multiple files, but be aware that some actions work in a slightly different way. Let's dive in:
|
All the actions are available when working with multiple files, but be aware that some actions work in a slightly different way. Let's dive in:
|
||||||
|
|
||||||
- *Copy*: whenever you copy a file, you'll be prompted to insert the destination name. When working with multiple file, this name refers to the destination directory where all these files will be copied.
|
- *Copy*: whenever you copy a file, you'll be prompted to insert the destination name. When working with multiple file, this name refers to the destination directory where all these files will be copied.
|
||||||
- *Rename*: same as copy, but will move files there.
|
- *Rename*: same as copy, but will move files there.
|
||||||
- *Save as*: same as copy, but will write them there.
|
- *Save as*: same as copy, but will write them there.
|
||||||
|
|
||||||
|
If you select a file in a directory (e.g. `/home`) and then you change directory the file will be kept selected and it will be displayed in the **transfer queue** in the bottom panel.
|
||||||
|
|
||||||
|
When a file gets selected the current *remote* directory is associated to its entry; so in case the file gets transferred it will be transferred to the directory associated to the file.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
If we select a file on local `/home/a.txt` and we're currently at `/tmp` on remote and then we move to `/var` and we select `/var/b.txt` and on the remote panel we're at `/home` and we perform a transfer the result will be:
|
||||||
|
|
||||||
|
- `/home/a.txt` transferred to `/tmp/a.txt`
|
||||||
|
- `/var/b.txt` transferred to `/home/b.txt`
|
||||||
|
|
||||||
### Synchronized browsing ⏲️
|
### Synchronized browsing ⏲️
|
||||||
|
|
||||||
When enabled, synchronized browsing, will allow you to synchronize the navigation between the two panels.
|
When enabled, synchronized browsing, will allow you to synchronize the navigation between the two panels.
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/veeso/termscp/blob/main/docs/ptbr/README.md"
|
href="https://github.com/veeso/termscp/blob/main/docs/pt-BR/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="/assets/images/flags/br.png"
|
src="/assets/images/flags/br.png"
|
||||||
@@ -15,7 +15,8 @@
|
|||||||
- [Credenciais do S3 🦊](#credenciais-do-s3-)
|
- [Credenciais do S3 🦊](#credenciais-do-s3-)
|
||||||
- [Explorador de Arquivos 📂](#explorador-de-arquivos-)
|
- [Explorador de Arquivos 📂](#explorador-de-arquivos-)
|
||||||
- [Atalhos de Teclado ⌨](#atalhos-de-teclado-)
|
- [Atalhos de Teclado ⌨](#atalhos-de-teclado-)
|
||||||
- [Trabalhar com Vários Arquivos 🥷](#trabalhar-com-vários-arquivos-)
|
- [Trabalhar com múltiplos arquivos 🥷](#trabalhar-com-múltiplos-arquivos-)
|
||||||
|
- [Exemplo](#exemplo)
|
||||||
- [Navegação Sincronizada ⏲️](#navegação-sincronizada-️)
|
- [Navegação Sincronizada ⏲️](#navegação-sincronizada-️)
|
||||||
- [Abrir e Abrir Com 🚪](#abrir-e-abrir-com-)
|
- [Abrir e Abrir Com 🚪](#abrir-e-abrir-com-)
|
||||||
- [Favoritos ⭐](#favoritos-)
|
- [Favoritos ⭐](#favoritos-)
|
||||||
@@ -272,17 +273,34 @@ Para trocar de painel, você precisa pressionar `<LEFT>` para mover para o paine
|
|||||||
| `<CTRL+C>` | Abortir processo de transferência de arquivo | |
|
| `<CTRL+C>` | Abortir processo de transferência de arquivo | |
|
||||||
| `<CTRL+T>` | Mostrar todos os caminhos sincronizados | Track |
|
| `<CTRL+T>` | Mostrar todos os caminhos sincronizados | Track |
|
||||||
|
|
||||||
### Trabalhar com Vários Arquivos 🥷
|
### Trabalhar com múltiplos arquivos 🥷
|
||||||
|
|
||||||
Você pode optar por trabalhar com vários arquivos, selecionando-os pressionando `<M>`, para selecionar o arquivo atual, ou pressionando `<CTRL+A>`, que selecionará todos os arquivos no diretório de trabalho.
|
Você pode optar por trabalhar com vários arquivos, usando estes controles simples:
|
||||||
Uma vez que um arquivo esteja marcado para seleção, ele será exibido com um `*` à esquerda.
|
|
||||||
Ao trabalhar com seleção, apenas o arquivo selecionado será processado para ações, enquanto o item destacado atual será ignorado.
|
|
||||||
É possível trabalhar com vários arquivos também quando estiver no painel de resultados da busca.
|
|
||||||
Todas as ações estão disponíveis ao trabalhar com vários arquivos, mas tenha em mente que algumas ações funcionam de forma ligeiramente diferente. Vamos explicar algumas delas:
|
|
||||||
|
|
||||||
- *Copiar*: sempre que você copiar um arquivo, você será solicitado a inserir o nome de destino. Ao trabalhar com vários arquivos, esse nome refere-se ao diretório de destino onde todos esses arquivos serão copiados.
|
- `<M>`: marcar um arquivo para seleção
|
||||||
- *Renomear*: igual ao copiar, mas moverá os arquivos para lá.
|
- `<CTRL+A>`: selecionar todos os arquivos no diretório atual
|
||||||
- *Salvar como*: igual ao copiar, mas gravará lá.
|
- `<ALT+A>`: desselecionar todos os arquivos
|
||||||
|
|
||||||
|
Uma vez marcado, o arquivo será **exibido com fundo destacado** .
|
||||||
|
Ao trabalhar com seleção, apenas os arquivos selecionados serão processados, enquanto o item atualmente destacado será ignorado.
|
||||||
|
|
||||||
|
É possível trabalhar com múltiplos arquivos também no painel de resultados de busca.
|
||||||
|
|
||||||
|
Todas as ações estão disponíveis ao trabalhar com múltiplos arquivos, mas algumas funcionam de forma ligeiramente diferente. Vamos ver:
|
||||||
|
|
||||||
|
- *Copiar*: ao copiar, será solicitado o nome de destino. Com múltiplos arquivos, esse nome será o diretório de destino para todos eles.
|
||||||
|
- *Renomear*: igual a copiar, mas moverá os arquivos.
|
||||||
|
- *Salvar como*: igual a copiar, mas escreverá os arquivos nesse local.
|
||||||
|
|
||||||
|
Se você selecionar um arquivo num diretório (ex: `/home`) e mudar de diretório, ele continuará selecionado e aparecerá na **fila de transferência** no painel inferior.
|
||||||
|
Ao selecionar um arquivo, o diretório *remoto* atual é associado a ele; então, se for transferido, será enviado para esse diretório associado.
|
||||||
|
|
||||||
|
#### Exemplo
|
||||||
|
|
||||||
|
Se selecionarmos `/home/a.txt` localmente e estivermos em `/tmp` no painel remoto, depois mudarmos para `/var` e selecionarmos `/var/b.txt`, e estivermos em `/home` no painel remoto, ao transferir teremos:
|
||||||
|
|
||||||
|
- `/home/a.txt` transferido para `/tmp/a.txt`
|
||||||
|
- `/var/b.txt` transferido para `/home/b.txt`
|
||||||
|
|
||||||
### Navegação Sincronizada ⏲️
|
### Navegação Sincronizada ⏲️
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/veeso/termscp/blob/main/docs/ptbr/README.md"
|
href="https://github.com/veeso/termscp/blob/main/docs/pt-BR/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="/assets/images/flags/br.png"
|
src="/assets/images/flags/br.png"
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
- [Aws S3 凭证](#aws-s3-凭证)
|
- [Aws S3 凭证](#aws-s3-凭证)
|
||||||
- [文件浏览](#文件浏览)
|
- [文件浏览](#文件浏览)
|
||||||
- [快捷键](#快捷键)
|
- [快捷键](#快捷键)
|
||||||
- [处理多个文件](#处理多个文件)
|
- [操作多个文件 🥷](#操作多个文件-)
|
||||||
|
- [示例](#示例)
|
||||||
- [同步浏览](#同步浏览)
|
- [同步浏览](#同步浏览)
|
||||||
- [打开/打开方式](#打开打开方式)
|
- [打开/打开方式](#打开打开方式)
|
||||||
- [书签](#书签)
|
- [书签](#书签)
|
||||||
@@ -254,14 +255,34 @@ termscp中的文件资源管理器是指你与远程建立连接后可以看到
|
|||||||
| `<CTRL+C>` | 终止文件传输 | |
|
| `<CTRL+C>` | 终止文件传输 | |
|
||||||
| `<CTRL+T>` | 显示所有同步路径 | Track |
|
| `<CTRL+T>` | 显示所有同步路径 | Track |
|
||||||
|
|
||||||
### 处理多个文件
|
### 操作多个文件 🥷
|
||||||
|
|
||||||
你可以同时操作多个文件,按`<M>`选定它们,或者按`<CTRL+A>` 全选当前工作目录中的所有文件。一旦一个文件被标记为选择,它将在左边显示一个 "*"。在这种模式下,只有选定的文件会被处理,而当前光标高亮显示的项目会被忽略。在查找结果面板中,也可以对多个文件进行处理。
|
你可以通过以下简单的控制操作多个文件:
|
||||||
在处理多个文件时,所有的操作都是可用的,但请注意,有些操作的工作方式略有不同。让我们深入了解一下:
|
|
||||||
|
|
||||||
- *复制*: 当你复制一个文件时,你会被提示输入完整目标路径名。当处理多个文件时,这个名称指的是所有这些文件将被复制到的目标目录。
|
- `<M>`:标记文件以进行选择
|
||||||
- *重命名*: 和复制操作类似, 但是会移动文件到目标路径。
|
- `<CTRL+A>`:选择当前目录下的所有文件
|
||||||
- *保存为*: 和复制操作类似, 但是会写入文件到目标路径。
|
- `<ALT+A>`:取消选择所有文件
|
||||||
|
|
||||||
|
被标记的文件将会以**高亮背景** 显示。
|
||||||
|
当进行选择操作时,只有被选中的文件会执行操作,而当前高亮显示的项目会被忽略。
|
||||||
|
|
||||||
|
即使是在查找结果面板中,也可以操作多个文件。
|
||||||
|
|
||||||
|
在操作多个文件时,所有功能都可用,但某些功能会有些许不同。具体如下:
|
||||||
|
|
||||||
|
- *复制*:复制时会提示你输入目标名称。操作多个文件时,该名称是目标目录,所有文件将被复制到此目录中。
|
||||||
|
- *重命名*:与复制相同,但文件将被移动到该目录。
|
||||||
|
- *另存为*:与复制相同,但文件将被写入该目录。
|
||||||
|
|
||||||
|
如果你在某个目录(如 `/home`)中选择了文件,然后切换目录,文件仍会保持被选中状态,并在底部面板的**传输队列** 中显示。
|
||||||
|
文件被选中时,会将当前*远程*目录与该文件关联;如果文件被传输,它将被传输到与之关联的目录中。
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
|
||||||
|
如果我们在本地选择 `/home/a.txt`,此时远程目录是 `/tmp`,然后我们切换到 `/var`,选择 `/var/b.txt`,而此时远程目录为 `/home`,执行传输后的结果为:
|
||||||
|
|
||||||
|
- `/home/a.txt` 传输到 `/tmp/a.txt`
|
||||||
|
- `/var/b.txt` 传输到 `/home/b.txt`
|
||||||
|
|
||||||
### 同步浏览
|
### 同步浏览
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ pub(crate) mod builder;
|
|||||||
mod formatter;
|
mod formatter;
|
||||||
// Locals
|
// Locals
|
||||||
use std::cmp::Reverse;
|
use std::cmp::Reverse;
|
||||||
use std::collections::VecDeque;
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@@ -42,14 +42,24 @@ pub enum GroupDirs {
|
|||||||
|
|
||||||
/// File explorer states
|
/// File explorer states
|
||||||
pub struct FileExplorer {
|
pub struct FileExplorer {
|
||||||
pub wrkdir: PathBuf, // Current directory
|
/// Current working directory
|
||||||
pub(crate) dirstack: VecDeque<PathBuf>, // Stack of visited directory (max 16)
|
pub wrkdir: PathBuf,
|
||||||
pub(crate) stack_size: usize, // Directory stack size
|
/// Stack of visited directories
|
||||||
pub(crate) file_sorting: FileSorting, // File sorting criteria
|
pub(crate) dirstack: VecDeque<PathBuf>,
|
||||||
pub(crate) group_dirs: Option<GroupDirs>, // If Some, defines how to group directories
|
/// Stack size
|
||||||
pub(crate) opts: ExplorerOpts, // Explorer options
|
pub(crate) stack_size: usize,
|
||||||
pub(crate) fmt: Formatter, // File formatter
|
/// Criteria to sort file
|
||||||
files: Vec<File>, // Files in directory
|
pub(crate) file_sorting: FileSorting,
|
||||||
|
/// defines how to group directories in the explorer
|
||||||
|
pub(crate) group_dirs: Option<GroupDirs>,
|
||||||
|
/// Explorer options
|
||||||
|
pub(crate) opts: ExplorerOpts,
|
||||||
|
/// Formatter for file entries
|
||||||
|
pub(crate) fmt: Formatter,
|
||||||
|
/// Files in directory
|
||||||
|
files: Vec<File>,
|
||||||
|
/// files enqueued for transfer. Map between source and destination
|
||||||
|
transfer_queue: HashMap<PathBuf, PathBuf>, // transfer queue
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FileExplorer {
|
impl Default for FileExplorer {
|
||||||
@@ -63,6 +73,7 @@ impl Default for FileExplorer {
|
|||||||
opts: ExplorerOpts::empty(),
|
opts: ExplorerOpts::empty(),
|
||||||
fmt: Formatter::default(),
|
fmt: Formatter::default(),
|
||||||
files: Vec::new(),
|
files: Vec::new(),
|
||||||
|
transfer_queue: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,6 +150,35 @@ impl FileExplorer {
|
|||||||
filtered.get(idx).copied()
|
filtered.get(idx).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enqueue a file for transfer
|
||||||
|
pub fn enqueue(&mut self, src: &Path, dst: &Path) {
|
||||||
|
self.transfer_queue
|
||||||
|
.insert(PathBuf::from(src), PathBuf::from(dst));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enqueue all files for transfer
|
||||||
|
pub fn enqueue_all(&mut self, dst: &Path) {
|
||||||
|
let files: Vec<_> = self.iter_files().map(|f| f.path.clone()).collect();
|
||||||
|
for file in files {
|
||||||
|
self.enqueue(&file, dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get enqueued files
|
||||||
|
pub fn enqueued(&self) -> &HashMap<PathBuf, PathBuf> {
|
||||||
|
&self.transfer_queue
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dequeue a file
|
||||||
|
pub fn dequeue(&mut self, src: &Path) {
|
||||||
|
self.transfer_queue.remove(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear transfer queue
|
||||||
|
pub fn clear_queue(&mut self) {
|
||||||
|
self.transfer_queue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// Formatting
|
// Formatting
|
||||||
|
|
||||||
/// Format a file entry
|
/// Format a file entry
|
||||||
@@ -586,6 +626,26 @@ mod tests {
|
|||||||
assert_eq!(explorer.files.len(), 3);
|
assert_eq!(explorer.files.len(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_should_enqueue_and_dequeue_files() {
|
||||||
|
let mut explorer: FileExplorer = FileExplorer::default();
|
||||||
|
// Create files (files are then sorted by name)
|
||||||
|
explorer.set_files(vec![
|
||||||
|
make_fs_entry("CONTRIBUTING.md", false),
|
||||||
|
make_fs_entry("docs", true),
|
||||||
|
make_fs_entry("src", true),
|
||||||
|
make_fs_entry("README.md", false),
|
||||||
|
]);
|
||||||
|
// Enqueue
|
||||||
|
explorer.enqueue(Path::new("CONTRIBUTING.md"), Path::new("CONTRIBUTING.md"));
|
||||||
|
explorer.enqueue(Path::new("docs"), Path::new("docs"));
|
||||||
|
// Dequeue
|
||||||
|
explorer.dequeue(Path::new("CONTRIBUTING.md"));
|
||||||
|
assert_eq!(explorer.enqueued().len(), 1);
|
||||||
|
explorer.dequeue(Path::new("docs"));
|
||||||
|
assert_eq!(explorer.enqueued().len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
fn make_fs_entry(name: &str, is_dir: bool) -> File {
|
fn make_fs_entry(name: &str, is_dir: bool) -> File {
|
||||||
let t: SystemTime = SystemTime::now();
|
let t: SystemTime = SystemTime::now();
|
||||||
let metadata = Metadata {
|
let metadata = Metadata {
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ impl KeyStorage for KeyringStorage {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "isolated-tests"))]
|
#[cfg(all(not(feature = "github-actions"), not(feature = "isolated-tests")))]
|
||||||
fn test_system_keys_keyringstorage() {
|
fn test_system_keys_keyringstorage() {
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use whoami::username;
|
use whoami::username;
|
||||||
|
|||||||
@@ -18,14 +18,15 @@ impl FileTransferActivity {
|
|||||||
self.local_copy_file(&entry, dest_path.as_path());
|
self.local_copy_file(&entry, dest_path.as_path());
|
||||||
}
|
}
|
||||||
SelectedFile::Many(entries) => {
|
SelectedFile::Many(entries) => {
|
||||||
// Try to copy each file to Input/{FILE_NAME}
|
|
||||||
let base_path: PathBuf = PathBuf::from(input);
|
|
||||||
// Iter files
|
// Iter files
|
||||||
for entry in entries.iter() {
|
for (entry, mut dest_path) in entries.into_iter() {
|
||||||
let mut dest_path: PathBuf = base_path.clone();
|
|
||||||
dest_path.push(entry.name());
|
dest_path.push(entry.name());
|
||||||
self.local_copy_file(entry, dest_path.as_path());
|
self.local_copy_file(&entry, dest_path.as_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.host_bridge_mut().clear_queue();
|
||||||
|
self.reload_host_bridge_filelist();
|
||||||
}
|
}
|
||||||
SelectedFile::None => {}
|
SelectedFile::None => {}
|
||||||
}
|
}
|
||||||
@@ -39,14 +40,15 @@ impl FileTransferActivity {
|
|||||||
self.remote_copy_file(entry, dest_path.as_path());
|
self.remote_copy_file(entry, dest_path.as_path());
|
||||||
}
|
}
|
||||||
SelectedFile::Many(entries) => {
|
SelectedFile::Many(entries) => {
|
||||||
// Try to copy each file to Input/{FILE_NAME}
|
|
||||||
let base_path: PathBuf = PathBuf::from(input);
|
|
||||||
// Iter files
|
// Iter files
|
||||||
for entry in entries.into_iter() {
|
for (entry, mut dest_path) in entries.into_iter() {
|
||||||
let mut dest_path: PathBuf = base_path.clone();
|
|
||||||
dest_path.push(entry.name());
|
dest_path.push(entry.name());
|
||||||
self.remote_copy_file(entry, dest_path.as_path());
|
self.remote_copy_file(entry, dest_path.as_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.remote_mut().clear_queue();
|
||||||
|
self.reload_remote_filelist();
|
||||||
}
|
}
|
||||||
SelectedFile::None => {}
|
SelectedFile::None => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,14 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
SelectedFile::Many(entries) => {
|
SelectedFile::Many(entries) => {
|
||||||
// Iter files
|
// Iter files
|
||||||
for entry in entries.iter() {
|
for (entry, _) in entries.iter() {
|
||||||
// Delete file
|
// Delete file
|
||||||
self.local_remove_file(entry);
|
self.local_remove_file(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.host_bridge_mut().clear_queue();
|
||||||
|
self.reload_host_bridge_filelist();
|
||||||
}
|
}
|
||||||
SelectedFile::None => {}
|
SelectedFile::None => {}
|
||||||
}
|
}
|
||||||
@@ -33,10 +37,14 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
SelectedFile::Many(entries) => {
|
SelectedFile::Many(entries) => {
|
||||||
// Iter files
|
// Iter files
|
||||||
for entry in entries.iter() {
|
for (entry, _) in entries.iter() {
|
||||||
// Delete file
|
// Delete file
|
||||||
self.remote_remove_file(entry);
|
self.remote_remove_file(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.remote_mut().clear_queue();
|
||||||
|
self.reload_remote_filelist();
|
||||||
}
|
}
|
||||||
SelectedFile::None => {}
|
SelectedFile::None => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ impl FileTransferActivity {
|
|||||||
pub(crate) fn action_edit_local_file(&mut self) {
|
pub(crate) fn action_edit_local_file(&mut self) {
|
||||||
let entries: Vec<File> = match self.get_local_selected_entries() {
|
let entries: Vec<File> = match self.get_local_selected_entries() {
|
||||||
SelectedFile::One(entry) => vec![entry],
|
SelectedFile::One(entry) => vec![entry],
|
||||||
SelectedFile::Many(entries) => entries,
|
SelectedFile::Many(entries) => entries.into_iter().map(|(f, _)| f).collect(),
|
||||||
SelectedFile::None => vec![],
|
SelectedFile::None => vec![],
|
||||||
};
|
};
|
||||||
// Edit all entries
|
// Edit all entries
|
||||||
@@ -38,12 +38,16 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.host_bridge_mut().clear_queue();
|
||||||
|
self.reload_host_bridge_filelist();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn action_edit_remote_file(&mut self) {
|
pub(crate) fn action_edit_remote_file(&mut self) {
|
||||||
let entries: Vec<File> = match self.get_remote_selected_entries() {
|
let entries: Vec<File> = match self.get_remote_selected_entries() {
|
||||||
SelectedFile::One(entry) => vec![entry],
|
SelectedFile::One(entry) => vec![entry],
|
||||||
SelectedFile::Many(entries) => entries,
|
SelectedFile::Many(entries) => entries.into_iter().map(|(f, _)| f).collect(),
|
||||||
SelectedFile::None => vec![],
|
SelectedFile::None => vec![],
|
||||||
};
|
};
|
||||||
// Edit all entries
|
// Edit all entries
|
||||||
@@ -60,6 +64,10 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.remote_mut().clear_queue();
|
||||||
|
self.reload_remote_filelist();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Edit a file on localhost
|
/// Edit a file on localhost
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use super::{File, FileTransferActivity, LogLevel, SelectedFile, TransferOpts, Tr
|
|||||||
impl FileTransferActivity {
|
impl FileTransferActivity {
|
||||||
pub(crate) fn action_find_changedir(&mut self) {
|
pub(crate) fn action_find_changedir(&mut self) {
|
||||||
// Match entry
|
// Match entry
|
||||||
if let SelectedFile::One(entry) = self.get_found_selected_entries() {
|
if let Some(entry) = self.get_found_selected_file() {
|
||||||
debug!("Changedir to: {}", entry.name());
|
debug!("Changedir to: {}", entry.name());
|
||||||
// Get path: if a directory, use directory path; if it is a File, get parent path
|
// Get path: if a directory, use directory path; if it is a File, get parent path
|
||||||
let path = if entry.is_dir() {
|
let path = if entry.is_dir() {
|
||||||
@@ -103,11 +103,12 @@ impl FileTransferActivity {
|
|||||||
// Check which file would be replaced
|
// Check which file would be replaced
|
||||||
let existing_files: Vec<&File> = entries
|
let existing_files: Vec<&File> = entries
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| {
|
.filter(|(x, dest_path)| {
|
||||||
self.remote_file_exists(
|
self.remote_file_exists(
|
||||||
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
|
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
.map(|(x, _)| x)
|
||||||
.collect();
|
.collect();
|
||||||
// Check whether to replace files
|
// Check whether to replace files
|
||||||
if !existing_files.is_empty()
|
if !existing_files.is_empty()
|
||||||
@@ -117,7 +118,7 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(err) = self.filetransfer_send(
|
if let Err(err) = self.filetransfer_send(
|
||||||
TransferPayload::Many(entries),
|
TransferPayload::TransferQueue(entries),
|
||||||
dest_path.as_path(),
|
dest_path.as_path(),
|
||||||
None,
|
None,
|
||||||
) {
|
) {
|
||||||
@@ -134,11 +135,12 @@ impl FileTransferActivity {
|
|||||||
// Check which file would be replaced
|
// Check which file would be replaced
|
||||||
let existing_files: Vec<&File> = entries
|
let existing_files: Vec<&File> = entries
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| {
|
.filter(|(x, dest_path)| {
|
||||||
self.host_bridge_file_exists(
|
self.host_bridge_file_exists(
|
||||||
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
|
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
.map(|(x, _)| x)
|
||||||
.collect();
|
.collect();
|
||||||
// Check whether to replace files
|
// Check whether to replace files
|
||||||
if !existing_files.is_empty()
|
if !existing_files.is_empty()
|
||||||
@@ -148,7 +150,7 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(err) = self.filetransfer_recv(
|
if let Err(err) = self.filetransfer_recv(
|
||||||
TransferPayload::Many(entries),
|
TransferPayload::TransferQueue(entries),
|
||||||
dest_path.as_path(),
|
dest_path.as_path(),
|
||||||
None,
|
None,
|
||||||
) {
|
) {
|
||||||
@@ -157,6 +159,12 @@ impl FileTransferActivity {
|
|||||||
format!("Could not download file: {err}"),
|
format!("Could not download file: {err}"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
if let Some(f) = self.found_mut() {
|
||||||
|
f.clear_queue();
|
||||||
|
self.update_find_list();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,10 +180,16 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
SelectedFile::Many(entries) => {
|
SelectedFile::Many(entries) => {
|
||||||
// Iter files
|
// Iter files
|
||||||
for entry in entries.iter() {
|
for (entry, _) in entries.iter() {
|
||||||
// Delete file
|
// Delete file
|
||||||
self.remove_found_file(entry);
|
self.remove_found_file(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
if let Some(f) = self.found_mut() {
|
||||||
|
f.clear_queue();
|
||||||
|
self.update_find_list();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SelectedFile::None => {}
|
SelectedFile::None => {}
|
||||||
}
|
}
|
||||||
@@ -200,10 +214,15 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
SelectedFile::Many(entries) => {
|
SelectedFile::Many(entries) => {
|
||||||
// Iter files
|
// Iter files
|
||||||
for entry in entries.iter() {
|
for (entry, _) in entries.iter() {
|
||||||
// Open file
|
// Open file
|
||||||
self.open_found_file(entry, None);
|
self.open_found_file(entry, None);
|
||||||
}
|
}
|
||||||
|
// clear selection
|
||||||
|
if let Some(f) = self.found_mut() {
|
||||||
|
f.clear_queue();
|
||||||
|
self.update_find_list();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SelectedFile::None => {}
|
SelectedFile::None => {}
|
||||||
}
|
}
|
||||||
@@ -217,10 +236,15 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
SelectedFile::Many(entries) => {
|
SelectedFile::Many(entries) => {
|
||||||
// Iter files
|
// Iter files
|
||||||
for entry in entries.iter() {
|
for (entry, _) in entries.iter() {
|
||||||
// Open file
|
// Open file
|
||||||
self.open_found_file(entry, Some(with));
|
self.open_found_file(entry, Some(with));
|
||||||
}
|
}
|
||||||
|
// clear selection
|
||||||
|
if let Some(f) = self.found_mut() {
|
||||||
|
f.clear_queue();
|
||||||
|
self.update_find_list();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SelectedFile::None => {}
|
SelectedFile::None => {}
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/ui/activities/filetransfer/actions/mark.rs
Normal file
19
src/ui/activities/filetransfer/actions/mark.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//! ## FileTransferActivity
|
||||||
|
//!
|
||||||
|
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
|
||||||
|
|
||||||
|
use super::FileTransferActivity;
|
||||||
|
|
||||||
|
impl FileTransferActivity {
|
||||||
|
pub(crate) fn action_mark_file(&mut self, index: usize) {
|
||||||
|
self.enqueue_file(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn action_mark_all(&mut self) {
|
||||||
|
self.enqueue_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn action_mark_clear(&mut self) {
|
||||||
|
self.clear_queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,15 +2,19 @@
|
|||||||
//!
|
//!
|
||||||
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
|
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
|
||||||
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use remotefs::File;
|
use remotefs::File;
|
||||||
use remotefs::fs::UnixPex;
|
use remotefs::fs::UnixPex;
|
||||||
use tuirealm::{State, StateValue};
|
use tuirealm::{State, StateValue};
|
||||||
|
|
||||||
use super::browser::FileExplorerTab;
|
use super::browser::FileExplorerTab;
|
||||||
|
use super::lib::browser::FoundExplorerTab;
|
||||||
use super::{
|
use super::{
|
||||||
FileTransferActivity, Id, LogLevel, Msg, PendingActionMsg, TransferMsg, TransferOpts,
|
FileTransferActivity, Id, LogLevel, Msg, PendingActionMsg, TransferMsg, TransferOpts,
|
||||||
TransferPayload, UiMsg,
|
TransferPayload, UiMsg,
|
||||||
};
|
};
|
||||||
|
use crate::explorer::FileExplorer;
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
pub(crate) mod change_dir;
|
pub(crate) mod change_dir;
|
||||||
@@ -21,6 +25,7 @@ pub(crate) mod edit;
|
|||||||
pub(crate) mod exec;
|
pub(crate) mod exec;
|
||||||
pub(crate) mod filter;
|
pub(crate) mod filter;
|
||||||
pub(crate) mod find;
|
pub(crate) mod find;
|
||||||
|
pub(crate) mod mark;
|
||||||
pub(crate) mod mkdir;
|
pub(crate) mod mkdir;
|
||||||
pub(crate) mod newfile;
|
pub(crate) mod newfile;
|
||||||
pub(crate) mod open;
|
pub(crate) mod open;
|
||||||
@@ -36,7 +41,8 @@ pub(crate) mod watcher;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum SelectedFile {
|
pub(crate) enum SelectedFile {
|
||||||
One(File),
|
One(File),
|
||||||
Many(Vec<File>),
|
/// List of file with their destination path
|
||||||
|
Many(Vec<(File, PathBuf)>),
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +51,10 @@ impl SelectedFile {
|
|||||||
/// In case is `Many` the first item mode is returned
|
/// In case is `Many` the first item mode is returned
|
||||||
pub fn unix_pex(&self) -> Option<UnixPex> {
|
pub fn unix_pex(&self) -> Option<UnixPex> {
|
||||||
match self {
|
match self {
|
||||||
Self::Many(files) => files.iter().next().and_then(|file| file.metadata().mode),
|
Self::Many(files) => files
|
||||||
|
.iter()
|
||||||
|
.next()
|
||||||
|
.and_then(|(file, _)| file.metadata().mode),
|
||||||
Self::One(file) => file.metadata().mode,
|
Self::One(file) => file.metadata().mode,
|
||||||
Self::None => None,
|
Self::None => None,
|
||||||
}
|
}
|
||||||
@@ -55,7 +64,7 @@ impl SelectedFile {
|
|||||||
pub fn get_files(self) -> Vec<File> {
|
pub fn get_files(self) -> Vec<File> {
|
||||||
match self {
|
match self {
|
||||||
Self::One(file) => vec![file],
|
Self::One(file) => vec![file],
|
||||||
Self::Many(files) => files,
|
Self::Many(files) => files.into_iter().map(|(f, _)| f).collect(),
|
||||||
Self::None => vec![],
|
Self::None => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,7 +73,6 @@ impl SelectedFile {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum SelectedFileIndex {
|
enum SelectedFileIndex {
|
||||||
One(usize),
|
One(usize),
|
||||||
Many(Vec<usize>),
|
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,68 +85,42 @@ impl From<Option<&File>> for SelectedFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<&File>> for SelectedFile {
|
|
||||||
fn from(files: Vec<&File>) -> Self {
|
|
||||||
SelectedFile::Many(files.into_iter().cloned().collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileTransferActivity {
|
impl FileTransferActivity {
|
||||||
/// Get local file entry
|
/// Get local file entry
|
||||||
pub(crate) fn get_local_selected_entries(&self) -> SelectedFile {
|
pub(crate) fn get_local_selected_entries(&mut self) -> SelectedFile {
|
||||||
match self.get_selected_index(&Id::ExplorerHostBridge) {
|
self.get_selected_files(&Id::ExplorerHostBridge)
|
||||||
SelectedFileIndex::One(idx) => SelectedFile::from(self.host_bridge().get(idx)),
|
}
|
||||||
SelectedFileIndex::Many(files) => {
|
|
||||||
let files: Vec<&File> = files
|
pub(crate) fn get_local_selected_file(&self) -> Option<File> {
|
||||||
.iter()
|
self.get_selected_file(&Id::ExplorerHostBridge)
|
||||||
.filter_map(|x| self.host_bridge().get(*x)) // Usize to Option<File>
|
|
||||||
.collect();
|
|
||||||
SelectedFile::from(files)
|
|
||||||
}
|
|
||||||
SelectedFileIndex::None => SelectedFile::None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get remote file entry
|
/// Get remote file entry
|
||||||
pub(crate) fn get_remote_selected_entries(&self) -> SelectedFile {
|
pub(crate) fn get_remote_selected_entries(&mut self) -> SelectedFile {
|
||||||
match self.get_selected_index(&Id::ExplorerRemote) {
|
self.get_selected_files(&Id::ExplorerRemote)
|
||||||
SelectedFileIndex::One(idx) => SelectedFile::from(self.remote().get(idx)),
|
}
|
||||||
SelectedFileIndex::Many(files) => {
|
|
||||||
let files: Vec<&File> = files
|
pub(crate) fn get_remote_selected_file(&self) -> Option<File> {
|
||||||
.iter()
|
self.get_selected_file(&Id::ExplorerRemote)
|
||||||
.filter_map(|x| self.remote().get(*x)) // Usize to Option<File>
|
|
||||||
.collect();
|
|
||||||
SelectedFile::from(files)
|
|
||||||
}
|
|
||||||
SelectedFileIndex::None => SelectedFile::None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether only one entry is selected on local host
|
/// Returns whether only one entry is selected on local host
|
||||||
pub(crate) fn is_local_selected_one(&self) -> bool {
|
pub(crate) fn is_local_selected_one(&mut self) -> bool {
|
||||||
matches!(self.get_local_selected_entries(), SelectedFile::One(_))
|
matches!(self.get_local_selected_entries(), SelectedFile::One(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether only one entry is selected on remote host
|
/// Returns whether only one entry is selected on remote host
|
||||||
pub(crate) fn is_remote_selected_one(&self) -> bool {
|
pub(crate) fn is_remote_selected_one(&mut self) -> bool {
|
||||||
matches!(self.get_remote_selected_entries(), SelectedFile::One(_))
|
matches!(self.get_remote_selected_entries(), SelectedFile::One(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get remote file entry
|
/// Get remote file entry
|
||||||
pub(crate) fn get_found_selected_entries(&self) -> SelectedFile {
|
pub(crate) fn get_found_selected_entries(&mut self) -> SelectedFile {
|
||||||
match self.get_selected_index(&Id::ExplorerFind) {
|
self.get_selected_files(&Id::ExplorerFind)
|
||||||
SelectedFileIndex::One(idx) => {
|
}
|
||||||
SelectedFile::from(self.found().as_ref().unwrap().get(idx))
|
|
||||||
}
|
pub(crate) fn get_found_selected_file(&self) -> Option<File> {
|
||||||
SelectedFileIndex::Many(files) => {
|
self.get_selected_file(&Id::ExplorerFind)
|
||||||
let files: Vec<&File> = files
|
|
||||||
.iter()
|
|
||||||
.filter_map(|x| self.found().as_ref().unwrap().get(*x)) // Usize to Option<File>
|
|
||||||
.collect();
|
|
||||||
SelectedFile::from(files)
|
|
||||||
}
|
|
||||||
SelectedFileIndex::None => SelectedFile::None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- private
|
// -- private
|
||||||
@@ -146,17 +128,69 @@ impl FileTransferActivity {
|
|||||||
fn get_selected_index(&self, id: &Id) -> SelectedFileIndex {
|
fn get_selected_index(&self, id: &Id) -> SelectedFileIndex {
|
||||||
match self.app.state(id) {
|
match self.app.state(id) {
|
||||||
Ok(State::One(StateValue::Usize(idx))) => SelectedFileIndex::One(idx),
|
Ok(State::One(StateValue::Usize(idx))) => SelectedFileIndex::One(idx),
|
||||||
Ok(State::Vec(files)) => {
|
|
||||||
let list: Vec<usize> = files
|
|
||||||
.iter()
|
|
||||||
.map(|x| match x {
|
|
||||||
StateValue::Usize(v) => *v,
|
|
||||||
_ => 0,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
SelectedFileIndex::Many(list)
|
|
||||||
}
|
|
||||||
_ => SelectedFileIndex::None,
|
_ => SelectedFileIndex::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_selected_files(&mut self, id: &Id) -> SelectedFile {
|
||||||
|
let browser = self.browser_by_id(id);
|
||||||
|
// if transfer queue is not empty, return that
|
||||||
|
let transfer_queue = browser.enqueued().clone();
|
||||||
|
if !transfer_queue.is_empty() {
|
||||||
|
return SelectedFile::Many(
|
||||||
|
transfer_queue
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(src, dest)| {
|
||||||
|
let src_file = self.get_file_from_path(id, src)?;
|
||||||
|
Some((src_file, dest.clone()))
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let browser = self.browser_by_id(id);
|
||||||
|
// if no transfer queue, return selected files
|
||||||
|
match self.get_selected_index(id) {
|
||||||
|
SelectedFileIndex::One(idx) => {
|
||||||
|
let Some(f) = browser.get(idx) else {
|
||||||
|
return SelectedFile::None;
|
||||||
|
};
|
||||||
|
SelectedFile::One(f.clone())
|
||||||
|
}
|
||||||
|
SelectedFileIndex::None => SelectedFile::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_file_from_path(&mut self, id: &Id, path: &Path) -> Option<File> {
|
||||||
|
match *id {
|
||||||
|
Id::ExplorerHostBridge => self.host_bridge.stat(path).ok(),
|
||||||
|
Id::ExplorerRemote => self.client.stat(path).ok(),
|
||||||
|
Id::ExplorerFind => {
|
||||||
|
let found = self.browser.found_tab().unwrap();
|
||||||
|
match found {
|
||||||
|
FoundExplorerTab::Local => self.host_bridge.stat(path).ok(),
|
||||||
|
FoundExplorerTab::Remote => self.client.stat(path).ok(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn browser_by_id(&self, id: &Id) -> &FileExplorer {
|
||||||
|
match *id {
|
||||||
|
Id::ExplorerHostBridge => self.host_bridge(),
|
||||||
|
Id::ExplorerRemote => self.remote(),
|
||||||
|
Id::ExplorerFind => self.found().as_ref().unwrap(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_selected_file(&self, id: &Id) -> Option<File> {
|
||||||
|
let browser = self.browser_by_id(id);
|
||||||
|
// if no transfer queue, return selected files
|
||||||
|
match self.get_selected_index(id) {
|
||||||
|
SelectedFileIndex::One(idx) => browser.get(idx).cloned(),
|
||||||
|
SelectedFileIndex::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,24 +13,32 @@ impl FileTransferActivity {
|
|||||||
pub(crate) fn action_open_local(&mut self) {
|
pub(crate) fn action_open_local(&mut self) {
|
||||||
let entries: Vec<File> = match self.get_local_selected_entries() {
|
let entries: Vec<File> = match self.get_local_selected_entries() {
|
||||||
SelectedFile::One(entry) => vec![entry],
|
SelectedFile::One(entry) => vec![entry],
|
||||||
SelectedFile::Many(entries) => entries,
|
SelectedFile::Many(entries) => entries.into_iter().map(|(f, _)| f).collect(),
|
||||||
SelectedFile::None => vec![],
|
SelectedFile::None => vec![],
|
||||||
};
|
};
|
||||||
entries
|
entries
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|x| self.action_open_local_file(x, None));
|
.for_each(|x| self.action_open_local_file(x, None));
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.host_bridge_mut().clear_queue();
|
||||||
|
self.reload_host_bridge_filelist();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open local file
|
/// Open local file
|
||||||
pub(crate) fn action_open_remote(&mut self) {
|
pub(crate) fn action_open_remote(&mut self) {
|
||||||
let entries: Vec<File> = match self.get_remote_selected_entries() {
|
let entries: Vec<File> = match self.get_remote_selected_entries() {
|
||||||
SelectedFile::One(entry) => vec![entry],
|
SelectedFile::One(entry) => vec![entry],
|
||||||
SelectedFile::Many(entries) => entries,
|
SelectedFile::Many(entries) => entries.into_iter().map(|(f, _)| f).collect(),
|
||||||
SelectedFile::None => vec![],
|
SelectedFile::None => vec![],
|
||||||
};
|
};
|
||||||
entries
|
entries
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|x| self.action_open_remote_file(x, None));
|
.for_each(|x| self.action_open_remote_file(x, None));
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.remote_mut().clear_queue();
|
||||||
|
self.reload_remote_filelist();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform open lopcal file
|
/// Perform open lopcal file
|
||||||
@@ -86,26 +94,33 @@ impl FileTransferActivity {
|
|||||||
pub(crate) fn action_local_open_with(&mut self, with: &str) {
|
pub(crate) fn action_local_open_with(&mut self, with: &str) {
|
||||||
let entries: Vec<File> = match self.get_local_selected_entries() {
|
let entries: Vec<File> = match self.get_local_selected_entries() {
|
||||||
SelectedFile::One(entry) => vec![entry],
|
SelectedFile::One(entry) => vec![entry],
|
||||||
SelectedFile::Many(entries) => entries,
|
SelectedFile::Many(entries) => entries.into_iter().map(|(f, _)| f).collect(),
|
||||||
SelectedFile::None => vec![],
|
SelectedFile::None => vec![],
|
||||||
};
|
};
|
||||||
// Open all entries
|
// Open all entries
|
||||||
entries
|
entries
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|x| self.action_open_local_file(x, Some(with)));
|
.for_each(|x| self.action_open_local_file(x, Some(with)));
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.host_bridge_mut().clear_queue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open selected file with provided application
|
/// Open selected file with provided application
|
||||||
pub(crate) fn action_remote_open_with(&mut self, with: &str) {
|
pub(crate) fn action_remote_open_with(&mut self, with: &str) {
|
||||||
let entries: Vec<File> = match self.get_remote_selected_entries() {
|
let entries: Vec<File> = match self.get_remote_selected_entries() {
|
||||||
SelectedFile::One(entry) => vec![entry],
|
SelectedFile::One(entry) => vec![entry],
|
||||||
SelectedFile::Many(entries) => entries,
|
SelectedFile::Many(entries) => entries.into_iter().map(|(f, _)| f).collect(),
|
||||||
SelectedFile::None => vec![],
|
SelectedFile::None => vec![],
|
||||||
};
|
};
|
||||||
// Open all entries
|
// Open all entries
|
||||||
entries
|
entries
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|x| self.action_open_remote_file(x, Some(with)));
|
.for_each(|x| self.action_open_remote_file(x, Some(with)));
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.remote_mut().clear_queue();
|
||||||
|
self.reload_remote_filelist();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_bridged_file(&mut self, entry: &File, open_with: Option<&str>) {
|
fn open_bridged_file(&mut self, entry: &File, open_with: Option<&str>) {
|
||||||
|
|||||||
@@ -18,13 +18,15 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
SelectedFile::Many(entries) => {
|
SelectedFile::Many(entries) => {
|
||||||
// Try to copy each file to Input/{FILE_NAME}
|
// Try to copy each file to Input/{FILE_NAME}
|
||||||
let base_path: PathBuf = PathBuf::from(input);
|
|
||||||
// Iter files
|
// Iter files
|
||||||
for entry in entries.iter() {
|
for (entry, mut dest_path) in entries.into_iter() {
|
||||||
let mut dest_path: PathBuf = base_path.clone();
|
|
||||||
dest_path.push(entry.name());
|
dest_path.push(entry.name());
|
||||||
self.local_rename_file(entry, dest_path.as_path());
|
self.local_rename_file(&entry, dest_path.as_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.host_bridge_mut().clear_queue();
|
||||||
|
self.reload_host_bridge_filelist();
|
||||||
}
|
}
|
||||||
SelectedFile::None => {}
|
SelectedFile::None => {}
|
||||||
}
|
}
|
||||||
@@ -38,13 +40,16 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
SelectedFile::Many(entries) => {
|
SelectedFile::Many(entries) => {
|
||||||
// Try to copy each file to Input/{FILE_NAME}
|
// Try to copy each file to Input/{FILE_NAME}
|
||||||
let base_path: PathBuf = PathBuf::from(input);
|
|
||||||
// Iter files
|
// Iter files
|
||||||
for entry in entries.iter() {
|
for (entry, mut dest_path) in entries.into_iter() {
|
||||||
let mut dest_path: PathBuf = base_path.clone();
|
|
||||||
dest_path.push(entry.name());
|
dest_path.push(entry.name());
|
||||||
self.remote_rename_file(entry, dest_path.as_path());
|
self.remote_rename_file(&entry, dest_path.as_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear selection
|
||||||
|
self.remote_mut().clear_queue();
|
||||||
|
// reload remote
|
||||||
|
self.reload_remote_filelist();
|
||||||
}
|
}
|
||||||
SelectedFile::None => {}
|
SelectedFile::None => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,11 +64,12 @@ impl FileTransferActivity {
|
|||||||
// Check which file would be replaced
|
// Check which file would be replaced
|
||||||
let existing_files: Vec<&File> = entries
|
let existing_files: Vec<&File> = entries
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| {
|
.filter(|(x, dest_path)| {
|
||||||
self.remote_file_exists(
|
self.remote_file_exists(
|
||||||
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
|
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
.map(|(x, _)| x)
|
||||||
.collect();
|
.collect();
|
||||||
// Check whether to replace files
|
// Check whether to replace files
|
||||||
if !existing_files.is_empty() && !self.should_replace_files(existing_files) {
|
if !existing_files.is_empty() && !self.should_replace_files(existing_files) {
|
||||||
@@ -76,7 +77,7 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(err) = self.filetransfer_send(
|
if let Err(err) = self.filetransfer_send(
|
||||||
TransferPayload::Many(entries),
|
TransferPayload::TransferQueue(entries),
|
||||||
dest_path.as_path(),
|
dest_path.as_path(),
|
||||||
None,
|
None,
|
||||||
) {
|
) {
|
||||||
@@ -86,6 +87,10 @@ impl FileTransferActivity {
|
|||||||
format!("Could not upload file: {err}"),
|
format!("Could not upload file: {err}"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// clear selection
|
||||||
|
self.host_bridge_mut().clear_queue();
|
||||||
|
self.reload_host_bridge_filelist();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SelectedFile::None => {}
|
SelectedFile::None => {}
|
||||||
@@ -128,11 +133,12 @@ impl FileTransferActivity {
|
|||||||
// Check which file would be replaced
|
// Check which file would be replaced
|
||||||
let existing_files: Vec<&File> = entries
|
let existing_files: Vec<&File> = entries
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| {
|
.filter(|(x, dest_path)| {
|
||||||
self.host_bridge_file_exists(
|
self.host_bridge_file_exists(
|
||||||
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
|
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
.map(|(x, _)| x)
|
||||||
.collect();
|
.collect();
|
||||||
// Check whether to replace files
|
// Check whether to replace files
|
||||||
if !existing_files.is_empty() && !self.should_replace_files(existing_files) {
|
if !existing_files.is_empty() && !self.should_replace_files(existing_files) {
|
||||||
@@ -140,7 +146,7 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(err) = self.filetransfer_recv(
|
if let Err(err) = self.filetransfer_recv(
|
||||||
TransferPayload::Many(entries),
|
TransferPayload::TransferQueue(entries),
|
||||||
dest_path.as_path(),
|
dest_path.as_path(),
|
||||||
None,
|
None,
|
||||||
) {
|
) {
|
||||||
@@ -150,6 +156,11 @@ impl FileTransferActivity {
|
|||||||
format!("Could not download file: {err}"),
|
format!("Could not download file: {err}"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// clear selection
|
||||||
|
self.remote_mut().clear_queue();
|
||||||
|
// reload remote
|
||||||
|
self.reload_remote_filelist();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SelectedFile::None => {}
|
SelectedFile::None => {}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
// locals
|
// locals
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use super::{FileTransferActivity, LogLevel, SelectedFile};
|
use super::{FileTransferActivity, LogLevel};
|
||||||
|
|
||||||
impl FileTransferActivity {
|
impl FileTransferActivity {
|
||||||
/// Create symlink on localhost
|
/// Create symlink on localhost
|
||||||
pub(crate) fn action_local_symlink(&mut self, name: String) {
|
pub(crate) fn action_local_symlink(&mut self, name: String) {
|
||||||
if let SelectedFile::One(entry) = self.get_local_selected_entries() {
|
if let Some(entry) = self.get_local_selected_file() {
|
||||||
match self
|
match self
|
||||||
.host_bridge
|
.host_bridge
|
||||||
.symlink(PathBuf::from(name.as_str()).as_path(), entry.path())
|
.symlink(PathBuf::from(name.as_str()).as_path(), entry.path())
|
||||||
@@ -34,7 +34,7 @@ impl FileTransferActivity {
|
|||||||
|
|
||||||
/// Copy file on remote
|
/// Copy file on remote
|
||||||
pub(crate) fn action_remote_symlink(&mut self, name: String) {
|
pub(crate) fn action_remote_symlink(&mut self, name: String) {
|
||||||
if let SelectedFile::One(entry) = self.get_remote_selected_entries() {
|
if let Some(entry) = self.get_remote_selected_file() {
|
||||||
match self
|
match self
|
||||||
.client
|
.client
|
||||||
.symlink(PathBuf::from(name.as_str()).as_path(), entry.path())
|
.symlink(PathBuf::from(name.as_str()).as_path(), entry.path())
|
||||||
|
|||||||
@@ -166,6 +166,12 @@ impl Component<Msg, NoUserEvent> for Log {
|
|||||||
self.perform(Cmd::Move(Direction::Up));
|
self.perform(Cmd::Move(Direction::Up));
|
||||||
Some(Msg::None)
|
Some(Msg::None)
|
||||||
}
|
}
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Right, ..
|
||||||
|
}) => Some(Msg::Ui(UiMsg::BottomPanelRight)),
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Left, ..
|
||||||
|
}) => Some(Msg::Ui(UiMsg::BottomPanelLeft)),
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::PageUp, ..
|
code: Key::PageUp, ..
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use super::{Msg, PendingActionMsg, TransferMsg, UiMsg};
|
|||||||
mod log;
|
mod log;
|
||||||
mod misc;
|
mod misc;
|
||||||
mod popups;
|
mod popups;
|
||||||
|
mod selected_files;
|
||||||
mod transfer;
|
mod transfer;
|
||||||
|
|
||||||
pub use misc::FooterBar;
|
pub use misc::FooterBar;
|
||||||
@@ -26,6 +27,7 @@ pub use popups::{
|
|||||||
pub use transfer::{ExplorerFind, ExplorerFuzzy, ExplorerLocal, ExplorerRemote};
|
pub use transfer::{ExplorerFind, ExplorerFuzzy, ExplorerLocal, ExplorerRemote};
|
||||||
|
|
||||||
pub use self::log::Log;
|
pub use self::log::Log;
|
||||||
|
pub use self::selected_files::SelectedFilesList;
|
||||||
|
|
||||||
#[derive(Default, MockComponent)]
|
#[derive(Default, MockComponent)]
|
||||||
pub struct GlobalListener {
|
pub struct GlobalListener {
|
||||||
|
|||||||
@@ -670,7 +670,7 @@ impl KeybindingsPopup {
|
|||||||
))
|
))
|
||||||
.add_row()
|
.add_row()
|
||||||
.add_col(TextSpan::new("<P>").bold().fg(key_color))
|
.add_col(TextSpan::new("<P>").bold().fg(key_color))
|
||||||
.add_col(TextSpan::from(" Toggle log panel"))
|
.add_col(TextSpan::from(" Toggle bottom panel"))
|
||||||
.add_row()
|
.add_row()
|
||||||
.add_col(TextSpan::new("<Q|F10>").bold().fg(key_color))
|
.add_col(TextSpan::new("<Q|F10>").bold().fg(key_color))
|
||||||
.add_col(TextSpan::from(" Quit termscp"))
|
.add_col(TextSpan::from(" Quit termscp"))
|
||||||
|
|||||||
126
src/ui/activities/filetransfer/components/selected_files.rs
Normal file
126
src/ui/activities/filetransfer/components/selected_files.rs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use tui_realm_stdlib::List;
|
||||||
|
use tuirealm::command::{Cmd, Direction, Position};
|
||||||
|
use tuirealm::event::{Key, KeyEvent};
|
||||||
|
use tuirealm::props::{Alignment, BorderType, Borders, Color, TextSpan};
|
||||||
|
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||||
|
|
||||||
|
use crate::ui::activities::filetransfer::{MarkQueue, Msg, UiMsg};
|
||||||
|
|
||||||
|
#[derive(MockComponent)]
|
||||||
|
pub struct SelectedFilesList {
|
||||||
|
component: List,
|
||||||
|
paths: Vec<PathBuf>,
|
||||||
|
queue: MarkQueue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectedFilesList {
|
||||||
|
pub fn new(
|
||||||
|
paths: &[(PathBuf, PathBuf)],
|
||||||
|
queue: MarkQueue,
|
||||||
|
color: Color,
|
||||||
|
title: &'static str,
|
||||||
|
) -> Self {
|
||||||
|
let enqueued_paths = paths
|
||||||
|
.iter()
|
||||||
|
.map(|(src, _)| src.clone())
|
||||||
|
.collect::<Vec<PathBuf>>();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
queue,
|
||||||
|
paths: enqueued_paths,
|
||||||
|
component: List::default()
|
||||||
|
.borders(Borders::default().color(color).modifiers(BorderType::Plain))
|
||||||
|
.rewind(true)
|
||||||
|
.scroll(true)
|
||||||
|
.step(4)
|
||||||
|
.highlighted_color(color)
|
||||||
|
.highlighted_str("➤ ")
|
||||||
|
.title(title, Alignment::Left)
|
||||||
|
.rows(
|
||||||
|
paths
|
||||||
|
.iter()
|
||||||
|
.map(|(src, dest)| {
|
||||||
|
vec![
|
||||||
|
TextSpan::from(Self::filename(src)),
|
||||||
|
TextSpan::from(" -> "),
|
||||||
|
TextSpan::from(Self::filename(dest)),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filename(p: &Path) -> String {
|
||||||
|
p.file_name()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component<Msg, NoUserEvent> for SelectedFilesList {
|
||||||
|
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||||
|
match ev {
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Down, ..
|
||||||
|
}) => {
|
||||||
|
self.perform(Cmd::Move(Direction::Down));
|
||||||
|
Some(Msg::None)
|
||||||
|
}
|
||||||
|
Event::Keyboard(KeyEvent { code: Key::Up, .. }) => {
|
||||||
|
self.perform(Cmd::Move(Direction::Up));
|
||||||
|
Some(Msg::None)
|
||||||
|
}
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::PageDown,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
self.perform(Cmd::Scroll(Direction::Down));
|
||||||
|
Some(Msg::None)
|
||||||
|
}
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::PageUp, ..
|
||||||
|
}) => {
|
||||||
|
self.perform(Cmd::Scroll(Direction::Up));
|
||||||
|
Some(Msg::None)
|
||||||
|
}
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Home, ..
|
||||||
|
}) => {
|
||||||
|
self.perform(Cmd::GoTo(Position::Begin));
|
||||||
|
Some(Msg::None)
|
||||||
|
}
|
||||||
|
Event::Keyboard(KeyEvent { code: Key::End, .. }) => {
|
||||||
|
self.perform(Cmd::GoTo(Position::End));
|
||||||
|
Some(Msg::None)
|
||||||
|
}
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Right, ..
|
||||||
|
}) => Some(Msg::Ui(UiMsg::BottomPanelRight)),
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Left, ..
|
||||||
|
}) => Some(Msg::Ui(UiMsg::BottomPanelLeft)),
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::BackTab | Key::Tab | Key::Char('p'),
|
||||||
|
..
|
||||||
|
}) => Some(Msg::Ui(UiMsg::LogBackTabbed)),
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Enter | Key::Delete,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
// unmark the selected file
|
||||||
|
let State::One(StateValue::Usize(idx)) = self.state() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = self.paths.get(idx)?;
|
||||||
|
|
||||||
|
Some(Msg::Ui(UiMsg::MarkRemove(self.queue, path.clone())))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,20 +17,17 @@ const PROP_DOT_DOT: &str = "dot_dot";
|
|||||||
/// OwnStates contains states for this component
|
/// OwnStates contains states for this component
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
struct OwnStates {
|
struct OwnStates {
|
||||||
list_index: usize, // Index of selected element in list
|
list_index: usize, // Index of selected element in list
|
||||||
selected: Vec<usize>, // Selected files
|
list_len: usize, // Length of the list
|
||||||
|
dot_dot: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OwnStates {
|
impl OwnStates {
|
||||||
/// Initialize list states
|
/// Initialize list states
|
||||||
pub fn init_list_states(&mut self, len: usize, has_dot_dot: bool) {
|
pub fn init_list_states(&mut self, len: usize, has_dot_dot: bool) {
|
||||||
self.selected = Vec::with_capacity(len + if has_dot_dot { 1 } else { 0 });
|
self.list_len = len + if has_dot_dot { 1 } else { 0 };
|
||||||
self.fix_list_index();
|
self.fix_list_index();
|
||||||
}
|
self.dot_dot = has_dot_dot;
|
||||||
|
|
||||||
/// Return current value for list index
|
|
||||||
pub fn list_index(&self) -> usize {
|
|
||||||
self.list_index
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Incremenet list index.
|
/// Incremenet list index.
|
||||||
@@ -44,6 +41,14 @@ impl OwnStates {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn real_index(&self) -> usize {
|
||||||
|
if self.dot_dot {
|
||||||
|
self.list_index.saturating_sub(1)
|
||||||
|
} else {
|
||||||
|
self.list_index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Decrement list index
|
/// Decrement list index
|
||||||
/// If `can_rewind` is `true` the index rewinds when boundary is reached
|
/// If `can_rewind` is `true` the index rewinds when boundary is reached
|
||||||
pub fn decr_list_index(&mut self, can_rewind: bool) {
|
pub fn decr_list_index(&mut self, can_rewind: bool) {
|
||||||
@@ -68,22 +73,7 @@ impl OwnStates {
|
|||||||
|
|
||||||
/// Returns the length of the file list, which is actually the capacity of the selection vector
|
/// Returns the length of the file list, which is actually the capacity of the selection vector
|
||||||
pub fn list_len(&self) -> usize {
|
pub fn list_len(&self) -> usize {
|
||||||
self.selected.capacity()
|
self.list_len
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the file with index `entry` is selected
|
|
||||||
pub fn is_selected(&self, entry: usize) -> bool {
|
|
||||||
self.selected.contains(&entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the selection is currently empty
|
|
||||||
pub fn is_selection_empty(&self) -> bool {
|
|
||||||
self.selected.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns current file selection
|
|
||||||
pub fn get_selection(&self) -> Vec<usize> {
|
|
||||||
self.selected.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Keep index if possible, otherwise set to lenght - 1
|
/// Keep index if possible, otherwise set to lenght - 1
|
||||||
@@ -94,44 +84,6 @@ impl OwnStates {
|
|||||||
self.list_index = 0;
|
self.list_index = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- select manipulation
|
|
||||||
|
|
||||||
/// Select or deselect file with provided entry index
|
|
||||||
pub fn toggle_file(&mut self, entry: usize) {
|
|
||||||
match self.is_selected(entry) {
|
|
||||||
true => self.deselect(entry),
|
|
||||||
false => self.select(entry),
|
|
||||||
}
|
|
||||||
// increment index
|
|
||||||
self.incr_list_index(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select all files
|
|
||||||
pub fn select_all(&mut self, has_dot_dot: bool) {
|
|
||||||
for i in 0..self.list_len() {
|
|
||||||
self.select(i + if has_dot_dot { 1 } else { 0 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select all files
|
|
||||||
pub fn deselect_all(&mut self) {
|
|
||||||
self.selected.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select provided index if not selected yet
|
|
||||||
fn select(&mut self, entry: usize) {
|
|
||||||
if !self.is_selected(entry) {
|
|
||||||
self.selected.push(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove element file with associated index
|
|
||||||
fn deselect(&mut self, entry: usize) {
|
|
||||||
if self.is_selected(entry) {
|
|
||||||
self.selected.retain(|&x| x != entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -222,27 +174,12 @@ impl MockComponent for FileList {
|
|||||||
Some(table) => init_table_iter
|
Some(table) => init_table_iter
|
||||||
.iter()
|
.iter()
|
||||||
.chain(table.iter())
|
.chain(table.iter())
|
||||||
.enumerate()
|
.map(|row| {
|
||||||
.map(|(num, row)| {
|
|
||||||
let real_num = num;
|
|
||||||
let num = if self.has_dot_dot() {
|
|
||||||
num.checked_sub(1).unwrap_or_default()
|
|
||||||
} else {
|
|
||||||
num
|
|
||||||
};
|
|
||||||
|
|
||||||
let columns: Vec<Span> = row
|
let columns: Vec<Span> = row
|
||||||
.iter()
|
.iter()
|
||||||
.map(|col| {
|
.map(|col| {
|
||||||
let (fg, bg, mut modifiers) =
|
let (fg, bg, modifiers) =
|
||||||
tui_realm_stdlib::utils::use_or_default_styles(&self.props, col);
|
tui_realm_stdlib::utils::use_or_default_styles(&self.props, col);
|
||||||
if !(self.has_dot_dot() && real_num == 0)
|
|
||||||
&& self.states.is_selected(num)
|
|
||||||
{
|
|
||||||
modifiers |= TextModifiers::REVERSED
|
|
||||||
| TextModifiers::UNDERLINED
|
|
||||||
| TextModifiers::ITALIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
Span::styled(
|
Span::styled(
|
||||||
col.content.clone(),
|
col.content.clone(),
|
||||||
@@ -302,20 +239,11 @@ impl MockComponent for FileList {
|
|||||||
return State::One(StateValue::String("..".to_string()));
|
return State::One(StateValue::String("..".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.states.is_selection_empty() {
|
State::One(StateValue::Usize(if self.has_dot_dot() {
|
||||||
true => State::One(StateValue::Usize(if self.has_dot_dot() {
|
self.states.list_index.checked_sub(1).unwrap_or_default()
|
||||||
self.states.list_index.checked_sub(1).unwrap_or_default()
|
} else {
|
||||||
} else {
|
self.states.list_index
|
||||||
self.states.list_index
|
}))
|
||||||
})),
|
|
||||||
false => State::Vec(
|
|
||||||
self.states
|
|
||||||
.get_selection()
|
|
||||||
.into_iter()
|
|
||||||
.map(StateValue::Usize)
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perform(&mut self, cmd: Cmd) -> CmdResult {
|
fn perform(&mut self, cmd: Cmd) -> CmdResult {
|
||||||
@@ -374,25 +302,18 @@ impl MockComponent for FileList {
|
|||||||
CmdResult::None
|
CmdResult::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cmd::Custom(FILE_LIST_CMD_SELECT_ALL) => {
|
|
||||||
self.states.select_all(self.has_dot_dot());
|
|
||||||
CmdResult::None
|
|
||||||
}
|
|
||||||
Cmd::Custom(FILE_LIST_CMD_DESELECT_ALL) => {
|
|
||||||
self.states.deselect_all();
|
|
||||||
CmdResult::None
|
|
||||||
}
|
|
||||||
Cmd::Toggle => {
|
Cmd::Toggle => {
|
||||||
if self.has_dot_dot() && self.states.list_index() == 0 {
|
if self.states.list_index == 0 && self.has_dot_dot() {
|
||||||
return CmdResult::None;
|
return CmdResult::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.states.toggle_file(if self.has_dot_dot() {
|
let index = self.states.real_index();
|
||||||
self.states.list_index().checked_sub(1).unwrap_or_default()
|
self.states.list_index = self
|
||||||
} else {
|
.states
|
||||||
self.states.list_index()
|
.list_index
|
||||||
});
|
.saturating_add(1)
|
||||||
CmdResult::None
|
.min(self.states.list_len.saturating_sub(1));
|
||||||
|
CmdResult::Changed(State::One(StateValue::Usize(index)))
|
||||||
}
|
}
|
||||||
_ => CmdResult::None,
|
_ => CmdResult::None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,21 +132,26 @@ impl ExplorerFuzzy {
|
|||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkAll))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('a'),
|
code: Key::Char('a'),
|
||||||
modifiers: KeyModifiers::ALT,
|
modifiers: KeyModifiers::ALT,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkClear))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('m'),
|
code: Key::Char('m'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Toggle);
|
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||||
Some(Msg::None)
|
self.perform(Cmd::Toggle)
|
||||||
|
else {
|
||||||
|
return Some(Msg::None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Msg::Ui(UiMsg::MarkFile(index)))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent { code: Key::Tab, .. }) => {
|
Event::Keyboard(KeyEvent { code: Key::Tab, .. }) => {
|
||||||
self.perform(Cmd::Change);
|
self.perform(Cmd::Change);
|
||||||
@@ -277,21 +282,26 @@ impl Component<Msg, NoUserEvent> for ExplorerFind {
|
|||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkAll))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('a'),
|
code: Key::Char('a'),
|
||||||
modifiers: KeyModifiers::ALT,
|
modifiers: KeyModifiers::ALT,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkClear))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('m'),
|
code: Key::Char('m'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Toggle);
|
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||||
Some(Msg::None)
|
self.perform(Cmd::Toggle)
|
||||||
|
else {
|
||||||
|
return Some(Msg::None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Msg::Ui(UiMsg::MarkFile(index)))
|
||||||
}
|
}
|
||||||
// -- comp msg
|
// -- comp msg
|
||||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||||
@@ -410,21 +420,26 @@ impl Component<Msg, NoUserEvent> for ExplorerLocal {
|
|||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkAll))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('a'),
|
code: Key::Char('a'),
|
||||||
modifiers: KeyModifiers::ALT,
|
modifiers: KeyModifiers::ALT,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkClear))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('m'),
|
code: Key::Char('m'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Toggle);
|
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||||
Some(Msg::None)
|
self.perform(Cmd::Toggle)
|
||||||
|
else {
|
||||||
|
return Some(Msg::None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Msg::Ui(UiMsg::MarkFile(index)))
|
||||||
}
|
}
|
||||||
// -- comp msg
|
// -- comp msg
|
||||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||||
@@ -508,7 +523,7 @@ impl Component<Msg, NoUserEvent> for ExplorerLocal {
|
|||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('p'),
|
code: Key::Char('p'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => Some(Msg::Ui(UiMsg::ShowLogPanel)),
|
}) => Some(Msg::Ui(UiMsg::GoToTransferQueue)),
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('r') | Key::Function(6),
|
code: Key::Char('r') | Key::Function(6),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
@@ -619,21 +634,26 @@ impl Component<Msg, NoUserEvent> for ExplorerRemote {
|
|||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkAll))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('a'),
|
code: Key::Char('a'),
|
||||||
modifiers: KeyModifiers::ALT,
|
modifiers: KeyModifiers::ALT,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkClear))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('m'),
|
code: Key::Char('m'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Toggle);
|
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||||
Some(Msg::None)
|
self.perform(Cmd::Toggle)
|
||||||
|
else {
|
||||||
|
return Some(Msg::None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Msg::Ui(UiMsg::MarkFile(index)))
|
||||||
}
|
}
|
||||||
// -- comp msg
|
// -- comp msg
|
||||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||||
@@ -717,7 +737,7 @@ impl Component<Msg, NoUserEvent> for ExplorerRemote {
|
|||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('p'),
|
code: Key::Char('p'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => Some(Msg::Ui(UiMsg::ShowLogPanel)),
|
}) => Some(Msg::Ui(UiMsg::GoToTransferQueue)),
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('r') | Key::Function(6),
|
code: Key::Char('r') | Key::Function(6),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
|
|||||||
@@ -60,6 +60,23 @@ impl Browser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn other_explorer_no_found(&self) -> &FileExplorer {
|
||||||
|
match self.tab {
|
||||||
|
FileExplorerTab::HostBridge | FileExplorerTab::FindHostBridge => &self.remote,
|
||||||
|
FileExplorerTab::Remote | FileExplorerTab::FindRemote => &self.host_bridge,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn explorer_mut(&mut self) -> &mut FileExplorer {
|
||||||
|
match self.tab {
|
||||||
|
FileExplorerTab::HostBridge => &mut self.host_bridge,
|
||||||
|
FileExplorerTab::Remote => &mut self.remote,
|
||||||
|
FileExplorerTab::FindHostBridge | FileExplorerTab::FindRemote => {
|
||||||
|
self.found.as_mut().map(|x| &mut x.explorer).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn host_bridge(&self) -> &FileExplorer {
|
pub fn host_bridge(&self) -> &FileExplorer {
|
||||||
&self.host_bridge
|
&self.host_bridge
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use bytesize::ByteSize;
|
use bytesize::ByteSize;
|
||||||
use tuirealm::props::{
|
use tuirealm::props::{
|
||||||
Alignment, AttrValue, Attribute, Color, PropPayload, PropValue, TableBuilder, TextSpan,
|
Alignment, AttrValue, Attribute, Color, PropPayload, PropValue, TableBuilder, TextModifiers,
|
||||||
|
TextSpan,
|
||||||
};
|
};
|
||||||
use tuirealm::{PollStrategy, Update};
|
use tuirealm::{PollStrategy, Update};
|
||||||
|
|
||||||
@@ -227,7 +228,7 @@ impl FileTransferActivity {
|
|||||||
transfer_stats
|
transfer_stats
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TransferPayload::Many(entries) => {
|
TransferPayload::TransferQueue(entries) => {
|
||||||
format!(
|
format!(
|
||||||
"{} files has been successfully transferred ({})",
|
"{} files has been successfully transferred ({})",
|
||||||
entries.len(),
|
entries.len(),
|
||||||
@@ -240,6 +241,11 @@ impl FileTransferActivity {
|
|||||||
/// Update host bridge file list
|
/// Update host bridge file list
|
||||||
pub(super) fn update_host_bridge_filelist(&mut self) {
|
pub(super) fn update_host_bridge_filelist(&mut self) {
|
||||||
self.reload_host_bridge_dir();
|
self.reload_host_bridge_dir();
|
||||||
|
self.reload_host_bridge_filelist();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update host bridge file list
|
||||||
|
pub(super) fn reload_host_bridge_filelist(&mut self) {
|
||||||
// Get width
|
// Get width
|
||||||
let width = self
|
let width = self
|
||||||
.context_mut()
|
.context_mut()
|
||||||
@@ -261,7 +267,15 @@ impl FileTransferActivity {
|
|||||||
let files: Vec<Vec<TextSpan>> = self
|
let files: Vec<Vec<TextSpan>> = self
|
||||||
.host_bridge()
|
.host_bridge()
|
||||||
.iter_files()
|
.iter_files()
|
||||||
.map(|x| vec![TextSpan::from(self.host_bridge().fmt_file(x))])
|
.map(|x| {
|
||||||
|
let mut span = TextSpan::from(self.host_bridge().fmt_file(x));
|
||||||
|
if self.host_bridge().enqueued().contains_key(x.path()) {
|
||||||
|
span.modifiers |=
|
||||||
|
TextModifiers::REVERSED | TextModifiers::UNDERLINED | TextModifiers::ITALIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![span]
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
// Update content and title
|
// Update content and title
|
||||||
assert!(
|
assert!(
|
||||||
@@ -287,7 +301,10 @@ impl FileTransferActivity {
|
|||||||
/// Update remote file list
|
/// Update remote file list
|
||||||
pub(super) fn update_remote_filelist(&mut self) {
|
pub(super) fn update_remote_filelist(&mut self) {
|
||||||
self.reload_remote_dir();
|
self.reload_remote_dir();
|
||||||
|
self.reload_remote_filelist();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn reload_remote_filelist(&mut self) {
|
||||||
let width = self
|
let width = self
|
||||||
.context_mut()
|
.context_mut()
|
||||||
.terminal()
|
.terminal()
|
||||||
@@ -308,7 +325,15 @@ impl FileTransferActivity {
|
|||||||
let files: Vec<Vec<TextSpan>> = self
|
let files: Vec<Vec<TextSpan>> = self
|
||||||
.remote()
|
.remote()
|
||||||
.iter_files()
|
.iter_files()
|
||||||
.map(|x| vec![TextSpan::from(self.remote().fmt_file(x))])
|
.map(|x| {
|
||||||
|
let mut span = TextSpan::from(self.remote().fmt_file(x));
|
||||||
|
if self.remote().enqueued().contains_key(x.path()) {
|
||||||
|
span.modifiers |=
|
||||||
|
TextModifiers::REVERSED | TextModifiers::UNDERLINED | TextModifiers::ITALIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![span]
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
// Update content and title
|
// Update content and title
|
||||||
assert!(
|
assert!(
|
||||||
@@ -460,7 +485,14 @@ impl FileTransferActivity {
|
|||||||
.found()
|
.found()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter_files()
|
.iter_files()
|
||||||
.map(|x| vec![TextSpan::from(self.found().unwrap().fmt_file(x))])
|
.map(|x| {
|
||||||
|
let mut span = TextSpan::from(self.found().unwrap().fmt_file(x));
|
||||||
|
if self.found().unwrap().enqueued().contains_key(x.path()) {
|
||||||
|
span.modifiers |=
|
||||||
|
TextModifiers::REVERSED | TextModifiers::UNDERLINED | TextModifiers::ITALIC;
|
||||||
|
}
|
||||||
|
vec![span]
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
assert!(
|
assert!(
|
||||||
self.app
|
self.app
|
||||||
@@ -482,6 +514,15 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn reload_browser_file_list(&mut self) {
|
||||||
|
match self.browser.tab() {
|
||||||
|
FileExplorerTab::HostBridge | FileExplorerTab::FindHostBridge => {
|
||||||
|
self.reload_host_bridge_filelist()
|
||||||
|
}
|
||||||
|
FileExplorerTab::Remote | FileExplorerTab::FindRemote => self.reload_remote_filelist(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn update_browser_file_list_swapped(&mut self) {
|
pub(super) fn update_browser_file_list_swapped(&mut self) {
|
||||||
match self.browser.tab() {
|
match self.browser.tab() {
|
||||||
FileExplorerTab::HostBridge | FileExplorerTab::FindHostBridge => {
|
FileExplorerTab::HostBridge | FileExplorerTab::FindHostBridge => {
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ use crate::system::watcher::FsWatcher;
|
|||||||
|
|
||||||
// -- components
|
// -- components
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
|
pub enum MarkQueue {
|
||||||
|
Local,
|
||||||
|
Remote,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||||
enum Id {
|
enum Id {
|
||||||
ChmodPopup,
|
ChmodPopup,
|
||||||
@@ -74,6 +80,8 @@ enum Id {
|
|||||||
StatusBarRemote,
|
StatusBarRemote,
|
||||||
SymlinkPopup,
|
SymlinkPopup,
|
||||||
SyncBrowsingMkdirPopup,
|
SyncBrowsingMkdirPopup,
|
||||||
|
TransferQueueHostBridge,
|
||||||
|
TransferQueueRemote,
|
||||||
WaitPopup,
|
WaitPopup,
|
||||||
WatchedPathsList,
|
WatchedPathsList,
|
||||||
WatcherPopup,
|
WatcherPopup,
|
||||||
@@ -125,6 +133,8 @@ enum TransferMsg {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum UiMsg {
|
enum UiMsg {
|
||||||
|
BottomPanelLeft,
|
||||||
|
BottomPanelRight,
|
||||||
ChangeFileSorting(FileSorting),
|
ChangeFileSorting(FileSorting),
|
||||||
ChangeTransferWindow,
|
ChangeTransferWindow,
|
||||||
CloseChmodPopup,
|
CloseChmodPopup,
|
||||||
@@ -153,6 +163,13 @@ enum UiMsg {
|
|||||||
FilterFiles(String),
|
FilterFiles(String),
|
||||||
FuzzySearch(String),
|
FuzzySearch(String),
|
||||||
LogBackTabbed,
|
LogBackTabbed,
|
||||||
|
/// Mark file on the list; usize is the index of the file
|
||||||
|
MarkFile(usize),
|
||||||
|
MarkRemove(MarkQueue, PathBuf),
|
||||||
|
/// Mark all file at tab
|
||||||
|
MarkAll,
|
||||||
|
/// Clear all marks
|
||||||
|
MarkClear,
|
||||||
Quit,
|
Quit,
|
||||||
ReplacePopupTabbed,
|
ReplacePopupTabbed,
|
||||||
ShowChmodPopup,
|
ShowChmodPopup,
|
||||||
@@ -165,7 +182,7 @@ enum UiMsg {
|
|||||||
ShowFilterPopup,
|
ShowFilterPopup,
|
||||||
ShowGotoPopup,
|
ShowGotoPopup,
|
||||||
ShowKeybindingsPopup,
|
ShowKeybindingsPopup,
|
||||||
ShowLogPanel,
|
GoToTransferQueue,
|
||||||
ShowMkdirPopup,
|
ShowMkdirPopup,
|
||||||
ShowNewFilePopup,
|
ShowNewFilePopup,
|
||||||
ShowOpenWithPopup,
|
ShowOpenWithPopup,
|
||||||
@@ -307,6 +324,45 @@ impl FileTransferActivity {
|
|||||||
self.browser.found_mut()
|
self.browser.found_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enqueue a file to be transferred
|
||||||
|
fn enqueue_file(&mut self, index: usize) {
|
||||||
|
let Some(src) = self
|
||||||
|
.browser
|
||||||
|
.explorer()
|
||||||
|
.get(index)
|
||||||
|
.map(|item| item.path().to_path_buf())
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.browser.explorer().enqueued().contains_key(&src) {
|
||||||
|
debug!("File already marked, unmarking {}", src.display());
|
||||||
|
self.browser.explorer_mut().dequeue(&src);
|
||||||
|
} else {
|
||||||
|
debug!("Marking file {}", src.display());
|
||||||
|
let dest = self.browser.other_explorer_no_found().wrkdir.clone();
|
||||||
|
self.browser.explorer_mut().enqueue(&src, &dest);
|
||||||
|
}
|
||||||
|
self.reload_browser_file_list();
|
||||||
|
self.refresh_host_bridge_transfer_queue();
|
||||||
|
self.refresh_remote_transfer_queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enqueue_all(&mut self) {
|
||||||
|
let dest = self.browser.other_explorer_no_found().wrkdir.clone();
|
||||||
|
self.browser.explorer_mut().enqueue_all(&dest);
|
||||||
|
self.reload_browser_file_list();
|
||||||
|
self.refresh_host_bridge_transfer_queue();
|
||||||
|
self.refresh_remote_transfer_queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_queue(&mut self) {
|
||||||
|
self.browser.explorer_mut().clear_queue();
|
||||||
|
self.reload_browser_file_list();
|
||||||
|
self.refresh_host_bridge_transfer_queue();
|
||||||
|
self.refresh_remote_transfer_queue();
|
||||||
|
}
|
||||||
|
|
||||||
/// Get file name for a file in cache
|
/// Get file name for a file in cache
|
||||||
fn get_cache_tmp_name(&self, name: &str, file_type: Option<&str>) -> Option<String> {
|
fn get_cache_tmp_name(&self, name: &str, file_type: Option<&str>) -> Option<String> {
|
||||||
self.cache.as_ref().map(|_| {
|
self.cache.as_ref().map(|_| {
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ enum TransferErrorReason {
|
|||||||
pub(super) enum TransferPayload {
|
pub(super) enum TransferPayload {
|
||||||
File(File),
|
File(File),
|
||||||
Any(File),
|
Any(File),
|
||||||
Many(Vec<File>),
|
/// List of file with their destination name
|
||||||
|
TransferQueue(Vec<(File, PathBuf)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileTransferActivity {
|
impl FileTransferActivity {
|
||||||
@@ -264,8 +265,8 @@ impl FileTransferActivity {
|
|||||||
TransferPayload::File(ref file) => {
|
TransferPayload::File(ref file) => {
|
||||||
self.filetransfer_send_file(file, curr_remote_path, dst_name)
|
self.filetransfer_send_file(file, curr_remote_path, dst_name)
|
||||||
}
|
}
|
||||||
TransferPayload::Many(ref entries) => {
|
TransferPayload::TransferQueue(ref entries) => {
|
||||||
self.filetransfer_send_many(entries, curr_remote_path)
|
self.filetransfer_send_transfer_queue(entries)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Notify
|
// Notify
|
||||||
@@ -331,18 +332,17 @@ impl FileTransferActivity {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send many entries to remote
|
/// Send transfer queue entries to remote
|
||||||
fn filetransfer_send_many(
|
fn filetransfer_send_transfer_queue(
|
||||||
&mut self,
|
&mut self,
|
||||||
entries: &[File],
|
entries: &[(File, PathBuf)],
|
||||||
curr_remote_path: &Path,
|
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Reset states
|
// Reset states
|
||||||
self.transfer.reset();
|
self.transfer.reset();
|
||||||
// Calculate total size of transfer
|
// Calculate total size of transfer
|
||||||
let total_transfer_size: usize = entries
|
let total_transfer_size: usize = entries
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| self.get_total_transfer_size_host(x))
|
.map(|(x, _)| self.get_total_transfer_size_host(x))
|
||||||
.sum();
|
.sum();
|
||||||
self.transfer.full.init(total_transfer_size);
|
self.transfer.full.init(total_transfer_size);
|
||||||
// Mount progress bar
|
// Mount progress bar
|
||||||
@@ -350,7 +350,7 @@ impl FileTransferActivity {
|
|||||||
// Send recurse
|
// Send recurse
|
||||||
let result = entries
|
let result = entries
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| self.filetransfer_send_recurse(x, curr_remote_path, None))
|
.map(|(x, remote)| self.filetransfer_send_recurse(x, remote, None))
|
||||||
.find(|x| x.is_err())
|
.find(|x| x.is_err())
|
||||||
.unwrap_or(Ok(()));
|
.unwrap_or(Ok(()));
|
||||||
// Umount progress bar
|
// Umount progress bar
|
||||||
@@ -704,8 +704,8 @@ impl FileTransferActivity {
|
|||||||
self.filetransfer_recv_any(entry, host_bridge_path, dst_name)
|
self.filetransfer_recv_any(entry, host_bridge_path, dst_name)
|
||||||
}
|
}
|
||||||
TransferPayload::File(ref file) => self.filetransfer_recv_file(file, host_bridge_path),
|
TransferPayload::File(ref file) => self.filetransfer_recv_file(file, host_bridge_path),
|
||||||
TransferPayload::Many(ref entries) => {
|
TransferPayload::TransferQueue(ref entries) => {
|
||||||
self.filetransfer_recv_many(entries, host_bridge_path)
|
self.filetransfer_recv_transfer_queue(entries)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Notify
|
// Notify
|
||||||
@@ -764,18 +764,17 @@ impl FileTransferActivity {
|
|||||||
result.map_err(|x| x.to_string())
|
result.map_err(|x| x.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send many entries to remote
|
/// Receive transfer queue from remote
|
||||||
fn filetransfer_recv_many(
|
fn filetransfer_recv_transfer_queue(
|
||||||
&mut self,
|
&mut self,
|
||||||
entries: &[File],
|
entries: &[(File, PathBuf)],
|
||||||
curr_remote_path: &Path,
|
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Reset states
|
// Reset states
|
||||||
self.transfer.reset();
|
self.transfer.reset();
|
||||||
// Calculate total size of transfer
|
// Calculate total size of transfer
|
||||||
let total_transfer_size: usize = entries
|
let total_transfer_size: usize = entries
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| self.get_total_transfer_size_remote(x))
|
.map(|(x, _)| self.get_total_transfer_size_remote(x))
|
||||||
.sum();
|
.sum();
|
||||||
self.transfer.full.init(total_transfer_size);
|
self.transfer.full.init(total_transfer_size);
|
||||||
// Mount progress bar
|
// Mount progress bar
|
||||||
@@ -783,7 +782,7 @@ impl FileTransferActivity {
|
|||||||
// Send recurse
|
// Send recurse
|
||||||
let result = entries
|
let result = entries
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| self.filetransfer_recv_recurse(x, curr_remote_path, None))
|
.map(|(x, path)| self.filetransfer_recv_recurse(x, path, None))
|
||||||
.find(|x| x.is_err())
|
.find(|x| x.is_err())
|
||||||
.unwrap_or(Ok(()));
|
.unwrap_or(Ok(()));
|
||||||
// Umount progress bar
|
// Umount progress bar
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ use tuirealm::{State, StateValue, Update};
|
|||||||
use super::actions::SelectedFile;
|
use super::actions::SelectedFile;
|
||||||
use super::actions::walkdir::WalkdirError;
|
use super::actions::walkdir::WalkdirError;
|
||||||
use super::browser::{FileExplorerTab, FoundExplorerTab};
|
use super::browser::{FileExplorerTab, FoundExplorerTab};
|
||||||
use super::{ExitReason, FileTransferActivity, Id, Msg, TransferMsg, TransferOpts, UiMsg};
|
use super::{
|
||||||
|
ExitReason, FileTransferActivity, Id, MarkQueue, Msg, TransferMsg, TransferOpts, UiMsg,
|
||||||
|
};
|
||||||
|
|
||||||
impl Update<Msg> for FileTransferActivity {
|
impl Update<Msg> for FileTransferActivity {
|
||||||
fn update(&mut self, msg: Option<Msg>) -> Option<Msg> {
|
fn update(&mut self, msg: Option<Msg>) -> Option<Msg> {
|
||||||
@@ -113,7 +115,7 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TransferMsg::EnterDirectory if self.browser.tab() == FileExplorerTab::HostBridge => {
|
TransferMsg::EnterDirectory if self.browser.tab() == FileExplorerTab::HostBridge => {
|
||||||
if let SelectedFile::One(entry) = self.get_local_selected_entries() {
|
if let Some(entry) = self.get_local_selected_file() {
|
||||||
self.action_submit_local(entry);
|
self.action_submit_local(entry);
|
||||||
// Update file list if sync
|
// Update file list if sync
|
||||||
if self.browser.sync_browsing && self.browser.found().is_none() {
|
if self.browser.sync_browsing && self.browser.found().is_none() {
|
||||||
@@ -123,7 +125,7 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TransferMsg::EnterDirectory if self.browser.tab() == FileExplorerTab::Remote => {
|
TransferMsg::EnterDirectory if self.browser.tab() == FileExplorerTab::Remote => {
|
||||||
if let SelectedFile::One(entry) = self.get_remote_selected_entries() {
|
if let Some(entry) = self.get_remote_selected_file() {
|
||||||
self.action_submit_remote(entry);
|
self.action_submit_remote(entry);
|
||||||
// Update file list if sync
|
// Update file list if sync
|
||||||
if self.browser.sync_browsing && self.browser.found().is_none() {
|
if self.browser.sync_browsing && self.browser.found().is_none() {
|
||||||
@@ -473,12 +475,33 @@ impl FileTransferActivity {
|
|||||||
self.browser.fuzzy_search(&needle);
|
self.browser.fuzzy_search(&needle);
|
||||||
self.update_find_list();
|
self.update_find_list();
|
||||||
}
|
}
|
||||||
UiMsg::ShowLogPanel => {
|
UiMsg::GoToTransferQueue => {
|
||||||
assert!(self.app.active(&Id::Log).is_ok());
|
assert!(self.app.active(&Id::TransferQueueHostBridge).is_ok());
|
||||||
}
|
}
|
||||||
UiMsg::LogBackTabbed => {
|
UiMsg::LogBackTabbed => {
|
||||||
assert!(self.app.active(&Id::ExplorerHostBridge).is_ok());
|
assert!(self.app.active(&Id::ExplorerHostBridge).is_ok());
|
||||||
}
|
}
|
||||||
|
UiMsg::MarkFile(index) => {
|
||||||
|
self.action_mark_file(index);
|
||||||
|
}
|
||||||
|
UiMsg::MarkAll => {
|
||||||
|
self.action_mark_all();
|
||||||
|
}
|
||||||
|
UiMsg::MarkClear => {
|
||||||
|
self.action_mark_clear();
|
||||||
|
}
|
||||||
|
UiMsg::MarkRemove(tab, path) => match tab {
|
||||||
|
MarkQueue::Local => {
|
||||||
|
self.host_bridge_mut().dequeue(&path);
|
||||||
|
self.reload_host_bridge_filelist();
|
||||||
|
self.refresh_host_bridge_transfer_queue();
|
||||||
|
}
|
||||||
|
MarkQueue::Remote => {
|
||||||
|
self.remote_mut().dequeue(&path);
|
||||||
|
self.reload_remote_filelist();
|
||||||
|
self.refresh_remote_transfer_queue();
|
||||||
|
}
|
||||||
|
},
|
||||||
UiMsg::Quit => {
|
UiMsg::Quit => {
|
||||||
self.disconnect_and_quit();
|
self.disconnect_and_quit();
|
||||||
self.umount_quit();
|
self.umount_quit();
|
||||||
@@ -584,6 +607,31 @@ impl FileTransferActivity {
|
|||||||
UiMsg::WindowResized => {
|
UiMsg::WindowResized => {
|
||||||
self.redraw = true;
|
self.redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UiMsg::BottomPanelLeft => match self.app.focus() {
|
||||||
|
Some(Id::TransferQueueHostBridge) => {
|
||||||
|
assert!(self.app.active(&Id::Log).is_ok())
|
||||||
|
}
|
||||||
|
Some(Id::TransferQueueRemote) => {
|
||||||
|
assert!(self.app.active(&Id::TransferQueueHostBridge).is_ok())
|
||||||
|
}
|
||||||
|
Some(Id::Log) => {
|
||||||
|
assert!(self.app.active(&Id::TransferQueueRemote).is_ok())
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
UiMsg::BottomPanelRight => match self.app.focus() {
|
||||||
|
Some(Id::TransferQueueHostBridge) => {
|
||||||
|
assert!(self.app.active(&Id::TransferQueueRemote).is_ok())
|
||||||
|
}
|
||||||
|
Some(Id::TransferQueueRemote) => {
|
||||||
|
assert!(self.app.active(&Id::Log).is_ok())
|
||||||
|
}
|
||||||
|
Some(Id::Log) => {
|
||||||
|
assert!(self.app.active(&Id::TransferQueueHostBridge).is_ok())
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use super::browser::{FileExplorerTab, FoundExplorerTab};
|
|||||||
use super::components::ATTR_FILES;
|
use super::components::ATTR_FILES;
|
||||||
use super::{Context, FileTransferActivity, Id, components};
|
use super::{Context, FileTransferActivity, Id, components};
|
||||||
use crate::explorer::FileSorting;
|
use crate::explorer::FileSorting;
|
||||||
|
use crate::ui::activities::filetransfer::MarkQueue;
|
||||||
use crate::utils::ui::{Popup, Size};
|
use crate::utils::ui::{Popup, Size};
|
||||||
|
|
||||||
impl FileTransferActivity {
|
impl FileTransferActivity {
|
||||||
@@ -81,6 +82,8 @@ impl FileTransferActivity {
|
|||||||
)
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
);
|
);
|
||||||
|
self.refresh_host_bridge_transfer_queue();
|
||||||
|
self.refresh_remote_transfer_queue();
|
||||||
// Load status bar
|
// Load status bar
|
||||||
self.refresh_local_status_bar();
|
self.refresh_local_status_bar();
|
||||||
self.refresh_remote_status_bar();
|
self.refresh_remote_status_bar();
|
||||||
@@ -138,6 +141,17 @@ impl FileTransferActivity {
|
|||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.horizontal_margin(1)
|
.horizontal_margin(1)
|
||||||
.split(bottom_chunks[0]);
|
.split(bottom_chunks[0]);
|
||||||
|
let bottom_components = Layout::default()
|
||||||
|
.constraints(
|
||||||
|
[
|
||||||
|
Constraint::Percentage(25),
|
||||||
|
Constraint::Percentage(25),
|
||||||
|
Constraint::Percentage(50),
|
||||||
|
]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.split(bottom_chunks[1]);
|
||||||
// Draw footer
|
// Draw footer
|
||||||
self.app.view(&Id::FooterBar, f, body[1]);
|
self.app.view(&Id::FooterBar, f, body[1]);
|
||||||
// Draw explorers
|
// Draw explorers
|
||||||
@@ -153,8 +167,13 @@ impl FileTransferActivity {
|
|||||||
} else {
|
} else {
|
||||||
self.app.view(&Id::ExplorerRemote, f, tabs_chunks[1]);
|
self.app.view(&Id::ExplorerRemote, f, tabs_chunks[1]);
|
||||||
}
|
}
|
||||||
|
// draw transfer queues
|
||||||
|
self.app
|
||||||
|
.view(&Id::TransferQueueHostBridge, f, bottom_components[0]);
|
||||||
|
self.app
|
||||||
|
.view(&Id::TransferQueueRemote, f, bottom_components[1]);
|
||||||
// Draw log box
|
// Draw log box
|
||||||
self.app.view(&Id::Log, f, bottom_chunks[1]);
|
self.app.view(&Id::Log, f, bottom_components[2]);
|
||||||
// Draw status bar
|
// Draw status bar
|
||||||
self.app
|
self.app
|
||||||
.view(&Id::StatusBarHostBridge, f, status_bar_chunks[0]);
|
.view(&Id::StatusBarHostBridge, f, status_bar_chunks[0]);
|
||||||
@@ -928,6 +947,56 @@ impl FileTransferActivity {
|
|||||||
let _ = self.app.umount(&Id::FileInfoPopup);
|
let _ = self.app.umount(&Id::FileInfoPopup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn refresh_host_bridge_transfer_queue(&mut self) {
|
||||||
|
let enqueued = self
|
||||||
|
.host_bridge()
|
||||||
|
.enqueued()
|
||||||
|
.iter()
|
||||||
|
.map(|(src, dest)| (src.clone(), dest.clone()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let log_panel = self.theme().transfer_log_window;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
self.app
|
||||||
|
.remount(
|
||||||
|
Id::TransferQueueHostBridge,
|
||||||
|
Box::new(components::SelectedFilesList::new(
|
||||||
|
&enqueued,
|
||||||
|
MarkQueue::Local,
|
||||||
|
log_panel,
|
||||||
|
"Host Bridge selected files",
|
||||||
|
)),
|
||||||
|
vec![]
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn refresh_remote_transfer_queue(&mut self) {
|
||||||
|
let enqueued = self
|
||||||
|
.remote()
|
||||||
|
.enqueued()
|
||||||
|
.iter()
|
||||||
|
.map(|(src, dest)| (src.clone(), dest.clone()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let log_panel = self.theme().transfer_log_window;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
self.app
|
||||||
|
.remount(
|
||||||
|
Id::TransferQueueRemote,
|
||||||
|
Box::new(components::SelectedFilesList::new(
|
||||||
|
&enqueued,
|
||||||
|
MarkQueue::Remote,
|
||||||
|
log_panel,
|
||||||
|
"Remote transfer selected files",
|
||||||
|
)),
|
||||||
|
vec![]
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn refresh_local_status_bar(&mut self) {
|
pub(super) fn refresh_local_status_bar(&mut self) {
|
||||||
let sorting_color = self.theme().transfer_status_sorting;
|
let sorting_color = self.theme().transfer_status_sorting;
|
||||||
let hidden_color = self.theme().transfer_status_hidden;
|
let hidden_color = self.theme().transfer_status_hidden;
|
||||||
|
|||||||
Reference in New Issue
Block a user