""" Application.py

This is the main module that binds everything together.

"""
__copyright__ = "Copyright (c) 2002-2005 Free Software Foundation, Inc."
__license__ = """ GNU General Public License

This program is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA. """

import time
from xml.sax import saxutils
import pygtk
pygtk.require('2.0')
import gobject
import gtk
import gtk.glade
import gnome
import gconf
import utils
import subscribe
import Event
import MessageManager
import FeedList
import FeedCategoryList
import Config
import error
from ItemView import ItemView
from ItemList import ItemListPresenter, ItemListView
from FeedListView import FeedsView, FeedsPresenter
from OfflineToggle import OfflineToggle
from CategorySelector import CategoryPresenter, CategoryView
from MainloopManager import MainloopManager
from FeedPropertiesDialog import FeedPropertiesDialog
from PreferencesDialog import PreferencesDialog
from Find import FindPresenter, FindView
import OPMLImport
import MVP
import ItemStore
import ImageCache
import PollManager
import dialogs
import constants

class StatusPresenter(MVP.BasicPresenter):
    def _initialize(self):
        self._mmgr = MessageManager.get_instance()
        self._mmgr.signal_connect(Event.StatusDisplaySignal,
                                  self._display)
        return

    def _display(self, signal):
        cid =  self._view.get_context_id("straw_main")
        self._view.pop(cid)
        self._view.push(cid, self._mmgr.read_message())
        return

class ErrorPresenter:
    def __init__(self, widget):
        self._widget = widget
        self._tooltips = gtk.Tooltips()
        self._text = ''
        self._curr_feed = None
        self._curr_category = None
        fclist = FeedCategoryList.get_instance()
        fclist.signal_connect(Event.FeedCategoryChangedSignal,
                              self._category_changed)

    def display_feed_error(self, feed):
        if self._curr_feed is not None:
            self._curr_feed.signal_disconnect(Event.FeedErrorStatusChangedSignal,
                                              self._error_status_changed)
        self._curr_feed = feed
        self._curr_feed.signal_connect(Event.FeedErrorStatusChangedSignal,
                                       self._error_status_changed)
        self._update_view()
        return

    def display_category_error(self, category):
        if category:
            self._curr_category = category
            self._update_view()
        return

    def _update_view(self):
        text = list()
        if self._curr_category and self._curr_category.subscription and self._curr_category.subscription.error:
            text.append(_("Category error:"))
            text.append(self._curr_category.subscription.error)
        if self._curr_feed and self._curr_feed.error:
            text.append(_("Feed Error:"))
            text.append(self._curr_feed.error)
        if text:
            t = "\n".join(text)
            self._tooltips.set_tip(self._widget,t,t)
            self._tooltips.enable()
            self._widget.show()
        else:
            self._tooltips.disable()
            self._widget.hide()
        return

    def _category_changed(self, signal):
        if signal.sender is self._curr_category:
            self.display_category_error(self._curr_category)

    def _error_status_changed(self, signal):
        if signal.sender is self._curr_feed:
            self.display_feed_error(signal.sender)
        return

    def hide(self):
        self._widget.hide()

class FeedInfoView(MVP.WidgetView):
    def _initialize(self):
        widget_tree = gtk.glade.get_widget_tree(self._widget)
        self._title = widget_tree.get_widget('feed_item_label')
        self._refresh = widget_tree.get_widget('feed_refresh_label')

    def set_title(self, title):
        self._title.set_label(title)
        return

    def set_refresh(self, refresh):
        self._refresh.set_label(refresh)
        self._refresh.show()
        return

    def hide(self):
        self._widget.hide()
        return

    def show(self):
        self._widget.show()
        return

class FeedInfoPresenter(MVP.BasicPresenter):
    def display(self, feed):
        next = feed.next_refresh
        if feed:
            text = _("Next Refresh: %s") % utils.format_date(time.gmtime(next))
            self._view.set_refresh(text)
            self._view.set_title(_("%s") % saxutils.escape(feed.title))
            self._view.show()
        else:
            self._view.hide()
        return

    def hide(self):
        self._view.set_title("")
        self._view.set_refresh("")

    def show(self): self._view.show()

