def _input_filter(self, keys, raw): """ handles keypresses. This function gets triggered directly by class:`urwid.MainLoop` upon user input and is supposed to pass on its `keys` parameter to let the root widget handle keys. We intercept the input here to trigger custom commands as defined in our keybindings. """ logging.debug("Got key (%s, %s)" % (keys, raw)) # work around: escape triggers this twice, with keys = raw = [] # the first time.. if not keys: return # let widgets handle input if key is virtual window resize keypress # or we are in "passall" mode elif 'window resize' in keys or self._passall: return keys # end "lockdown" mode if the right key was pressed elif self._locked and keys[0] == self._unlock_key: self._locked = False self.mainloop.widget = self.root_widget if callable(self._unlock_callback): self._unlock_callback() # otherwise interpret keybinding else: # define callback that resets input queue def clear(*args): if self._alarm is not None: self.mainloop.remove_alarm(self._alarm) self.input_queue = [] self.update() key = keys[0] self.input_queue.append(key) keyseq = ' '.join(self.input_queue) candidates = settings.get_mapped_input_keysequences(self.mode, prefix=keyseq) if keyseq in candidates: # case: current input queue is a mapped keysequence # get binding and interpret it if non-null cmdline = settings.get_keybinding(self.mode, keyseq) if cmdline: clear() logging.debug("cmdline: '%s'" % cmdline) # move keys are always passed if cmdline.startswith('move '): movecmd = cmdline[5:].rstrip() logging.debug("GOT MOVE: '%s'" % movecmd) if movecmd in ['up', 'down', 'page up', 'page down']: return [movecmd] elif not self._locked: try: self.apply_commandline(cmdline) except CommandParseError, e: self.notify(e.message, priority='error') elif not candidates: # case: no sequence with prefix keyseq is mapped # just clear the input queue clear()
def _input_filter(self, keys, raw): """ handles keypresses. This function gets triggered directly by class:`urwid.MainLoop` upon user input and is supposed to pass on its `keys` parameter to let the root widget handle keys. We intercept the input here to trigger custom commands as defined in our keybindings. """ logging.debug("Got key (%s, %s)" % (keys, raw)) # work around: escape triggers this twice, with keys = raw = [] # the first time.. if not keys: return # let widgets handle input if key is virtual window resize keypress # or we are in "passall" mode elif 'window resize' in keys or self._passall: return keys # end "lockdown" mode if the right key was pressed elif self._locked and keys[0] == self._unlock_key: self._locked = False self.mainloop.widget = self.root_widget if callable(self._unlock_callback): self._unlock_callback() # otherwise interpret keybinding else: # define callback that resets input queue def clear(*args): if self._alarm is not None: self.mainloop.remove_alarm(self._alarm) self.input_queue = [] self.update() key = keys[0] self.input_queue.append(key) keyseq = ' '.join(self.input_queue) cmdline = settings.get_keybinding(self.mode, keyseq) if cmdline: clear() logging.debug("cmdline: '%s'" % cmdline) # move keys are always passed if cmdline.startswith('move '): movecmd = cmdline[5:].rstrip() logging.debug("GOT MOVE: '%s'" % movecmd) if movecmd in ['up', 'down', 'page up', 'page down']: return [movecmd] elif not self._locked: try: self.apply_commandline(cmdline) except CommandParseError, e: self.notify(e.message, priority='error') timeout = float(settings.get('input_timeout')) if self._alarm is not None: self.mainloop.remove_alarm(self._alarm) self._alarm = self.mainloop.set_alarm_in(timeout, clear) # update statusbar self.update()
def keypress(self, size, key): """overwrites `urwid.WidgetWrap.keypress`""" mode = self.ui.mode if self.select_cancel_only: mode = 'global' cmdline = settings.get_keybinding(mode, key) if cmdline: try: cmd = commandfactory(cmdline, mode) if self.allowed_command(cmd): self.ui.apply_command(cmd) return None except CommandParseError, e: self.ui.notify(e.message, priority='error')
class UI(object): """ This class integrates all components of alot and offers methods for user interaction like :meth:`prompt`, :meth:`notify` etc. It handles the urwid widget tree and mainloop (we use twisted) and is responsible for opening, closing and focussing buffers. """ def __init__(self, dbman, initialcmdline): """ :param dbman: :class:`~alot.db.DBManager` :param initialcmdline: commandline applied after setting up interface :type initialcmdline: str :param colourmode: determines which theme to chose :type colourmode: int in [1,16,256] """ self.dbman = dbman """Database Manager (:class:`~alot.db.manager.DBManager`)""" self.buffers = [] """list of active buffers""" self.current_buffer = None """points to currently active :class:`~alot.buffers.Buffer`""" self.db_was_locked = False """flag used to prevent multiple 'index locked' notifications""" self.mode = 'global' """interface mode identifier - type of current buffer""" self.commandprompthistory = [] """history of the command line prompt""" self.input_queue = [] """stores partial keyboard input""" self.last_commandline = None """saves the last executed commandline""" # define empty notification pile self._notificationbar = None # should we show a status bar? self._show_statusbar = settings.get('show_statusbar') # pass keypresses to the root widget and never interpret bindings self._passall = False # indicates "input lock": only focus move commands are interpreted self._locked = False self._unlock_callback = None # will be called after input lock ended self._unlock_key = None # key that ends input lock # alarm handle for callback that clears input queue (to cancel alarm) self._alarm = None # create root widget global_att = settings.get_theming_attribute('global', 'body') mainframe = urwid.Frame(urwid.SolidFill()) self.root_widget = urwid.AttrMap(mainframe, global_att) # set up main loop self.mainloop = urwid.MainLoop(self.root_widget, handle_mouse=False, event_loop=urwid.TwistedEventLoop(), unhandled_input=self._unhandeled_input, input_filter=self._input_filter) # set up colours colourmode = int(settings.get('colourmode')) logging.info('setup gui in %d colours' % colourmode) self.mainloop.screen.set_terminal_properties(colors=colourmode) logging.debug('fire first command') self.apply_commandline(initialcmdline) # start urwids mainloop self.mainloop.run() def _input_filter(self, keys, raw): """ handles keypresses. This function gets triggered directly by class:`urwid.MainLoop` upon user input and is supposed to pass on its `keys` parameter to let the root widget handle keys. We intercept the input here to trigger custom commands as defined in our keybindings. """ logging.debug("Got key (%s, %s)" % (keys, raw)) # work around: escape triggers this twice, with keys = raw = [] # the first time.. if not keys: return # let widgets handle input if key is virtual window resize keypress # or we are in "passall" mode elif 'window resize' in keys or self._passall: return keys # end "lockdown" mode if the right key was pressed elif self._locked and keys[0] == self._unlock_key: self._locked = False self.mainloop.widget = self.root_widget if callable(self._unlock_callback): self._unlock_callback() # otherwise interpret keybinding else: # define callback that resets input queue def clear(*args): if self._alarm is not None: self.mainloop.remove_alarm(self._alarm) self.input_queue = [] def fire(ignored, cmdline): clear() logging.debug("cmdline: '%s'" % cmdline) if not self._locked: try: self.apply_commandline(cmdline) except CommandParseError, e: self.notify(e.message, priority='error') # move keys are always passed elif cmdline in [ 'move up', 'move down', 'move page up', 'move page down' ]: return [cmdline[5:]] key = keys[0] self.input_queue.append(key) keyseq = ' '.join(self.input_queue) candidates = settings.get_mapped_input_keysequences(self.mode, prefix=keyseq) if keyseq in candidates: # case: current input queue is a mapped keysequence # get binding and interpret it if non-null cmdline = settings.get_keybinding(self.mode, keyseq) if cmdline: if len(candidates) > 1: timeout = float(settings.get('input_timeout')) if self._alarm is not None: self.mainloop.remove_alarm(self._alarm) self._alarm = self.mainloop.set_alarm_in( timeout, fire, cmdline) else: return fire(self.mainloop, cmdline) elif not candidates: # case: no sequence with prefix keyseq is mapped # just clear the input queue clear() else: # case: some sequences with proper prefix keyseq is mapped timeout = float(settings.get('input_timeout')) if self._alarm is not None: self.mainloop.remove_alarm(self._alarm) self._alarm = self.mainloop.set_alarm_in(timeout, clear) # update statusbar self.update()