mirror of
https://github.com/markqvist/NomadNet.git
synced 2026-10-04 06:18:02 +00:00
235 lines
8.5 KiB
Python
235 lines
8.5 KiB
Python
import os
|
|
import nomadnet
|
|
import urwid
|
|
|
|
class FileBrowserEntry(urwid.WidgetWrap):
|
|
signals = ["click"]
|
|
|
|
def __init__(self, name, full_path, is_dir=False, is_parent=False, selected=False):
|
|
self.full_path = full_path
|
|
self.name = name
|
|
self.is_dir = is_dir
|
|
self.is_parent = is_parent
|
|
self.selected = selected
|
|
g = nomadnet.NomadNetworkApp.get_shared_instance().ui.glyphs
|
|
if is_parent:
|
|
display = g["arrow_l"]+" .."
|
|
elif is_dir:
|
|
display = g["arrow_r"]+" "+name+"/"
|
|
elif selected:
|
|
display = g["check"]+" "+name
|
|
else:
|
|
display = " "+name
|
|
self.text_widget = urwid.SelectableIcon(display, 0)
|
|
if is_dir or is_parent:
|
|
style = "list_trusted"
|
|
focus_style = "list_focus"
|
|
elif selected:
|
|
style = "list_trusted"
|
|
focus_style = "list_focus_trusted"
|
|
else:
|
|
style = "list_unknown"
|
|
focus_style = "list_focus"
|
|
display_widget = urwid.AttrMap(self.text_widget, style, focus_style)
|
|
super().__init__(display_widget)
|
|
|
|
def keypress(self, size, key):
|
|
if key == "enter":
|
|
self._emit("click")
|
|
else:
|
|
return key
|
|
|
|
def mouse_event(self, size, event, button, x, y, focus):
|
|
if button == 1 and urwid.util.is_mouse_press(event):
|
|
self._emit("click")
|
|
return True
|
|
return False
|
|
|
|
class FileBrowser(urwid.WidgetWrap):
|
|
def __init__(self, on_done=None, on_cancel=None, on_change=None, title="Select File", multi_select=True, start_path=None, selected=None, max_file_size=None):
|
|
self.on_done_cb = on_done
|
|
self.on_cancel_cb = on_cancel
|
|
self.on_change_cb = on_change
|
|
self.multi_select = multi_select
|
|
self.max_file_size = max_file_size
|
|
self.selected = list(selected) if selected else []
|
|
|
|
app = nomadnet.NomadNetworkApp.get_shared_instance()
|
|
self.g = app.ui.glyphs
|
|
self.current_path = os.path.expanduser(start_path) if start_path else os.path.expanduser("~")
|
|
|
|
self.path_label = urwid.Text("")
|
|
self.status_label = urwid.Text("")
|
|
self.file_walker = urwid.SimpleFocusListWalker([])
|
|
self.file_listbox = urwid.ListBox(self.file_walker)
|
|
|
|
self.button_columns = urwid.Columns([
|
|
(urwid.WEIGHT, 0.45, urwid.Button("Done", on_press=self._dismiss)),
|
|
(urwid.WEIGHT, 0.1, urwid.Text("")),
|
|
(urwid.WEIGHT, 0.45, urwid.Button("Cancel", on_press=self._cancel)),
|
|
])
|
|
|
|
header_pile = urwid.Pile([
|
|
self.path_label,
|
|
self.status_label,
|
|
urwid.Divider(self.g["divider1"]),
|
|
])
|
|
footer_pile = urwid.Pile([
|
|
urwid.Divider(self.g["divider1"]),
|
|
self.button_columns,
|
|
])
|
|
|
|
self._populate()
|
|
|
|
self.browser_frame = urwid.Frame(self.file_listbox, header=header_pile, footer=footer_pile)
|
|
self._main_widget = urwid.LineBox(self.browser_frame, title=title)
|
|
self._notice_open = False
|
|
super().__init__(self._main_widget)
|
|
|
|
def _show_notice(self, message, title="Notice"):
|
|
def dismiss(_b=None):
|
|
self._notice_open = False
|
|
self._w = self._main_widget
|
|
|
|
box = urwid.LineBox(urwid.Pile([
|
|
urwid.Text(message, align=urwid.CENTER),
|
|
urwid.Divider(),
|
|
urwid.Padding(urwid.Button("OK", on_press=dismiss), align=urwid.CENTER, width=8),
|
|
]), title=title)
|
|
self._notice_open = True
|
|
self._w = urwid.Overlay(box, self._main_widget, align=urwid.CENTER, width=("relative", 75), valign=urwid.MIDDLE, height=urwid.PACK)
|
|
|
|
def _dismiss_notice(self):
|
|
self._notice_open = False
|
|
self._w = self._main_widget
|
|
|
|
def _update_status(self):
|
|
if self.selected:
|
|
names = [os.path.basename(p) for p in self.selected]
|
|
self.status_label.set_text(" "+self.g["file"]+" "+str(len(self.selected))+" selected: "+", ".join(names))
|
|
elif self.multi_select:
|
|
self.status_label.set_text(" No files selected")
|
|
else:
|
|
self.status_label.set_text(" Select a file")
|
|
|
|
def _populate(self):
|
|
self.path_label.set_text(" "+self.current_path)
|
|
self._update_status()
|
|
|
|
focus_pos = None
|
|
try:
|
|
focus_pos = self.file_listbox.focus_position
|
|
except Exception:
|
|
pass
|
|
|
|
entries = []
|
|
parent = os.path.dirname(self.current_path)
|
|
if parent != self.current_path:
|
|
entry = FileBrowserEntry("..", parent, is_parent=True)
|
|
urwid.connect_signal(entry, "click", self._entry_clicked, entry)
|
|
entries.append(entry)
|
|
|
|
try:
|
|
items = sorted(os.listdir(self.current_path))
|
|
except PermissionError:
|
|
entries.append(urwid.Text(("error_text", " Permission denied")))
|
|
self.file_walker[:] = entries
|
|
return
|
|
except Exception:
|
|
entries.append(urwid.Text(("error_text", " Cannot read directory")))
|
|
self.file_walker[:] = entries
|
|
return
|
|
|
|
dirs = []
|
|
files = []
|
|
for item in items:
|
|
if item.startswith("."):
|
|
continue
|
|
full = os.path.join(self.current_path, item)
|
|
if os.path.isdir(full):
|
|
dirs.append((item, full))
|
|
elif os.path.isfile(full):
|
|
files.append((item, full))
|
|
|
|
for name, full in dirs:
|
|
entry = FileBrowserEntry(name, full, is_dir=True)
|
|
urwid.connect_signal(entry, "click", self._entry_clicked, entry)
|
|
entries.append(entry)
|
|
|
|
for name, full in files:
|
|
entry = FileBrowserEntry(name, full, selected=(full in self.selected))
|
|
urwid.connect_signal(entry, "click", self._entry_clicked, entry)
|
|
entries.append(entry)
|
|
|
|
if not dirs and not files:
|
|
entries.append(urwid.Text(("inactive_text", " (empty)")))
|
|
|
|
self.file_walker[:] = entries
|
|
if focus_pos is not None and focus_pos < len(entries):
|
|
self.file_listbox.set_focus(focus_pos)
|
|
elif entries:
|
|
self.file_listbox.set_focus(0)
|
|
|
|
def _entry_clicked(self, entry_widget, user_data=None):
|
|
entry = user_data if user_data else entry_widget
|
|
if entry.is_dir or entry.is_parent:
|
|
self.current_path = entry.full_path
|
|
self._populate()
|
|
return
|
|
|
|
if self.max_file_size is not None and entry.full_path not in self.selected:
|
|
try:
|
|
file_size = os.path.getsize(entry.full_path)
|
|
except Exception:
|
|
file_size = 0
|
|
if file_size > self.max_file_size:
|
|
limit_mb = self.max_file_size / (1024 * 1024)
|
|
size_mb = file_size / (1024 * 1024)
|
|
self._show_notice(
|
|
"%s is %.1f MB.\nAttachments are limited to %.0f MB." % (entry.name, size_mb, limit_mb),
|
|
title="File too large",
|
|
)
|
|
return
|
|
|
|
if self.multi_select:
|
|
if entry.full_path in self.selected:
|
|
self.selected.remove(entry.full_path)
|
|
else:
|
|
self.selected.append(entry.full_path)
|
|
if self.on_change_cb is not None:
|
|
self.on_change_cb(list(self.selected))
|
|
self._populate()
|
|
else:
|
|
self.selected = [entry.full_path]
|
|
if self.on_change_cb is not None:
|
|
self.on_change_cb(list(self.selected))
|
|
if self.on_done_cb is not None:
|
|
self.on_done_cb(list(self.selected))
|
|
|
|
def _dismiss(self, sender):
|
|
if self.on_done_cb is not None:
|
|
self.on_done_cb(list(self.selected))
|
|
|
|
def _cancel(self, sender):
|
|
self.selected = []
|
|
if self.on_cancel_cb is not None:
|
|
self.on_cancel_cb()
|
|
|
|
def keypress(self, size, key):
|
|
if self._notice_open:
|
|
if key in ("esc", "enter"):
|
|
self._dismiss_notice()
|
|
return None
|
|
return super().keypress(size, key)
|
|
if key == "esc":
|
|
self._cancel(None)
|
|
return None
|
|
result = super().keypress(size, key)
|
|
if result == "down" and self.browser_frame.focus_position == "body":
|
|
self.browser_frame.focus_position = "footer"
|
|
return None
|
|
elif result == "up" and self.browser_frame.focus_position == "footer":
|
|
self.browser_frame.focus_position = "body"
|
|
return None
|
|
return result
|