def _finished_update(self, *args):
        """Renders menu and shows notification after updating is finished."""
        if self._feeds_thread is not None and self._feeds_thread.is_alive():
            return

        if self._feeds_thread:
            self._feeds_thread.join()
        sleep(1)
        self._feeds_thread = None
        self._render_menu()

        if self._config_manager.show_notifications:
            with SQLite() as db:
                feeds = db.s('SELECT id, title, img FROM feeds WHERE ' +
                             'feed_url IN (SELECT feed_id FROM posts WHERE ' +
                             'read="false" GROUP BY feed_id LIMIT 50) ORDER' +
                             ' BY (SELECT count(feed_id) AS c FROM posts ' +
                             'WHERE read="false" GROUP BY feed_id ORDER BY ' +
                             'c desc), UPPER(title)')
                for feed in feeds:
                    img = os.path.join(config.app_cache_dir,
                                       feed[2]) if feed[2] else None
                    posts = db.s(
                        'SELECT title FROM posts WHERE read=' +
                        '"false" AND feed_id=? LIMIT 3', (feed[0], ))
                    if len(posts) > 0:
                        msg = '\n'.join('* %s' % p[0] for p in posts)
                        self._notify(feed[1], msg, img)
                if len(feeds) == 0:
                    if self._config_manager.show_update_notifications:
                        self._notify(feedindicator.__app_name__,
                                     _('Finished updating feeds.'))
    def _open_unread(self, widget, feed_id=None):
        """Opens all unread post of the given feed or all unread posts.

        Args:
            widget: Gtk widget
            feed_id: optional feed id, if not given opens all unread
        """
        with SQLite() as db:
            if feed_id:
                posts = db.s(
                    'SELECT url FROM posts WHERE feed_id=? AND ' +
                    'read="false"', (feed_id, ))
                for post in posts:
                    webbrowser.open(post[0])
                    sleep(0.6)

                db.s('UPDATE posts SET read="true" WHERE feed_id=?',
                     (feed_id, ))
            else:
                for feed in db.s('SELECT id FROM feeds'):
                    posts = db.s(
                        'SELECT url FROM posts WHERE feed_id=? AND ' +
                        'read="false"', feed)
                    for post in posts:
                        webbrowser.open(post[0])
                        sleep(0.6)
                    db.s('UPDATE posts SET read="true" WHERE feed_id=?', feed)
        sleep(0.5)
        self._render_menu()
    def _update(self, widget=None, timeroverride=False, starttimer=False):
        """Start updating feeds.

        Args:
            widget: Gtk widget
            timeroverride: if true timer will be overridden
            starttimer: if true timer will be started
        """
        if self._config_manager.stoptimer and not timeroverride:
            return False

        with SQLite() as db:
            if db.s('SELECT COUNT(*) FROM feeds')[0][0] > 0:
                if starttimer:
                    GLib.timeout_add_seconds(self._config_manager.refreshtime,
                                             self._update, True)

                self._loading_menu()
                if db.s('SELECT COUNT(*) FROM feeds')[0][0] != 0:
                    self._feeds_thread = FeedThread(self._finished_update)
                    self._feeds_thread.start()
                    if self._config_manager.show_notifications:
                        if self._config_manager.show_update_notifications:
                            self._notify(feedindicator.__app_name__,
                                         _('Begin updating Feeds.'))
            else:
                self._render_menu()
        return True
    def _mark_feed_as_unread(self, widget, feed_id=None):
        """Mark all posts of all feeds or a specific feed as read.

        Args:
            widget: Gtk widget
            feed_id: optinal feed id, if not given all will be updated
        """
        with SQLite() as db:
            f = 'WHERE feed_id=?' if feed_id else ''
            db.d('UPDATE posts SET read="false" %s' % f,
                 (feed_id, ) if feed_id else ())
        sleep(0.5)
        self._render_menu()
    def _mark_displayed_as_read(self, widget, feed_id):
        """Marks displayed posts of given feed as read.

        Args:
            widget: Gtk widget
            feed_id: feed id
        """
        with SQLite() as db:
            db.s(
                'UPDATE posts SET read="true" WHERE feed_id=? AND id IN ' +
                '(SELECT id FROM posts WHERE feed_id=? AND read="false" ' +
                'ORDER BY id DESC LIMIT %d)' %
                self._config_manager.items_per_feed, (feed_id, feed_id))
        sleep(0.5)
        self._render_menu()
    def _open_website(self, widget, url, post_id=None):
        """Open website.

        Args:
            widget: Gtk widget
            url: url to open
            post_id: optional post id to change status to read
        """
        webbrowser.open(url)

        if post_id:
            with SQLite() as db:
                db.s('UPDATE posts SET read="true" WHERE id=?', (post_id, ))
            sleep(0.5)
            self._render_menu()
    def _render_menu(self):
        """Populate the menu."""
        self._clear_menu()

        with SQLite() as db:
            feeds = db.s('SELECT id, title, url, feed_url, (SELECT COUNT(*) ' +
                         'FROM posts WHERE posts.feed_id=feeds.id AND ' +
                         'read="false") AS c FROM feeds ORDER BY c DESC, ' +
                         'UPPER(title)')

            if not self._config_manager.feeds_at_top:
                self._conf_menu(len(feeds) > 0)
                self._menu.append(Gtk.SeparatorMenuItem())

            if len(feeds) > 0:
                self._feeds_menu_header()
                for feed in feeds:
                    posts = db.s(
                        'SELECT id, title, url FROM posts WHERE ' +
                        'feed_id=? AND read="false" ORDER BY id ' +
                        'LIMIT %d' % self._config_manager.items_per_feed,
                        (feed[0], ))
                    if self._config_manager.show_unread_feeds:
                        self._feed_submenu(feed, posts)
                    else:
                        if feed[4] > 0:
                            self._feed_submenu(feed, posts)
                if db.s('SELECT COUNT(*) FROM posts WHERE ' +
                        'read="false"')[0][0] == 0:
                    menu_notice = Gtk.MenuItem(label=_('No unread posts.'))
                    menu_notice.set_sensitive(False)
                    self._menu.append(menu_notice)
            else:
                item = Gtk.MenuItem(label=_('No feeds defined!'))
                item.set_sensitive(False)
                self._menu.append(item)

            if self._config_manager.feeds_at_top:
                self._menu.append(Gtk.SeparatorMenuItem())
                self._conf_menu(len(feeds) > 0)

            self._menu.show_all()
            if appindicator:
                self._indicator.set_menu(self._menu)
            self._set_status(
                db.s('SELECT COUNT(*) FROM posts WHERE ' +
                     'read="false"')[0][0] > 0)
    def _open_displayed(self, widget, feed_id):
        """Opens all unread post of the given feed or all unread posts.

        Args:
            widget: Gtk widget
            feed_id: optional feed id, if not given opens all unread
        """
        with SQLite() as db:
            posts = db.s(
                'SELECT id, url FROM posts WHERE feed_id=? AND ' +
                'read="false" ORDER BY id DESC LIMIT %d' %
                self._config_manager.items_per_feed, (feed_id, ))
            for post in posts:
                webbrowser.open(post[1])
                db.s('UPDATE posts SET read="true" WHERE id=?', (post[0], ))
                sleep(0.6)
        self._render_menu()
    def _save(self, widget):
        """Save config and feeds and close dialog.

        Args:
            widget: Gtk widget
        """
        self._config_manager.update(self._configs)
        self._config_manager.save()
        with SQLite() as db:
            for feed in self._feeds:
                if feed[0] == -1 and feed[2]:
                    if not feed[1]:
                        feed[1] = None
                    db.s('INSERT INTO feeds (feed_url, title) VALUES (?,?)',
                         (feed[2], feed[1]))
                else:
                    db.s('UPDATE feeds set title=?, feed_url=? WHERE id=?',
                         (feed[1], feed[2], feed[0]))
        self._close_window(widget)
        self._callback(self._callback_args)
    def __init__(self, widget, config_manager, callback, *args):
        """Init dialog.

        Args:
            widget: Gtk widget
            configs: dict with configs
            callback: callback function after url has been added
            *args: callback function args
        """
        Gtk.Window.__init__(self, title=_('Preferences'))
        self._config_manager = config_manager
        self._configs = self._config_manager.items()
        self._callback = callback
        self._callback_args = args

        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_keep_above(True)
        self.set_icon_name(feedindicator.__app_name__)
        self.connect('delete_event', self._close_window)
        self.connect('key-press-event', self._keypress)

        vbox = Gtk.VBox(False, 1)
        vbox.set_border_width(1)
        self.add(vbox)

        notebook = Gtk.Notebook()
        notebook.set_tab_pos(Gtk.PositionType.LEFT)
        vbox.pack_start(notebook, False, True, 0)

        # Feed List
        frame = Gtk.Frame(label=_('Feed list'))
        frame.set_border_width(1)

        box = Gtk.VBox(False, 1)
        box.set_border_width(1)
        frame.add(box)

        label = Gtk.Label(label=_('Configure the feeds.'))
        label.set_justify(Gtk.Justification.LEFT)
        label.set_line_wrap(True)
        box.pack_start(label, False, True, 0)

        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
                                   Gtk.PolicyType.ALWAYS)
        scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
        scrolled_window.set_min_content_height(500)
        scrolled_window.set_min_content_width(500)
        box.pack_start(scrolled_window, False, True, 0)

        self._feeds = Gtk.ListStore(int, str, str)
        with SQLite() as db:
            for feed in db.s('SELECT id, title, feed_url FROM feeds ORDER ' +
                             'BY UPPER(title)'):
                self._feeds.append(feed)

        self._treeview = Gtk.TreeView.new_with_model(self._feeds)
        for i, column_title in enumerate([_('ID'), _('Title'), _('URL')]):
            renderer = Gtk.CellRendererText()
            if i > 0:
                renderer.set_property('editable', True)
                renderer.connect('edited', self._cell_edited, i)
            column = Gtk.TreeViewColumn(column_title, renderer, text=i)
            self._treeview.append_column(column)

        self._treeview.set_headers_clickable(False)
        self._treeview.set_reorderable(True)
        self._treeview.connect('cursor-changed', self._selection_made)
        scrolled_window.add(self._treeview)

        hbox = Gtk.HBox(True, 1)
        box.pack_start(hbox, False, True, 0)

        btn = Gtk.Button(label=_('Add'))
        btn.connect('clicked', self._add_feed)
        hbox.pack_start(btn, False, True, 0)

        self._btn_remove_feed = Gtk.Button(label=_('Remove'))
        self._btn_remove_feed.connect('clicked', self._remove_feed)
        self._btn_remove_feed.set_sensitive(False)
        hbox.pack_start(self._btn_remove_feed, False, True, 0)
        notebook.append_page(frame, Gtk.Label(label=_('Feed list')))

        # Options
        frame = Gtk.Frame(label=_('Options'))
        frame.set_border_width(1)

        box = Gtk.VBox(False, 1)
        box.set_border_width(1)
        frame.add(box)

        btn = Gtk.CheckButton(label=_('Auto update feeds'))
        btn.set_active(not self._configs['stoptimer'])
        btn.connect('toggled', self._toggle_config, 'stoptimer')
        box.pack_start(btn, False, True, 0)

        hbox = Gtk.HBox(True, 0)
        box.pack_start(hbox, False, True, 0)

        self._refreshtime_label = Gtk.Label(label='')
        hbox.pack_start(self._refreshtime_label, False, True, 0)

        adjtimer = Gtk.Adjustment(value=self._configs['refreshtime'] / 60,
                                  lower=1.0,
                                  upper=90.0,
                                  step_increment=1.0,
                                  page_increment=10.0,
                                  page_size=0.0)
        adjtimer.connect('value_changed', self._change_refreshtime)
        self._scaletime = Gtk.HScale(adjustment=adjtimer)
        self._scaletime.set_draw_value(False)
        self._scaletime.set_digits(0)
        self._scaletime.set_sensitive(not self._configs['stoptimer'])
        hbox.pack_start(self._scaletime, False, True, 0)
        self._change_refreshtime(adjtimer)

        hbox = Gtk.HBox(True, 0)
        box.pack_start(hbox, False, True, 0)

        self._items_per_feed_label = Gtk.Label(label='')
        hbox.pack_start(self._items_per_feed_label, False, False, 0)

        adjitems = Gtk.Adjustment(value=self._configs['items_per_feed'],
                                  lower=1.0,
                                  upper=30.0,
                                  step_increment=1.0,
                                  page_increment=10.0,
                                  page_size=0.0)
        adjitems.connect('value_changed', self._change_items_per_feed)
        scaleitems = Gtk.HScale(adjustment=adjitems)
        scaleitems.set_draw_value(False)
        scaleitems.set_digits(0)
        hbox.pack_start(scaleitems, True, True, 0)
        self._change_items_per_feed(adjitems)

        btn = Gtk.CheckButton(label=_('Show feeds at top of menu'))
        btn.set_active(self._configs['feeds_at_top'])
        btn.connect('toggled', self._toggle_config, 'feeds_at_top')
        box.pack_start(btn, False, True, 0)

        btn = Gtk.CheckButton(label=_('Show feeds with no unread posts'))
        btn.set_active(self._configs['show_unread_feeds'])
        btn.connect('toggled', self._toggle_config, 'show_unread_feeds')
        box.pack_start(btn, False, True, 0)

        btn = Gtk.CheckButton(label=_('Launch at system startup'))
        btn.set_active(self._configs['autostart'])
        btn.connect('toggled', self._toggle_config, 'autostart')
        box.pack_start(btn, False, True, 0)

        btn = Gtk.CheckButton(label=_('Show notifications'))
        btn.set_active(self._configs['show_notifications'])
        btn.connect('toggled', self._toggle_config, 'show_notifications')
        box.pack_start(btn, False, True, 0)

        self._btn_show_update_notifications = Gtk. \
            CheckButton(label=_('Show notifications at beginning ' +
                                'and end of update'))
        self._btn_show_update_notifications. \
            set_sensitive(self._configs['show_notifications'])
        self._btn_show_update_notifications. \
            set_active(self._configs['show_update_notifications'])
        self._btn_show_update_notifications. \
            connect('toggled', self._toggle_config,
                    'show_update_notifications')
        box.pack_start(self._btn_show_update_notifications, False, True, 0)

        notebook.append_page(frame, Gtk.Label(label=_('Options')))

        box = Gtk.HBox(True, 0)
        vbox.pack_end(box, False, True, 0)

        btn = Gtk.Button(label=_('Cancel'))
        btn.connect('clicked', self._close_window)
        box.pack_start(btn, False, True, 0)

        btn = Gtk.Button(label=_('Save'))
        btn.connect('clicked', self._save)
        box.pack_start(btn, False, True, 0)
        self.show_all()