diff --git a/deluge/ui/console/modes/alltorrents.py b/deluge/ui/console/modes/alltorrents.py index 2e4056ed3..4c606e009 100644 --- a/deluge/ui/console/modes/alltorrents.py +++ b/deluge/ui/console/modes/alltorrents.py @@ -331,11 +331,21 @@ class AllTorrents(BaseMode): td = TorrentDetail(self,tid,self.stdscr,self.encoding) component.get("ConsoleUI").set_mode(td) - def show_preferences(self, core_config): - component.stop(["AllTorrentsStateUpdater"]) - self.stdscr.clear() - prefs = Preferences(self,core_config,self.stdscr,self.encoding) - component.get("ConsoleUI").set_mode(prefs) + def show_preferences(self): + def _on_get_config(config): + client.core.get_listen_port().addCallback(_on_get_listen_port,config) + + def _on_get_listen_port(port,config): + client.core.get_cache_status().addCallback(_on_get_cache_status,port,config) + + def _on_get_cache_status(status,port,config): + component.stop(["AllTorrentsStateUpdater"]) + self.stdscr.clear() + prefs = Preferences(self,config,port,status,self.stdscr,self.encoding) + component.get("ConsoleUI").set_mode(prefs) + + client.core.get_config().addCallback(_on_get_config) + def __show_events(self): component.stop(["AllTorrentsStateUpdater"]) @@ -619,7 +629,7 @@ class AllTorrents(BaseMode): for l in HELP_LINES: self.popup.add_line(l) elif chr(c) == 'p': - client.core.get_config().addCallback(self.show_preferences) + self.show_preferences() return elif chr(c) == 'e': self.__show_events() diff --git a/deluge/ui/console/modes/input_popup.py b/deluge/ui/console/modes/input_popup.py index 9c1dc4547..b59c914cd 100644 --- a/deluge/ui/console/modes/input_popup.py +++ b/deluge/ui/console/modes/input_popup.py @@ -49,6 +49,7 @@ from popup import Popup log = logging.getLogger(__name__) class InputField: + depend = None # render the input. return number of rows taken up def render(self,screen,row,width,selected,col=1): return 0 @@ -61,6 +62,20 @@ class InputField: def set_value(self, value): pass + def set_depend(self,i,inverse=False): + if not isinstance(i,CheckedInput): + raise Exception("Can only depend on CheckedInputs") + self.depend = i + self.inverse = inverse + + def depend_skip(self): + if not self.depend: + return False + if self.inverse: + return self.depend.checked + else: + return not self.depend.checked + class CheckedInput(InputField): def __init__(self, parent, message, name, checked=False): self.parent = parent @@ -92,6 +107,71 @@ class CheckedInput(InputField): def set_value(self, c): self.checked = c + +class CheckedPlusInput(InputField): + def __init__(self, parent, message, name, child,checked=False): + self.parent = parent + self.chkd_inact = "[X] %s"%message + self.unchkd_inact = "[ ] %s"%message + self.chkd_act = "[{!black,white,bold!}X{!white,black!}] %s"%message + self.unchkd_act = "[{!black,white,bold!} {!white,black!}] %s"%message + self.name = name + self.checked = checked + self.msglen = len(self.chkd_inact)+1 + self.child = child + self.child_active = False + + def render(self, screen, row, width, active, col=1): + isact = active and not self.child_active + if self.checked and isact: + self.parent.add_string(row,self.chkd_act,screen,col,False,True) + elif self.checked: + self.parent.add_string(row,self.chkd_inact,screen,col,False,True) + elif isact: + self.parent.add_string(row,self.unchkd_act,screen,col,False,True) + else: + self.parent.add_string(row,self.unchkd_inact,screen,col,False,True) + + if active and self.checked and self.child_active: + self.parent.add_string(row+1,"(esc to leave)",screen,col,False,True) + elif active and self.checked: + self.parent.add_string(row+1,"(right arrow to edit)",screen,col,False,True) + rows = 2 + # show child + if self.checked: + if isinstance(self.child,(TextInput,IntSpinInput,FloatSpinInput)): + crows = self.child.render(screen,row,width-self.msglen,self.child_active and active,col+self.msglen,self.msglen) + else: + crows = self.child.render(screen,row,width-self.msglen,self.child_active and active,col+self.msglen) + rows = max(rows,crows) + else: + self.parent.add_string(row,"(enable to view/edit value)",screen,col+self.msglen,False,True) + return rows + + def handle_read(self, c): + if self.child_active: + if c == 27: # leave child on esc + self.child_active = False + return + # pass keys through to child + self.child.handle_read(c) + else: + if c == 32: + self.checked = not self.checked + if c == curses.KEY_RIGHT: + self.child_active = True + + def get_value(self): + return self.checked + + def set_value(self, c): + self.checked = c + + def get_child(self): + return self.child + + + class IntSpinInput(InputField): def __init__(self, parent, message, name, move_func, value, min_val, max_val): self.parent = parent @@ -106,7 +186,7 @@ class IntSpinInput(InputField): self.min_val = min_val self.max_val = max_val - def render(self, screen, row, width, active, col=1): + def render(self, screen, row, width, active, col=1, cursor_offset=0): if not active and not self.valstr: self.value = self.initvalue self.valstr = "%d"%self.value @@ -119,7 +199,7 @@ class IntSpinInput(InputField): self.parent.add_string(row,"%s [ %d ]"%(self.message,self.value),screen,col,False,True) if active: - self.move_func(row,self.cursor+self.cursoff) + self.move_func(row,self.cursor+self.cursoff+cursor_offset) return 1 @@ -166,6 +246,112 @@ class IntSpinInput(InputField): self.valstr = "%d"%self.value self.cursor = len(self.valstr) + +class FloatSpinInput(InputField): + def __init__(self, parent, message, name, move_func, value, inc_amt, precision, min_val, max_val): + self.parent = parent + self.message = message + self.name = name + self.precision = precision + self.inc_amt = inc_amt + self.value = round(float(value),self.precision) + self.initvalue = self.value + self.fmt = "%%.%df"%precision + self.valstr = self.fmt%self.value + self.cursor = len(self.valstr) + self.cursoff = len(self.message)+4 # + 4 for the " [ " in the rendered string + self.move_func = move_func + self.min_val = min_val + self.max_val = max_val + self.need_update = False + + def render(self, screen, row, width, active, col=1, cursor_offset=0): + if not active and not self.valstr: + self.value = self.initvalue + self.valstr = self.fmt%self.value + self.cursor = len(self.valstr) + if not active and self.need_update: + self.value = round(float(self.valstr),self.precision) + self.valstr = self.fmt%self.value + self.cursor = len(self.valstr) + if not self.valstr: + self.parent.add_string(row,"%s [ ]"%self.message,screen,col,False,True) + elif active: + self.parent.add_string(row,"%s [ {!black,white,bold!}%s{!white,black!} ]"%(self.message,self.valstr),screen,col,False,True) + else: + self.parent.add_string(row,"%s [ %s ]"%(self.message,self.valstr),screen,col,False,True) + if active: + self.move_func(row,self.cursor+self.cursoff+cursor_offset) + + return 1 + + def handle_read(self, c): + if c == curses.KEY_PPAGE: + self.value+=self.inc_amt + self.valstr = self.fmt%self.value + self.cursor = len(self.valstr) + elif c == curses.KEY_NPAGE: + self.value-=self.inc_amt + self.valstr = self.fmt%self.value + self.cursor = len(self.valstr) + elif c == curses.KEY_LEFT: + self.cursor = max(0,self.cursor-1) + elif c == curses.KEY_RIGHT: + self.cursor = min(len(self.valstr),self.cursor+1) + elif c == curses.KEY_HOME: + self.cursor = 0 + elif c == curses.KEY_END: + self.cursor = len(self.value) + elif c == curses.KEY_BACKSPACE or c == 127: + if self.valstr and self.cursor > 0: + self.valstr = self.valstr[:self.cursor - 1] + self.valstr[self.cursor:] + self.cursor-=1 + self.need_update = True + elif c == curses.KEY_DC: + if self.valstr and self.cursor < len(self.valstr): + self.valstr = self.valstr[:self.cursor] + self.valstr[self.cursor+1:] + self.need_update = True + elif c == 45 and self.cursor == 0 and self.min_val < 0: + minus_place = self.valstr.find('-') + if minus_place >= 0: return + self.valstr = chr(c)+self.valstr + self.cursor += 1 + self.need_update = True + elif c == 46: + minus_place = self.valstr.find('-') + if self.cursor <= minus_place: return + point_place = self.valstr.find('.') + if point_place >= 0: return + if self.cursor == len(self.valstr): + self.valstr += chr(c) + else: + # Insert into string + self.valstr = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:] + self.need_update = True + # Move the cursor forward + self.cursor+=1 + elif (c > 47 and c < 58): + minus_place = self.valstr.find('-') + if self.cursor <= minus_place: return + if self.cursor == len(self.valstr): + self.valstr += chr(c) + else: + # Insert into string + self.valstr = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:] + self.need_update = True + # Move the cursor forward + self.cursor+=1 + + + def get_value(self): + return self.value + + def set_value(self, val): + self.value = round(float(val),self.precision) + self.valstr = self.fmt%self.value + self.cursor = len(self.valstr) + + class SelectInput(InputField): def __init__(self, parent, message, name, opts, vals, selidx): self.parent = parent @@ -225,23 +411,28 @@ class TextInput(InputField): self.opts = None self.opt_off = 0 - def render(self,screen,row,width,selected,col=1): + def render(self,screen,row,width,selected,col=1,cursor_offset=0): + if self.message: + self.parent.add_string(row,self.message,screen,col,False,True) + row += 1 if selected: if self.opts: - self.parent.add_string(row+2,self.opts[self.opt_off:],screen,col,False,True) + self.parent.add_string(row+1,self.opts[self.opt_off:],screen,col,False,True) if self.cursor > (width-3): - self.move_func(row+1,width-2) + self.move_func(row,width-2) else: - self.move_func(row+1,self.cursor+1) - self.parent.add_string(row,self.message,screen,col,False,True) + self.move_func(row,self.cursor+1+cursor_offset) slen = len(self.value)+3 if slen > width: vstr = self.value[(slen-width):] else: vstr = self.value.ljust(width-2) - self.parent.add_string(row+1,"{!black,white,bold!}%s"%vstr,screen,col,False,False) + self.parent.add_string(row,"{!black,white,bold!}%s"%vstr,screen,col,False,False) - return 3 + if self.message: + return 3 + else: + return 2 def get_value(self): return self.value diff --git a/deluge/ui/console/modes/preference_panes.py b/deluge/ui/console/modes/preference_panes.py index 572adb5a4..d30a4f978 100644 --- a/deluge/ui/console/modes/preference_panes.py +++ b/deluge/ui/console/modes/preference_panes.py @@ -33,7 +33,7 @@ # # -from deluge.ui.console.modes.input_popup import TextInput,SelectInput,CheckedInput,IntSpinInput +from deluge.ui.console.modes.input_popup import TextInput,SelectInput,CheckedInput,IntSpinInput,FloatSpinInput,CheckedPlusInput try: import curses @@ -44,12 +44,17 @@ import logging log = logging.getLogger(__name__) -class Header: +class NoInput: + def depend_skip(self): + return False + +class Header(NoInput): def __init__(self, parent, header, space_above, space_below): self.parent = parent self.header = "{!white,black,bold!}%s"%header self.space_above = space_above self.space_below = space_below + self.name = header def render(self, screen, row, width, active, offset): rows = 1 @@ -60,6 +65,24 @@ class Header: if self.space_below: rows += 1 return rows +class InfoField(NoInput): + def __init__(self,parent,label,value,name): + self.parent = parent + self.label = label + self.value = value + self.txt = "%s %s"%(label,value) + self.name = name + + def render(self, screen, row, width, active, offset): + self.parent.add_string(row,self.txt,screen,offset-1,False,True) + return 1 + + def set_value(self, v): + self.value = v + if type(v) == float: + self.txt = "%s %.2f"%(self.label,self.value) + else: + self.txt = "%s %s"%(self.label,self.value) class BasePane: def __init__(self, offset, parent, width): @@ -75,26 +98,45 @@ class BasePane: def add_config_values(self,conf_dict): for ipt in self.inputs: - if not isinstance(ipt,Header): - conf_dict[ipt.name] = ipt.get_value() + if not isinstance(ipt,NoInput): + # gross, have to special case in/out ports since they are tuples + if ipt.name in ("listen_ports_to","listen_ports_from", + "out_ports_from","out_ports_to"): + if ipt.name == "listen_ports_to": + conf_dict["listen_ports"] = (self.infrom.get_value(),self.into.get_value()) + if ipt.name == "out_ports_to": + conf_dict["outgoing_ports"] = (self.outfrom.get_value(),self.outto.get_value()) + else: + conf_dict[ipt.name] = ipt.get_value() + if hasattr(ipt,"get_child"): + c = ipt.get_child() + conf_dict[c.name] = c.get_value() def update_values(self, conf_dict): for ipt in self.inputs: - if not isinstance(ipt,Header): + if not isinstance(ipt,NoInput): try: ipt.set_value(conf_dict[ipt.name]) except KeyError: # just ignore if it's not in dict pass + if hasattr(ipt,"get_child"): + try: + c = ipt.get_child() + c.set_value(conf_dict[c.name]) + except KeyError: # just ignore if it's not in dict + pass def render(self, mode, screen, width, active): self._cursor_row = -1 if self.active_input < 0: for i,ipt in enumerate(self.inputs): - if not isinstance(ipt,Header): + if not isinstance(ipt,NoInput): self.active_input = i - break + break crow = 1 for i,ipt in enumerate(self.inputs): + if ipt.depend_skip(): + continue act = active and i==self.active_input crow += ipt.render(screen,crow,width, act, self.offset) @@ -104,6 +146,8 @@ class BasePane: else: curses.curs_set(0) + return crow + # just handles setting the active input def handle_read(self,c): if not self.inputs: # no inputs added yet @@ -111,18 +155,20 @@ class BasePane: if c == curses.KEY_UP: nc = max(0,self.active_input-1) - while isinstance(self.inputs[nc], Header): + while isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip(): nc-=1 if nc <= 0: break - if not isinstance(self.inputs[nc], Header): + if not isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip(): self.active_input = nc elif c == curses.KEY_DOWN: ilen = len(self.inputs) nc = min(self.active_input+1,ilen-1) - while isinstance(self.inputs[nc], Header): + while isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip(): nc+=1 - if nc >= ilen: break - if not isinstance(self.inputs[nc], Header): + if nc >= ilen: + nc-=1 + break + if not isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip(): self.active_input = nc else: self.inputs[self.active_input].handle_read(c) @@ -131,6 +177,9 @@ class BasePane: def add_header(self, header, space_above=False, space_below=False): self.inputs.append(Header(self.parent, header, space_above, space_below)) + def add_info_field(self, label, value, name): + self.inputs.append(InfoField(self.parent, label, value, name)) + def add_text_input(self, name, msg, dflt_val): self.inputs.append(TextInput(self.parent,self.move,self.width,msg,name,dflt_val,False)) @@ -140,9 +189,15 @@ class BasePane: def add_checked_input(self, name, message, checked): self.inputs.append(CheckedInput(self.parent,message,name,checked)) + def add_checkedplus_input(self, name, message, child, checked): + self.inputs.append(CheckedPlusInput(self.parent,message,name,child,checked)) + def add_int_spin_input(self, name, message, value, min_val, max_val): self.inputs.append(IntSpinInput(self.parent,message,name,self.move,value,min_val,max_val)) + def add_float_spin_input(self, name, message, value, inc_amt, precision, min_val, max_val): + self.inputs.append(FloatSpinInput(self.parent,message,name,self.move,value,inc_amt,precision,min_val,max_val)) + class DownloadsPane(BasePane): def __init__(self, offset, parent, width): @@ -150,13 +205,21 @@ class DownloadsPane(BasePane): self.add_header("Folders") self.add_text_input("download_location","Download To:",parent.core_config["download_location"]) - self.add_header("Allocation") + cmptxt = TextInput(self.parent,self.move,self.width,None,"move_completed_path",parent.core_config["move_completed_path"],False) + self.add_checkedplus_input("move_completed","Move completed to:",cmptxt,parent.core_config["move_completed"]) + autotxt = TextInput(self.parent,self.move,self.width,None,"autoadd_location",parent.core_config["autoadd_location"],False) + self.add_checkedplus_input("autoadd_enable","Auto add .torrents from:",autotxt,parent.core_config["autoadd_enable"]) + copytxt = TextInput(self.parent,self.move,self.width,None,"torrentfiles_location",parent.core_config["torrentfiles_location"],False) + self.add_checkedplus_input("copy_torrent_file","Copy of .torrent files to:",copytxt,parent.core_config["copy_torrent_file"]) + self.add_checked_input("del_copy_torrent_file","Delete copy of torrent file on remove",parent.core_config["del_copy_torrent_file"]) + + self.add_header("Allocation",True) if parent.core_config["compact_allocation"]: alloc_idx = 1 else: alloc_idx = 0 - self.add_select_input("compact_allocation","Allocation:",["Use Full Allocation","Use Compact Allocation"],[False,True],alloc_idx) + self.add_select_input("compact_allocation",None,["Use Full Allocation","Use Compact Allocation"],[False,True],alloc_idx) self.add_header("Options",True) self.add_checked_input("prioritize_first_last_pieces","Prioritize first and last pieces of torrent",parent.core_config["prioritize_first_last_pieces"]) self.add_checked_input("add_paused","Add torrents in paused state",parent.core_config["add_paused"]) @@ -165,35 +228,91 @@ class DownloadsPane(BasePane): class NetworkPane(BasePane): def __init__(self, offset, parent, width): BasePane.__init__(self,offset,parent,width) + self.add_header("Incomming Ports") + inrand = CheckedInput(parent,"Use Random Ports Active Port: %d"%parent.active_port,"random_port",parent.core_config["random_port"]) + self.inputs.append(inrand) + listen_ports = parent.core_config["listen_ports"] + self.infrom = IntSpinInput(self.parent," From:","listen_ports_from",self.move,listen_ports[0],0,65535) + self.infrom.set_depend(inrand,True) + self.into = IntSpinInput(self.parent," To: ","listen_ports_to",self.move,listen_ports[1],0,65535) + self.into.set_depend(inrand,True) + self.inputs.append(self.infrom) + self.inputs.append(self.into) + + + self.add_header("Outgoing Ports",True) + outrand = CheckedInput(parent,"Use Random Ports","random_outgoing_ports",parent.core_config["random_outgoing_ports"]) + self.inputs.append(outrand) + out_ports = parent.core_config["outgoing_ports"] + self.outfrom = IntSpinInput(self.parent," From:","out_ports_from",self.move,out_ports[0],0,65535) + self.outfrom.set_depend(outrand,True) + self.outto = IntSpinInput(self.parent," To: ","out_ports_to",self.move,out_ports[1],0,65535) + self.outto.set_depend(outrand,True) + self.inputs.append(self.outfrom) + self.inputs.append(self.outto) + + + self.add_header("Interface",True) + self.add_text_input("listen_interface","IP address of the interface to listen on (leave empty for default):",parent.core_config["listen_interface"]) + + self.add_header("TOS",True) + self.add_text_input("peer_tos","Peer TOS Byte:",parent.core_config["peer_tos"]) + + self.add_header("Network Extras") + self.add_checked_input("upnp","UPnP",parent.core_config["upnp"]) + self.add_checked_input("natpmp","NAT-PMP",parent.core_config["natpmp"]) + self.add_checked_input("utpex","Peer Exchange",parent.core_config["utpex"]) + self.add_checked_input("lsd","LSD",parent.core_config["lsd"]) + self.add_checked_input("dht","DHT",parent.core_config["dht"]) + + self.add_header("Encryption",True) + self.add_select_input("enc_in_policy","Inbound:",["Forced","Enabled","Disabled"],[0,1,2],parent.core_config["enc_in_policy"]) + self.add_select_input("enc_out_policy","Outbound:",["Forced","Enabled","Disabled"],[0,1,2],parent.core_config["enc_out_policy"]) + self.add_select_input("enc_level","Level:",["Handshake","Full Stream","Either"],[0,1,2],parent.core_config["enc_level"]) + self.add_checked_input("enc_prefer_rc4","Encrypt Entire Stream",parent.core_config["enc_prefer_rc4"]) + class BandwidthPane(BasePane): def __init__(self, offset, parent, width): BasePane.__init__(self,offset,parent,width) self.add_header("Global Bandwidth Usage") - self.add_int_spin_input("max_connections_global","Maximum Connections:",parent.core_config["max_connections_global"],0,1000) - self.add_int_spin_input("max_upload_slots_global","Maximum Upload Slots:",parent.core_config["max_upload_slots_global"],0,1000) - #self.add_int_spin_input("max_download_speed","Maximum Download Speed (KiB/s):",-1,0,1000) + self.add_int_spin_input("max_connections_global","Maximum Connections:",parent.core_config["max_connections_global"],-1,9000) + self.add_int_spin_input("max_upload_slots_global","Maximum Upload Slots:",parent.core_config["max_upload_slots_global"],-1,9000) + self.add_float_spin_input("max_download_speed","Maximum Download Speed (KiB/s):",parent.core_config["max_download_speed"],1.0,1,-1.0,60000.0) + self.add_float_spin_input("max_upload_speed","Maximum Upload Speed (KiB/s):",parent.core_config["max_upload_speed"],1.0,1,-1.0,60000.0) + self.add_int_spin_input("max_half_open_connections","Maximum Half-Open Connections:",parent.core_config["max_half_open_connections"],-1,9999) + self.add_int_spin_input("max_connections_per_second","Maximum Connection Attempts per Second:",parent.core_config["max_connections_per_second"],-1,9999) + self.add_checked_input("ignore_limits_on_local_network","Ignore limits on local network",parent.core_config["ignore_limits_on_local_network"]) + self.add_checked_input("rate_limit_ip_overhead","Rate Limit IP Overhead",parent.core_config["rate_limit_ip_overhead"]) + self.add_header("Per Torrent Bandwidth Usage",True) + self.add_int_spin_input("max_connections_per_torrent","Maximum Connections:",parent.core_config["max_connections_per_torrent"],-1,9000) + self.add_int_spin_input("max_upload_slots_per_torrent","Maximum Upload Slots:",parent.core_config["max_upload_slots_per_torrent"],-1,9000) + self.add_float_spin_input("max_download_speed_per_torrent","Maximum Download Speed (KiB/s):",parent.core_config["max_download_speed_per_torrent"],1.0,1,-1.0,60000.0) + self.add_float_spin_input("max_upload_speed_per_torrent","Maximum Upload Speed (KiB/s):",parent.core_config["max_upload_speed_per_torrent"],1.0,1,-1.0,60000.0) class InterfacePane(BasePane): def __init__(self, offset, parent, width): BasePane.__init__(self,offset,parent,width) - # does classic mode make sense in console? - #self.add_header("Classic Mode") - #self.add_checked_input("classic_mode","Enable",False) - + self.add_header("Interface Settings Comming Soon") # add title bar control here + class OtherPane(BasePane): def __init__(self, offset, parent, width): BasePane.__init__(self,offset,parent,width) - self.add_header("GeoIP Database") + self.add_header("System Information") + self.add_info_field(" Help us improve Deluge by sending us your","","") + self.add_info_field(" Python version, PyGTK version, OS and processor","","") + self.add_info_field(" types. Absolutely no other information is sent.","","") + self.add_checked_input("send_info","Yes, please send anonymous statistics.",parent.core_config["send_info"]) + self.add_header("GeoIP Database",True) self.add_text_input("geoip_db_location","Location:",parent.core_config["geoip_db_location"]) class DaemonPane(BasePane): def __init__(self, offset, parent, width): BasePane.__init__(self,offset,parent,width) self.add_header("Port") - self.add_int_spin_input("daemon_port","Daemon Port:",parent.core_config["daemon_port"],0,1000) + self.add_int_spin_input("daemon_port","Daemon Port:",parent.core_config["daemon_port"],0,65535) self.add_header("Connections",True) self.add_checked_input("allow_remote","Allow remote connections",parent.core_config["allow_remote"]) self.add_header("Other",True) @@ -202,11 +321,50 @@ class DaemonPane(BasePane): class QueuePane(BasePane): def __init__(self, offset, parent, width): BasePane.__init__(self,offset,parent,width) + self.add_header("General") + self.add_checked_input("queue_new_to_top","Queue new torrents to top",parent.core_config["queue_new_to_top"]) + self.add_header("Active Torrents",True) + self.add_int_spin_input("max_active_limit","Total active:",parent.core_config["max_active_limit"],-1,9999) + self.add_int_spin_input("max_active_downloading","Total active downloading:",parent.core_config["max_active_downloading"],-1,9999) + self.add_int_spin_input("max_active_seeding","Total active seeding:",parent.core_config["max_active_seeding"],-1,9999) + self.add_checked_input("dont_count_slow_torrents","Do not count slow torrents",parent.core_config["dont_count_slow_torrents"]) + self.add_header("Seeding",True) + self.add_float_spin_input("share_ratio_limit","Share Ratio Limit:",parent.core_config["share_ratio_limit"],1.0,2,-1.0,100.0) + self.add_float_spin_input("seed_time_ratio_limit","Share Time Ratio:",parent.core_config["seed_time_ratio_limit"],1.0,2,-1.0,100.0) + self.add_int_spin_input("seed_time_limit","Seed time (m):",parent.core_config["seed_time_limit"],-1,10000) + seedratio = FloatSpinInput(self.parent,"","stop_seed_ratio",self.move,parent.core_config["stop_seed_ratio"],0.1,2,0.5,100.0) + self.add_checkedplus_input("stop_seed_at_ratio","Stop seeding when share ratio reaches:",seedratio,parent.core_config["stop_seed_at_ratio"]) + self.add_checked_input("remove_seed_at_ratio","Remove torrent when share ratio reached",parent.core_config["remove_seed_at_ratio"]) class ProxyPane(BasePane): def __init__(self, offset, parent, width): BasePane.__init__(self,offset,parent,width) + self.add_header("Proxy Settings Comming Soon") class CachePane(BasePane): def __init__(self, offset, parent, width): BasePane.__init__(self,offset,parent,width) + self.add_header("Settings") + self.add_int_spin_input("cache_size","Cache Size (16 KiB blocks):",parent.core_config["cache_size"],0,99999) + self.add_int_spin_input("cache_expiry","Cache Expiry (seconds):",parent.core_config["cache_expiry"],1,32000) + self.add_header("Status (press 'r' to refresh status)",True) + self.add_header(" Write") + self.add_info_field(" Blocks Written:",self.parent.status["blocks_written"],"blocks_written") + self.add_info_field(" Writes:",self.parent.status["writes"],"writes") + self.add_info_field(" Write Cache Hit Ratio:","%.2f"%self.parent.status["write_hit_ratio"],"write_hit_ratio") + self.add_header(" Read") + self.add_info_field(" Blocks Read:",self.parent.status["blocks_read"],"blocks_read") + self.add_info_field(" Blocks Read hit:",self.parent.status["blocks_read_hit"],"blocks_read_hit") + self.add_info_field(" Reads:",self.parent.status["reads"],"reads") + self.add_info_field(" Read Cache Hit Ratio:","%.2f"%self.parent.status["read_hit_ratio"],"read_hit_ratio") + self.add_header(" Size") + self.add_info_field(" Cache Size:",self.parent.status["cache_size"],"cache_size") + self.add_info_field(" Read Cache Size:",self.parent.status["read_cache_size"],"read_cache_size") + + def update_cache_status(self, status): + for ipt in self.inputs: + if isinstance(ipt,InfoField): + try: + ipt.set_value(status[ipt.name]) + except KeyError: + pass diff --git a/deluge/ui/console/modes/preferences.py b/deluge/ui/console/modes/preferences.py index b59b49932..929101332 100644 --- a/deluge/ui/console/modes/preferences.py +++ b/deluge/ui/console/modes/preferences.py @@ -59,7 +59,7 @@ class ZONE: ACTIONS = 2 class Preferences(BaseMode): - def __init__(self, parent_mode, core_config, stdscr, encoding=None): + def __init__(self, parent_mode, core_config, active_port, status, stdscr, encoding=None): self.parent_mode = parent_mode self.categories = [_("Downloads"), _("Network"), _("Bandwidth"), _("Interface"), _("Other"), _("Daemon"), _("Queue"), _("Proxy"), @@ -70,6 +70,8 @@ class Preferences(BaseMode): self.action_input = None self.core_config = core_config + self.active_port = active_port + self.status = status self.active_zone = ZONE.CATEGORIES @@ -220,6 +222,9 @@ class Preferences(BaseMode): if self.active_zone < ZONE.CATEGORIES: self.active_zone = ZONE.ACTIONS + elif c == 114 and isinstance(self.panes[self.cur_cat],CachePane): + client.core.get_cache_status().addCallback(self.panes[self.cur_cat].update_cache_status) + else: if self.active_zone == ZONE.CATEGORIES: self.__category_read(c)