def draw_statusbars(self, top_row=0, bottom_row=-1, topbar=None, bottombar=None, bottombar_help=True, scr=None): self.add_string(top_row, topbar if topbar else self.statusbars.topbar, scr=scr) bottombar = bottombar if bottombar else self.statusbars.bottombar if bottombar_help: if bottombar_help is True: bottombar_help = self.help_hstr bottombar += ' ' * (self.cols - len(remove_formatting(bottombar)) - len(remove_formatting(bottombar_help))) + bottombar_help self.add_string(self.rows + bottom_row, bottombar, scr=scr)
def render(self, screen, row, active=False, focused=False, col=0, width=None, **kwargs): util.safe_curs_set(util.Curser.INVISIBLE ) # Make cursor invisible when text field is active fmt = self.build_fmt_string(focused, active) self.fmt_keys['msg'] = self.txt if self.fill_width: self.fmt_keys['msg'] = '' string_len = len(remove_formatting(fmt % self.fmt_keys)) fill_len = width - string_len - (len(self.txt) - 1) self.fmt_keys['msg'] = self.txt * fill_len string = fmt % self.fmt_keys self.parent.add_string(row, string, scr=screen, col=col, pad=False, trim=False) return 1
def render(self, screen, row, active=False, focused=False, col=0, width=None, **kwargs): util.safe_curs_set(util.Curser.INVISIBLE) # Make cursor invisible when text field is active fmt = self.build_fmt_string(focused, active) self.fmt_keys['msg'] = self.txt if self.fill_width: self.fmt_keys['msg'] = '' string_len = len(remove_formatting(fmt % self.fmt_keys)) fill_len = width - string_len - (len(self.txt) - 1) self.fmt_keys['msg'] = self.txt * fill_len string = fmt % self.fmt_keys self.parent.add_string(row, string, scr=screen, col=col, pad=False, trim=False) return 1
def complete_line(line, possible_matches): """Find the common prefix of possible matches. Proritizing matching-case elements. """ if not possible_matches: return line line = line.replace(r'\ ', ' ') matches1 = [] matches2 = [] for match in possible_matches: match = remove_formatting(match) match = match.replace(r'\ ', ' ') m1, m2 = '', '' for i, c in enumerate(line): if m1 and m2: break if not m1 and c != line[i]: m1 = line[:i] if not m2 and c.lower() != line[i].lower(): m2 = line[:i] if not m1: matches1.append(match) elif not m2: matches2.append(match) possible_matches = matches1 + matches2 maxlen = 9001 for match in possible_matches[1:]: for i, c in enumerate(match): try: if c.lower() != possible_matches[0][i].lower(): maxlen = min(maxlen, i) break except IndexError: maxlen = min(maxlen, i) break return possible_matches[0][:maxlen].replace(' ', r'\ ')
def complete_line(line, possible_matches): 'Find the common prefix of possible matches, proritizing matching-case elements' if not possible_matches: return line line = line.replace(r'\ ', ' ') matches1 = [] matches2 = [] for match in possible_matches: match = remove_formatting(match) match = match.replace(r'\ ', ' ') m1, m2 = '', '' for i, c in enumerate(line): if m1 and m2: break if not m1 and c != line[i]: m1 = line[:i] if not m2 and c.lower() != line[i].lower(): m2 = line[:i] if not m1: matches1.append(match) elif not m2: matches2.append(match) possible_matches = matches1 + matches2 maxlen = 9001 for match in possible_matches[1:]: for i, c in enumerate(match): try: if c.lower() != possible_matches[0][i].lower(): maxlen = min(maxlen, i) break except IndexError: maxlen = min(maxlen, i) break return possible_matches[0][:maxlen].replace(' ', r'\ ')
def tab_completer(self, line, cursor, hits): """ Called when the user hits 'tab' and will autocomplete or show options. If a command is already supplied in the line, this function will call the complete method of the command. :param line: str, the current input string :param cursor: int, the cursor position in the line :param second_hit: bool, if this is the second time in a row the tab key has been pressed :returns: 2-tuple (string, cursor position) """ # First check to see if there is no space, this will mean that it's a # command that needs to be completed. # We don't want to split by escaped spaces def split(string): return re.split(r'(?<!\\) ', string) if ' ' not in line: possible_matches = [] # Iterate through the commands looking for ones that startwith the # line. for cmd in self.console._commands: if cmd.startswith(line): possible_matches.append(cmd) line_prefix = '' else: cmd = split(line)[0] if cmd in self.console._commands: # Call the command's complete method to get 'er done possible_matches = self.console._commands[cmd].complete(split(line)[-1]) line_prefix = ' '.join(split(line)[:-1]) + ' ' else: # This is a bogus command return (line, cursor) # No matches, so just return what we got passed if len(possible_matches) == 0: return (line, cursor) # If we only have 1 possible match, then just modify the line and # return it, else we need to print out the matches without modifying # the line. elif len(possible_matches) == 1: # Do not append space after directory names new_line = line_prefix + possible_matches[0] if not new_line.endswith('/') and not new_line.endswith(r'\\'): new_line += ' ' # We only want to print eventual colors or other control characters, not return them new_line = remove_formatting(new_line) return (new_line, len(new_line)) else: if hits == 1: p = ' '.join(split(line)[:-1]) try: l_arg = split(line)[-1] except IndexError: l_arg = '' new_line = ' '.join( [p, complete_line(l_arg, possible_matches)] ).lstrip() if len(remove_formatting(new_line)) > len(line): line = new_line cursor = len(line) elif hits >= 2: max_list = self.console_config['cmdline']['torrents_per_tab_press'] match_count = len(possible_matches) listed = (hits - 2) * max_list pages = (match_count - 1) // max_list + 1 left = match_count - listed if hits == 2: self.write(' ') if match_count >= 4: self.write('{!green!}Autocompletion matches:') # Only list some of the matching torrents as there can be hundreds of them if self.console_config['cmdline']['third_tab_lists_all']: if hits == 2 and left > max_list: for i in range(listed, listed + max_list): match = possible_matches[i] self.write(match.replace(r'\ ', ' ')) self.write( '{!error!}And %i more. Press <tab> to list them' % (left - max_list) ) else: self.tab_count = 0 for match in possible_matches[listed:]: self.write(match.replace(r'\ ', ' ')) else: if left > max_list: for i in range(listed, listed + max_list): match = possible_matches[i] self.write(match.replace(r'\ ', ' ')) self.write( '{!error!}And %i more (%i/%i). Press <tab> to view more' % (left - max_list, hits - 1, pages) ) else: self.tab_count = 0 for match in possible_matches[listed:]: self.write(match.replace(r'\ ', ' ')) if hits > 2: self.write( '{!green!}Finished listing %i torrents (%i/%i)' % (match_count, hits - 1, pages) ) # We only want to print eventual colors or other control characters, not return them line = remove_formatting(line) cursor = len(line) return (line, cursor)
def __init__(self, stdscr, encoding=None): # Get a handle to the main console self.console = component.get('ConsoleUI') Commander.__init__(self, self.console._commands, interactive=True) self.batch_write = False # A list of strings to be displayed based on the offset (scroll) self.lines = [] # The offset to display lines self.display_lines_offset = 0 # Holds the user input and is cleared on 'enter' self.input = '' self.input_incomplete = '' # Keep track of where the cursor is self.input_cursor = 0 # Keep a history of inputs self.input_history = [] self.input_history_index = 0 # Keep track of double- and multi-tabs self.tab_count = 0 self.console_config = component.get('TorrentList').config # To avoid having to truncate the file every time we're writing # or doing it on exit(and therefore relying on an error-less # or in other words clean exit, we're going to have two files # that we swap around based on length config_dir = deluge.configmanager.get_config_dir() self.history_file = [ os.path.join(config_dir, 'cmd_line.hist1'), os.path.join(config_dir, 'cmd_line.hist2'), ] self._hf_lines = [0, 0] if self.console_config['cmdline']['save_command_history']: try: with open(self.history_file[0], 'r', encoding='utf8') as _file: lines1 = _file.read().splitlines() self._hf_lines[0] = len(lines1) except IOError: lines1 = [] self._hf_lines[0] = 0 try: with open(self.history_file[1], 'r', encoding='utf8') as _file: lines2 = _file.read().splitlines() self._hf_lines[1] = len(lines2) except IOError: lines2 = [] self._hf_lines[1] = 0 # The non-full file is the active one if self._hf_lines[0] > self._hf_lines[1]: self.lines = lines1 + lines2 else: self.lines = lines2 + lines1 if len(self.lines) > MAX_HISTFILE_SIZE: self.lines = self.lines[-MAX_HISTFILE_SIZE:] # Instead of having additional input history file, we can # simply scan for lines beginning with ">>> " for i, line in enumerate(self.lines): line = remove_formatting(line) if line.startswith('>>> '): console_input = line[4:] if self.console_config['cmdline']['ignore_duplicate_lines']: if len(self.input_history) > 0: if self.input_history[-1] != console_input: self.input_history.append(console_input) else: self.input_history.append(console_input) self.input_history_index = len(self.input_history) # show the cursor util.safe_curs_set(util.Curser.VERY_VISIBLE) BaseMode.__init__(self, stdscr, encoding, depend=['SessionProxy'])
def __init__(self, stdscr, encoding=None): # Get a handle to the main console self.console = component.get('ConsoleUI') Commander.__init__(self, self.console._commands, interactive=True) self.batch_write = False # A list of strings to be displayed based on the offset (scroll) self.lines = [] # The offset to display lines self.display_lines_offset = 0 # Holds the user input and is cleared on 'enter' self.input = '' self.input_incomplete = '' # Keep track of where the cursor is self.input_cursor = 0 # Keep a history of inputs self.input_history = [] self.input_history_index = 0 # Keep track of double- and multi-tabs self.tab_count = 0 self.console_config = component.get('TorrentList').config # To avoid having to truncate the file every time we're writing # or doing it on exit(and therefore relying on an error-less # or in other words clean exit, we're going to have two files # that we swap around based on length config_dir = deluge.configmanager.get_config_dir() self.history_file = [ os.path.join(config_dir, 'cmd_line.hist1'), os.path.join(config_dir, 'cmd_line.hist2') ] self._hf_lines = [0, 0] if self.console_config['cmdline']['save_command_history']: try: with open(self.history_file[0], 'r', encoding='utf8') as _file: lines1 = _file.read().splitlines() self._hf_lines[0] = len(lines1) except IOError: lines1 = [] self._hf_lines[0] = 0 try: with open(self.history_file[1], 'r', encoding='utf8') as _file: lines2 = _file.read().splitlines() self._hf_lines[1] = len(lines2) except IOError: lines2 = [] self._hf_lines[1] = 0 # The non-full file is the active one if self._hf_lines[0] > self._hf_lines[1]: self.lines = lines1 + lines2 else: self.lines = lines2 + lines1 if len(self.lines) > MAX_HISTFILE_SIZE: self.lines = self.lines[-MAX_HISTFILE_SIZE:] # Instead of having additional input history file, we can # simply scan for lines beginning with ">>> " for i, line in enumerate(self.lines): line = remove_formatting(line) if line.startswith('>>> '): console_input = line[4:] if self.console_config['cmdline']['ignore_duplicate_lines']: if len(self.input_history) > 0: if self.input_history[-1] != console_input: self.input_history.append(console_input) else: self.input_history.append(console_input) self.input_history_index = len(self.input_history) # show the cursor util.safe_curs_set(util.Curser.VERY_VISIBLE) BaseMode.__init__(self, stdscr, encoding, depend=['SessionProxy'])
def tab_completer(self, line, cursor, hits): """ Called when the user hits 'tab' and will autocomplete or show options. If a command is already supplied in the line, this function will call the complete method of the command. :param line: str, the current input string :param cursor: int, the cursor position in the line :param second_hit: bool, if this is the second time in a row the tab key has been pressed :returns: 2-tuple (string, cursor position) """ # First check to see if there is no space, this will mean that it's a # command that needs to be completed. # We don't want to split by escaped spaces def split(string): return re.split(r'(?<!\\) ', string) if ' ' not in line: possible_matches = [] # Iterate through the commands looking for ones that startwith the # line. for cmd in self.console._commands: if cmd.startswith(line): possible_matches.append(cmd) line_prefix = '' else: cmd = split(line)[0] if cmd in self.console._commands: # Call the command's complete method to get 'er done possible_matches = self.console._commands[cmd].complete(split(line)[-1]) line_prefix = ' '.join(split(line)[:-1]) + ' ' else: # This is a bogus command return (line, cursor) # No matches, so just return what we got passed if len(possible_matches) == 0: return (line, cursor) # If we only have 1 possible match, then just modify the line and # return it, else we need to print out the matches without modifying # the line. elif len(possible_matches) == 1: # Do not append space after directory names new_line = line_prefix + possible_matches[0] if not new_line.endswith('/') and not new_line.endswith(r'\\'): new_line += ' ' # We only want to print eventual colors or other control characters, not return them new_line = remove_formatting(new_line) return (new_line, len(new_line)) else: if hits == 1: p = ' '.join(split(line)[:-1]) try: l_arg = split(line)[-1] except IndexError: l_arg = '' new_line = ' '.join([p, complete_line(l_arg, possible_matches)]).lstrip() if len(remove_formatting(new_line)) > len(line): line = new_line cursor = len(line) elif hits >= 2: max_list = self.console_config['cmdline']['torrents_per_tab_press'] match_count = len(possible_matches) listed = (hits - 2) * max_list pages = (match_count - 1) // max_list + 1 left = match_count - listed if hits == 2: self.write(' ') if match_count >= 4: self.write('{!green!}Autocompletion matches:') # Only list some of the matching torrents as there can be hundreds of them if self.console_config['cmdline']['third_tab_lists_all']: if hits == 2 and left > max_list: for i in range(listed, listed + max_list): match = possible_matches[i] self.write(match.replace(r'\ ', ' ')) self.write('{!error!}And %i more. Press <tab> to list them' % (left - max_list)) else: self.tab_count = 0 for match in possible_matches[listed:]: self.write(match.replace(r'\ ', ' ')) else: if left > max_list: for i in range(listed, listed + max_list): match = possible_matches[i] self.write(match.replace(r'\ ', ' ')) self.write('{!error!}And %i more (%i/%i). Press <tab> to view more' % ( left - max_list, hits - 1, pages)) else: self.tab_count = 0 for match in possible_matches[listed:]: self.write(match.replace(r'\ ', ' ')) if hits > 2: self.write('{!green!}Finished listing %i torrents (%i/%i)' % (match_count, hits - 1, pages)) # We only want to print eventual colors or other control characters, not return them line = remove_formatting(line) cursor = len(line) return (line, cursor)
def tlen(string): return strwidth(remove_formatting(string))