diff options
-rw-r--r-- | doc/print_keys.py | 14 | ||||
-rw-r--r-- | ranger/api/keys.py | 1 | ||||
-rw-r--r-- | ranger/defaults/commands.py | 41 | ||||
-rw-r--r-- | ranger/defaults/options.py | 3 | ||||
-rw-r--r-- | ranger/ext/command_parser.py | 16 | ||||
-rw-r--r-- | ranger/ext/shutil_generatorized.py | 12 | ||||
-rw-r--r-- | ranger/gui/ui.py | 33 | ||||
-rw-r--r-- | ranger/shared/settings.py | 22 |
8 files changed, 121 insertions, 21 deletions
diff --git a/doc/print_keys.py b/doc/print_keys.py new file mode 100644 index 00000000..0790acab --- /dev/null +++ b/doc/print_keys.py @@ -0,0 +1,14 @@ +#!/usr/bin/python +""" +You can use this tool to find out values of keypresses +""" + +from curses import * + +sep = '; ' + +@wrapper +def main(w): + while True: + w.addstr(str(w.getch()) + sep) + diff --git a/ranger/api/keys.py b/ranger/api/keys.py index 97e07ae5..d7a688b9 100644 --- a/ranger/api/keys.py +++ b/ranger/api/keys.py @@ -76,7 +76,6 @@ def narg(number_, function_, *args_, **keywords_): narg(50, foo, 123) == foo(123, narg=50) """ args, keywords = replace_narg(number_, function_, args_, keywords_) - print(args, keywords) return function_(*args, **keywords) def replace_narg(number, function, args, keywords): diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index 89b1a3cb..c5c47340 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -58,7 +58,7 @@ class Command(FileManagerAware): rel_dirname = dirname(rel_dest) try: - # are we after a directory? + # are we at the end of a directory? if rel_dest.endswith('/') or rel_dest == '': _, dirnames, _ = os.walk(abs_dest).next() @@ -107,7 +107,7 @@ class Command(FileManagerAware): rel_dirname = dirname(rel_dest) try: - # are we after a directory? + # are we at the end of a directory? if rel_dest.endswith('/') or rel_dest == '': _, dirnames, filenames = os.walk(abs_dest).next() names = dirnames + filenames @@ -236,6 +236,43 @@ class find(Command): return self.count == 1 +class set(Command): + """ + :set <option name>=<python expression> + + Gives an option a new value. + """ + def execute(self): + line = parse(self.line) + name = line.chunk(1) + name, value, _ = line.parse_setting_line() + if name and value: + try: + value = eval(value) + except: + pass + self.fm.settings[name] = value + + def tab(self): + line = parse(self.line) + from ranger import log + log(line.parse_setting_line()) + name, value, name_done = line.parse_setting_line() + settings = self.fm.settings + if not name: + return (line + setting for setting in settings) + if not value and not name_done: + return (line + setting for setting in settings \ + if setting.startswith(name)) + if not value: + return line + repr(settings[name]) + if bool in settings.types_of(name): + if 'true'.startswith(value.lower()): + return line + 'True' + if 'false'.startswith(value.lower()): + return line + 'False' + + class quit(Command): """ :quit diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 6b2a0dc9..d96955b7 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -61,6 +61,9 @@ draw_bookmark_borders = True # How many columns are there, and what are their relative widths? column_ratios = (1, 1, 4, 3) +# Enable the mouse support? +mouse_enabled = True + # Display the file size in the main column or status bar? display_size_in_main_column = True display_size_in_status_bar = False diff --git a/ranger/ext/command_parser.py b/ranger/ext/command_parser.py index a6971631..3a676e8f 100644 --- a/ranger/ext/command_parser.py +++ b/ranger/ext/command_parser.py @@ -13,12 +13,16 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import re +SETTINGS_RE = re.compile(r'^([^\s]+?)=(.*)$') + class LazyParser(object): """Parse commands and extract information""" def __init__(self, line): self.line = line self._chunks = None self._rests = None + self._setting_line = None self._rests_loaded = 0 self._rests_gen_instance = None @@ -62,5 +66,17 @@ class LazyParser(object): lastrest = lastrest[n:] n = 0 + def parse_setting_line(self): + if self._setting_line is not None: + return self._setting_line + match = SETTINGS_RE.match(self.rest(1)) + if match: + self.firstpart += match.group(1) + '=' + result = [match.group(1), match.group(2), True] + else: + result = [self.chunk(1), self.rest(2), ' ' in self.rest(1)] + self._setting_line = result + return result + def __add__(self, newpart): return self.firstpart + newpart diff --git a/ranger/ext/shutil_generatorized.py b/ranger/ext/shutil_generatorized.py index 8bf07ace..ca6be426 100644 --- a/ranger/ext/shutil_generatorized.py +++ b/ranger/ext/shutil_generatorized.py @@ -83,18 +83,22 @@ def copymode(src, dst): if hasattr(os, 'chmod'): st = os.stat(src) mode = stat.S_IMODE(st.st_mode) - os.chmod(dst, mode) + try: os.chmod(dst, mode) + except: pass def copystat(src, dst): """Copy all stat info (mode bits, atime, mtime, flags) from src to dst""" st = os.stat(src) mode = stat.S_IMODE(st.st_mode) if hasattr(os, 'utime'): - os.utime(dst, (st.st_atime, st.st_mtime)) + try: os.utime(dst, (st.st_atime, st.st_mtime)) + except: pass if hasattr(os, 'chmod'): - os.chmod(dst, mode) + try: os.chmod(dst, mode) + except: pass if hasattr(os, 'chflags') and hasattr(st, 'st_flags'): - os.chflags(dst, st.st_flags) + try: os.chflags(dst, st.st_flags) + except: pass def copy(src, dst, overwrite=False): diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index f81f1c5d..41acb39f 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -27,9 +27,25 @@ TERMINALS_WITH_TITLE = ("xterm", "xterm-256color", "rxvt", "rxvt-256color", "rxvt-unicode", "aterm", "Eterm", "screen", "screen-256color") +MOUSEMASK = curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION + +def _setup_mouse(signal): + if signal['value']: + curses.mousemask(MOUSEMASK) + curses.mouseinterval(0) + + ## this line solves this problem: + ## If an action, following a mouse click, includes the + ## suspension and re-initializion of the ui (e.g. running a + ## file by clicking on its preview) and the next key is another + ## mouse click, the bstate of this mouse event will be invalid. + ## (atm, invalid bstates are recognized as scroll-down) + curses.ungetmouse(0,0,0,0,0) + else: + curses.mousemask(0) + class UI(DisplayableContainer): is_set_up = False - mousemask = curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION load_mode = False def __init__(self, env=None, fm=None): self._draw_title = os.environ["TERM"] in TERMINALS_WITH_TITLE @@ -62,16 +78,8 @@ class UI(DisplayableContainer): curses.start_color() curses.use_default_colors() - curses.mousemask(self.mousemask) - curses.mouseinterval(0) - - ## this line solves this problem: - ## If an action, following a mouse click, includes the - ## suspension and re-initializion of the ui (e.g. running a - ## file by clicking on its preview) and the next key is another - ## mouse click, the bstate of this mouse event will be invalid. - ## (atm, invalid bstates are recognized as scroll-down) - curses.ungetmouse(0,0,0,0,0) + self.settings.signal_bind('setopt.mouse_enabled', _setup_mouse) + _setup_mouse(dict(value=self.settings.mouse_enabled)) if not self.is_set_up: self.is_set_up = True @@ -87,7 +95,8 @@ class UI(DisplayableContainer): curses.curs_set(1) except: pass - curses.mousemask(0) + if self.settings.mouse_enabled: + _setup_mouse(dict(value=False)) curses.endwin() def set_load_mode(self, boolean): diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py index 4c01f570..e7098792 100644 --- a/ranger/shared/settings.py +++ b/ranger/shared/settings.py @@ -39,6 +39,7 @@ ALLOWED_SETTINGS = { 'scroll_offset': int, 'preview_files': bool, 'preview_directories': bool, + 'mouse_enabled': bool, 'flushinput': bool, 'colorscheme': str, 'colorscheme_overlay': (type(None), type(lambda:0)), @@ -57,6 +58,9 @@ class SettingObject(SignalDispatcher): SignalDispatcher.__init__(self) self.__dict__['_settings'] = dict() self.__dict__['_setting_sources'] = list() + for name in ALLOWED_SETTINGS: + self.signal_bind('setopt.'+name, + self._raw_set_with_signal, priority=0.2) def __setattr__(self, name, value): if name[0] == '_': @@ -66,8 +70,6 @@ class SettingObject(SignalDispatcher): assert self._check_type(name, value) kws = dict(setting=name, value=value, previous=self._settings[name]) - self.signal_bind('setopt.'+name, - self._raw_set_with_signal, priority=0.2) self.signal_emit('setopt', **kws) self.signal_emit('setopt.'+name, **kws) @@ -89,6 +91,22 @@ class SettingObject(SignalDispatcher): self.__setattr__(name, value) return self._settings[name] + def __iter__(self): + for x in self._settings: + yield x + + def types_of(self, name): + try: + typ = ALLOWED_SETTINGS[name] + except KeyError: + return tuple() + else: + if isinstance(typ, tuple): + return typ + else: + return (typ, ) + + def _check_type(self, name, value): from inspect import isfunction typ = ALLOWED_SETTINGS[name] |