212 lines
6.3 KiB
Python
212 lines
6.3 KiB
Python
from __future__ import annotations
|
|
import re
|
|
from typing import TYPE_CHECKING
|
|
from ..items.Folder import Folder
|
|
from ..core.Prompt import Prompt
|
|
|
|
if TYPE_CHECKING:
|
|
from .App import App
|
|
|
|
BACKSPACE_KEYS = ("KEY_BACKSPACE", "\x7f", "\x08")
|
|
ENTER_KEYS = ("\n", "KEY_ENTER")
|
|
ESCAPE_KEY = "\x1b"
|
|
|
|
|
|
class Input:
|
|
def __init__(self, app: App) -> None:
|
|
self._app = app
|
|
self._actions = {
|
|
"quit": self._quit,
|
|
"down": self._down,
|
|
"up": self._up,
|
|
"enter": self._enter,
|
|
"back": self._back,
|
|
"parent": self._parent,
|
|
"sort_direction": self._sort_direction,
|
|
"sort_grouping": self._sort_grouping,
|
|
"new_folder": self._new_folder,
|
|
"delete_folder": self._delete_folder,
|
|
"new_file": self._new_file,
|
|
"search": self._search,
|
|
"search_next": self._search_next,
|
|
"search_previous": self._search_previous,
|
|
"go_top": self._go_top,
|
|
"go_bottom": self._go_bottom,
|
|
}
|
|
|
|
def handle(self, key: str) -> bool:
|
|
if self.app.prompt is not None:
|
|
return self._handle_prompt(key)
|
|
action = self.app.config.action_for(key)
|
|
handler = self._actions.get(action)
|
|
return handler() if handler else True
|
|
|
|
def _handle_prompt(self, key: str) -> bool:
|
|
prompt = self.app.prompt
|
|
|
|
if prompt.kind == "error":
|
|
self.app.close_prompt()
|
|
prompt.on_submit(None)
|
|
return True
|
|
|
|
if prompt.kind == "confirm":
|
|
if key in ("y", "Y"):
|
|
self.app.close_prompt()
|
|
prompt.on_submit(True)
|
|
elif key in ("n", "N", ESCAPE_KEY):
|
|
self.app.close_prompt()
|
|
return True
|
|
|
|
if key in ENTER_KEYS:
|
|
self.app.close_prompt()
|
|
prompt.on_submit(prompt.buffer)
|
|
elif key == ESCAPE_KEY:
|
|
self.app.close_prompt()
|
|
elif key in BACKSPACE_KEYS:
|
|
prompt.buffer = prompt.buffer[:-1]
|
|
elif len(key) == 1 and key.isprintable():
|
|
prompt.buffer += key
|
|
return True
|
|
|
|
def _quit(self) -> bool:
|
|
return False
|
|
|
|
def _down(self) -> bool:
|
|
navigator = self.app.navigator
|
|
navigator.cursor.down(len(navigator.items))
|
|
return True
|
|
|
|
def _up(self) -> bool:
|
|
self.app.navigator.cursor.up()
|
|
return True
|
|
|
|
def _go_top(self) -> bool:
|
|
self.app.navigator.cursor.set(0)
|
|
return True
|
|
|
|
def _go_bottom(self) -> bool:
|
|
navigator = self.app.navigator
|
|
if navigator.items:
|
|
navigator.cursor.set(len(navigator.items) - 1)
|
|
return True
|
|
|
|
def _maybe_show_error(self) -> None:
|
|
error = self.app.navigator.error
|
|
if error:
|
|
self.app.open_prompt(Prompt.error(error))
|
|
|
|
def _enter(self) -> bool:
|
|
navigator = self.app.navigator
|
|
items = navigator.items
|
|
if items:
|
|
navigator.enter(items[navigator.cursor.pos])
|
|
self._maybe_show_error()
|
|
return True
|
|
|
|
def _back(self) -> bool:
|
|
self.app.navigator.back()
|
|
self._maybe_show_error()
|
|
return True
|
|
|
|
def _parent(self) -> bool:
|
|
self.app.navigator.up()
|
|
self._maybe_show_error()
|
|
return True
|
|
|
|
def _sort_direction(self) -> bool:
|
|
self.app.navigator.cycle_sort_direction()
|
|
return True
|
|
|
|
def _sort_grouping(self) -> bool:
|
|
self.app.navigator.cycle_sort_grouping()
|
|
return True
|
|
|
|
def _new_folder(self) -> bool:
|
|
navigator = self.app.navigator
|
|
|
|
def on_submit(name: str) -> None:
|
|
if not name:
|
|
return
|
|
try:
|
|
self.app.file_actions.create_folder(navigator.current.path,
|
|
name)
|
|
except OSError as e:
|
|
self.app.open_prompt(Prompt.error(str(e)))
|
|
|
|
self.app.open_prompt(Prompt("New folder name: ", "text", on_submit))
|
|
return True
|
|
|
|
def _delete_folder(self) -> bool:
|
|
navigator = self.app.navigator
|
|
items = navigator.items
|
|
if not items:
|
|
return True
|
|
item = items[navigator.cursor.pos]
|
|
if not isinstance(item, Folder):
|
|
return True
|
|
|
|
def on_confirm(confirmed: bool) -> None:
|
|
if not confirmed:
|
|
return
|
|
try:
|
|
self.app.file_actions.delete_folder(item.path)
|
|
except OSError as e:
|
|
self.app.open_prompt(Prompt.error(str(e)))
|
|
|
|
self.app.open_prompt(
|
|
Prompt(f"Delete '{item.name}'? (y/n) ", "confirm", on_confirm))
|
|
return True
|
|
|
|
def _new_file(self) -> bool:
|
|
navigator = self.app.navigator
|
|
|
|
def on_submit(name: str) -> None:
|
|
if not name:
|
|
return
|
|
try:
|
|
self.app.file_actions.create_file(navigator.current.path,
|
|
name)
|
|
except OSError as e:
|
|
self.app.open_prompt(Prompt.error(str(e)))
|
|
|
|
self.app.open_prompt(Prompt("New file name: ", "text", on_submit))
|
|
return True
|
|
|
|
def _search(self) -> bool:
|
|
navigator = self.app.navigator
|
|
|
|
def on_submit(pattern: str) -> None:
|
|
if not pattern:
|
|
return
|
|
try:
|
|
regex = re.compile(pattern)
|
|
except re.error as e:
|
|
self.app.open_prompt(Prompt.error(f"Invalid regex: {e}"))
|
|
return
|
|
if not navigator.search(regex):
|
|
self.app.open_prompt(Prompt.error(f"No match: {pattern}"))
|
|
|
|
self.app.open_prompt(Prompt("Search: ", "text", on_submit))
|
|
return True
|
|
|
|
def _search_next(self) -> bool:
|
|
navigator = self.app.navigator
|
|
if navigator.last_pattern is None:
|
|
return True
|
|
if not navigator.search_next():
|
|
self.app.open_prompt(Prompt.error(f"No match: {navigator.last_pattern}"))
|
|
return True
|
|
|
|
def _search_previous(self) -> bool:
|
|
navigator = self.app.navigator
|
|
if navigator.last_pattern is None:
|
|
return True
|
|
if not navigator.search_previous():
|
|
self.app.open_prompt(Prompt.error(f"No match: {navigator.last_pattern}"))
|
|
return True
|
|
|
|
@property
|
|
def app(self) -> App:
|
|
return self._app
|
|
|
|
|