Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # # This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with # the additional special exception to link portions of this program with the OpenSSL library. # See LICENSE for more details. #
import logging from collections import deque from sys import maxint
import deluge.component as component from deluge.common import FILE_PRIORITY, fdate, fsize, ftime from deluge.ui.client import client from deluge.ui.console import colors from deluge.ui.console.modes import format_utils from deluge.ui.console.modes.basemode import BaseMode from deluge.ui.console.modes.input_popup import InputPopup from deluge.ui.console.modes.popup import MessagePopup, SelectablePopup from deluge.ui.console.modes.torrent_actions import ACTION, torrent_actions_popup
try: import curses except ImportError: pass
log = logging.getLogger(__name__)
# Big help string that gets displayed when the user hits 'h' HELP_STR = """\ This screen shows detailed information about a torrent, and also the \ information about the individual files in the torrent.
You can navigate the file list with the Up/Down arrows and use space to \ collapse/expand the file tree.
All popup windows can be closed/canceled by hitting the Esc key \ (you might need to wait a second for an Esc to register)
The actions you can perform and the keys to perform them are as follows:
{!info!}'h'{!normal!} - Show this help
{!info!}'a'{!normal!} - Show torrent actions popup. Here you can do things like \ pause/resume, recheck, set torrent options and so on.
{!info!}'r'{!normal!} - Rename currently highlighted folder or a file. You can't \ rename multiple files at once so you need to first clear your selection \ with {!info!}'c'{!normal!}
{!info!}'m'{!normal!} - Mark or unmark a file or a folder {!info!}'c'{!normal!} - Un-mark all files
{!info!}Space{!normal!} - Expand/Collapse currently selected folder
{!info!}Enter{!normal!} - Show priority popup in which you can set the \ download priority of selected files and folders.
{!info!}Left Arrow{!normal!} - Go back to torrent overview. """
class TorrentDetail(BaseMode, component.Component): def __init__(self, alltorrentmode, torrentid, stdscr, console_config, encoding=None):
self.console_config = console_config self.alltorrentmode = alltorrentmode self.torrentid = torrentid self.torrent_state = None self.popup = None self.messages = deque() self._status_keys = ["files", "name", "state", "download_payload_rate", "upload_payload_rate", "progress", "eta", "all_time_download", "total_uploaded", "ratio", "num_seeds", "total_seeds", "num_peers", "total_peers", "active_time", "seeding_time", "time_added", "distributed_copies", "num_pieces", "piece_length", "download_location", "file_progress", "file_priorities", "message", "total_wanted", "tracker_host", "owner"]
self.file_list = None self.current_file = None self.current_file_idx = 0 self.file_limit = maxint self.file_off = 0 self.more_to_draw = False self.full_names = None
self.column_string = "" self.files_sep = None
self.marked = {}
BaseMode.__init__(self, stdscr, encoding) component.Component.__init__(self, "TorrentDetail", 1, depend=["SessionProxy"])
self.column_names = ["Filename", "Size", "Progress", "Priority"] self.__update_columns()
component.start(["TorrentDetail"])
self._listing_start = self.rows // 2 self._listing_space = self._listing_start - self._listing_start
client.register_event_handler("TorrentFileRenamedEvent", self._on_torrentfilerenamed_event) client.register_event_handler("TorrentFolderRenamedEvent", self._on_torrentfolderrenamed_event) client.register_event_handler("TorrentRemovedEvent", self._on_torrentremoved_event)
curses.curs_set(0) self.stdscr.notimeout(0)
# component start/update def start(self): component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
def update(self): component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
def set_state(self, state): log.debug("got state")
if state.get("files"): self.full_names = dict([(x["index"], x["path"]) for x in state["files"]])
need_prio_update = False if not self.file_list: # don't keep getting the files once we've got them once if state.get("files"): self.files_sep = "{!green,black,bold,underline!}%s" % ( ("Files (torrent has %d files)" % len(state["files"])).center(self.cols)) self.file_list, self.file_dict = self.build_file_list(state["files"], state["file_progress"], state["file_priorities"]) self._status_keys.remove("files") else: self.files_sep = "{!green,black,bold,underline!}%s" % (("Files (File list unknown)").center(self.cols)) need_prio_update = True self.__fill_progress(self.file_list, state["file_progress"]) for i, prio in enumerate(state["file_priorities"]): if self.file_dict[i][6] != prio: need_prio_update = True self.file_dict[i][6] = prio if need_prio_update: self.__fill_prio(self.file_list) del state["file_progress"] del state["file_priorities"] self.torrent_state = state self.refresh()
# split file list into directory tree. this function assumes all files in a # particular directory are returned together. it won't work otherwise. # returned list is a list of lists of the form: # [file/dir_name,index,size,children,expanded,progress,priority] # for directories index values count down from maxint (for marking usage), # for files the index is the value returned in the # state object for use with other libtorrent calls (i.e. setting prio) # # Also returns a dictionary that maps index values to the file leaves # for fast updating of progress and priorities def build_file_list(self, file_tuples, prog, prio): ret = [] retdict = {} diridx = maxint for f in file_tuples: cur = ret ps = f["path"].split("/") fin = ps[-1] for p in ps: if not cur or p != cur[-1][0]: cl = [] if p == fin: ent = [p, f["index"], f["size"], cl, False, format_utils.format_progress(prog[f["index"]] * 100), prio[f["index"]]] retdict[f["index"]] = ent else: ent = [p, diridx, -1, cl, False, 0, -1] retdict[diridx] = ent diridx -= 1 cur.append(ent) cur = cl else: cur = cur[-1][3] self.__build_sizes(ret) self.__fill_progress(ret, prog) return (ret, retdict)
# fill in the sizes of the directory entries based on their children def __build_sizes(self, fs): ret = 0 for f in fs: if f[2] == -1: val = self.__build_sizes(f[3]) ret += val f[2] = val else: ret += f[2] return ret
# fills in progress fields in all entries based on progs # returns the # of bytes complete in all the children of fs def __fill_progress(self, fs, progs): if not progs: return 0 tb = 0 for f in fs: if f[3]: # dir, has some children bd = self.__fill_progress(f[3], progs) f[5] = format_utils.format_progress((bd / f[2]) * 100) else: # file, update own prog and add to total bd = f[2] * progs[f[1]] f[5] = format_utils.format_progress(progs[f[1]] * 100) tb += bd return tb
def __fill_prio(self, fs): for f in fs: if f[3]: # dir, so fill in children and compute our prio self.__fill_prio(f[3]) child_prios = [e[6] for e in f[3]] if len(child_prios) > 1: f[6] = -2 # mixed else: f[6] = child_prios.pop(0)
def __update_columns(self): self.column_widths = [-1, 15, 15, 20] req = sum(filter(lambda x: x >= 0, self.column_widths)) if (req > self.cols): # can't satisfy requests, just spread out evenly cw = int(self.cols / len(self.column_names)) for i in range(0, len(self.column_widths)): self.column_widths[i] = cw else: rem = self.cols - req var_cols = len(filter(lambda x: x < 0, self.column_widths)) vw = int(rem / var_cols) for i in range(0, len(self.column_widths)): if (self.column_widths[i] < 0): self.column_widths[i] = vw
self.column_string = "{!green,black,bold!}%s" % ("".join(["%s%s" % (self.column_names[i], " " * ( self.column_widths[i] - len(self.column_names[i]))) for i in range(0, len(self.column_names))]))
def report_message(self, title, message): self.messages.append((title, message))
def clear_marks(self): self.marked = {}
def set_popup(self, pu): self.popup = pu self.refresh()
def _on_torrentremoved_event(self, torrent_id): if torrent_id == self.torrentid: self.back_to_overview()
def _on_torrentfilerenamed_event(self, torrent_id, index, new_name): if torrent_id == self.torrentid: self.file_dict[index][0] = new_name.split("/")[-1] component.get("SessionProxy").get_torrent_status( self.torrentid, self._status_keys).addCallback(self.set_state)
def _on_torrentfolderrenamed_event(self, torrent_id, old_folder, new_folder): if torrent_id == self.torrentid: fe = None fl = None for i in old_folder.strip("/").split("/"): if not fl: fe = fl = self.file_list
s = filter(lambda x: x[0].strip("/") == i, fl)[0]
fe = s fl = s[3] fe[0] = new_folder.strip("/").rpartition("/")[-1]
# self.__get_file_by_name(old_folder, self.file_list)[0] = new_folder.strip("/") component.get("SessionProxy").get_torrent_status( self.torrentid, self._status_keys).addCallback(self.set_state)
def draw_files(self, files, depth, off, idx):
color_selected = "blue" color_partially_selected = "magenta" color_highlighted = "white" for fl in files: # from sys import stderr # print >> stderr, fl[6] # kick out if we're going to draw too low on the screen if (off >= self.rows - 1): self.more_to_draw = True return -1, -1
self.file_limit = idx
# default color values fg = "white" bg = "black" attr = ""
if fl[6] == -2: pass # Mixed elif fl[6] == 0: fg = "red" # Do Not Download elif fl[6] == 1: pass # Normal elif fl[6] <= 6: fg = "yellow" # High elif fl[6] == 7: fg = "green" # Highest
if idx >= self.file_off: # set fg/bg colors based on whether the file is selected/marked or not
if fl[1] in self.marked: bg = color_selected if fl[3]: if self.marked[fl[1]] < self.__get_contained_files_count(file_list=fl[3]): bg = color_partially_selected attr = "bold"
if idx == self.current_file_idx: self.current_file = fl bg = color_highlighted if fl[1] in self.marked: fg = color_selected if fl[3]: if self.marked[fl[1]] < self.__get_contained_files_count(file_list=fl[3]): fg = color_partially_selected else: if fg == "white": fg = "black" attr = "bold"
if attr: color_string = "{!%s,%s,%s!}" % (fg, bg, attr) else: color_string = "{!%s,%s!}" % (fg, bg)
# actually draw the dir/file string if fl[3] and fl[4]: # this is an expanded directory xchar = "v" elif fl[3]: # collapsed directory xchar = ">" else: # file xchar = "-"
r = format_utils.format_row(["%s%s %s" % (" " * depth, xchar, fl[0]), fsize(fl[2]), fl[5], format_utils.format_priority(fl[6])], self.column_widths)
self.add_string(off, "%s%s" % (color_string, r), trim=False) off += 1
if fl[3] and fl[4]: # recurse if we have children and are expanded off, idx = self.draw_files(fl[3], depth + 1, off, idx + 1) if off < 0: return (off, idx) else: idx += 1
return (off, idx)
def __get_file_list_length(self, file_list=None): """ Counts length of the displayed file list. """ if file_list is None: file_list = self.file_list length = 0 if file_list: for element in file_list: length += 1 if element[3] and element[4]: length += self.__get_file_list_length(element[3]) return length
def __get_contained_files_count(self, file_list=None, idx=None): length = 0 if file_list is None: file_list = self.file_list if idx is not None: for element in file_list: if element[1] == idx: return self.__get_contained_files_count(file_list=element[3]) elif element[3]: c = self.__get_contained_files_count(file_list=element[3], idx=idx) if c > 0: return c else: for element in file_list: length += 1 if element[3]: length -= 1 length += self.__get_contained_files_count(element[3]) return length
def on_resize(self, *args): BaseMode.on_resize_norefresh(self, *args)
# Always refresh Legacy(it will also refresh AllTorrents), otherwise it will bug deluge out legacy = component.get("LegacyUI") legacy.on_resize(*args)
self.__update_columns() if self.popup: self.popup.handle_resize()
self._listing_start = self.rows / 2 self.refresh()
def render_header(self, off): status = self.torrent_state
up_color = colors.state_color["Seeding"] down_color = colors.state_color["Downloading"]
# Name s = "{!info!}Name: {!input!}%s" % status["name"] self.add_string(off, s) off += 1
# Print DL info and ETA if status["download_payload_rate"] > 0: s = "%sDownloading: {!input!}" % down_color else: s = "{!info!}Downloaded: {!input!}" s += fsize(status["all_time_download"]) if status["progress"] != 100.0: s += "/%s" % fsize(status["total_wanted"]) if status["download_payload_rate"] > 0: s += " {!yellow!}@ %s%s" % (down_color, fsize(status["download_payload_rate"])) s += "{!info!} ETA: {!input!}%s" % format_utils.format_time(status["eta"]) self.add_string(off, s) off += 1
# Print UL info and ratio if status["upload_payload_rate"] > 0: s = "%sUploading: {!input!}" % up_color else: s = "{!info!}Uploaded: {!input!}" s += fsize(status["total_uploaded"]) if status["upload_payload_rate"] > 0: s += " {!yellow!}@ %s%s" % (up_color, fsize(status["upload_payload_rate"])) ratio_str = format_utils.format_float(status["ratio"]) if ratio_str == "-": ratio_str = "inf" s += " {!info!}Ratio: {!input!}%s" % ratio_str self.add_string(off, s) off += 1
# Seed/peer info s = "{!info!}Seeds:{!green!} %s {!input!}(%s)" % (status["num_seeds"], status["total_seeds"]) self.add_string(off, s) off += 1 s = "{!info!}Peers:{!red!} %s {!input!}(%s)" % (status["num_peers"], status["total_peers"]) self.add_string(off, s) off += 1
# Tracker if status["message"] == "OK": color = "{!green!}" else: color = "{!red!}" s = "{!info!}Tracker: {!magenta!}%s{!input!} says \"%s%s{!input!}\"" % ( status["tracker_host"], color, status["message"]) self.add_string(off, s) off += 1
# Pieces and availability s = "{!info!}Pieces: {!yellow!}%s {!input!}x {!yellow!}%s" % ( status["num_pieces"], fsize(status["piece_length"])) if status["distributed_copies"]: s += " {!info!}Availability: {!input!}%s" % format_utils.format_float(status["distributed_copies"]) self.add_string(off, s) off += 1
# Time added s = "{!info!}Added: {!input!}%s" % fdate(status["time_added"]) self.add_string(off, s) off += 1
# Time active s = "{!info!}Time active: {!input!}%s" % (ftime(status["active_time"])) if status["seeding_time"]: s += ", {!cyan!}%s{!input!} seeding" % (ftime(status["seeding_time"])) self.add_string(off, s) off += 1
# Download Folder s = "{!info!}Download Folder: {!input!}%s" % status["download_location"] self.add_string(off, s) off += 1
# Owner if status["owner"]: s = "{!info!}Owner: {!input!}%s" % status["owner"]
return off
def refresh(self, lines=None): # show a message popup if there's anything queued if self.popup is None and self.messages: title, msg = self.messages.popleft() self.popup = MessagePopup(self, title, msg)
# Update the status bars self.stdscr.erase() self.add_string(0, self.statusbars.topbar)
# This will quite likely fail when switching modes try: rf = format_utils.remove_formatting string = self.statusbars.bottombar hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help"
string += " " * (self.cols - len(rf(string)) - len(rf(hstr))) + hstr
self.add_string(self.rows - 1, string) except: pass
off = 1 if self.torrent_state: off = self.render_header(off) else: self.add_string(1, "Waiting for torrent state")
off += 1
if self.files_sep: self.add_string(off, self.files_sep) off += 1
self._listing_start = off self._listing_space = self.rows - self._listing_start
self.add_string(off, self.column_string) if self.file_list: off += 1 self.more_to_draw = False self.draw_files(self.file_list, 0, off, 0)
if component.get("ConsoleUI").screen != self: return
self.stdscr.noutrefresh()
if self.popup: self.popup.refresh()
curses.doupdate()
def expcol_cur_file(self): """ Expand or collapse current file """ self.current_file[4] = not self.current_file[4] self.refresh()
def file_list_down(self, rows=1): maxlen = self.__get_file_list_length() - 1
self.current_file_idx += rows
if self.current_file_idx > maxlen: self.current_file_idx = maxlen
if self.current_file_idx > self.file_off + (self._listing_space - 3): self.file_off = self.current_file_idx - (self._listing_space - 3)
self.refresh()
def file_list_up(self, rows=1): self.current_file_idx = max(0, self.current_file_idx - rows) self.file_off = min(self.file_off, self.current_file_idx) self.refresh()
def back_to_overview(self): component.stop(["TorrentDetail"]) component.deregister(self) self.stdscr.erase() component.get("ConsoleUI").set_mode(self.alltorrentmode) self.alltorrentmode._go_top = False self.alltorrentmode.resume()
# build list of priorities for all files in the torrent # based on what is currently selected and a selected priority. def build_prio_list(self, files, ret_list, parent_prio, selected_prio): # has a priority been set on my parent (if so, I inherit it) for f in files: # Do not set priorities for the whole dir, just selected contents if f[3]: self.build_prio_list(f[3], ret_list, parent_prio, selected_prio) else: # file, need to add to list if f[1] in self.marked or parent_prio >= 0: # selected (or parent selected), use requested priority ret_list.append((f[1], selected_prio)) else: # not selected, just keep old priority ret_list.append((f[1], f[6]))
def do_priority(self, idx, data, was_empty): plist = [] self.build_prio_list(self.file_list, plist, -1, data) plist.sort() priorities = [p[1] for p in plist] log.debug("priorities: %s", priorities)
client.core.set_torrent_file_priorities(self.torrentid, priorities)
if was_empty: self.marked = {} return True
# show popup for priority selections def show_priority_popup(self, was_empty): func = lambda idx, data, we=was_empty: self.do_priority(idx, data, we) if self.marked: self.popup = SelectablePopup(self, "Set File Priority", func) self.popup.add_line("_Do Not Download", data=FILE_PRIORITY["Do Not Download"], foreground="red") self.popup.add_line("_Normal Priority", data=FILE_PRIORITY["Normal Priority"]) self.popup.add_line("_High Priority", data=FILE_PRIORITY["High Priority"], foreground="yellow") self.popup.add_line("H_ighest Priority", data=FILE_PRIORITY["Highest Priority"], foreground="green") self.popup._selected = 1
def __mark_unmark(self, idx): """ Selects or unselects file or a catalog(along with contained files) """ fc = self.__get_contained_files_count(idx=idx) if idx not in self.marked: # Not selected, select it self.__mark_tree(self.file_list, idx) elif self.marked[idx] < fc: # Partially selected, unselect all contents self.__unmark_tree(self.file_list, idx) else: # Selected, unselect it self.__unmark_tree(self.file_list, idx)
def __mark_tree(self, file_list, idx, mark_all=False): """ Given file_list of TorrentDetail and index of file or folder, recursively selects all files contained as well as marks folders higher in hierarchy as partially selected """ total_marked = 0 for element in file_list: marked = 0 # Select the file if it's the one we want or # if it's inside a directory that got selected if (element[1] == idx) or mark_all: # If it's a folder then select everything inside if element[3]: marked = self.__mark_tree(element[3], idx, True) self.marked[element[1]] = marked else: marked = 1 self.marked[element[1]] = 1 else: # Does not match but the item to be selected might be inside, recurse if element[3]: marked = self.__mark_tree(element[3], idx, False) # Partially select the folder if it contains files that were selected if marked > 0: self.marked[element[1]] = marked else: if element[1] in self.marked: # It's not the element we want but it's marked so count it marked = 1 # Count and then return total amount of files selected in all subdirectories total_marked += marked
return total_marked
def __get_file_by_num(self, num, file_list, idx=0): for element in file_list: if idx == num: return element
if element[3] and element[4]: i = self.__get_file_by_num(num, element[3], idx + 1) if not isinstance(i, int): return i else: idx = i else: idx += 1
return idx
def __get_file_by_name(self, name, file_list, idx=0): for element in file_list: if element[0].strip("/") == name.strip("/"): return element
if element[3] and element[4]: i = self.__get_file_by_name(name, element[3], idx + 1) if not isinstance(i, int): return i else: idx = i else: idx += 1
return idx
def __unmark_tree(self, file_list, idx, unmark_all=False): """ Given file_list of TorrentDetail and index of file or folder, recursively deselects all files contained as well as marks folders higher in hierarchy as unselected or partially selected """ total_marked = 0 for element in file_list: marked = 0 # It's either the item we want to select or # a contained item, deselect it if (element[1] == idx) or unmark_all: if element[1] in self.marked: del self.marked[element[1]] # Deselect all contents if it's a catalog if element[3]: self.__unmark_tree(element[3], idx, True) else: # Not file we wanted but it might be inside this folder, recurse inside if element[3]: marked = self.__unmark_tree(element[3], idx, False) # If none of the contents remain selected, unselect this folder as well if marked == 0: if element[1] in self.marked: del self.marked[element[1]] # Otherwise update selection count else: self.marked[element[1]] = marked else: if element[1] in self.marked: marked = 1
# Count and then return selection count so we can update # directories higher up in the hierarchy total_marked += marked return total_marked
def _selection_to_file_idx(self, file_list=None, idx=0, true_idx=0, closed=False): if not file_list: file_list = self.file_list
for element in file_list: if idx == self.current_file_idx: return true_idx
# It's a folder if element[3]: i = self._selection_to_file_idx(element[3], idx + 1, true_idx, closed or not element[4]) if isinstance(i, tuple): idx, true_idx = i if element[4]: idx, true_idx = i else: idx += 1 _, true_idx = i else: return i else: if not closed: idx += 1 true_idx += 1
return (idx, true_idx)
def _get_full_folder_path(self, num, file_list=None, path="", idx=0): if not file_list: file_list = self.file_list
for element in file_list: if not element[3]: idx += 1 continue
if num == idx: return "%s%s/" % (path, element[0])
if element[4]: i = self._get_full_folder_path(num, element[3], path + element[0] + "/", idx + 1) if not isinstance(i, int): return i else: idx = i else: idx += 1
return idx
def _do_rename_folder(self, torrent_id, folder, new_folder): client.core.rename_folder(torrent_id, folder, new_folder)
def _do_rename_file(self, torrent_id, file_idx, new_filename): if not new_filename: return client.core.rename_files(torrent_id, [(file_idx, new_filename)])
def _show_rename_popup(self): # Perhaps in the future: Renaming multiple files if self.marked: title = "Error (Enter to close)" text = "Sorry, you can't rename multiple files, please clear selection with {!info!}'c'{!normal!} key" self.popup = MessagePopup(self, title, text) else: _file = self.__get_file_by_num(self.current_file_idx, self.file_list) old_filename = _file[0]
idx = self._selection_to_file_idx() tid = self.torrentid
if _file[3]:
def do_rename(result): if not result["new_foldername"]: return old_fname = self._get_full_folder_path(self.current_file_idx) new_fname = "%s/%s/" % (old_fname.strip("/").rpartition("/")[0], result["new_foldername"]) self._do_rename_folder(tid, old_fname, new_fname)
popup = InputPopup(self, "Rename folder (Esc to cancel)", close_cb=do_rename) popup.add_text("{!info!}Renaming folder:{!input!}") popup.add_text(" * %s\n" % old_filename) popup.add_text_input("Enter new folder name:", "new_foldername", old_filename.strip("/"))
self.popup = popup else:
def do_rename(result): fname = "%s/%s" % (self.full_names[idx].rpartition("/")[0], result["new_filename"]) self._do_rename_file(tid, idx, fname)
popup = InputPopup(self, "Rename file (Esc to cancel)", close_cb=do_rename) popup.add_text("{!info!}Renaming file:{!input!}") popup.add_text(" * %s\n" % old_filename) popup.add_text_input("Enter new filename:", "new_filename", old_filename)
self.popup = popup
def read_input(self): c = self.stdscr.getch()
if self.popup: if self.popup.handle_read(c): self.popup = None self.refresh() return
if c > 31 and c < 256: if chr(c) == "Q": from twisted.internet import reactor if client.connected(): def on_disconnect(result): reactor.stop() client.disconnect().addCallback(on_disconnect) else: reactor.stop() return elif chr(c) == "q": self.back_to_overview() return
if c == 27 or c == curses.KEY_LEFT: self.back_to_overview() return
if not self.torrent_state: # actions below only make sense if there is a torrent state return
# Navigate the torrent list if c == curses.KEY_UP: self.file_list_up() elif c == curses.KEY_PPAGE: self.file_list_up(self._listing_space - 2) elif c == curses.KEY_HOME: self.file_off = 0 self.current_file_idx = 0 elif c == curses.KEY_DOWN: self.file_list_down() elif c == curses.KEY_NPAGE: self.file_list_down(self._listing_space - 2) elif c == curses.KEY_END: self.current_file_idx = self.__get_file_list_length() - 1 self.file_off = self.current_file_idx - (self._listing_space - 3) elif c == curses.KEY_DC: torrent_actions_popup(self, [self.torrentid], action=ACTION.REMOVE) # Enter Key elif c == curses.KEY_ENTER or c == 10: was_empty = (self.marked == {}) self.__mark_tree(self.file_list, self.current_file[1]) self.show_priority_popup(was_empty)
# space elif c == 32: self.expcol_cur_file() else: if c > 31 and c < 256: if chr(c) == "m": if self.current_file: self.__mark_unmark(self.current_file[1]) elif chr(c) == "r": self._show_rename_popup() elif chr(c) == "c": self.marked = {} elif chr(c) == "a": torrent_actions_popup(self, [self.torrentid], details=False) return elif chr(c) == "o": torrent_actions_popup(self, [self.torrentid], action=ACTION.TORRENT_OPTIONS) return elif chr(c) == "h": self.popup = MessagePopup(self, "Help", HELP_STR, width_req=0.75) elif chr(c) == "j": self.file_list_up() if chr(c) == "k": self.file_list_down()
self.refresh() |