Esempio n. 1
0
class AvailablePane(SoftwarePane):
    """Widget that represents the available panel in software-center
       It contains a search entry and navigation buttons
    """

    class Pages():
        # page names, useful for debugging
        NAMES = ('lobby',
                 'subcategory',
                 'list',
                 'details',
                 'purchase',
                 )
        # actual page id's
        (LOBBY,
         SUBCATEGORY,
         LIST,
         DETAILS,
         PURCHASE,
         PREVIOUS_PURCHASES) = range(6)
        # the default page
        HOME = LOBBY

    __gsignals__ = {'available-pane-created': (GObject.SignalFlags.RUN_FIRST,
                                               None,
                                               ())}

    class TransactionDetails(object):
        """ Simple class to keep track of aptdaemon transaction details """
        def __init__(self, db, pkgname, appname, trans_id, trans_type):
            self.db = db
            self.app = Application(pkgname=pkgname, appname=appname)
            self.trans_id = trans_id
            self.trans_type = trans_type
            self.__app_details = None
            self.__real_desktop = None

            if trans_type != TransactionTypes.INSTALL:
                self.guess_final_desktop_file()

        @property
        def app_details(self):
            if not self.__app_details:
                self.__app_details = self.app.get_details(self.db)
            return self.__app_details

        @property
        def desktop_file(self):
            return self.app_details.desktop_file

        @property
        def final_desktop_file(self):
            return self.guess_final_desktop_file()

        def guess_final_desktop_file(self):
            if self.__real_desktop:
                return self.__real_desktop

            # convert the app-install desktop file location to the actual installed
            # desktop file location (or in the case of a purchased item from the
            # agent, generate the correct installed desktop file location)
            desktop_file = (
                convert_desktop_file_to_installed_location(self.desktop_file,
                                                           self.app.pkgname))
            # we only add items to the launcher that have a desktop file
            if not desktop_file:
                return
            # do not add apps that have no Exec entry in their desktop file
            # (e.g. wine, see LP: #848437 or ubuntu-restricted-extras,
            # see LP: #913756), also, don't add the item if NoDisplay is
            # specified (see LP: #1006483)
            if (os.path.exists(desktop_file) and
                    (not get_exec_line_from_desktop(desktop_file) or
                    is_no_display_desktop_file(desktop_file))):
                return

            self.__real_desktop = desktop_file
            return self.__real_desktop

    def __init__(self,
                 cache,
                 db,
                 distro,
                 icons,
                 navhistory_back_action,
                 navhistory_forward_action):
        # parent
        SoftwarePane.__init__(self, cache, db, distro, icons)
        self.searchentry.set_sensitive(False)
        # navigation history actions
        self.navhistory_back_action = navhistory_back_action
        self.navhistory_forward_action = navhistory_forward_action
        # configure any initial state attrs
        self.state.filter = AppFilter(db, cache)
        # the spec says we mix installed/not installed
        #self.apps_filter.set_not_installed_only(True)
        self.current_app_by_category = {}
        self.current_app_by_subcategory = {}
        self.pane_name = _("Get Software")

        # views to be created in init_view
        self.cat_view = None
        self.subcategories_view = None

        # integrate with the Unity launcher
        self.unity_launcher = UnityLauncher()

        # keep track of transactions
        self.transactions_queue = {}

    def init_view(self):
        if self.view_initialized:
            return

        self.show_appview_spinner()

        window = self.get_window()
        if window is not None:
            window.set_cursor(self.busy_cursor)

        with ExecutionTime("AvailablePane.init_view pending events"):
            while Gtk.events_pending():
                Gtk.main_iteration()

        with ExecutionTime("SoftwarePane.init_view()"):
            SoftwarePane.init_view(self)
        # set the AppTreeView model, available pane uses list models
        with ExecutionTime("create AppListStore"):
            liststore = AppListStore(self.db, self.cache, self.icons)
        #~ def on_appcount_changed(widget, appcount):
            #~ self.subcategories_view._append_appcount(appcount)
            #~ self.app_view._append_appcount(appcount)
        #~ liststore.connect('appcount-changed', on_appcount_changed)
        self.app_view.set_model(liststore)
        liststore.connect("needs-refresh",
            lambda helper, pkgname: self.app_view.queue_draw())

        # purchase view
        self.purchase_view = PurchaseView()
        app_manager = get_appmanager()
        app_manager.connect("purchase-requested",
            self.on_purchase_requested)
        self.purchase_view.connect("purchase-succeeded",
            self.on_purchase_succeeded)
        self.purchase_view.connect("purchase-failed",
            self.on_purchase_failed)
        self.purchase_view.connect("purchase-cancelled-by-user",
            self.on_purchase_cancelled_by_user)
        self.purchase_view.connect("terms-of-service-declined",
            self.on_terms_of_service_declined)
        self.purchase_view.connect("purchase-needs-spinner",
            self.on_purchase_needs_spinner)

        # categories, appview and details into the notebook in the bottom
        self.scroll_categories = Gtk.ScrolledWindow()
        self.scroll_categories.set_policy(Gtk.PolicyType.AUTOMATIC,
                                        Gtk.PolicyType.AUTOMATIC)
        with ExecutionTime("create LobbyView"):
            self.cat_view = LobbyView(
                self.cache, self.db, self.icons, self.apps_filter)
        self.scroll_categories.add(self.cat_view)
        self.notebook.append_page(self.scroll_categories,
            Gtk.Label(label="categories"))

        # sub-categories view
        with ExecutionTime("create SubCategoryView"):
            self.subcategories_view = SubCategoryView(
                self.cache, self.db, self.icons, self.apps_filter,
                root_category=self.cat_view.categories[0])
        self.subcategories_view.connect(
            "category-selected", self.on_subcategory_activated)
        self.subcategories_view.connect(
            "show-category-applist", self.on_show_category_applist)
        self.subcategories_view.connect(
            "application-activated", self.on_application_activated)
        self.scroll_subcategories = Gtk.ScrolledWindow()
        self.scroll_subcategories.set_policy(
            Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        self.scroll_subcategories.add(self.subcategories_view)
        self.notebook.append_page(self.scroll_subcategories,
                                    Gtk.Label(label=NavButtons.SUBCAT))

        # app list
        self.notebook.append_page(self.box_app_list,
                                    Gtk.Label(label=NavButtons.LIST))

        self.cat_view.connect(
            "category-selected", self.on_category_activated)
        self.cat_view.connect(
            "application-activated", self.on_application_activated)

        # details
        self.notebook.append_page(self.scroll_details,
            Gtk.Label(label=NavButtons.DETAILS))

        # purchase view
        self.notebook.append_page(self.purchase_view,
            Gtk.Label(label=NavButtons.PURCHASE))

        # install backend
        # FIXME: move this out of the available pane really
        self.backend.connect("transaction-started",
            self.on_transaction_started)
        self.backend.connect("transactions-changed",
            self.on_transactions_changed)
        self.backend.connect("transaction-finished",
            self.on_transaction_complete)
        # a transaction error is treated the same as a cancellation
        self.backend.connect("transaction-stopped",
            self.on_transaction_cancelled)
        self.backend.connect("transaction-cancelled",
            self.on_transaction_cancelled)

        # now we are initialized
        self.searchentry.set_sensitive(True)
        self.emit("available-pane-created")
        self.show_all()
        self.hide_appview_spinner()

        # consider the view initialized here already as display_page()
        # may run into a endless recursion otherwise (it will call init_view())
        # again (LP: #851671)
        self.view_initialized = True

        # important to "seed" the initial history stack (LP: #1005104)
        vm = get_viewmanager()
        vm.display_page(self, self.Pages.LOBBY, self.state)

        if window is not None:
            window.set_cursor(None)

    def on_purchase_requested(self, appmanager, app, iconname, url):
        if self.purchase_view.initiate_purchase(app, iconname, url):
            vm = get_viewmanager()
            vm.display_page(self, self.Pages.PURCHASE, self.state)

    def on_purchase_needs_spinner(self, appmanager, active):
        vm = get_viewmanager()
        vm.set_spinner_active(active)

    def on_purchase_succeeded(self, widget):
        # switch to the details page to display the transaction is in progress
        self._return_to_appdetails_view()

    def on_purchase_failed(self, widget):
        self._return_to_appdetails_view()
        dialogs.error(None,
                      _("Failure in the purchase process."),
                      _("Sorry, something went wrong. Your payment "
                        "has been cancelled."))

    def on_purchase_cancelled_by_user(self, widget):
        self._return_to_appdetails_view()

    def on_terms_of_service_declined(self, widget):
        """ The Terms of Service dialog was declined by the user, so we just
            reset the purchase button in case they want another chance
        """
        if self.is_app_details_view_showing():
            self.app_details_view.pkg_statusbar.button.set_sensitive(True)
        elif self.is_applist_view_showing():
            self.app_view.tree_view.reset_action_button()

    def _return_to_appdetails_view(self):
        vm = get_viewmanager()
        vm.nav_back()
        # don't keep the purchase view in navigation history
        # as its contents are no longer valid
        vm.clear_forward_history()
        window = self.get_window()
        if window is not None:
            window.set_cursor(None)

    def get_query(self):
        """helper that gets the query for the current category/search mode"""
        # NoDisplay is a special case
        if self._in_no_display_category():
            return xapian.Query()
        # get current sub-category (or category, but sub-category wins)
        query = None

        if self.state.channel and self.state.channel.query:
            query = self.state.channel.query
        elif self.state.subcategory:
            query = self.state.subcategory.query
        elif self.state.category:
            query = self.state.category.query
        # mix channel/category with the search terms and return query
        return self.db.get_query_list_from_search_entry(
                            self.state.search_term, query)

    def _in_no_display_category(self):
        """return True if we are in a category with NoDisplay set in the XML"""
        return (self.state.category and
                self.state.category.dont_display and
                not self.state.subcategory and
                not self.state.search_term)

    def _get_header_for_view_state(self, view_state):
        channel = view_state.channel
        category = view_state.category
        subcategory = view_state.subcategory

        line1 = None
        line2 = None
        if channel is not None:
            name = channel.display_name or channel.name
            line1 = GLib.markup_escape_text(name)
        elif subcategory is not None:
            line1 = GLib.markup_escape_text(category.name)
            line2 = GLib.markup_escape_text(subcategory.name)
        elif category is not None:
            line1 = GLib.markup_escape_text(category.name)
        else:
            line1 = _("All Software")
        return line1, line2

    #~ def _show_hide_subcategories(self, show_category_applist=False):
        #~ # check if have subcategories and are not in a subcategory
        #~ # view - if so, show it
        #~ current_page = self.notebook.get_current_page()
        #~ if (current_page == self.Pages.LOBBY or
            #~ current_page == self.Pages.DETAILS):
            #~ return
        #~ if (not show_category_applist and
            #~ self.state.category and
            #~ self.state.category.subcategories and
            #~ not (self.state.search_term or self.state.subcategory)):
            #~ self.subcategories_view.set_subcategory(self.state.category,
                #~ num_items=len(self.app_view.get_model()))
            #~ self.notebook.set_current_page(self.Pages.SUBCATEGORY)
        #~ else:
            #~ self.notebook.set_current_page(self.Pages.LIST)

    def get_current_app(self):
        """return the current active application object"""
        if self.is_category_view_showing():
            return None
        else:
            if self.state.subcategory:
                return self.current_app_by_subcategory.get(
                    self.state.subcategory)
            else:
                return self.current_app_by_category.get(self.state.category)

    def get_current_category(self):
        """ return the current category that is in use or None """
        if self.state.subcategory:
            return self.state.subcategory
        elif self.state.category:
            return self.state.category

    def unset_current_category(self):
        """ unset the current showing category, but keep e.g. the current
            search
        """
        self.state.category = None
        self.state.subcategory = None
        # reset the non-global filters see (LP: #985389)
        if self.state.filter:
            self.state.filter.reset()

    def on_transaction_started(self, backend, pkgname, appname, trans_id,
                               trans_type):
        details = self.TransactionDetails(self.db, pkgname, appname, trans_id, trans_type)
        self.transactions_queue[pkgname] = details

        config = get_config()
        if (trans_type == TransactionTypes.INSTALL and
            trans_id != PURCHASE_TRANSACTION_ID and
                config.add_to_unity_launcher and
                softwarecenter.utils.is_unity_running()):
            if details.desktop_file is not None:
                self._add_application_to_unity_launcher(details)

    def on_transaction_cancelled(self, backend, result):
        """ handle a transaction that has been cancelled
        """
        if result.pkgname:
            self.unity_launcher.cancel_application_to_launcher(result.pkgname)
        if result.pkgname in self.transactions_queue:
            self.transactions_queue.pop(result.pkgname)

    def on_transactions_changed(self, backend, pending_transactions):
        """internal helper that keeps the action bar up-to-date by
           keeping track of the transaction-started signals
        """
        if self._is_custom_list_search(self.state.search_term):
            self._update_action_bar()

    def _add_application_to_unity_launcher(self, trans_details):
        # do not add apps that have no Exec entry in their desktop file
        # (e.g. wine, see LP: #848437 or ubuntu-restricted-extras,
        # see LP: #913756), also, don't add the item if NoDisplay is
        # specified (see LP: #1006483)
        if (os.path.exists(trans_details.desktop_file) and
                (not get_exec_line_from_desktop(trans_details.desktop_file) or
                 is_no_display_desktop_file(trans_details.desktop_file))):
            return

        # now gather up the unity launcher info items and send the app to the
        # launcher service
        launcher_info = self._get_unity_launcher_info(trans_details)
        self.unity_launcher.send_application_to_launcher(
            trans_details.app.pkgname, launcher_info)

    def on_transaction_complete(self, backend, result):
        """ handle a transaction that has completed successfully
        """
        if result.pkgname in self.transactions_queue:
            details = self.transactions_queue.pop(result.pkgname)

            if details.trans_type == TransactionTypes.INSTALL:
                ZeitgeistLogger(self.distro).log_install_event(details.final_desktop_file)
            elif details.trans_type == TransactionTypes.REMOVE:
                ZeitgeistLogger(self.distro).log_uninstall_event(details.final_desktop_file)

    def _get_unity_launcher_info(self, trans_details):
        (icon_size, icon_x, icon_y) = (
                self._get_onscreen_icon_details_for_launcher_service(trans_details.app))
        icon_path = get_file_path_from_iconname(
                                self.icons,
                                iconname=trans_details.app_details.icon)
        launcher_info = UnityLauncherInfo(trans_details.app.name,
                                          trans_details.app_details.icon,
                                          icon_path,
                                          icon_x,
                                          icon_y,
                                          icon_size,
                                          trans_details.desktop_file,
                                          trans_details.trans_id)
        return launcher_info

    def _get_onscreen_icon_details_for_launcher_service(self, app):
        if self.is_app_details_view_showing():
            return self.app_details_view.get_app_icon_details()
        elif self.is_applist_view_showing():
            return self.app_view.get_app_icon_details()
        else:
            # set a default, even though we cannot install from the other panes
            return (0, 0, 0)

    def on_app_list_changed(self, pane, length):
        """internal helper that keeps the status text and the action
           bar up-to-date by keeping track of the app-list-changed
           signals
        """
        LOG.debug("applist-changed %s %s" % (pane, length))
        super(AvailablePane, self).on_app_list_changed(pane, length)
        self._update_action_bar()

    def _update_action_bar(self):
        self._update_action_bar_buttons()

    def _update_action_bar_buttons(self):
        """Update buttons in the action bar to implement the custom package
           lists feature, see
           https://wiki.ubuntu.com/SoftwareCenter#Custom%20package%20lists
        """
        if self._is_custom_list_search(self.state.search_term):
            installable = []
            for doc in self.enquirer.get_documents():
                pkgname = self.db.get_pkgname(doc)
                if (pkgname in self.cache and
                        not self.cache[pkgname].is_installed and
                        not len(self.backend.pending_transactions) > 0):
                    app = Application(pkgname=pkgname)
                    installable.append(app)
            button_text = gettext.ngettext(
                "Install %(amount)s Item",
                "Install %(amount)s Items",
                len(installable)) % {'amount': len(installable)}
            button = self.action_bar.get_button(ActionButtons.INSTALL)
            if button and installable:
                # Install all already offered. Update offer.
                if button.get_label() != button_text:
                    button.set_label(button_text)
            elif installable:
                # Install all not yet offered. Offer.
                self.action_bar.add_button(ActionButtons.INSTALL, button_text,
                                           self._install_current_appstore)
            else:
                # Install offered, but nothing to install. Clear offer.
                self.action_bar.remove_button(ActionButtons.INSTALL)
        else:
            # Ensure button is removed.
            self.action_bar.remove_button(ActionButtons.INSTALL)

    def _install_current_appstore(self):
        '''
        Function that installs all applications displayed in the pane.
        '''
        apps = []
        iconnames = []
        self.action_bar.remove_button(ActionButtons.INSTALL)
        for doc in self.enquirer.get_documents():
            pkgname = self.db.get_pkgname(doc)
            if (pkgname in self.cache and
                    not self.cache[pkgname].is_installed and
                    pkgname not in self.backend.pending_transactions):
                apps.append(self.db.get_application(doc))
                # add iconnames
                iconnames.append(self.db.get_iconname(doc))
        self.backend.install_multiple(apps, iconnames)

    def _show_or_hide_search_combo_box(self, view_state):
        # show/hide the sort combobox headers if the category forces a
        # custom sort mode
        category = view_state.category
        allow_user_sort = category is None or not category.is_forced_sort_mode
        self.app_view.set_allow_user_sorting(allow_user_sort)

    def set_state(self, nav_item):
        pass

    def _clear_search(self):
        self.searchentry.clear_with_no_signal()
        self.apps_limit = 0
        self.apps_search_term = ""
        self.state.search_term = ""

    def _is_custom_list_search(self, search_term):
        return (search_term and
                ',' in search_term)

    # callbacks
    def on_cache_ready(self, cache):
        """ refresh the application list when the cache is re-opened """
        # just re-draw in the available pane, nothing but the
        # "is-installed" overlay icon will change when something
        # is installed or removed in the available pane
        self.app_view.queue_draw()

    def on_search_terms_changed(self, widget, new_text):
        """callback when the search entry widget changes"""
        LOG.debug("on_search_terms_changed: %s" % new_text)

        # reset the flag in the app_view because each new search should
        # reset the sort criteria
        self.app_view.reset_default_sort_mode()

        self.state.search_term = new_text

        # do not hide technical items for a custom list search
        if self._is_custom_list_search(self.state.search_term):
            self.nonapps_visible = NonAppVisibility.ALWAYS_VISIBLE

        vm = get_viewmanager()
        adj = self.app_view.tree_view_scroll.get_vadjustment()
        if adj:
            adj.set_value(0.0)

        # yeah for special cases - as discussed on irc, mpt
        # wants this to return to the category screen *if*
        # we are searching but we are not in any category or channel
        if not self.state.category and not self.state.channel and not new_text:
            # category activate will clear search etc
            self.state.reset()
            vm.display_page(self, self.Pages.LOBBY, self.state)
        elif self.state.subcategory and not new_text:
            vm.display_page(self, self.Pages.LIST, self.state)
        elif (self.state.category and
              self.state.category.subcategories and not new_text):
            vm.display_page(self, self.Pages.SUBCATEGORY, self.state)
        else:
            vm.display_page(self, self.Pages.LIST, self.state)

        return False

    def on_db_reopen(self, db):
        """Called when the database is reopened."""
        super(AvailablePane, self).on_db_reopen(db)
        self.refresh_apps()
        if self.app_details_view:
            self.app_details_view.refresh_app()

    def enter_page(self, page, state):
        if page == self.Pages.LIST:
            if state.search_term:
                self.display_search_page(state)
            else:
                self.display_app_view_page(state)
        elif page == self.Pages.SUBCATEGORY:
            self.display_subcategory_page(state)
        elif page == self.Pages.DETAILS:
            self.display_details_page(state)
        elif page == self.Pages.PURCHASE:
            self.display_purchase(state)
        elif page == self.Pages.SUBCATEGORY:
            self.display_subcategory_page(state)
        elif page == self.Pages.PREVIOUS_PURCHASES:
            self.display_previous_purchases(state)
        else:
            # page is self.Pages.LOBBY or unknown
            self.display_lobby_page(state)

    def leave_page(self, state):
        # if previous page is a list view, then store the scroll positions
        if self.is_applist_view_showing():
            # store last adjustment to use later
            v = self.app_view.tree_view_scroll.get_vadjustment()
            self.state.vadjustment = v.get_value()
        elif self.is_app_details_view_showing():
            self.app_details_view.videoplayer.stop()

    def display_lobby_page(self, view_state):
        self.state.reset()
        self.hide_appview_spinner()
        self.emit("app-list-changed", len(self.db))
        self._clear_search()
        self.action_bar.clear()
        return True

    def display_list_page(self, view_state):
        header_strings = self._get_header_for_view_state(view_state)
        self.app_view.set_header_labels(*header_strings)
        self._show_or_hide_search_combo_box(view_state)

        self.app_view.vadj = view_state.vadjustment

        self.refresh_apps()
        return True

    def display_search_page(self, view_state):
        new_text = view_state.search_term
        # DTRT if the search is reset
        if not new_text:
            self._clear_search()
        else:
            self.state.limit = DEFAULT_SEARCH_LIMIT

        return self.display_list_page(view_state)

    def display_subcategory_page(self, view_state):
        category = view_state.category
        self.set_category(category)
        if self.state.search_term or self.searchentry.get_text():
            self._clear_search()
            self.refresh_apps()

        query = self.get_query()
        n_matches = self.quick_query_len(query)
        self.subcategories_view.set_subcategory(category, n_matches)

        self.action_bar.clear()
        return True

    def display_app_view_page(self, view_state):
        category = view_state.category
        subcategory = view_state.subcategory
        self.set_category(category)
        self.set_subcategory(subcategory)

        result = self.display_list_page(view_state)

        if view_state.search_term:
            self._clear_search()

        return result

    def display_details_page(self, view_state):
        if self.searchentry.get_text() != self.state.search_term:
            self.searchentry.set_text_with_no_signal(self.state.search_term)

        self.action_bar.clear()

        SoftwarePane.display_details_page(self, view_state)
        return True

    def display_purchase(self, view_state):
        self.notebook.set_current_page(self.Pages.PURCHASE)
        self.action_bar.clear()

    def display_previous_purchases(self, view_state):
        self.nonapps_visible = NonAppVisibility.ALWAYS_VISIBLE
        header_strings = self._get_header_for_view_state(view_state)
        self.app_view.set_header_labels(*header_strings)
        self.notebook.set_current_page(self.Pages.LIST)
        # clear any search terms
        self._clear_search()
        self.refresh_apps()
        self.action_bar.clear()
        return True

    def on_subcategory_activated(self, subcat_view, category):
        LOG.debug("on_subcategory_activated: %s %s" % (
                category.name, category))
        self.state.subcategory = category
        self.state.application = None
        page = AvailablePane.Pages.LIST

        vm = get_viewmanager()
        vm.display_page(self, page, self.state)

    def on_category_activated(self, lobby_view, category):
        """ callback when a category is selected """
        LOG.debug("on_category_activated: %s %s" % (
                category.name, category))

        if category.subcategories:
            page = self.Pages.SUBCATEGORY
        else:
            page = self.Pages.LIST

        self.state.category = category
        self.state.subcategory = None
        self.state.application = None

        vm = get_viewmanager()
        vm.display_page(self, page, self.state)

    def on_application_activated(self, appview, app):
        """Callback for when an app is activated."""
        super(AvailablePane, self).on_application_activated(appview, app)
        if self.state.subcategory:
            self.current_app_by_subcategory[self.state.subcategory] = app
        else:
            self.current_app_by_category[self.state.category] = app

    def on_show_category_applist(self, widget):
        self._show_hide_subcategories(show_category_applist=True)

    def on_previous_purchases_activated(self, query):
        """ called to activate the previous purchases view """
        #print cat_view, name, query
        LOG.debug("on_previous_purchases_activated with query: %s" % query)
        self.state.channel = SoftwareChannel("Previous Purchases",
                                             "software-center-agent",
                                             None, channel_query=query)
        vm = get_viewmanager()
        vm.display_page(self, self.Pages.PREVIOUS_PURCHASES, self.state)

    def is_category_view_showing(self):
        """Return whether a category/sub-category page is being displayed."""
        current_page = self.notebook.get_current_page()
        return current_page in (self.Pages.LOBBY, self.Pages.SUBCATEGORY)

    def is_purchase_view_showing(self):
        """Return whether a purchase page is being displayed."""
        current_page = self.notebook.get_current_page()
        return current_page == self.Pages.PURCHASE

    def set_subcategory(self, subcategory):
        LOG.debug('set_subcategory: %s' % subcategory)
        self.state.subcategory = subcategory
        self._apply_filters_for_category_or_subcategory(subcategory)

    def set_category(self, category):
        LOG.debug('set_category: %s' % category)
        self.state.category = category
        self._apply_filters_for_category_or_subcategory(category)

    def _apply_filters_for_category_or_subcategory(self, category):
        # apply flags
        if category:
            if 'nonapps-visible' in category.flags:
                self.nonapps_visible = NonAppVisibility.ALWAYS_VISIBLE
            else:
                self.nonapps_visible = NonAppVisibility.MAYBE_VISIBLE

        # apply any category based filters
        if not self.state.filter:
            self.state.filter = AppFilter(self.db, self.cache)

        if (category and category.flags and
                'available-only' in category.flags):
            self.state.filter.set_available_only(True)
        else:
            self.state.filter.set_available_only(False)

        if (category and category.flags and
                'not-installed-only' in category.flags):
            self.state.filter.set_not_installed_only(True)
        else:
            self.state.filter.set_not_installed_only(False)

    def refresh_apps(self, query=None):
        SoftwarePane.refresh_apps(self, query)
        # tell the lobby to update its content
        if self.cat_view:
            self.cat_view.refresh_apps()
        # and the subcat view as well...
        if self.subcategories_view:
            self.subcategories_view.refresh_apps()
Esempio n. 2
0
    def init_view(self):
        if self.view_initialized:
            return

        self.show_appview_spinner()

        window = self.get_window()
        if window is not None:
            window.set_cursor(self.busy_cursor)

        with ExecutionTime("AvailablePane.init_view pending events"):
            while Gtk.events_pending():
                Gtk.main_iteration()

        with ExecutionTime("SoftwarePane.init_view()"):
            SoftwarePane.init_view(self)
        # set the AppTreeView model, available pane uses list models
        with ExecutionTime("create AppListStore"):
            liststore = AppListStore(self.db, self.cache, self.icons)
        #~ def on_appcount_changed(widget, appcount):
            #~ self.subcategories_view._append_appcount(appcount)
            #~ self.app_view._append_appcount(appcount)
        #~ liststore.connect('appcount-changed', on_appcount_changed)
        self.app_view.set_model(liststore)
        liststore.connect("needs-refresh",
            lambda helper, pkgname: self.app_view.queue_draw())

        # purchase view
        self.purchase_view = PurchaseView()
        app_manager = get_appmanager()
        app_manager.connect("purchase-requested",
            self.on_purchase_requested)
        self.purchase_view.connect("purchase-succeeded",
            self.on_purchase_succeeded)
        self.purchase_view.connect("purchase-failed",
            self.on_purchase_failed)
        self.purchase_view.connect("purchase-cancelled-by-user",
            self.on_purchase_cancelled_by_user)
        self.purchase_view.connect("terms-of-service-declined",
            self.on_terms_of_service_declined)
        self.purchase_view.connect("purchase-needs-spinner",
            self.on_purchase_needs_spinner)

        # categories, appview and details into the notebook in the bottom
        self.scroll_categories = Gtk.ScrolledWindow()
        self.scroll_categories.set_policy(Gtk.PolicyType.AUTOMATIC,
                                        Gtk.PolicyType.AUTOMATIC)
        with ExecutionTime("create LobbyView"):
            self.cat_view = LobbyView(
                self.cache, self.db, self.icons, self.apps_filter)
        self.scroll_categories.add(self.cat_view)
        self.notebook.append_page(self.scroll_categories,
            Gtk.Label(label="categories"))

        # sub-categories view
        with ExecutionTime("create SubCategoryView"):
            self.subcategories_view = SubCategoryView(
                self.cache, self.db, self.icons, self.apps_filter,
                root_category=self.cat_view.categories[0])
        self.subcategories_view.connect(
            "category-selected", self.on_subcategory_activated)
        self.subcategories_view.connect(
            "show-category-applist", self.on_show_category_applist)
        self.subcategories_view.connect(
            "application-activated", self.on_application_activated)
        self.scroll_subcategories = Gtk.ScrolledWindow()
        self.scroll_subcategories.set_policy(
            Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        self.scroll_subcategories.add(self.subcategories_view)
        self.notebook.append_page(self.scroll_subcategories,
                                    Gtk.Label(label=NavButtons.SUBCAT))

        # app list
        self.notebook.append_page(self.box_app_list,
                                    Gtk.Label(label=NavButtons.LIST))

        self.cat_view.connect(
            "category-selected", self.on_category_activated)
        self.cat_view.connect(
            "application-activated", self.on_application_activated)

        # details
        self.notebook.append_page(self.scroll_details,
            Gtk.Label(label=NavButtons.DETAILS))

        # purchase view
        self.notebook.append_page(self.purchase_view,
            Gtk.Label(label=NavButtons.PURCHASE))

        # install backend
        # FIXME: move this out of the available pane really
        self.backend.connect("transaction-started",
            self.on_transaction_started)
        self.backend.connect("transactions-changed",
            self.on_transactions_changed)
        self.backend.connect("transaction-finished",
            self.on_transaction_complete)
        # a transaction error is treated the same as a cancellation
        self.backend.connect("transaction-stopped",
            self.on_transaction_cancelled)
        self.backend.connect("transaction-cancelled",
            self.on_transaction_cancelled)

        # now we are initialized
        self.searchentry.set_sensitive(True)
        self.emit("available-pane-created")
        self.show_all()
        self.hide_appview_spinner()

        # consider the view initialized here already as display_page()
        # may run into a endless recursion otherwise (it will call init_view())
        # again (LP: #851671)
        self.view_initialized = True

        # important to "seed" the initial history stack (LP: #1005104)
        vm = get_viewmanager()
        vm.display_page(self, self.Pages.LOBBY, self.state)

        if window is not None:
            window.set_cursor(None)