class MenuFeedPropsPresenter(MVP.BasicPresenter):
    def set_sensitive(self, s):
        self._view.set_sensitive(s)
        return

class ToolbarView(MVP.WidgetView):
    """ Widget: gtk.Toolbar"""
    GCONF_DESKTOP_INTERFACE = "/desktop/gnome/interface"

    def _initialize(self):
        client = gconf.client_get_default()
        if not client.dir_exists(self.GCONF_DESKTOP_INTERFACE):
            return
        client.add_dir(self.GCONF_DESKTOP_INTERFACE,
                       gconf.CLIENT_PRELOAD_NONE)
        client.notify_add(self.GCONF_DESKTOP_INTERFACE+"/toolbar_style",
                          self._toolbar_style_changed)
        self._widget.set_tooltips(True)

    def _toolbar_style_changed(self, client, notify_id, entry, *args):
        value = entry.value.get_string()
        self._presenter.style_changed(value)

    def set_style(self, style):
        self._widget.set_style(style)

    def get_style(self):
        client = gconf.client_get_default()
        current_style = client.get_string(self.GCONF_DESKTOP_INTERFACE+"/toolbar_style")
        return current_style

class ToolbarPresenter(MVP.BasicPresenter):
    STYLES = {'both':gtk.TOOLBAR_BOTH,
              'text':gtk.TOOLBAR_TEXT,
              'icons':gtk.TOOLBAR_ICONS,
              'both-horiz':gtk.TOOLBAR_BOTH_HORIZ}

    def _initialize(self):
        style = self._view.get_style()
        if style in self.STYLES.keys():
            self._view.set_style(self.STYLES[style])
        return

    def style_changed(self, value):
        if value in self.STYLES.keys():
            self._view.set_style(self.STYLES[value])
        return

