import abc
import os
import pathlib
from time import sleep
from fortrace.core.qemu_monitor import QEMUMonitorSession
from fortrace.utility.applications.application import (
ApplicationType,
GenericApplication,
ParentNotifier,
)
from fortrace.utility.image_processing.text_detection import text_line_contains
[docs]
class GenericFileManager(GenericApplication):
"""OS-independent representation of a file manager window."""
_current_location = os.PathLike
def __init__(
self, name: str, qs: QEMUMonitorSession, parent_notifier: ParentNotifier
):
super().__init__(name, ApplicationType.FILE_MANAGER, qs, parent_notifier)
self._current_location = pathlib.Path("~")
self._show_hidden = False
[docs]
def browse_to_directory(self, path: os.PathLike):
"""Browse to a specified directory.
Args:
path: directory to be browsed to. If <path> points to a file, the file CAN
be selected (application specific behavior)
"""
self._qs.send_key_combination("ctrl-l")
sleep(0.5)
self._qs.send_text(str(path), True)
self._current_location = path
[docs]
@abc.abstractmethod
def focus_on_item(self, name: str):
"""Lays the focus on an item.
Then the focused item can be opened, moved, ...
Notes:
some file managers support pattern matching and will select multiple
matching items
Args:
name: name of the item to be matched
"""
pass
[docs]
@abc.abstractmethod
def open_file(
self, path: os.PathLike, app_type: ApplicationType, app_name: str
) -> GenericApplication:
"""Browse to a directory and open the specified file.
Browse to the specified directory and open the file (assuming the specified
application is used). This method will not fail if the wrong application is
specified. It might even work if no complex action with the opened application
is performed (e.g. it is closed after the file was opened). The opened
application will be focused. You have to refocus the file manager before
continuing.
Args:
path: path of the file to be opened (relative paths are allowed, including
only the name of the file)
app_type: assumed type of application which opens the file
app_name: assumed name of the application which opens the file
# TODO: determine application name and type
Returns:
handle to the new application that opened the file
"""
pass
[docs]
def copy_file(
self,
source: os.PathLike,
destination: os.PathLike,
overwrite: bool = False,
ignore_suffix: bool = True,
):
"""Copy a file <source> to the folder <destination>.
Args:
source: file/folder to be copied
destination: destination folder
overwrite: should a file with the same name in destination be overwritten
ignore_suffix: should the suffix of a file be ignored (useful if ending is
changed by, e.g., the browser)
Note:
The file manager will remain at <destination>.
"""
self.browse_to_directory(pathlib.Path(source).parent)
self.focus_on_item(
str(
pathlib.Path(source).stem
if ignore_suffix
else pathlib.Path(source).name
)
)
self._qs.send_key_combination("ctrl-c")
self.browse_to_directory(destination)
self._qs.send_key_combination("ctrl-v")
if text_line_contains(self.extract_text()[1], ["already exists", "replace"]):
if overwrite:
self._qs.send_key_combination("ret")
else:
self._qs.send_key_combination("esc")
[docs]
@abc.abstractmethod
def move_file(
self,
source: os.PathLike,
destination: os.PathLike,
overwrite: bool = False,
ignore_suffix: bool = True,
):
"""Moves a file <source> to the folder <destination>.
Args:
source: the file to be moved
destination: the destination folder
overwrite: should a file with the same name in destination be overwritten
ignore_suffix: should the suffix of a file be ignored (useful if ending is
changed by, e.g., the browser)
Note:
The file manager will remain at <destination>.
"""
pass
def _move_file(
self,
source: pathlib.PurePath,
destination: pathlib.PurePath,
overwrite: bool,
ignore_suffix: bool,
):
self.browse_to_directory(source.parent)
self.focus_on_item(source.stem if ignore_suffix else source.name)
self._qs.send_key_combination("ctrl-x")
self.browse_to_directory(destination)
self._qs.send_key_combination("ctrl-v")
if text_line_contains(self.extract_text()[1], ["already exists", "replace"]):
if overwrite:
self._qs.send_key_combination("ret")
else:
self._qs.send_key_combination("esc")
[docs]
def rename_file(self, source: os.PathLike, name: str):
"""Rename a file behind source to a specified name.
Args:
source: file to be renamed
name: the name to be given
"""
self.browse_to_directory(pathlib.Path(source).parent)
self.focus_on_item(str(pathlib.Path(source).name))
self._qs.send_key_combination("f2")
self._qs.send_text(name, True)
[docs]
def delete_file(self, path: os.PathLike):
"""Move a file to system trash.
Perform a normal delete. The file manager will remain at the parent of the
file/dir that was deleted.
Args:
path: file/directory to be deleted
"""
self.browse_to_directory(pathlib.Path(path).parent)
self.focus_on_item(pathlib.Path(path).name)
self._qs.send_key_combination("delete")
[docs]
def create_folder(self, name: str):
"""Create folder at the current location.
Args:
name: name of the folder to be created
"""
self._qs.send_key_combination("ctrl-shift-n")
sleep(2)
self._qs.send_text(name, True)
[docs]
@abc.abstractmethod
def bookmark_current_location(self):
"""Create a bookmark of the current location.
Notes:
OS specific method
"""
pass
[docs]
@abc.abstractmethod
def toggle_show_hidden(self):
"""Toggle the state of showing hidden files in the file browser."""
pass
[docs]
@abc.abstractmethod
def empty_trash(self):
"""Empty the trash bin."""
pass
[docs]
@abc.abstractmethod
def search(self, query: str):
"""Perform a search and focus result window (NOT results themselves)."""
pass
@property
def show_hidden(self):
return self._show_hidden