Example #1
0
    def build_help(self):
        """ Fetch all key bindings and build help message """
        self.bindings = {}
        self.help_msg = []
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text('\n Key bindings \n'), 'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        for binding in self.config.parser.items('keybindings'):
            self.bindings[binding[0]] = binding[1]
            line = urwid.AttrWrap(
                urwid.Text(' %s: %s ' %
                           (binding[1], binding[0].replace('_', ' '))), 'help')
            self.help_msg.append(line)
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(
            urwid.AttrWrap(
                urwid.Text(' Thanks for using Pyhn %s! ' % VERSION,
                           align='center'), 'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text(' Author : socketubs '), 'help'))
        self.help_msg.append(
            urwid.AttrWrap(
                urwid.Text(' Code   : https://github.com/socketubs/pyhn '),
                'help'))
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text(' Website: http://socketubs.org '),
                           'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))

        self.help = Popup(self.help_msg, ('help', 'help'), (0, 1), self.view)
Example #2
0
    def build_help(self):
        """ Fetch all key bindings and build help message """
        self.bindings = {}
        self.help_msg = []
        self.help_msg.append(urwid.AttrWrap(urwid.Text('\n Key bindings \n'), 'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        for binding in self.config.parser.items('keybindings'):
            self.bindings[binding[0]] = binding[1]
            line = urwid.AttrWrap(
                urwid.Text(' %s: %s ' % (binding[1], binding[0].replace('_', ' '))),
                'help')
            self.help_msg.append(line)
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(
            urwid.Text(' Thanks for using Pyhn %s! ' % VERSION, align='center'),
            'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(' Author : socketubs '), 'help'))
        self.help_msg.append(urwid.AttrWrap(
            urwid.Text(' Code   : https://github.com/socketubs/pyhn '),
            'help'))
        self.help_msg.append(urwid.AttrWrap(
            urwid.Text(' Website: http://socketubs.org '),
            'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))

        self.help = Popup(self.help_msg, ('help', 'help'), (0, 1), self.view)
Example #3
0
    def build_help(self):
        """ Fetch all key bindings and build help message """
        self.bindings = {}
        self.help_msg = []
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text('\n Key bindings \n'), 'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        for binding in self.config.parser.items('keybindings'):
            self.bindings[binding[0]] = binding[1]
            line = urwid.AttrWrap(
                urwid.Text(' %s: %s ' %
                           (binding[1], binding[0].replace('_', ' '))), 'help')
            self.help_msg.append(line)
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text(' ctrl mouse-left: open story link'),
                           'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(
            urwid.AttrWrap(
                urwid.Text(' Thanks for using Pyhn %s! ' % __version__,
                           align='center'), 'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text(' Author : toxinu'), 'help'))
        self.help_msg.append(
            urwid.AttrWrap(
                urwid.Text(' Code   : https://github.com/toxinu/pyhn '),
                'help'))
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text(' Website: http://toxinu.github.io '),
                           'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))

        self.help = Popup(self.help_msg, ('help', 'help'), (0, 1), self.view)
Example #4
0
File: gui.py Project: toxinu/pyhn
    def build_help(self):
        """ Fetch all key bindings and build help message """
        self.bindings = {}
        self.help_msg = []
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text('\n Key bindings \n'), 'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        for binding in self.config.parser.items('keybindings'):
            self.bindings[binding[0]] = binding[1]
            line = urwid.AttrWrap(
                urwid.Text(
                    ' %s: %s ' % (binding[1], binding[0].replace('_', ' '))),
                'help')
            self.help_msg.append(line)
        self.help_msg.append(urwid.AttrWrap(
            urwid.Text(' ctrl mouse-left: open story link'), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(
            urwid.Text(
                ' Thanks for using Pyhn %s! ' % __version__, align='center'),
            'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text(
                ' Author : toxinu (Geoffrey Lehée)'), 'help'))
        self.help_msg.append(urwid.AttrWrap(
            urwid.Text(' Code   : https://github.com/toxinu/pyhn '),
            'help'))
        self.help_msg.append(urwid.AttrWrap(
            urwid.Text(' Website: http://toxi.nu '),
            'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))

        self.help = Popup(self.help_msg, ('help', 'help'), (0, 1), self.view)
Example #5
0
class HNGui(object):
    """ The Pyhn Gui object """
    def __init__(self, cache_manager):
        self.cache_manager = cache_manager
        self.already_build = False
        self.on_comments = False
        self.which = "top"

        self.config = Config()
        self.poller = Poller(
            self,
            delay=int(self.config.parser.get('settings', 'refresh_interval')))
        self.palette = self.config.get_palette()
        self.show_comments = self.config.parser.get(
            'interface', 'show_comments') in TRUE_WORDS
        self.show_score = self.config.parser.get('interface',
                                                 'show_score') in TRUE_WORDS
        self.show_published_time = self.config.parser.get(
            'interface', 'show_published_time') in TRUE_WORDS

    def main(self):
        """
        Main Gui function which create Ui object,
        build interface and run the loop
        """
        self.ui = urwid.raw_display.Screen()
        self.ui.register_palette(self.palette)
        self.build_interface()
        self.ui.run_wrapper(self.run)

    def build_help(self):
        """ Fetch all key bindings and build help message """
        self.bindings = {}
        self.help_msg = []
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text('\n Key bindings \n'), 'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        for binding in self.config.parser.items('keybindings'):
            self.bindings[binding[0]] = binding[1]
            line = urwid.AttrWrap(
                urwid.Text(' %s: %s ' %
                           (binding[1], binding[0].replace('_', ' '))), 'help')
            self.help_msg.append(line)
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text(' ctrl mouse-left: open story link'),
                           'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(
            urwid.AttrWrap(
                urwid.Text(' Thanks for using Pyhn %s! ' % __version__,
                           align='center'), 'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text(' Author : toxinu'), 'help'))
        self.help_msg.append(
            urwid.AttrWrap(
                urwid.Text(' Code   : https://github.com/toxinu/pyhn '),
                'help'))
        self.help_msg.append(
            urwid.AttrWrap(urwid.Text(' Website: http://toxinu.github.io '),
                           'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))

        self.help = Popup(self.help_msg, ('help', 'help'), (0, 1), self.view)

    def build_interface(self):
        """
        Build interface, refresh cache if needed, update stories listbox,
        create header, footer, view and the loop.
        """
        if self.cache_manager.is_outdated():
            self.cache_manager.refresh()

        self.stories = self.cache_manager.get_stories()
        self.update_stories(self.stories)
        self.header_content = [
            ('fixed', 4,
             urwid.Padding(urwid.AttrWrap(urwid.Text(' N°'), 'header'))),
            urwid.AttrWrap(urwid.Text('TOP STORIES', align="center"), 'title'),
        ]
        if self.show_published_time:
            self.header_content.append(
                ('fixed', 15,
                 urwid.Padding(
                     urwid.AttrWrap(urwid.Text('PUBLISHED TIME'),
                                    'header'))), )
        if self.show_score:
            self.header_content.append(
                ('fixed', 5,
                 urwid.Padding(urwid.AttrWrap(urwid.Text('SCORE'),
                                              'header'))), )
        if self.show_comments:
            self.header_content.append(
                ('fixed', 8,
                 urwid.Padding(urwid.AttrWrap(urwid.Text('COMMENTS'),
                                              'header'))))
        self.header = urwid.Columns(self.header_content, dividechars=1)
        self.footer = urwid.AttrMap(
            urwid.Text(
                'Welcome in pyhn by toxinu '
                '(https://github.com/toxinu/pyhn)',
                align='center'), 'footer')

        self.view = urwid.Frame(urwid.AttrWrap(self.listbox, 'body'),
                                header=self.header,
                                footer=self.footer)
        self.loop = urwid.MainLoop(self.view,
                                   self.palette,
                                   screen=self.ui,
                                   handle_mouse=True,
                                   unhandled_input=self.keystroke)

        self.build_help()
        self.already_build = True

    def set_help(self):
        """ Set help msg in footer """
        self.view.set_footer(
            urwid.AttrWrap(urwid.Text(self.help, align="center"), 'help'))

    def set_footer(self, msg, style="normal"):
        """ Set centered footer message """
        if style == "normal":
            self.footer = urwid.AttrWrap(urwid.Text(msg), 'footer')
            self.view.set_footer(self.footer)
        elif style == "error":
            self.footer = urwid.AttrWrap(urwid.Text(msg), 'footer-error')
            self.view.set_footer(self.footer)

    def set_header(self, msg):
        """ Set header story message """
        self.header_content[1] = urwid.AttrWrap(
            urwid.Text(msg, align="center"), 'title')
        self.view.set_header(urwid.Columns(self.header_content, dividechars=1))

    def keystroke(self, input):
        """ All key bindings are computed here """
        # QUIT
        if input in ('q', 'Q'):
            self.exit(must_raise=True)
        # LINKS
        if input in self.bindings['open_comments_link'].split(','):
            if not self.listbox.get_focus()[0].comments_url:
                self.set_footer('No comments')
            else:
                if not self.on_comments:
                    self.show_comments(self.listbox.get_focus()[0])
                    self.on_comments = True
                else:
                    self.update_stories(
                        self.cache_manager.get_stories(self.which))
                    self.on_comments = False
                self.open_webbrowser(self.listbox.get_focus()[0].comments_url)
        if input in self.bindings['show_comments_link'].split(','):
            if not self.listbox.get_focus()[0].comments_url:
                self.set_footer('No comments')
            else:
                self.set_footer(self.listbox.get_focus()[0].comments_url)
        if input in self.bindings['open_story_link'].split(','):
            self.open_webbrowser(self.listbox.get_focus()[0].url)
        if input in self.bindings['show_story_link'].split(','):
            self.set_footer(self.listbox.get_focus()[0].url)
        if input in self.bindings['open_submitter_link'].split(','):
            if not self.listbox.get_focus()[0].submitter_url:
                self.set_footer('No submitter')
            else:
                self.open_webbrowser(self.listbox.get_focus()[0].submitter_url)
        if input in self.bindings['show_submitter_link'].split(','):
            if not self.listbox.get_focus()[0].submitter_url:
                self.set_footer('No submitter')
            else:
                self.set_footer(self.listbox.get_focus()[0].submitter_url)
        # MOVEMENTS
        if input in self.bindings['down'].split(','):
            if self.listbox.focus_position - 1 in self.walker.positions():
                self.listbox.set_focus(
                    self.walker.prev_position(self.listbox.focus_position))
        if input in self.bindings['up'].split(','):
            if self.listbox.focus_position + 1 in self.walker.positions():
                self.listbox.set_focus(
                    self.walker.next_position(self.listbox.focus_position))
        if input in self.bindings['page_up'].split(','):
            self.listbox._keypress_page_up(self.ui.get_cols_rows())
        if input in self.bindings['page_down'].split(','):
            self.listbox._keypress_page_down(self.ui.get_cols_rows())
        if input in self.bindings['first_story'].split(','):
            self.listbox.set_focus(self.walker.positions()[0])
        if input in self.bindings['last_story'].split(','):
            self.listbox.set_focus(self.walker.positions()[-1])
        # STORIES
        if input in self.bindings['newest_stories'].split(','):
            self.set_footer('Syncing newest stories...')
            threading.Thread(None, self.async_refresher, None,
                             ('newest', 'NEWEST STORIES'), {}).start()
        if input in self.bindings['top_stories'].split(','):
            self.set_footer('Syncing top stories...')
            threading.Thread(None, self.async_refresher, None,
                             ('top', 'TOP STORIES'), {}).start()
        if input in self.bindings['best_stories'].split(','):
            self.set_footer('Syncing best stories...')
            threading.Thread(None, self.async_refresher, None,
                             ('best', 'BEST STORIES'), {}).start()
        if input in self.bindings['show_stories'].split(','):
            self.set_footer('Syncing show stories...')
            threading.Thread(None, self.async_refresher, None,
                             ('show', 'SHOW STORIES'), {}).start()
        if input in self.bindings['show_newest_stories'].split(','):
            self.set_footer('Syncing show newest stories...')
            threading.Thread(None, self.async_refresher, None,
                             ('show_newest', 'SHOW NEWEST STORIES'),
                             {}).start()
        if input in self.bindings['ask_stories'].split(','):
            self.set_footer('Syncing ask stories...')
            threading.Thread(None, self.async_refresher, None,
                             ('ask', 'ASK STORIES'), {}).start()
        if input in self.bindings['jobs_stories'].split(','):
            self.set_footer('Syncing jobs stories...')
            threading.Thread(None, self.async_refresher, None,
                             ('jobs', 'JOBS STORIES'), {}).start()
        # OTHERS
        if input in self.bindings['refresh'].split(','):
            self.set_footer('Refreshing new stories...')
            threading.Thread(None, self.async_refresher, None, (), {
                'force': True
            }).start()
        if input in self.bindings['reload_config'].split(','):
            self.reload_config()
        if input in ('h', 'H', '?'):
            keys = True
            while True:
                if keys:
                    self.ui.draw_screen(
                        self.ui.get_cols_rows(),
                        self.help.render(self.ui.get_cols_rows(), True))
                    keys = self.ui.get_input()
                    if 'h' or 'H' or '?' or 'escape' in keys:
                        break
        # MOUSE
        if len(input) > 1 and input[0] == 'ctrl mouse release':
            self.open_webbrowser(self.listbox.get_focus()[0].url)

    def async_refresher(self, which=None, header=None, force=False):
        if which is None:
            which = self.which
        if self.cache_manager.is_outdated(which) or force:
            self.cache_manager.refresh(which)
        stories = self.cache_manager.get_stories(which)
        self.update_stories(stories)
        if header is not None:
            self.set_header(header)
            self.which = which
        self.loop.draw_screen()

    def update_stories(self, stories):
        """ Reload listbox and walker with new stories """
        items = []
        item_ids = []
        for story in stories:
            if story.id is not None and story.id in item_ids:
                story.title = "- %s" % story.title
                items.append(
                    ItemWidget(story, self.show_published_time,
                               self.show_score, self.show_comments))
            else:
                items.append(
                    ItemWidget(story, self.show_published_time,
                               self.show_score, self.show_comments))
            item_ids.append(story.id)

        if self.already_build:
            self.walker[:] = items
            self.update()
        else:
            self.walker = urwid.SimpleListWalker(items)
            self.listbox = urwid.ListBox(self.walker)

    def show_comments(self, story):
        pass

    def open_webbrowser(self, url):
        """ Handle url and open sub process with web browser """
        if self.config.parser.get('settings', 'browser_cmd') == "__default__":
            python_bin = sys.executable
            subprocess.Popen([python_bin, '-m', 'webbrowser', '-t', url],
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        else:
            cmd = self.config.parser.get('settings', 'browser_cmd')
            try:
                p = subprocess.Popen(cmd.replace('__url__', url),
                                     shell=True,
                                     close_fds=True,
                                     stderr=subprocess.PIPE)

                returncode = p.wait()
            except KeyboardInterrupt:
                stderr = "User keyboard interrupt detected!"
                self.set_footer(stderr, style="error")
                return
            if returncode > 0:
                stderr = p.communicate()[1]
                self.set_footer("%s" % stderr, style="error")

    def update(self):
        """ Update footer about focus story """
        focus = self.listbox.get_focus()[0]
        if not focus.submitter:
            msg = "submitted %s" % focus.published_time
        else:
            msg = "submitted %s by %s" % (focus.published_time,
                                          focus.submitter)

        self.set_footer(msg)

    def reload_config(self):
        """
        Create new Config object, reload colors, refresh cache
        if needed and redraw screen.
        """
        self.set_footer('Reloading configuration')
        self.config = Config()
        self.build_help()
        self.palette = self.config.get_palette()
        self.build_interface()
        self.loop.draw_screen()
        self.set_footer('Configuration file reloaded!')

        if self.config.parser.get('settings',
                                  'cache') != self.cache_manager.cache_path:
            self.cache_manager.cache_path = self.config.parser.get(
                'settings', 'cache')

    def exit(self, must_raise=False):
        self.poller.is_running = False
        self.poller.join()
        if must_raise:
            raise urwid.ExitMainLoop()
        urwid.ExitMainLoop()

    def run(self):
        urwid.connect_signal(self.walker, 'modified', self.update)

        try:
            self.poller.start()
            self.loop.run()
        except KeyboardInterrupt:
            self.exit()
        print('Exiting... Bye!')
Example #6
0
class HNGui(object):
    """ The Pyhn Gui object """
    def __init__(self, cache_manager):
        self.cache_manager = cache_manager
        self.already_build = False
        self.which = "top"

        self.config = Config()
        self.palette = self.config.get_palette()

    def main(self):
        """
        Main Gui function which create Ui object,
        build interface and run the loop
        """
        self.ui = urwid.raw_display.Screen()
        self.ui.register_palette(self.palette)
        self.build_interface()
        self.ui.run_wrapper(self.run)

    def build_help(self):
        """ Fetch all key bindings and build help message """
        self.bindings = {}
        self.help_msg = []
        self.help_msg.append(urwid.AttrWrap(urwid.Text('\n Key bindings \n'), 'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        for binding in self.config.parser.items('keybindings'):
            self.bindings[binding[0]] = binding[1]
            line = urwid.AttrWrap(
                urwid.Text(' %s: %s ' % (binding[1], binding[0].replace('_', ' '))),
                'help')
            self.help_msg.append(line)
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(
            urwid.Text(' Thanks for using Pyhn %s! ' % VERSION, align='center'),
            'title'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(' Website: http://github.com/socketubs/pyhn '), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(' Author : socketubs '), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))
        self.help_msg.append(urwid.AttrWrap(urwid.Text(''), 'help'))

        self.help = Popup(self.help_msg, ('help', 'help'), (0, 1), self.view)

    def build_interface(self):
        """
        Build interface, refresh cache if needed, update stories listbox, create
        header, footer, view and the loop.
        """
        if self.cache_manager.is_outdated():
            self.cache_manager.refresh()

        self.stories = self.cache_manager.get_stories()
        self.update_stories(self.stories)
        self.header_content = [
                ('fixed', 4, urwid.Padding(urwid.AttrWrap(urwid.Text(' N°'), 'header'))),
                urwid.AttrWrap(urwid.Text('TOP STORIES', align="center"), 'title'),
                ('fixed', 5, urwid.Padding(urwid.AttrWrap(urwid.Text('SCORE'), 'header'))),
                ('fixed', 8, urwid.Padding(urwid.AttrWrap(urwid.Text('COMMENTS'), 'header')))]

        self.header = urwid.Columns(self.header_content, dividechars=1)
        self.footer = urwid.AttrMap(
            urwid.Text('Welcome in pyhn by socketubs (https://github.com/socketubs/pyhn)', align='center'),
            'footer')

        self.view = urwid.Frame(urwid.AttrWrap(self.listbox, 'body'), header=self.header, footer=self.footer)
        self.loop = urwid.MainLoop(
                        self.view, self.palette,
                        screen=self.ui, handle_mouse=False,
                        unhandled_input=self.keystroke)

        self.build_help()
        self.already_build = True

    def set_help(self):
        """ Set help msg in footer """
        self.view.set_footer(urwid.AttrWrap(urwid.Text(self.help, align="center"), 'help'))

    def set_footer(self, msg, style="normal"):
        """ Set centered footer message """
        if style == "normal":
            self.footer = urwid.AttrWrap(urwid.Text(msg), 'footer')
            self.view.set_footer(self.footer)
        elif style == "error":
            self.footer = urwid.AttrWrap(urwid.Text(msg), 'footer-error')
            self.view.set_footer(self.footer)

    def set_header(self, msg):
        """ Set header story message """
        self.header_content[1] = urwid.AttrWrap(urwid.Text(msg, align="center"), 'title')
        self.view.set_header(urwid.Columns(self.header_content, dividechars=1))

    def keystroke(self, input):
        """ All key bindings are computed here """
        # QUIT
        if input in ('q', 'Q'):
            raise urwid.ExitMainLoop()
        # LINKS
        elif input in self.bindings['open_story_link'].split(','):
            self.open_webbrowser(self.listbox.get_focus()[0].url)
        elif input in self.bindings['show_story_link'].split(','):
            self.set_footer(self.listbox.get_focus()[0].url)
        elif input in self.bindings['open_comments_link'].split(','):
            if self.listbox.get_focus()[0].comments_url == -1:
                self.set_footer('No comments')
            else:
                self.open_webbrowser(self.listbox.get_focus()[0].comments_url)
        elif input in self.bindings['show_comments_link'].split(','):
            if self.listbox.get_focus()[0].comments_url == -1:
                self.set_footer('No comments')
            else:
                self.set_footer(self.listbox.get_focus()[0].comments_url)
        elif input in self.bindings['open_submitter_link'].split(','):
            if self.listbox.get_focus()[0].submitter_url == -1:
                self.set_footer('Anonymous submitter')
            else:
                self.open_webbrowser(self.listbox.get_focus()[0].submitter_url)
        elif input in self.bindings['show_submitter_link'].split(','):
            if self.listbox.get_focus()[0].submitter_url == -1:
                self.set_footer('Anonymous submitter')
            else:
                self.set_footer(self.listbox.get_focus()[0].submitter_url)
        # MOVEMENTS
        elif input in self.bindings['down'].split(','):
            if self.listbox.focus_position - 1 in self.walker.positions():
                self.listbox.set_focus(self.walker.prev_position(self.listbox.focus_position))
        elif input in self.bindings['up'].split(','):
            if self.listbox.focus_position + 1 in self.walker.positions():
                self.listbox.set_focus(self.walker.next_position(self.listbox.focus_position))
        elif input in self.bindings['page_up'].split(','):
            self.listbox._keypress_page_up(self.ui.get_cols_rows())
        elif input in self.bindings['page_down'].split(','):
            self.listbox._keypress_page_down(self.ui.get_cols_rows())
        elif input in self.bindings['first_story'].split(','):
            self.listbox.set_focus(self.walker.positions()[0])
        elif input in self.bindings['last_story'].split(','):
            self.listbox.set_focus(self.walker.positions()[-1])
        # STORIES
        elif input in ('n',):
            threading.Thread(None, self.async_refresher, None, ('newest', 'NEWEST STORIES'), {}).start()
        elif input in ('t',):
            threading.Thread(None, self.async_refresher, None, ('top', 'TOP STORIES'), {}).start()
        elif input in ('b',):
            self.set_footer('Syncing best stories...')
            threading.Thread(None, self.async_refresher, None, ('best', 'BEST STORIES'), {}).start()
        # OTHERS
        elif input in self.bindings['refresh'].split(','):
            threading.Thread(None, self.async_refresher, None, (), {}).start()
        elif input in self.bindings['reload_config'].split(','):
            self.reload_config()
        elif input in ('h', 'H', '?'):
            keys = True
            while True:
                if keys:
                    self.ui.draw_screen(self.ui.get_cols_rows(), self.help.render(self.ui.get_cols_rows(), True))
                    keys = self.ui.get_input()
                    if 'h' or 'H' or '?' or 'escape' in keys:
                        break

    def async_refresher(self, which=None, header=None):
        if which is None:
            which = self.which
        if self.cache_manager.is_outdated(which):
            self.cache_manager.refresh(which)
        stories = self.cache_manager.get_stories(which)
        self.update_stories(stories)
        if header is not None:
            self.set_header(header)
            self.which = which
        self.loop.draw_screen()

    def update_stories(self, stories):
        """ Reload listbox and walker with new stories """
        items = []
        for story in stories:
            items.append(ItemWidget(story))

        if self.already_build:
            self.walker[:] = items
            self.update()
        else:
            self.walker = urwid.SimpleListWalker(items)
            self.listbox = urwid.ListBox(self.walker)

    def open_webbrowser(self, url):
        """ Handle url and open sub process with web browser """
        if self.config.parser.get('settings', 'browser_cmd') == "__default__":
            python_bin = sys.executable
            browser_output = subprocess.Popen(
                [python_bin, '-m', 'webbrowser', '-t', url],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE)
        else:
            cmd = self.config.parser.get('settings', 'browser_cmd')
            try:
                p = subprocess.Popen(
                    cmd.replace('__url__', url),
                    shell=True,
                    close_fds=True,
                    stderr=subprocess.PIPE)

                returncode = p.wait()
            except KeyboardInterrupt:
                stderr = "User keyboard interrupt detected!"
                self.set_footer(stderr, style="error")
                return
            if returncode > 0:
                stderr = p.communicate()[1]
                self.set_footer("%s" % stderr, style="error")

    def update(self):
        """ Update footer about focus story """
        focus = self.listbox.get_focus()[0]
        if focus.submitter == "":
            msg = "submitted %s" % focus.published_time
        else:
            msg = "submitted %s by %s" % (focus.published_time, focus.submitter)

        self.set_footer(msg)

    def reload_config(self):
        """
        Create new Config object, reload colors, refresh cache
        if needed and redraw screen.
        """
        self.set_footer('Reloading configuration')
        self.config = Config()
        self.build_help()
        self.palette = self.config.get_palette()
        self.build_interface()
        self.loop.draw_screen()
        self.set_footer('Configuration file reloaded!')

        if self.config.parser.get('settings', 'cache') != self.cache_manager.cache_path:
            self.cache_manager.cache_path = self.config.parser.get('settings', 'cache')

    def run(self):
        """ Run the loop """
        urwid.connect_signal(self.walker, 'modified', self.update)

        try:
            self.loop.run()
        except KeyboardInterrupt:
            urwid.ExitMainLoop()