class ApplicationPresenter(MVP.BasicPresenter):
    def _initialize(self):
        self._curr_category = None
        self._curr_feed = None
        self._curr_item = None
        self._init_widgets()
        self._init_presenters()
        self._init_signals()
        self._prefs_dialog = None
        self._view.present()

    def _init_widgets(self):
        widget_tree = self._view.get_widget_tree()
        self._itemlist_view_notebook = widget_tree.get_widget('mode_view_notebook')
        self._feedlist_view_notebook = widget_tree.get_widget('left_mode_view_notebook')

    def _init_presenters(self):
        widget_tree = self._view.get_widget_tree()
        toolbar_presenter = ToolbarPresenter(view=ToolbarView(widget_tree.get_widget('toolbar_default')))
        self._feed_list_presenter = FeedsPresenter(view=FeedsView(widget_tree.get_widget('feed_selection_treeview')))
        self._category_selector = CategoryPresenter(view=CategoryView(widget_tree.get_widget('category_combo')))
        self._error_presenter = ErrorPresenter(
            widget_tree.get_widget('statusbar_error_indicator'))
        self._offline_presenter = OfflineToggle(
            widget_tree.get_widget('offline_toggle'))
        self._itemlist_presenter = ItemListPresenter(view = ItemListView(widget_tree.get_widget('item_selection_treeview')))
        self._item_view = ItemView(widget_tree.get_widget('item_view_container'))
        self._status_presenter = StatusPresenter(view = widget_tree.get_widget("main_statusbar"))
        self._menufp_presenter = MenuFeedPropsPresenter( view = widget_tree.get_widget('menu_feed_properties'))
        self._menufp_presenter.set_sensitive(False)
        self._feedinfo_presenter = FeedInfoPresenter(view = FeedInfoView(widget_tree.get_widget('article_refresh_hbox')))
        self._find_presenter = FindPresenter(view=FindView(widget_tree.get_widget("find_vbox")))
        return

    def _init_signals(self):
        self._feed_list_presenter.signal_connect(Event.FeedSelectionChangedSignal,
                                                 self._feed_selection_changed)
        self._feed_list_presenter.signal_connect(Event.FeedsEmptySignal,
                                                 self._feeds_empty_cb)
        self._itemlist_presenter.signal_connect(Event.ItemSelectionChangedSignal,
                                                self._item_view.item_selection_changed)
        self._category_selector.signal_connect(Event.CategorySelectionChangedSignal,
                                               self._category_selection_changed)
        return

    def _feed_selection_changed(self, signal):
        if signal.old and self._curr_feed:
            self._curr_feed.unload_contents()
        if signal.new:
            if self._curr_feed:
                self._curr_feed.signal_disconnect(Event.FeedPolledSignal,
                                                  self._feed_polled)
                self._curr_feed.signal_disconnect(Event.ItemsAddedSignal,
                                                  self._feed_items_added)
            self._curr_feed = signal.new
            self._curr_feed.signal_connect(Event.FeedPolledSignal,
                                           self._feed_polled)
            self._curr_feed.signal_connect(Event.ItemsAddedSignal,
                                           self._feed_items_added)
            self._display_feed(self._curr_feed)
        return

    def _feed_polled(self, signal):
        self._feedinfo_presenter.display(signal.sender)
        return

    def _feed_items_added(self, signal):
        self._itemlist_presenter.display_feed_items(self._curr_feed, False)

    def _category_selection_changed(self, signal):
        self._display_category_feeds(signal.current)
        return

    def _display_category_feeds(self, category, *args):
        self._curr_category = category
        if category:
            self._feed_list_presenter.display_category_feeds(category)
            self._error_presenter.display_category_error(category)
        else:
            self._feed_list_presenter.display_empty_category()
            self._menufp_presenter.set_sensitive(False)
            self._itemlist_presenter.display_empty_feed()
            self._item_view.display_empty_feed()
        return

    def _display_feed(self, feed, select_first = 1):
        if feed and feed.number_of_items < 1:
            self._item_view.display_empty_feed()
        self._error_presenter.display_feed_error(feed)
        self._feedinfo_presenter.display(feed)
        self._itemlist_presenter.display_feed_items(feed, select_first)
        self._menufp_presenter.set_sensitive(True)
        return

    def _feeds_empty_cb(self, signal):
        self._itemlist_presenter.display_empty_feed()
        self._item_view.display_empty_feed()
        self._feedinfo_presenter.hide()
        self._error_presenter.hide()
        self._menufp_presenter.set_sensitive(False)
        return

    def copy_itemview_text_selection(self):
        utils.set_clipboard_text(self._item_view.get_selected_text())

    def check_allocation(self, widget, event):
        config = Config.get_instance()
        def check_size((width, height, widget)):
            if width == widget.allocation.width and height == widget.allocation.height:
                config.main_window_size = (width, height)
        if event.width != widget.allocation.width or event.height != widget.allocation.height:
            gobject.timeout_add(1000, check_size, (
                (event.width, event.height, widget)))

    def check_main_pane_position(self, widget):
        config = Config.get_instance()
        def check_position((position, widget)):
            if position == widget.get_position():
                config.main_pane_position = position
        pos = widget.get_position()
        if pos != config.main_pane_position:
            gobject.timeout_add(1000, check_position, (pos, widget))

    def check_sub_pane_position(self, widget):
        config = Config.get_instance()
        def check_position((position, widget)):
            if position == widget.get_position():
                config.sub_pane_position = position
        pos = widget.get_position()
        if pos != config.sub_pane_position:
            gobject.timeout_add(1000, check_position, (pos, widget))

    def credits(self):
        return dialogs.credits()

    def poll_all(self):
        if self._warn_if_offline():
            fclist = FeedCategoryList.get_instance()
            self._poll_categories(fclist.all_categories)
        return

    def poll_current_category(self):
        if self._warn_if_offline():
            self._poll_categories([self._curr_category])
        return

    def poll_current_feed(self):
        if self._warn_if_offline():
            pm = PollManager.get_instance()
            pm.poll([self._curr_feed])
        return

    def mark_feed_as_read(self):
        self._curr_feed.mark_all_read()

    def mark_all_as_read(self):
        mmgr = MainloopManager.get_instance()
        flist = FeedList.get_instance().flatten_list()
        mmgr.call_pending()
        for feed in flist:
            feed.mark_all_read()
            if feed is self._curr_feed:
                continue
            feed.unload_contents()
            mmgr.call_pending()

    def remove_selected_feed(self):
        self._feed_list_presenter.remove_selected_feed()

    def show_search(self):
        self._itemlist_presenter.signal_disconnect(Event.ItemSelectionChangedSignal,
                                                   self._item_view.item_selection_changed)
        self._find_presenter.item_list.signal_connect(Event.ItemSelectionChangedSignal,
                                                      self._item_view.item_selection_changed)
        self._item_view.display_empty_search()
        self._itemlist_view_notebook.set_current_page(1)
        self._feedlist_view_notebook.set_current_page(1)
        self._feedinfo_presenter.hide()

    def hide_search(self):
        self._find_presenter.clear()
        self._itemlist_view_notebook.set_current_page(0)
        self._feedlist_view_notebook.set_current_page(0)
        self._feedinfo_presenter.show()
        self._find_presenter.item_list.signal_disconnect(Event.ItemSelectionChangedSignal,
                                                     self._item_view.item_selection_changed)
        self._itemlist_presenter.signal_connect(Event.ItemSelectionChangedSignal,
                                                self._item_view.item_selection_changed)

    def _poll_categories(self, fclist):
        pm = PollManager.get_instance()
        pm.poll_categories(fclist)
        return

    def _warn_if_offline(self):
        config = Config.get_instance()
        will_poll = False
        if config.offline:
            response = self._view.show_offline_dialog()
            if response == gtk.RESPONSE_OK:
                config.offline = not config.offline
                will_poll = True
        else:
            will_poll = True
        return will_poll

    def _get_next_category(self, category = None):
        if not category:
            category = self._curr_category
        fclist = FeedCategoryList.get_instance()
        allcats = fclist.user_categories + list(fclist.pseudo_categories)
        if category is None:
            category = allcats[0]
        else:
            index = allcats.index(category)
            if index < len(allcats) - 1:
                index += 1
            else:
                index = 0
            category = allcats[index]
        return category

    def _get_previous_category(self, category = None):
        if category is None:
            category = self._curr_category
        fclist = FeedCategoryList.get_instance()
        allcats = fclist.user_categories + list(fclist.pseudo_categories)
        if category is None:
            category = allcats[-1]
        else:
            index = allcats.index(category)
            if index > 0:
                index -= 1
            else:
                index = len(allcats) - 1
            category = allcats[index]
        return category

    def display_previous_category(self, category = None):
        """
        Displays the category before the current selected category
        """
        category = self._get_previous_category(category)
        self._category_selector.category_selected(category)
        self._display_category_feeds(category)

    def display_next_category(self, category = None):
        """
        Display the category after the current selected category
        """
        category = self._get_next_category(category)
        self._category_selector.category_selected(category)
        self._display_category_feeds(category)

    def display_previous_feed(self, item = None):
        """
        Displays the feed before the current selected feed
        """
        self._feed_list_presenter.select_previous_feed()

    def display_next_feed(self, item=None):
        """
        Displays the feed after the current selected feed
        """
        is_next = self._feed_list_presenter.select_next_feed()
        if not is_next:
            self._feed_list_presenter.select_first_feed()
        return

    def display_next_unread_feed(self):
        """
        Displays the next feed with an unread item
        """
        self._feed_list_presenter.select_next_unread_feed()

    def display_previous_item(self, item=None):
        """
        Displays the item before the current selected item. If the item is the
        first item, scrolls to the previous feed
        """
        is_prev = self._itemlist_presenter.select_previous_item()
        if not is_prev:
            # TODO HACK - implement select_previous_feed(select_last=True) ...
            # ... to select previous feed's last item
            self._feed_list_presenter.select_previous_feed()
            self._itemlist_presenter.select_last_item()
        return

    def display_next_item(self, item=None):
        """
        Displays the item after the current selected item. If the item is the
        last item, selectes the next feed. If the current feed is the last
        feed in the list, it goes back and selects the first feed
        """
        is_next = self._itemlist_presenter.select_next_item()
        if not is_next:
            is_next_feed = self._feed_list_presenter.select_next_feed()
            if not is_next_feed:
                self._feed_list_presenter.select_first_feed()
        return

    def scroll_or_display_next_unread_item(self, item=None):
        has_unread_item = False
        if not self._item_view.scroll_down():
            has_unread_item = self._itemlist_presenter.select_next_unread_item()
            if not has_unread_item:
                self._feed_list_presenter.select_next_unread_feed()
        return

    def show_preferences_dialog(self, parent):
        if not self._prefs_dialog:
            xf = utils.find_glade_file()
            xml = gtk.glade.XML(xf, "preferences_dialog", gettext.textdomain())
            self._prefs_dialog = PreferencesDialog(xml, parent)
        self._prefs_dialog.show()

    def show_feed_properties(self, parent):
        fpd = FeedPropertiesDialog.show_feed_properties(parent, self._curr_feed)
        return

    def quit(self):
        gtk.main_quit()
        ItemStore.get_instance().stop()
        return

