class MainWindow(Gtk.Window): # __gsignals__ is used to register the names of custom signals __gsignals__ = { 'item_scraped_event': (GObject.SIGNAL_RUN_FIRST, None, ()), 'feed_gathered_event': (GObject.SIGNAL_RUN_FIRST, None, ()) } __gtype_name__ = 'TroughWindow' def __init__(self, preferences, cache, **kwargs): Gtk.Window.__init__(self, **kwargs) # Signals self.connect('key_press_event', self.on_key_press) self.connect('item_scraped_event', self.on_item_scraped) self.connect('feed_gathered_event', self.on_feed_gathered) self.set_good_default_size() self.set_window_icon() self.preferences = preferences self.css_provider = self.create_css() self.header_bar = self.create_header() self.cache = cache self.gatherer = Gatherer(self, self.preferences, self.cache) self.current_view = None self.switch_view() self.gatherer.request_feeds() def set_good_default_size(self): screen = self.get_screen() active_window = screen.get_active_window() if active_window: monitor = screen.get_monitor_at_window(active_window) geometry = screen.get_monitor_geometry(monitor) width = floor(.60*geometry.width) height = floor(.75*geometry.height) self.set_default_size(width, height) else: # Just guess self.set_default_size(600, 800) self.set_size_request(100, 100) # Minimum size, to prevent fading to nothing def set_window_icon(self): """ Attempts to find a generic RSS icon in the user's GTK theme, then associates it with the program if found. """ try: theme = Gtk.IconTheme.get_default() icon = theme.lookup_icon("rss", 32, Gtk.IconLookupFlags.GENERIC_FALLBACK) if icon: icon = icon.load_icon() self.set_icon(icon) except GLib.GError: # If the icon theme doesn't have an icon for RSS it's okay, purely visual pass def switch_view(self): """ Activates the view currently chosen in the preferences and returns it. """ view_key = self.preferences.appearance_preferences()['View'] views = {'Two-Pane': TwoPaneView, 'Three-Pane': ThreePaneView} view_class = views[view_key] if type(self.current_view) != view_class: # Will the new view actually be different from the current? if self.current_view: # Do we even have a current view? (we don't if just starting up) self.current_view.destroy_display() self.current_view = view_class(self.preferences, self.gatherer) # Make the new view self.current_view.populate(self.preferences.feed_list()) self.add(self.current_view.top_level()) self.show_all() # Workaround, silences GTK warning 'without calling gtk_widget_get_preferred_width/height().' PyGObject bug? self.get_preferred_size() return self.current_view def create_css(self): css_provider = Gtk.CssProvider() css_provider.load_from_data(self.preferences.get_appearance_css()) context = Gtk.StyleContext() context.add_provider_for_screen(self.get_screen(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) return css_provider def update_css(self): self.css_provider.load_from_data(self.preferences.get_appearance_css()) def create_header(self): header_bar = Gtk.HeaderBar(title="Trough", show_close_button=True) header_bar.pack_start(self.create_add_button()) header_bar.pack_start(self.create_preferences_button()) header_bar.pack_start(self.create_refresh_button()) self.set_titlebar(header_bar) return header_bar def create_add_button(self): add_button = make_button(theme_icon_string="add", tooltip_text="Quickly add a feed", signal="clicked", function=self.on_add_clicked) return add_button def create_preferences_button(self): preferences_button = make_button(theme_icon_string='gtk-preferences', backup_icon_string='preferences-system', tooltip_text="Preferences", signal="clicked", function=self.on_preferences_clicked) return preferences_button def create_refresh_button(self): refresh_button = make_button(theme_icon_string="view-refresh", tooltip_text="Refresh", signal="clicked", function=self.on_refresh_clicked) refresh_button.set_focus_on_click(False) # Don't keep it looking "pressed" after clicked return refresh_button def on_add_clicked(self, widget=None): dialog = FeedDialog(self, self.preferences.feeds()) feed = dialog.get_response() if feed: self.preferences.add_feed(feed) self.on_refresh_clicked() # Do a convenience refresh def on_preferences_clicked(self, widget=None): pw = PreferencesWindow(self, self.preferences, self.cache) response = pw.run() if response == Gtk.ResponseType.OK: pw.apply_choices() pw.destroy() def on_refresh_clicked(self, widget=None): self.current_view.refresh() @staticmethod def do_scroll(widget, scroll): try: widget.do_scroll_child(widget, scroll, False) except AttributeError: pass # TODO: It would be better to have this be in the view class but putting it here for now def on_item_scraped(self, unnecessary_arg=None): while True: item = self.gatherer.grab_scrape_result() if item and item.article: self.current_view.receive_article(item) else: break # TODO: It would be better to have this be in the view class but putting it here for now def on_feed_gathered(self, unnecessary_arg=None): while True: feed = self.gatherer.grab_feed_result() if feed and feed.items: self.filter_feed(feed) feed.sort_items() self.current_view.receive_feed(feed) else: break def filter_feed(self, feed): for fil in self.preferences.filters(): fil.inspect_feed(feed) # TODO: Idea: have a hotkey to add links to a buffer then you can open them all at once. def on_key_press(self, widget, event): key = Gdk.keyval_name(event.keyval) if key == "F5": self.on_refresh_clicked() elif key == "Left": self.current_view.change_position(-1) elif key == "Right": self.current_view.change_position(1) elif key == "Up": self.do_scroll(widget, Gtk.ScrollType.STEP_BACKWARD) elif key == "Down": self.do_scroll(widget, Gtk.ScrollType.STEP_FORWARD) elif key == "Return": if event.state & Gdk.ModifierType.CONTROL_MASK: self.current_view.get_then_open_link() else: self.current_view.change_position(0) else: pass