mirror of
https://github.com/veeso/termscp.git
synced 2026-09-26 05:51:20 -07:00
symlink command
This commit is contained in:
committed by
Christian Visintin
parent
24788fa894
commit
ced7573241
+4
-2
@@ -29,6 +29,8 @@ Released on FIXME:
|
|||||||
> ❄️ Winter update 2022 ⛄
|
> ❄️ Winter update 2022 ⛄
|
||||||
|
|
||||||
- **Enhancements**:
|
- **Enhancements**:
|
||||||
|
- **Synchronized browsing**:
|
||||||
|
- From now on, if synchronized browsing is *enabled* and you try to enter a directory that doesn't exist on the other host, you will be asked whether you'd like to create the directory.
|
||||||
- **Find** feature:
|
- **Find** feature:
|
||||||
- A "wait popup" will now be displayed while searching files
|
- A "wait popup" will now be displayed while searching files
|
||||||
- If find command doesn't return any result show an info dialog and not an empty explorer
|
- If find command doesn't return any result show an info dialog and not an empty explorer
|
||||||
@@ -60,11 +62,11 @@ Released on FIXME:
|
|||||||
- The supported parameters are described at <https://github.com/veeso/ssh2-config>.
|
- The supported parameters are described at <https://github.com/veeso/ssh2-config>.
|
||||||
- If the field is left empty, **no file will be loaded**.
|
- If the field is left empty, **no file will be loaded**.
|
||||||
- **By default, no file will be used**.
|
- **By default, no file will be used**.
|
||||||
|
- **Symlink command**:
|
||||||
|
- You can now create symlinks, pressing `<K>` key on the file explorer.
|
||||||
- **Less verbose logging**:
|
- **Less verbose logging**:
|
||||||
- By default the log level is now set to `INFO`
|
- By default the log level is now set to `INFO`
|
||||||
- It is now possible to enable the `TRACE` level with the `-D` CLI option.
|
- It is now possible to enable the `TRACE` level with the `-D` CLI option.
|
||||||
- **Synchronized browsing**:
|
|
||||||
- From now on, if synchronized browsing is *enabled* and you try to enter a directory that doesn't exist on the other host, you will be asked whether you'd like to create the directory.
|
|
||||||
- Dependencies:
|
- Dependencies:
|
||||||
- Updated `tui-realm` to `1.3.0`
|
- Updated `tui-realm` to `1.3.0`
|
||||||
- Updated `tui-realm-stdlib` to `1.1.4`
|
- Updated `tui-realm-stdlib` to `1.1.4`
|
||||||
|
|||||||
+2
-1
@@ -168,6 +168,7 @@ In order to change panel you need to type `<LEFT>` to move the remote explorer p
|
|||||||
| `<F>` | Search for files (wild match is supported) | Find |
|
| `<F>` | Search for files (wild match is supported) | Find |
|
||||||
| `<G>` | Go to supplied path | Go to |
|
| `<G>` | Go to supplied path | Go to |
|
||||||
| `<H|F1>` | Show help | Help |
|
| `<H|F1>` | Show help | Help |
|
||||||
|
| `<K>` | Create symlink pointing to the currently selected entry | symlinK |
|
||||||
| `<I>` | Show info about selected file or directory | Info |
|
| `<I>` | Show info about selected file or directory | Info |
|
||||||
| `<L>` | Reload current directory's content / Clear selection | List |
|
| `<L>` | Reload current directory's content / Clear selection | List |
|
||||||
| `<M>` | Select a file | Mark |
|
| `<M>` | Select a file | Mark |
|
||||||
@@ -175,7 +176,7 @@ In order to change panel you need to type `<LEFT>` to move the remote explorer p
|
|||||||
| `<O|F4>` | Edit file; see Text editor | Open |
|
| `<O|F4>` | Edit file; see Text editor | Open |
|
||||||
| `<Q|F10>` | Quit termscp | Quit |
|
| `<Q|F10>` | Quit termscp | Quit |
|
||||||
| `<R|F6>` | Rename file | Rename |
|
| `<R|F6>` | Rename file | Rename |
|
||||||
| `<F2|S>` | Save file as... | Save |
|
| `<S|F2>` | Save file as... | Save |
|
||||||
| `<U>` | Go to parent directory | Upper |
|
| `<U>` | Go to parent directory | Upper |
|
||||||
| `<V|F3>` | Open file with default program for filetype | View |
|
| `<V|F3>` | Open file with default program for filetype | View |
|
||||||
| `<W>` | Open file with provided program | With |
|
| `<W>` | Open file with provided program | With |
|
||||||
|
|||||||
+38
-37
@@ -146,43 +146,44 @@ Para cambiar de panel, debe escribir `<LEFT>` para mover el panel del explorador
|
|||||||
|
|
||||||
### Keybindings ⌨
|
### Keybindings ⌨
|
||||||
|
|
||||||
| Key | Command | Reminder |
|
| Key | Command | Reminder |
|
||||||
|---------------|-------------------------------------------------------|-------------|
|
|---------------|----------------------------------------------------------------------------|-------------|
|
||||||
| `<ESC>` | Desconecte; volver a la página de autenticación | |
|
| `<ESC>` | Desconecte; volver a la página de autenticación | |
|
||||||
| `<BACKSPACE>` | Ir al directorio anterior en la pila | |
|
| `<BACKSPACE>` | Ir al directorio anterior en la pila | |
|
||||||
| `<TAB>` | Cambiar pestaña del explorador | |
|
| `<TAB>` | Cambiar pestaña del explorador | |
|
||||||
| `<RIGHT>` | Mover a la pestaña del explorador remoto | |
|
| `<RIGHT>` | Mover a la pestaña del explorador remoto | |
|
||||||
| `<LEFT>` | Mover a la pestaña del explorador local | |
|
| `<LEFT>` | Mover a la pestaña del explorador local | |
|
||||||
| `<UP>` | Subir en la lista seleccionada | |
|
| `<UP>` | Subir en la lista seleccionada | |
|
||||||
| `<DOWN>` | Bajar en la lista seleccionada | |
|
| `<DOWN>` | Bajar en la lista seleccionada | |
|
||||||
| `<PGUP>` | Subir 8 filas en la lista seleccionada | |
|
| `<PGUP>` | Subir 8 filas en la lista seleccionada | |
|
||||||
| `<PGDOWN>` | Bajar 8 filas en la lista seleccionada | |
|
| `<PGDOWN>` | Bajar 8 filas en la lista seleccionada | |
|
||||||
| `<ENTER>` | Entrar directorio | |
|
| `<ENTER>` | Entrar directorio | |
|
||||||
| `<SPACE>` | Cargar / descargar el archivo seleccionado | |
|
| `<SPACE>` | Cargar / descargar el archivo seleccionado | |
|
||||||
| `<BACKTAB>` | Cambiar entre la pestaña de registro y el explorador | |
|
| `<BACKTAB>` | Cambiar entre la pestaña de registro y el explorador | |
|
||||||
| `<A>` | Alternar archivos ocultos | All |
|
| `<A>` | Alternar archivos ocultos | All |
|
||||||
| `<B>` | Ordenar archivos por | Bubblesort? |
|
| `<B>` | Ordenar archivos por | Bubblesort? |
|
||||||
| `<C|F5>` | Copiar archivo / directorio | Copy |
|
| `<C|F5>` | Copiar archivo / directorio | Copy |
|
||||||
| `<D|F7>` | Hacer directorio | Directory |
|
| `<D|F7>` | Hacer directorio | Directory |
|
||||||
| `<E|F8|DEL>` | Eliminar archivo | Erase |
|
| `<E|F8|DEL>` | Eliminar archivo | Erase |
|
||||||
| `<F>` | Búsqueda de archivos | Find |
|
| `<F>` | Búsqueda de archivos | Find |
|
||||||
| `<G>` | Ir a la ruta proporcionada | Go to |
|
| `<G>` | Ir a la ruta proporcionada | Go to |
|
||||||
| `<H|F1>` | Mostrar ayuda | Help |
|
| `<H|F1>` | Mostrar ayuda | Help |
|
||||||
| `<I>` | Mostrar información sobre el archivo | Info |
|
| `<I>` | Mostrar información sobre el archivo | Info |
|
||||||
| `<L>` | Recargar contenido del directorio / Borrar selección | List |
|
| `<K>` | Crear un enlace simbólico que apunte a la entrada seleccionada actualmente | symlinK |
|
||||||
| `<M>` | Seleccione un archivo | Mark |
|
| `<L>` | Recargar contenido del directorio / Borrar selección | List |
|
||||||
| `<N>` | Crear un nuevo archivo con el nombre proporcionado | New |
|
| `<M>` | Seleccione un archivo | Mark |
|
||||||
| `<O|F4>` | Editar archivo | Open |
|
| `<N>` | Crear un nuevo archivo con el nombre proporcionado | New |
|
||||||
| `<Q|F10>` | Salir de termscp | Quit |
|
| `<O|F4>` | Editar archivo | Open |
|
||||||
| `<R|F6>` | Renombrar archivo | Rename |
|
| `<Q|F10>` | Salir de termscp | Quit |
|
||||||
| `<F2|S>` | Guardar archivo como... | Save |
|
| `<R|F6>` | Renombrar archivo | Rename |
|
||||||
| `<U>` | Ir al directorio principal | Upper |
|
| `<S|F2>` | Guardar archivo como... | Save |
|
||||||
| `<V|F3>` | Abrir archivo con el programa predeterminado | View |
|
| `<U>` | Ir al directorio principal | Upper |
|
||||||
| `<W>` | Abrir archivo con el programa proporcionado | With |
|
| `<V|F3>` | Abrir archivo con el programa predeterminado | View |
|
||||||
| `<X>` | Ejecutar un comando | eXecute |
|
| `<W>` | Abrir archivo con el programa proporcionado | With |
|
||||||
| `<Y>` | Alternar navegación sincronizada | sYnc |
|
| `<X>` | Ejecutar un comando | eXecute |
|
||||||
| `<CTRL+A>` | Seleccionar todos los archivos | |
|
| `<Y>` | Alternar navegación sincronizada | sYnc |
|
||||||
| `<CTRL+C>` | Abortar el proceso de transferencia de archivos | |
|
| `<CTRL+A>` | Seleccionar todos los archivos | |
|
||||||
|
| `<CTRL+C>` | Abortar el proceso de transferencia de archivos | |
|
||||||
|
|
||||||
### Trabaja en varios archivos 🥷
|
### Trabaja en varios archivos 🥷
|
||||||
|
|
||||||
|
|||||||
+38
-37
@@ -144,43 +144,44 @@ Pour changer de panneau, vous devez taper `<LEFT>` pour déplacer le panneau de
|
|||||||
|
|
||||||
### Raccourcis clavier ⌨
|
### Raccourcis clavier ⌨
|
||||||
|
|
||||||
| Key | Command | Reminder |
|
| Key | Command | Reminder |
|
||||||
|---------------|---------------------------------------------------------------------|-------------|
|
|---------------|---------------------------------------------------------------------------|-------------|
|
||||||
| `<ESC>` | Se Déconnecter de le serveur; retour à la page d'authentification | |
|
| `<ESC>` | Se Déconnecter de le serveur; retour à la page d'authentification | |
|
||||||
| `<BACKSPACE>` | Aller au répertoire précédent dans la pile | |
|
| `<BACKSPACE>` | Aller au répertoire précédent dans la pile | |
|
||||||
| `<TAB>` | Changer d'onglet explorateur | |
|
| `<TAB>` | Changer d'onglet explorateur | |
|
||||||
| `<RIGHT>` | Déplacer vers l'onglet explorateur distant | |
|
| `<RIGHT>` | Déplacer vers l'onglet explorateur distant | |
|
||||||
| `<LEFT>` | Déplacer vers l'onglet explorateur local | |
|
| `<LEFT>` | Déplacer vers l'onglet explorateur local | |
|
||||||
| `<UP>` | Remonter dans la liste sélectionnée | |
|
| `<UP>` | Remonter dans la liste sélectionnée | |
|
||||||
| `<DOWN>` | Descendre dans la liste sélectionnée | |
|
| `<DOWN>` | Descendre dans la liste sélectionnée | |
|
||||||
| `<PGUP>` | Remonter dans la liste sélectionnée de 8 lignes | |
|
| `<PGUP>` | Remonter dans la liste sélectionnée de 8 lignes | |
|
||||||
| `<PGDOWN>` | Descendre dans la liste sélectionnée de 8 lignes | |
|
| `<PGDOWN>` | Descendre dans la liste sélectionnée de 8 lignes | |
|
||||||
| `<ENTER>` | Entrer dans le directoire | |
|
| `<ENTER>` | Entrer dans le directoire | |
|
||||||
| `<SPACE>` | Télécharger le fichier sélectionné | |
|
| `<SPACE>` | Télécharger le fichier sélectionné | |
|
||||||
| `<BACKTAB>` | Basculer entre l'onglet journal et l'explorateur | |
|
| `<BACKTAB>` | Basculer entre l'onglet journal et l'explorateur | |
|
||||||
| `<A>` | Basculer les fichiers cachés | All |
|
| `<A>` | Basculer les fichiers cachés | All |
|
||||||
| `<B>` | Trier les fichiers par | Bubblesort? |
|
| `<B>` | Trier les fichiers par | Bubblesort? |
|
||||||
| `<C|F5>` | Copier le fichier/répertoire | Copy |
|
| `<C|F5>` | Copier le fichier/répertoire | Copy |
|
||||||
| `<D|F7>` | Créer un dossier | Directory |
|
| `<D|F7>` | Créer un dossier | Directory |
|
||||||
| `<E|F8|DEL>` | Supprimer le fichier (Identique à `DEL`) | Erase |
|
| `<E|F8|DEL>` | Supprimer le fichier (Identique à `DEL`) | Erase |
|
||||||
| `<F>` | Rechercher des fichiers | Find |
|
| `<F>` | Rechercher des fichiers | Find |
|
||||||
| `<G>` | Aller au chemin fourni | Go to |
|
| `<G>` | Aller au chemin fourni | Go to |
|
||||||
| `<H|F1>` | Afficher l'aide | Help |
|
| `<H|F1>` | Afficher l'aide | Help |
|
||||||
| `<I>` | Afficher les informations sur le fichier ou le dossier sélectionné | Info |
|
| `<I>` | Afficher les informations sur le fichier ou le dossier sélectionné | Info |
|
||||||
| `<L>` | Recharger le contenu du répertoire actuel / Effacer la sélection | List |
|
| `<K>` | Créer un lien symbolique pointant vers l'entrée actuellement sélectionnée | symlinK |
|
||||||
| `<M>` | Sélectionner un fichier | Mark |
|
| `<L>` | Recharger le contenu du répertoire actuel / Effacer la sélection | List |
|
||||||
| `<N>` | Créer un nouveau fichier avec le nom fourni | New |
|
| `<M>` | Sélectionner un fichier | Mark |
|
||||||
| `<O|F4>` | Modifier le fichier | Open |
|
| `<N>` | Créer un nouveau fichier avec le nom fourni | New |
|
||||||
| `<Q|F10>` | Quitter termscp | Quit |
|
| `<O|F4>` | Modifier le fichier | Open |
|
||||||
| `<R|F6>` | Renommer le fichier | Rename |
|
| `<Q|F10>` | Quitter termscp | Quit |
|
||||||
| `<F2|S>` | Enregistrer le fichier sous... | Save |
|
| `<R|F6>` | Renommer le fichier | Rename |
|
||||||
| `<U>` | Aller dans le répertoire parent | Upper |
|
| `<S|F2>` | Enregistrer le fichier sous... | Save |
|
||||||
| `<V|F3>` | Ouvrir le fichier avec le programme défaut pour le type de fichier | View |
|
| `<U>` | Aller dans le répertoire parent | Upper |
|
||||||
| `<W>` | Ouvrir le fichier avec le programme spécifié | With |
|
| `<V|F3>` | Ouvrir le fichier avec le programme défaut pour le type de fichier | View |
|
||||||
| `<X>` | Exécuter une commande | eXecute |
|
| `<W>` | Ouvrir le fichier avec le programme spécifié | With |
|
||||||
| `<Y>` | Basculer la navigation synchronisée | sYnc |
|
| `<X>` | Exécuter une commande | eXecute |
|
||||||
| `<CTRL+A>` | Sélectionner tous les fichiers | |
|
| `<Y>` | Basculer la navigation synchronisée | sYnc |
|
||||||
| `<CTRL+C>` | Abandonner le processus de transfert de fichiers | |
|
| `<CTRL+A>` | Sélectionner tous les fichiers | |
|
||||||
|
| `<CTRL+C>` | Abandonner le processus de transfert de fichiers | |
|
||||||
|
|
||||||
### Travailler sur plusieurs fichiers 🥷
|
### Travailler sur plusieurs fichiers 🥷
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -163,13 +163,14 @@ Per cambiare pannello ti puoi muovere con le frecce, `<LEFT>` per andare sul pan
|
|||||||
| `<G>` | Vai al percorso indicato | Go to |
|
| `<G>` | Vai al percorso indicato | Go to |
|
||||||
| `<H|F1>` | Mostra help | Help |
|
| `<H|F1>` | Mostra help | Help |
|
||||||
| `<I>` | Mostra informazioni per il file selezionato | Info |
|
| `<I>` | Mostra informazioni per il file selezionato | Info |
|
||||||
|
| `<K>` | Crea un link simbolico che punta al file selezionato | symlinK |
|
||||||
| `<L>` | Ricarica posizione corrente / pulisci selezione file | List |
|
| `<L>` | Ricarica posizione corrente / pulisci selezione file | List |
|
||||||
| `<M>` | Seleziona file | Mark |
|
| `<M>` | Seleziona file | Mark |
|
||||||
| `<N>` | Crea nuovo file con il nome fornito | New |
|
| `<N>` | Crea nuovo file con il nome fornito | New |
|
||||||
| `<O|F4>` | Modifica file; Vedi text editor | Open |
|
| `<O|F4>` | Modifica file; Vedi text editor | Open |
|
||||||
| `<Q|F10>` | Termina termscp | Quit |
|
| `<Q|F10>` | Termina termscp | Quit |
|
||||||
| `<R|F6>` | Rinomina file | Rename |
|
| `<R|F6>` | Rinomina file | Rename |
|
||||||
| `<F2|S>` | Salva file con nome | Save |
|
| `<S|F2>` | Salva file con nome | Save |
|
||||||
| `<U>` | Vai alla directory padre | Upper |
|
| `<U>` | Vai alla directory padre | Upper |
|
||||||
| `<V|F3>` | Apri il file con il programma definito dal sistema | View |
|
| `<V|F3>` | Apri il file con il programma definito dal sistema | View |
|
||||||
| `<W>` | Apri il file con il programma specificato | With |
|
| `<W>` | Apri il file con il programma specificato | With |
|
||||||
|
|||||||
+38
-37
@@ -144,43 +144,44 @@ In order to change panel you need to type `<LEFT>` to move the remote explorer p
|
|||||||
|
|
||||||
### Keybindings ⌨
|
### Keybindings ⌨
|
||||||
|
|
||||||
| Key | Command | Reminder |
|
| Key | Command | Reminder |
|
||||||
|---------------|-------------------------------------------------------|-------------|
|
|---------------|---------------------------------------------------------|-------------|
|
||||||
| `<ESC>` | Disconnect from remote; return to authentication page | |
|
| `<ESC>` | Disconnect from remote; return to authentication page | |
|
||||||
| `<BACKSPACE>` | Go to previous directory in stack | |
|
| `<BACKSPACE>` | Go to previous directory in stack | |
|
||||||
| `<TAB>` | Switch explorer tab | |
|
| `<TAB>` | Switch explorer tab | |
|
||||||
| `<RIGHT>` | Move to remote explorer tab | |
|
| `<RIGHT>` | Move to remote explorer tab | |
|
||||||
| `<LEFT>` | Move to local explorer tab | |
|
| `<LEFT>` | Move to local explorer tab | |
|
||||||
| `<UP>` | Move up in selected list | |
|
| `<UP>` | Move up in selected list | |
|
||||||
| `<DOWN>` | Move down in selected list | |
|
| `<DOWN>` | Move down in selected list | |
|
||||||
| `<PGUP>` | Move up in selected list by 8 rows | |
|
| `<PGUP>` | Move up in selected list by 8 rows | |
|
||||||
| `<PGDOWN>` | Move down in selected list by 8 rows | |
|
| `<PGDOWN>` | Move down in selected list by 8 rows | |
|
||||||
| `<ENTER>` | Enter directory | |
|
| `<ENTER>` | Enter directory | |
|
||||||
| `<SPACE>` | Upload / download selected file | |
|
| `<SPACE>` | Upload / download selected file | |
|
||||||
| `<BACKTAB>` | Switch between log tab and explorer | |
|
| `<BACKTAB>` | Switch between log tab and explorer | |
|
||||||
| `<A>` | Toggle hidden files | All |
|
| `<A>` | Toggle hidden files | All |
|
||||||
| `<B>` | Sort files by | Bubblesort? |
|
| `<B>` | Sort files by | Bubblesort? |
|
||||||
| `<C|F5>` | Copy file/directory | Copy |
|
| `<C|F5>` | Copy file/directory | Copy |
|
||||||
| `<D|F7>` | Make directory | Directory |
|
| `<D|F7>` | Make directory | Directory |
|
||||||
| `<E|F8|DEL>` | Delete file | Erase |
|
| `<E|F8|DEL>` | Delete file | Erase |
|
||||||
| `<F>` | Search for files (wild match is supported) | Find |
|
| `<F>` | Search for files (wild match is supported) | Find |
|
||||||
| `<G>` | Go to supplied path | Go to |
|
| `<G>` | Go to supplied path | Go to |
|
||||||
| `<H|F1>` | Show help | Help |
|
| `<H|F1>` | Show help | Help |
|
||||||
| `<I>` | Show info about selected file or directory | Info |
|
| `<I>` | Show info about selected file or directory | Info |
|
||||||
| `<L>` | Reload current directory's content / Clear selection | List |
|
| `<K>` | Create symlink pointing to the currently selected entry | symlinK |
|
||||||
| `<M>` | Select a file | Mark |
|
| `<L>` | Reload current directory's content / Clear selection | List |
|
||||||
| `<N>` | Create new file with provided name | New |
|
| `<M>` | Select a file | Mark |
|
||||||
| `<O|F4>` | Edit file; see Text editor | Open |
|
| `<N>` | Create new file with provided name | New |
|
||||||
| `<Q|F10>` | Quit termscp | Quit |
|
| `<O|F4>` | Edit file; see Text editor | Open |
|
||||||
| `<R|F6>` | Rename file | Rename |
|
| `<Q|F10>` | Quit termscp | Quit |
|
||||||
| `<F2|S>` | Save file as... | Save |
|
| `<R|F6>` | Rename file | Rename |
|
||||||
| `<U>` | Go to parent directory | Upper |
|
| `<S|F2>` | Save file as... | Save |
|
||||||
| `<V|F3>` | Open file with default program for filetype | View |
|
| `<U>` | Go to parent directory | Up |
|
||||||
| `<W>` | Open file with provided program | With |
|
| `<V|F3>` | Open file with default program for filetype | View |
|
||||||
| `<X>` | Execute a command | eXecute |
|
| `<W>` | Open file with provided program | With |
|
||||||
| `<Y>` | Toggle synchronized browsing | sYnc |
|
| `<X>` | Execute a command | eXecute |
|
||||||
| `<CTRL+A>` | Select all files | |
|
| `<Y>` | Toggle synchronized browsing | sYnc |
|
||||||
| `<CTRL+C>` | Abort file transfer process | |
|
| `<CTRL+A>` | Select all files | |
|
||||||
|
| `<CTRL+C>` | Abort file transfer process | |
|
||||||
|
|
||||||
### Work on multiple files 🥷
|
### Work on multiple files 🥷
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -165,13 +165,14 @@ termscp中的文件资源管理器是指你与远程建立连接后可以看到
|
|||||||
| `<G>` | 跳转到指定路径 | Go to |
|
| `<G>` | 跳转到指定路径 | Go to |
|
||||||
| `<H|F1>` | 显示帮助 | Help |
|
| `<H|F1>` | 显示帮助 | Help |
|
||||||
| `<I>` | 显示选中文件(夹)信息 | Info |
|
| `<I>` | 显示选中文件(夹)信息 | Info |
|
||||||
|
| `<K>` | 创建指向当前选定条目的符号链接 | symlinK |
|
||||||
| `<L>` | 刷新当前目录列表 / 清除选中状态 | List |
|
| `<L>` | 刷新当前目录列表 / 清除选中状态 | List |
|
||||||
| `<M>` | 选中文件 | Mark |
|
| `<M>` | 选中文件 | Mark |
|
||||||
| `<N>` | 使用键入的名称新建文件 | New |
|
| `<N>` | 使用键入的名称新建文件 | New |
|
||||||
| `<O|F4>` | 编辑文件;参考文本编辑器文档 | Open |
|
| `<O|F4>` | 编辑文件;参考文本编辑器文档 | Open |
|
||||||
| `<Q|F10>` | 退出termscp | Quit |
|
| `<Q|F10>` | 退出termscp | Quit |
|
||||||
| `<R|F7>` | 重命名文件 | Rename |
|
| `<R|F7>` | 重命名文件 | Rename |
|
||||||
| `<F2|S>` | 另存为... | Save |
|
| `<S|F2>` | 另存为... | Save |
|
||||||
| `<U>` | 进入上层目录 | Upper |
|
| `<U>` | 进入上层目录 | Upper |
|
||||||
| `<V|F3>` | 使用默认方式打开文件 | View |
|
| `<V|F3>` | 使用默认方式打开文件 | View |
|
||||||
| `<W>` | 使用指定程序打开文件 | With |
|
| `<W>` | 使用指定程序打开文件 | With |
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ use std::time::Duration;
|
|||||||
|
|
||||||
/// ### NextActivity
|
/// ### NextActivity
|
||||||
///
|
///
|
||||||
/// NextActivity identified the next identity to run once the current has ended
|
/// NextActivity identifies the next identity to run once the current has ended
|
||||||
pub enum NextActivity {
|
pub enum NextActivity {
|
||||||
Authentication,
|
Authentication,
|
||||||
FileTransfer,
|
FileTransfer,
|
||||||
|
|||||||
@@ -672,6 +672,21 @@ impl Localhost {
|
|||||||
self.iter_search(self.wrkdir.as_path(), &WildMatch::new(search))
|
self.iter_search(self.wrkdir.as_path(), &WildMatch::new(search))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a symlink at path pointing at target
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
pub fn symlink(&self, path: &Path, target: &Path) -> Result<(), HostError> {
|
||||||
|
let path = self.to_path(path);
|
||||||
|
std::os::unix::fs::symlink(target, path.as_path()).map_err(|e| {
|
||||||
|
error!(
|
||||||
|
"Failed to create symlink at {} pointing at {}: {}",
|
||||||
|
path.display(),
|
||||||
|
target.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
HostError::new(HostErrorType::CouldNotCreateFile, Some(e), path.as_path())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// -- privates
|
// -- privates
|
||||||
|
|
||||||
/// Recursive call for `find` method.
|
/// Recursive call for `find` method.
|
||||||
@@ -1179,6 +1194,24 @@ mod tests {
|
|||||||
assert_eq!(result[1].name(), "examples.csv");
|
assert_eq!(result[1].name(), "examples.csv");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_create_symlink() {
|
||||||
|
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||||
|
let dir_path: &Path = tmpdir.path();
|
||||||
|
// Make file
|
||||||
|
assert!(make_file_at(dir_path, "pippo.txt").is_ok());
|
||||||
|
let host: Localhost = Localhost::new(PathBuf::from(dir_path)).ok().unwrap();
|
||||||
|
let mut p = dir_path.to_path_buf();
|
||||||
|
p.push("pippo.txt");
|
||||||
|
// Make symlink
|
||||||
|
assert!(host.symlink(Path::new("link.txt"), p.as_path()).is_ok());
|
||||||
|
// Fail symlink
|
||||||
|
assert!(host.symlink(Path::new("link.txt"), p.as_path()).is_err());
|
||||||
|
assert!(host
|
||||||
|
.symlink(Path::new("/tmp/oooo/aaaa"), p.as_path())
|
||||||
|
.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_host_fmt_error() {
|
fn test_host_fmt_error() {
|
||||||
let err: HostError = HostError::new(
|
let err: HostError = HostError::new(
|
||||||
|
|||||||
@@ -38,8 +38,6 @@ impl FileTransferActivity {
|
|||||||
SelectedEntry::One(entry) => {
|
SelectedEntry::One(entry) => {
|
||||||
let dest_path: PathBuf = PathBuf::from(input);
|
let dest_path: PathBuf = PathBuf::from(input);
|
||||||
self.local_copy_file(&entry, dest_path.as_path());
|
self.local_copy_file(&entry, dest_path.as_path());
|
||||||
// Reload entries
|
|
||||||
self.reload_local_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::Many(entries) => {
|
SelectedEntry::Many(entries) => {
|
||||||
// Try to copy each file to Input/{FILE_NAME}
|
// Try to copy each file to Input/{FILE_NAME}
|
||||||
@@ -50,8 +48,6 @@ impl FileTransferActivity {
|
|||||||
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());
|
||||||
}
|
}
|
||||||
// Reload entries
|
|
||||||
self.reload_local_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::None => {}
|
SelectedEntry::None => {}
|
||||||
}
|
}
|
||||||
@@ -63,8 +59,6 @@ impl FileTransferActivity {
|
|||||||
SelectedEntry::One(entry) => {
|
SelectedEntry::One(entry) => {
|
||||||
let dest_path: PathBuf = PathBuf::from(input);
|
let dest_path: PathBuf = PathBuf::from(input);
|
||||||
self.remote_copy_file(entry, dest_path.as_path());
|
self.remote_copy_file(entry, dest_path.as_path());
|
||||||
// Reload entries
|
|
||||||
self.reload_remote_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::Many(entries) => {
|
SelectedEntry::Many(entries) => {
|
||||||
// Try to copy each file to Input/{FILE_NAME}
|
// Try to copy each file to Input/{FILE_NAME}
|
||||||
@@ -75,8 +69,6 @@ impl FileTransferActivity {
|
|||||||
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());
|
||||||
}
|
}
|
||||||
// Reload entries
|
|
||||||
self.reload_remote_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::None => {}
|
SelectedEntry::None => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ impl FileTransferActivity {
|
|||||||
SelectedEntry::One(entry) => {
|
SelectedEntry::One(entry) => {
|
||||||
// Delete file
|
// Delete file
|
||||||
self.local_remove_file(&entry);
|
self.local_remove_file(&entry);
|
||||||
// Reload
|
|
||||||
self.reload_local_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::Many(entries) => {
|
SelectedEntry::Many(entries) => {
|
||||||
// Iter files
|
// Iter files
|
||||||
@@ -45,8 +43,6 @@ impl FileTransferActivity {
|
|||||||
// Delete file
|
// Delete file
|
||||||
self.local_remove_file(entry);
|
self.local_remove_file(entry);
|
||||||
}
|
}
|
||||||
// Reload entries
|
|
||||||
self.reload_local_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::None => {}
|
SelectedEntry::None => {}
|
||||||
}
|
}
|
||||||
@@ -57,8 +53,6 @@ impl FileTransferActivity {
|
|||||||
SelectedEntry::One(entry) => {
|
SelectedEntry::One(entry) => {
|
||||||
// Delete file
|
// Delete file
|
||||||
self.remote_remove_file(&entry);
|
self.remote_remove_file(&entry);
|
||||||
// Reload
|
|
||||||
self.reload_remote_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::Many(entries) => {
|
SelectedEntry::Many(entries) => {
|
||||||
// Iter files
|
// Iter files
|
||||||
@@ -66,8 +60,6 @@ impl FileTransferActivity {
|
|||||||
// Delete file
|
// Delete file
|
||||||
self.remote_remove_file(entry);
|
self.remote_remove_file(entry);
|
||||||
}
|
}
|
||||||
// Reload entries
|
|
||||||
self.reload_remote_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::None => {}
|
SelectedEntry::None => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,8 +56,6 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Reload entries
|
|
||||||
self.reload_local_dir();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn action_edit_remote_file(&mut self) {
|
pub(crate) fn action_edit_remote_file(&mut self) {
|
||||||
@@ -80,8 +78,6 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Reload entries
|
|
||||||
self.reload_remote_dir();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Edit a file on localhost
|
/// Edit a file on localhost
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ impl FileTransferActivity {
|
|||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
// Reload files
|
// Reload files
|
||||||
self.log(LogLevel::Info, format!("\"{}\": {}", input, output));
|
self.log(LogLevel::Info, format!("\"{}\": {}", input, output));
|
||||||
// Reload entries
|
|
||||||
self.reload_local_dir();
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// Report err
|
// Report err
|
||||||
@@ -55,7 +53,6 @@ impl FileTransferActivity {
|
|||||||
LogLevel::Info,
|
LogLevel::Info,
|
||||||
format!("\"{}\" (exitcode: {}): {}", input, rc, output),
|
format!("\"{}\" (exitcode: {}): {}", input, rc, output),
|
||||||
);
|
);
|
||||||
self.reload_remote_dir();
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// Report err
|
// Report err
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ impl FileTransferActivity {
|
|||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// Reload files
|
// Reload files
|
||||||
self.log(LogLevel::Info, format!("Created directory \"{}\"", input));
|
self.log(LogLevel::Info, format!("Created directory \"{}\"", input));
|
||||||
// Reload entries
|
|
||||||
self.reload_local_dir();
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// Report err
|
// Report err
|
||||||
@@ -56,7 +54,6 @@ impl FileTransferActivity {
|
|||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// Reload files
|
// Reload files
|
||||||
self.log(LogLevel::Info, format!("Created directory \"{}\"", input));
|
self.log(LogLevel::Info, format!("Created directory \"{}\"", input));
|
||||||
self.reload_remote_dir();
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// Report err
|
// Report err
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ mod pending;
|
|||||||
pub(crate) mod rename;
|
pub(crate) mod rename;
|
||||||
pub(crate) mod save;
|
pub(crate) mod save;
|
||||||
pub(crate) mod submit;
|
pub(crate) mod submit;
|
||||||
|
pub(crate) mod symlink;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum SelectedEntry {
|
pub(crate) enum SelectedEntry {
|
||||||
@@ -109,6 +110,16 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether only one entry is selected on local host
|
||||||
|
pub(crate) fn is_local_selected_one(&self) -> bool {
|
||||||
|
matches!(self.get_local_selected_entries(), SelectedEntry::One(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether only one entry is selected on remote host
|
||||||
|
pub(crate) fn is_remote_selected_one(&self) -> bool {
|
||||||
|
matches!(self.get_remote_selected_entries(), SelectedEntry::One(_))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get remote file entry
|
/// Get remote file entry
|
||||||
pub(crate) fn get_found_selected_entries(&self) -> SelectedEntry {
|
pub(crate) fn get_found_selected_entries(&self) -> SelectedEntry {
|
||||||
match self.get_selected_index(&Id::ExplorerFind) {
|
match self.get_selected_index(&Id::ExplorerFind) {
|
||||||
|
|||||||
@@ -59,8 +59,6 @@ impl FileTransferActivity {
|
|||||||
format!("Created file \"{}\"", file_path.display()),
|
format!("Created file \"{}\"", file_path.display()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Reload files
|
|
||||||
self.reload_local_dir();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn action_remote_newfile(&mut self, input: String) {
|
pub(crate) fn action_remote_newfile(&mut self, input: String) {
|
||||||
@@ -123,8 +121,6 @@ impl FileTransferActivity {
|
|||||||
LogLevel::Info,
|
LogLevel::Info,
|
||||||
format!("Created file \"{}\"", file_path.display()),
|
format!("Created file \"{}\"", file_path.display()),
|
||||||
);
|
);
|
||||||
// Reload files
|
|
||||||
self.reload_remote_dir();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ impl FileTransferActivity {
|
|||||||
SelectedEntry::One(entry) => {
|
SelectedEntry::One(entry) => {
|
||||||
let dest_path: PathBuf = PathBuf::from(input);
|
let dest_path: PathBuf = PathBuf::from(input);
|
||||||
self.local_rename_file(&entry, dest_path.as_path());
|
self.local_rename_file(&entry, dest_path.as_path());
|
||||||
// Reload entries
|
|
||||||
self.reload_local_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::Many(entries) => {
|
SelectedEntry::Many(entries) => {
|
||||||
// Try to copy each file to Input/{FILE_NAME}
|
// Try to copy each file to Input/{FILE_NAME}
|
||||||
@@ -49,8 +47,6 @@ impl FileTransferActivity {
|
|||||||
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());
|
||||||
}
|
}
|
||||||
// Reload entries
|
|
||||||
self.reload_local_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::None => {}
|
SelectedEntry::None => {}
|
||||||
}
|
}
|
||||||
@@ -61,8 +57,6 @@ impl FileTransferActivity {
|
|||||||
SelectedEntry::One(entry) => {
|
SelectedEntry::One(entry) => {
|
||||||
let dest_path: PathBuf = PathBuf::from(input);
|
let dest_path: PathBuf = PathBuf::from(input);
|
||||||
self.remote_rename_file(&entry, dest_path.as_path());
|
self.remote_rename_file(&entry, dest_path.as_path());
|
||||||
// Reload entries
|
|
||||||
self.reload_remote_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::Many(entries) => {
|
SelectedEntry::Many(entries) => {
|
||||||
// Try to copy each file to Input/{FILE_NAME}
|
// Try to copy each file to Input/{FILE_NAME}
|
||||||
@@ -73,8 +67,6 @@ impl FileTransferActivity {
|
|||||||
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());
|
||||||
}
|
}
|
||||||
// Reload entries
|
|
||||||
self.reload_remote_dir();
|
|
||||||
}
|
}
|
||||||
SelectedEntry::None => {}
|
SelectedEntry::None => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
//! ## FileTransferActivity
|
||||||
|
//!
|
||||||
|
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* termscp - Copyright (c) 2021 Christian Visintin
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
// locals
|
||||||
|
use super::{FileTransferActivity, LogLevel, SelectedEntry};
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
impl FileTransferActivity {
|
||||||
|
/// Create symlink on localhost
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
pub(crate) fn action_local_symlink(&mut self, name: String) {
|
||||||
|
if let SelectedEntry::One(entry) = self.get_local_selected_entries() {
|
||||||
|
match self
|
||||||
|
.host
|
||||||
|
.symlink(PathBuf::from(name.as_str()).as_path(), entry.path())
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
self.log(
|
||||||
|
LogLevel::Info,
|
||||||
|
format!(
|
||||||
|
"Created symlink at {}, pointing to {}",
|
||||||
|
name,
|
||||||
|
entry.path().display()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
self.log_and_alert(
|
||||||
|
LogLevel::Error,
|
||||||
|
format!("Could not create symlink: {}", err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "windows")]
|
||||||
|
pub(crate) fn action_local_symlink(&mut self, _name: String) {
|
||||||
|
self.mount_error("Symlinks are not supported on Windows hosts");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy file on remote
|
||||||
|
pub(crate) fn action_remote_symlink(&mut self, name: String) {
|
||||||
|
if let SelectedEntry::One(entry) = self.get_remote_selected_entries() {
|
||||||
|
match self
|
||||||
|
.client
|
||||||
|
.symlink(PathBuf::from(name.as_str()).as_path(), entry.path())
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
self.log(
|
||||||
|
LogLevel::Info,
|
||||||
|
format!(
|
||||||
|
"Created symlink at {}, pointing to {}",
|
||||||
|
name,
|
||||||
|
entry.path().display()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
self.log_and_alert(
|
||||||
|
LogLevel::Error,
|
||||||
|
format!(
|
||||||
|
"Could not create symlink pointing to {}: {}",
|
||||||
|
entry.path().display(),
|
||||||
|
err
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,7 +46,7 @@ pub use popups::{
|
|||||||
FindPopup, GoToPopup, KeybindingsPopup, MkdirPopup, NewfilePopup, OpenWithPopup,
|
FindPopup, GoToPopup, KeybindingsPopup, MkdirPopup, NewfilePopup, OpenWithPopup,
|
||||||
ProgressBarFull, ProgressBarPartial, QuitPopup, RenamePopup, ReplacePopup,
|
ProgressBarFull, ProgressBarPartial, QuitPopup, RenamePopup, ReplacePopup,
|
||||||
ReplacingFilesListPopup, SaveAsPopup, SortingPopup, StatusBarLocal, StatusBarRemote,
|
ReplacingFilesListPopup, SaveAsPopup, SortingPopup, StatusBarLocal, StatusBarRemote,
|
||||||
SyncBrowsingMkdirPopup, WaitPopup,
|
SymlinkPopup, SyncBrowsingMkdirPopup, WaitPopup,
|
||||||
};
|
};
|
||||||
pub use transfer::{ExplorerFind, ExplorerLocal, ExplorerRemote};
|
pub use transfer::{ExplorerFind, ExplorerLocal, ExplorerRemote};
|
||||||
|
|
||||||
|
|||||||
@@ -724,6 +724,11 @@ impl KeybindingsPopup {
|
|||||||
" Show info about selected file",
|
" Show info about selected file",
|
||||||
))
|
))
|
||||||
.add_row()
|
.add_row()
|
||||||
|
.add_col(TextSpan::new("<K>").bold().fg(key_color))
|
||||||
|
.add_col(TextSpan::from(
|
||||||
|
" Create symlink pointing to the current selected entry",
|
||||||
|
))
|
||||||
|
.add_row()
|
||||||
.add_col(TextSpan::new("<L>").bold().fg(key_color))
|
.add_col(TextSpan::new("<L>").bold().fg(key_color))
|
||||||
.add_col(TextSpan::from(" Reload directory content"))
|
.add_col(TextSpan::from(" Reload directory content"))
|
||||||
.add_row()
|
.add_row()
|
||||||
@@ -1657,6 +1662,95 @@ fn hidden_files_label(visible: bool) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(MockComponent)]
|
||||||
|
pub struct SymlinkPopup {
|
||||||
|
component: Input,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymlinkPopup {
|
||||||
|
pub fn new(color: Color) -> Self {
|
||||||
|
Self {
|
||||||
|
component: Input::default()
|
||||||
|
.borders(
|
||||||
|
Borders::default()
|
||||||
|
.color(color)
|
||||||
|
.modifiers(BorderType::Rounded),
|
||||||
|
)
|
||||||
|
.foreground(color)
|
||||||
|
.input_type(InputType::Text)
|
||||||
|
.placeholder(
|
||||||
|
"Symlink name",
|
||||||
|
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||||
|
)
|
||||||
|
.title(
|
||||||
|
"Create a symlink pointing to the selected entry",
|
||||||
|
Alignment::Center,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component<Msg, NoUserEvent> for SymlinkPopup {
|
||||||
|
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||||
|
match ev {
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Left, ..
|
||||||
|
}) => {
|
||||||
|
self.perform(Cmd::Move(Direction::Left));
|
||||||
|
Some(Msg::None)
|
||||||
|
}
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Right, ..
|
||||||
|
}) => {
|
||||||
|
self.perform(Cmd::Move(Direction::Right));
|
||||||
|
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::Delete, ..
|
||||||
|
}) => {
|
||||||
|
self.perform(Cmd::Cancel);
|
||||||
|
Some(Msg::None)
|
||||||
|
}
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Backspace,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
self.perform(Cmd::Delete);
|
||||||
|
Some(Msg::None)
|
||||||
|
}
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Char(ch),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
self.perform(Cmd::Type(ch));
|
||||||
|
Some(Msg::None)
|
||||||
|
}
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Enter, ..
|
||||||
|
}) => match self.state() {
|
||||||
|
State::One(StateValue::String(i)) => {
|
||||||
|
Some(Msg::Transfer(TransferMsg::CreateSymlink(i)))
|
||||||
|
}
|
||||||
|
_ => Some(Msg::None),
|
||||||
|
},
|
||||||
|
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||||
|
Some(Msg::Ui(UiMsg::CloseSymlinkPopup))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(MockComponent)]
|
#[derive(MockComponent)]
|
||||||
pub struct SyncBrowsingMkdirPopup {
|
pub struct SyncBrowsingMkdirPopup {
|
||||||
component: Radio,
|
component: Radio,
|
||||||
|
|||||||
@@ -282,6 +282,10 @@ impl Component<Msg, NoUserEvent> for ExplorerLocal {
|
|||||||
code: Key::Char('i'),
|
code: Key::Char('i'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => Some(Msg::Ui(UiMsg::ShowFileInfoPopup)),
|
}) => Some(Msg::Ui(UiMsg::ShowFileInfoPopup)),
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Char('k'),
|
||||||
|
modifiers: KeyModifiers::NONE,
|
||||||
|
}) => Some(Msg::Ui(UiMsg::ShowSymlinkPopup)),
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('l'),
|
code: Key::Char('l'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
@@ -450,6 +454,10 @@ impl Component<Msg, NoUserEvent> for ExplorerRemote {
|
|||||||
code: Key::Char('i'),
|
code: Key::Char('i'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => Some(Msg::Ui(UiMsg::ShowFileInfoPopup)),
|
}) => Some(Msg::Ui(UiMsg::ShowFileInfoPopup)),
|
||||||
|
Event::Keyboard(KeyEvent {
|
||||||
|
code: Key::Char('k'),
|
||||||
|
modifiers: KeyModifiers::NONE,
|
||||||
|
}) => Some(Msg::Ui(UiMsg::ShowSymlinkPopup)),
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('l'),
|
code: Key::Char('l'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
|
|||||||
@@ -213,6 +213,7 @@ impl FileTransferActivity {
|
|||||||
|
|
||||||
/// Update local file list
|
/// Update local file list
|
||||||
pub(super) fn update_local_filelist(&mut self) {
|
pub(super) fn update_local_filelist(&mut self) {
|
||||||
|
self.reload_local_dir();
|
||||||
// Get width
|
// Get width
|
||||||
let width = self
|
let width = self
|
||||||
.context_mut()
|
.context_mut()
|
||||||
@@ -260,6 +261,7 @@ 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();
|
||||||
let width = self
|
let width = self
|
||||||
.context_mut()
|
.context_mut()
|
||||||
.terminal()
|
.terminal()
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ enum Id {
|
|||||||
SortingPopup,
|
SortingPopup,
|
||||||
StatusBarLocal,
|
StatusBarLocal,
|
||||||
StatusBarRemote,
|
StatusBarRemote,
|
||||||
|
SymlinkPopup,
|
||||||
SyncBrowsingMkdirPopup,
|
SyncBrowsingMkdirPopup,
|
||||||
WaitPopup,
|
WaitPopup,
|
||||||
}
|
}
|
||||||
@@ -111,6 +112,7 @@ enum PendingActionMsg {
|
|||||||
enum TransferMsg {
|
enum TransferMsg {
|
||||||
AbortTransfer,
|
AbortTransfer,
|
||||||
CopyFileTo(String),
|
CopyFileTo(String),
|
||||||
|
CreateSymlink(String),
|
||||||
DeleteFile,
|
DeleteFile,
|
||||||
EnterDirectory,
|
EnterDirectory,
|
||||||
ExecuteCmd(String),
|
ExecuteCmd(String),
|
||||||
@@ -151,6 +153,7 @@ enum UiMsg {
|
|||||||
CloseQuitPopup,
|
CloseQuitPopup,
|
||||||
CloseRenamePopup,
|
CloseRenamePopup,
|
||||||
CloseSaveAsPopup,
|
CloseSaveAsPopup,
|
||||||
|
CloseSymlinkPopup,
|
||||||
Disconnect,
|
Disconnect,
|
||||||
ExplorerBackTabbed,
|
ExplorerBackTabbed,
|
||||||
LogBackTabbed,
|
LogBackTabbed,
|
||||||
@@ -171,6 +174,7 @@ enum UiMsg {
|
|||||||
ShowQuitPopup,
|
ShowQuitPopup,
|
||||||
ShowRenamePopup,
|
ShowRenamePopup,
|
||||||
ShowSaveAsPopup,
|
ShowSaveAsPopup,
|
||||||
|
ShowSymlinkPopup,
|
||||||
ToggleHiddenFiles,
|
ToggleHiddenFiles,
|
||||||
ToggleSyncBrowsing,
|
ToggleSyncBrowsing,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1051,8 +1051,6 @@ impl FileTransferActivity {
|
|||||||
LogLevel::Info,
|
LogLevel::Info,
|
||||||
format!("Changed directory on local: {}", path.display()),
|
format!("Changed directory on local: {}", path.display()),
|
||||||
);
|
);
|
||||||
// Reload files
|
|
||||||
self.reload_local_dir();
|
|
||||||
// Push prev_dir to stack
|
// Push prev_dir to stack
|
||||||
if push {
|
if push {
|
||||||
self.local_mut().pushd(prev_dir.as_path())
|
self.local_mut().pushd(prev_dir.as_path())
|
||||||
|
|||||||
@@ -70,6 +70,18 @@ impl FileTransferActivity {
|
|||||||
// Reload files
|
// Reload files
|
||||||
self.update_browser_file_list()
|
self.update_browser_file_list()
|
||||||
}
|
}
|
||||||
|
TransferMsg::CreateSymlink(name) => {
|
||||||
|
self.umount_symlink();
|
||||||
|
self.mount_blocking_wait("Creating symlink…");
|
||||||
|
match self.browser.tab() {
|
||||||
|
FileExplorerTab::Local => self.action_local_symlink(name),
|
||||||
|
FileExplorerTab::Remote => self.action_remote_symlink(name),
|
||||||
|
_ => panic!("Found tab doesn't support SYMLINK"),
|
||||||
|
}
|
||||||
|
self.umount_wait();
|
||||||
|
// Reload files
|
||||||
|
self.update_browser_file_list()
|
||||||
|
}
|
||||||
TransferMsg::DeleteFile => {
|
TransferMsg::DeleteFile => {
|
||||||
self.umount_radio_delete();
|
self.umount_radio_delete();
|
||||||
self.mount_blocking_wait("Removing file(s)…");
|
self.mount_blocking_wait("Removing file(s)…");
|
||||||
@@ -408,6 +420,7 @@ impl FileTransferActivity {
|
|||||||
UiMsg::CloseQuitPopup => self.umount_quit(),
|
UiMsg::CloseQuitPopup => self.umount_quit(),
|
||||||
UiMsg::CloseRenamePopup => self.umount_rename(),
|
UiMsg::CloseRenamePopup => self.umount_rename(),
|
||||||
UiMsg::CloseSaveAsPopup => self.umount_saveas(),
|
UiMsg::CloseSaveAsPopup => self.umount_saveas(),
|
||||||
|
UiMsg::CloseSymlinkPopup => self.umount_symlink(),
|
||||||
UiMsg::Disconnect => {
|
UiMsg::Disconnect => {
|
||||||
self.disconnect();
|
self.disconnect();
|
||||||
self.umount_disconnect();
|
self.umount_disconnect();
|
||||||
@@ -460,6 +473,20 @@ impl FileTransferActivity {
|
|||||||
UiMsg::ShowQuitPopup => self.mount_quit(),
|
UiMsg::ShowQuitPopup => self.mount_quit(),
|
||||||
UiMsg::ShowRenamePopup => self.mount_rename(),
|
UiMsg::ShowRenamePopup => self.mount_rename(),
|
||||||
UiMsg::ShowSaveAsPopup => self.mount_saveas(),
|
UiMsg::ShowSaveAsPopup => self.mount_saveas(),
|
||||||
|
UiMsg::ShowSymlinkPopup => {
|
||||||
|
if match self.browser.tab() {
|
||||||
|
FileExplorerTab::Local => self.is_local_selected_one(),
|
||||||
|
FileExplorerTab::Remote => self.is_remote_selected_one(),
|
||||||
|
FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => false,
|
||||||
|
} {
|
||||||
|
// Only if only one entry is selected
|
||||||
|
self.mount_symlink();
|
||||||
|
} else {
|
||||||
|
self.mount_error(
|
||||||
|
"Symlink cannot be performed if more than one file is selected",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
UiMsg::ToggleHiddenFiles => match self.browser.tab() {
|
UiMsg::ToggleHiddenFiles => match self.browser.tab() {
|
||||||
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
|
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
|
||||||
self.browser.local_mut().toggle_hidden_files();
|
self.browser.local_mut().toggle_hidden_files();
|
||||||
|
|||||||
@@ -216,6 +216,11 @@ impl FileTransferActivity {
|
|||||||
f.render_widget(Clear, popup);
|
f.render_widget(Clear, popup);
|
||||||
// make popup
|
// make popup
|
||||||
self.app.view(&Id::SaveAsPopup, f, popup);
|
self.app.view(&Id::SaveAsPopup, f, popup);
|
||||||
|
} else if self.app.mounted(&Id::SymlinkPopup) {
|
||||||
|
let popup = draw_area_in(f.size(), 50, 10);
|
||||||
|
f.render_widget(Clear, popup);
|
||||||
|
// make popup
|
||||||
|
self.app.view(&Id::SymlinkPopup, f, popup);
|
||||||
} else if self.app.mounted(&Id::ExecPopup) {
|
} else if self.app.mounted(&Id::ExecPopup) {
|
||||||
let popup = draw_area_in(f.size(), 40, 10);
|
let popup = draw_area_in(f.size(), 40, 10);
|
||||||
f.render_widget(Clear, popup);
|
f.render_widget(Clear, popup);
|
||||||
@@ -797,6 +802,23 @@ impl FileTransferActivity {
|
|||||||
.is_ok());
|
.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn mount_symlink(&mut self) {
|
||||||
|
let input_color = self.theme().misc_input_dialog;
|
||||||
|
assert!(self
|
||||||
|
.app
|
||||||
|
.remount(
|
||||||
|
Id::SymlinkPopup,
|
||||||
|
Box::new(components::SymlinkPopup::new(input_color)),
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.is_ok());
|
||||||
|
assert!(self.app.active(&Id::SymlinkPopup).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn umount_symlink(&mut self) {
|
||||||
|
let _ = self.app.umount(&Id::SymlinkPopup);
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn mount_sync_browsing_mkdir_popup(&mut self, dir_name: &str) {
|
pub(super) fn mount_sync_browsing_mkdir_popup(&mut self, dir_name: &str) {
|
||||||
let color = self.theme().misc_info_dialog;
|
let color = self.theme().misc_info_dialog;
|
||||||
assert!(self
|
assert!(self
|
||||||
@@ -969,9 +991,14 @@ impl FileTransferActivity {
|
|||||||
Box::new(SubClause::Not(Box::new(SubClause::IsMounted(
|
Box::new(SubClause::Not(Box::new(SubClause::IsMounted(
|
||||||
Id::SyncBrowsingMkdirPopup,
|
Id::SyncBrowsingMkdirPopup,
|
||||||
)))),
|
)))),
|
||||||
Box::new(SubClause::Not(Box::new(SubClause::IsMounted(
|
Box::new(SubClause::And(
|
||||||
Id::WaitPopup,
|
Box::new(SubClause::Not(Box::new(SubClause::IsMounted(
|
||||||
)))),
|
Id::SymlinkPopup,
|
||||||
|
)))),
|
||||||
|
Box::new(SubClause::Not(Box::new(SubClause::IsMounted(
|
||||||
|
Id::WaitPopup,
|
||||||
|
)))),
|
||||||
|
)),
|
||||||
)),
|
)),
|
||||||
)),
|
)),
|
||||||
)),
|
)),
|
||||||
|
|||||||
Reference in New Issue
Block a user