class ApplicationView(MVP.WidgetView):
    """
    Widget: straw_main
    """
    def _initialize(self):
        self._config = Config.get_instance()
        self._initialize_dnd()
        self._initialize_window_updater()
        self._create_unmodified_accelerator_group()
        self._attach_unmodified_accelerator_group()
        self._initialize_window()
        self._find_toggled = False

    def _initialize_window(self):
        widget_tree = gtk.glade.get_widget_tree(self._widget)
        if self._config.window_maximized:
            self._widget.maximize()
        else:
            # we use resize here since configure-event seems to
            # overwrite the default size if we use set_default_size.
            self._widget.resize(*self._config.main_window_size)
        mmp = widget_tree.get_widget('main_main_pane')
        msp = widget_tree.get_widget('main_sub_pane')
        mmp.set_position(self._config.main_pane_position)
        msp.set_position(self._config.sub_pane_position)

    def _initialize_dnd(self):
        self._widget.drag_dest_set(
                gtk.DEST_DEFAULT_ALL,
                [('_NETSCAPE_URL', 0, 0), ('text/uri-list ', 0, 1),
                 ('x-url/http', 0, 2)],
                gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
        return

    def _initialize_window_updater(self):
        feedlist = FeedList.get_instance()
        feedlist.signal_connect(Event.AllItemsReadSignal,
                                lambda signal: self._update_title(feedlist))
        feedlist.signal_connect(Event.ItemReadSignal,
                                lambda signal: self._update_title(feedlist))
        feedlist.signal_connect(Event.ItemsAddedSignal,
                                lambda signal: self._update_title(feedlist))
        feedlist.signal_connect(Event.FeedsChangedSignal,
                                lambda signal: self._update_title(feedlist))
        return

    def _update_title(self, flist):
        uritems = urfeeds = 0
        sfeeds = "feeds"
        listfeeds = flist.flatten_list()
        for ur in [f.n_items_unread for f in listfeeds]:
            if ur:
                uritems += ur
                urfeeds += 1
        else:
            urfeeds = len(listfeeds)
        if urfeeds < 2:
            sfeeds = "feed"
        item_feed_map = {'uritems': uritems,
                         'urfeeds': urfeeds,
                         'fstring' : sfeeds}
        title = _('%(uritems)d unread in %(urfeeds)d %(fstring)s') % item_feed_map
        self._widget.set_title( title + " - %s" % constants.APPNAME)
        return

    # We have a separate accelerator group for the unmodified and
    # shifted accelerators, that is, stuff like space, N, P, etc. This
    # is so that we can have the find pane work correctly
    def _create_unmodified_accelerator_group(self):
        xml = gtk.glade.get_widget_tree(self._widget)
        agroup = gtk.AccelGroup()
        accels = (('menu_mark_feed_as_read', 'R', gtk.gdk.SHIFT_MASK),
                  ('menu_mark_all_as_read', 'A', gtk.gdk.SHIFT_MASK),
                  ('menu_next', 'N', gtk.gdk.SHIFT_MASK),
                  ('menu_next_unread', ' ', 0),
                  ('menu_previous', 'P', gtk.gdk.SHIFT_MASK))
        for widget_name, key, mask in accels:
            widget = xml.get_widget(widget_name)
            widget.add_accelerator("activate", agroup, ord(key), mask,
                                   gtk.ACCEL_VISIBLE)
        self._unmodified_accelerator_group = agroup

    def _attach_unmodified_accelerator_group(self):
        self._widget.add_accel_group(self._unmodified_accelerator_group)

    def _detach_unmodified_accelerator_group(self):
        self._widget.remove_accel_group(self._unmodified_accelerator_group)

    def _on_straw_main_destroy_event(self, *args):
        return self._presenter.quit()

    def _on_straw_main_delete_event(self, *args):
        return self._presenter.quit()

    def _on_menu_quit_activate(self, *args):
        return self._presenter.quit()

    def _on_straw_main_configure_event(self, widget, event, *args):
        if widget.window.get_state() is not gtk.gdk.WINDOW_STATE_MAXIMIZED:
            self._config.window_maximized = False
            self._presenter.check_allocation(widget, event)
        else:
            self._config.window_maximized = True
        return

    def _on_main_main_pane_size_allocate(self, widget, *args):
        self._presenter.check_main_pane_position(widget)

    def _on_main_sub_pane_size_allocate(self, widget, *args):
        self._presenter.check_sub_pane_position(widget)

    def _on_menu_report_problem_activate(self, menuitem, *args):
        utils.url_show("http://bugzilla.gnome.org/simple-bug-guide.cgi?product=straw")

    def _on_menu_about_activate(self, menuitem, *args):
        widget = self._presenter.credits()
        widget.show()

    def _on_menu_refresh_all_activate(self, *args):
        self._presenter.poll_all()

    def _on_menu_refresh_category_activate(self, *args):
        self._presenter.poll_current_category()

    def _on_menu_refresh_selected_activate(self, *args):
        self._presenter.poll_current_feed()

    def _on_toolbar_refresh_all_button_clicked(self, *args):
        self._presenter.poll_all()

    def _on_menu_add_activate(self, *args):
        subscribe.show(self._widget)

    def _on_toolbar_subscribe_button_clicked(self, *args):
        subscribe.show(self._widget)

    def _on_menu_import_subscriptions_activate(self, *args):
        dialogs.import_subscriptions(self._widget)

    def _on_menu_export_subscriptions_activate(self, *args):
        dialogs.export_subscriptions(self._widget)

    def _on_menu_copy_text_activate(self, *args):
        self._presenter.copy_itemview_text_selection()

    def _on_menu_mark_feed_as_read_activate(self, *args):
        self._presenter.mark_feed_as_read()

    def _on_menu_mark_all_as_read_activate(self, *args):
        self._presenter.mark_all_as_read()

    def _on_find_activate(self, widget, *args):
        xml = gtk.glade.get_widget_tree(self._widget)
        menu_find = xml.get_widget('menu_find')
        accel_label = menu_find.get_child()
        
        if not self._find_toggled:
            self._presenter.show_search()
            self._detach_unmodified_accelerator_group()
            self._find_toggled = True
            
            # save the "Find..." stock text for later recovery
            self._old_label_text = accel_label.get_text()
            accel_label.set_text(_('Return to feed list...'))
            
        else:
            self._presenter.hide_search()
            self._attach_unmodified_accelerator_group()
            self._find_toggled = False
            accel_label.set_text(self._old_label_text)            

    def _on_menu_next_activate(self, *args):
        self._presenter.display_next_item()

    def _on_toolbar_scroll_or_next_button_clicked(self, *args):
        self._presenter.scroll_or_display_next_unread_item()

    def _on_menu_scroll_next_activate(self, *args):
        self._presenter.scroll_or_display_next_unread_item()

    def _on_menu_previous_activate(self, *args):
        self._presenter.display_previous_item()

    def _on_menu_next_feed_unread_activate(self, *args):
        self._presenter.display_next_unread_feed()

    def _on_menu_next_feed_activate(self, *args):
        self._presenter.display_next_feed()

    def _on_menu_previous_feed_activate(self, *args):
        self._presenter.display_previous_feed()

    def _on_menu_remove_selected_feed_activate(self, *args):
        self._presenter.remove_selected_feed()

    def _on_menu_next_category_activate(self, *args):
        self._presenter.display_next_category()

    def _on_menu_previous_category_activate(self, *args):
        self._presenter.display_previous_category()

    def _on_menu_next_category_activate(self, *args):
        self._presenter.display_next_category()

    def _on_menu_previous_category_activate(self, *args):
        self._presenter.display_previous_category()

    def _on_menu_preferences_activate(self, *args):
        self._presenter.show_preferences_dialog(self._widget)

    def _on_menu_feed_properties_activate(self, *args):
        self._presenter.show_feed_properties(self._widget)

    def _on_straw_main_drag_data_received(self, w, context,
                                          x, y, data, info, time):
        if data and data.format == 8:
            url = data.data.split("\n")[0]
            subscribe.show(self._widget, "%s" % url)
            context.finish(True, False, time)
        else:
            context.finish(False, False, time)

    def show_offline_dialog(self):
        return dialogs.report_offline_status(self._widget)

    def get_widget_tree(self):
        return gtk.glade.get_widget_tree(self._widget)

    def present(self):
        self._widget.present()

    def should_present(self):
        if self._widget.window.get_state() is not gtk.gdk.WINDOW_STATE_WITHDRAWN:
            self._widget.hide()
        else:
            self._widget.present()

import os
import gettext
import getopt
import sys
import strawdbus

class Application:
    def __init__(self):
        gnome.program_init(constants.APPNAME.lower(), constants.VERSION)
        self._initialize_config_from_environment()
        gtk.window_set_auto_startup_notification(True)
        self._initialize_gettext()

        config = Config.get_instance()
        config.proxy_config.lookup_host()

        feedlist = FeedList.get_instance()
        feed_categories = FeedCategoryList.get_instance()

        # initialise GUI
        xmlfile = utils.find_glade_file()
        xml = gtk.glade.XML(xmlfile, "straw_main", gettext.textdomain())
        window = xml.get_widget('straw_main')
        self._main_presenter = ApplicationPresenter(view =
                                                    ApplicationView(window))

        if config.first_time:
            libdir = utils.find_data_dir()
            filepath = os.path.join(libdir, "default_subscriptions.opml")
            OPMLImport.import_opml(filepath)
        else:
            feedlist.load_data()
        feed_categories.load_data()

        try:
            itemstore = ItemStore.get_instance(config.straw_dir)
        except ItemStore.ConvertException, ex:
            dialogs.report_error(_("There was a problem while converting the database."),
                                 _("Straw will not behave as expected. You should probably quit now. " +
                                   "The exception has been saved to the file '%s'. Please see the Straw README for further instructions."
                                   ) % ex.reason)
            sys.exit()

        ImageCache.initialize()
        itemstore.start()

        self._main_presenter.view.present()
        PollManager.get_instance().start_polling_loop()

         # set the default icon for the windows
        iconfile = os.path.join(utils.find_image_dir(),"straw.png")
        gtk.window_set_default_icon(gtk.gdk.pixbuf_new_from_file(iconfile))

        strawdbus.start_services()

    def mainloop(self):
        threads = Config.get_instance().use_threads
        if threads: gtk.threads_enter()
        gtk.main()
        if threads: gtk.threads_leave()
        return

    def _initialize_config_from_environment(self):
        threads = os.getenv('STRAW_THREAD_DNS') is not None
        if threads:
            try:
                gtk.threads_init()
            except:
                threads = False
            else:
                threads = True

        config = Config.get_instance()
        config.use_threads = threads
        config.reload_css = os.getenv('STRAW_RELOAD_CSS') is not None
        config.no_etags = os.getenv('STRAW_NO_ETAGS') is not None
        return

    def _initialize_gettext(self):
        import locale
        lname = constants.APPNAME.lower()
        try:
            localedir = utils.find_locale_dir()
            gettext.bindtextdomain(lname, localedir)
            gettext.textdomain(lname)
            gettext.install(lname, localedir, unicode=1)
            gtk.glade.bindtextdomain(lname, localedir)
        except IOError:
            def broken_gettext_workaround(s):
                return s
            __builtins__.__dict__['_'] = broken_gettext_workaround
        locale.setlocale(locale.LC_ALL, '')
        return

    def load_tray(self):
        from Tray import Tray
        tray = Tray()
        tray.connect('button_press_event', self._tray_clicked)

    def _tray_clicked(self, signal, event):
        if event.button == 1:
            self._main_presenter.view.present()
            self._main_presenter.scroll_or_display_next_unread_item()
        else:
            self._main_presenter.view.should_present()
        return


syntax highlighted by Code2HTML, v. 0.9.1