# Copyright (C) 2009,2010 Canonical
#
# Authors:
# Michael Vogt
#
# 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; version 3.
#
# 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.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import logging
from gi.repository import Gtk, GObject
from gettext import gettext as _
from softwarecenter.enums import SortMethods
from softwarecenter.ui.gtk3.em import StockEms
from softwarecenter.ui.gtk3.models.appstore2 import AppTreeStore
from softwarecenter.ui.gtk3.widgets.apptreeview import AppTreeView
from softwarecenter.ui.gtk3.models.appstore2 import AppPropertiesHelper
from softwarecenter.utils import ExecutionTime
LOG = logging.getLogger(__name__)
class AppView(Gtk.VBox):
__gsignals__ = {
"sort-method-changed": (GObject.SignalFlags.RUN_LAST,
None,
(GObject.TYPE_PYOBJECT, ),
),
"application-activated": (GObject.SignalFlags.RUN_LAST,
None,
(GObject.TYPE_PYOBJECT, ),
),
"application-selected": (GObject.SignalFlags.RUN_LAST,
None,
(GObject.TYPE_PYOBJECT, ),
),
}
(INSTALLED_MODE, AVAILABLE_MODE, DIFF_MODE) = range(3)
_SORT_METHOD_INDEX = (SortMethods.BY_ALPHABET,
SortMethods.BY_TOP_RATED,
SortMethods.BY_CATALOGED_TIME,
SortMethods.BY_SEARCH_RANKING,
)
# indices that relate to the above tuple
_SORT_BY_ALPHABET = 0
_SORT_BY_TOP_RATED = 1
_SORT_BY_NEWEST_FIRST = 2
_SORT_BY_SEARCH_RANKING = 3
def __init__(self, db, cache, icons, show_ratings):
Gtk.VBox.__init__(self)
#~ self.set_name("app-view")
# app properties helper
with ExecutionTime("Appview.__init__ create AppPropertiesHelper"):
self.helper = AppPropertiesHelper(db, cache, icons)
# misc internal containers
self.header_hbox = Gtk.HBox()
self.header_hbox.set_border_width(StockEms.MEDIUM)
self.pack_start(self.header_hbox, False, False, 0)
self.tree_view_scroll = Gtk.ScrolledWindow()
self.pack_start(self.tree_view_scroll, True, True, 0)
# category label
self.header_label = Gtk.Label()
self.header_label.set_use_markup(True)
self.header_hbox.pack_start(self.header_label, False, False, 0)
# sort methods comboboxs
# variant 1 includes sort by search relevance
self.sort_methods_combobox = self._get_sort_methods_combobox()
combo_alignment = Gtk.Alignment.new(0.5, 0.5, 1.0, 0.0)
combo_alignment.add(self.sort_methods_combobox)
self.header_hbox.pack_end(combo_alignment, False, False, 0)
# content views
self.tree_view = AppTreeView(self, db, icons,
show_ratings, store=None)
self.tree_view_scroll.add(self.tree_view)
self.appcount = None
self.vadj = 0.0
# list view sorting stuff
self._force_default_sort_method = True
self._handler = self.sort_methods_combobox.connect(
"changed",
self.on_sort_method_changed)
#~ def on_draw(self, w, cr):
#~ cr.set_source_rgb(1,1,1)
#~ cr.paint()
def _append_appcount(self, appcount, mode=AVAILABLE_MODE):
#~
#~ if mode == self.INSTALLED_MODE:
#~ text = gettext.ngettext("%(amount)s item installed",
#~ "%(amount)s items installed",
#~ appcount) % { 'amount' : appcount, }
#~ elif mode == self.DIFF_MODE:
#~ text = gettext.ngettext("%(amount)s item",
#~ "%(amount)s items",
#~ appcount) % { 'amount' : appcount, }
#~ else:
#~ text = gettext.ngettext("%(amount)s item available",
#~ "%(amount)s items available",
#~ appcount) % { 'amount' : appcount, }
#~
#~ if not self.appcount:
#~ self.appcount = Gtk.Label()
#~ self.appcount.set_alignment(0.5, 0.5)
#~ self.appcount.set_margin_top(4)
#~ self.appcount.set_margin_bottom(3)
#~ self.appcount.connect("draw", self.on_draw)
#~ self.vbox.pack_start(self.appcount, False, False, 0)
#~ self.appcount.set_text(text)
#~ self.appcount.show()
pass
def on_sort_method_changed(self, *args):
self.vadj = 0.0
self.emit("sort-method-changed", self.sort_methods_combobox)
def _get_sort_methods_combobox(self):
combo = Gtk.ComboBoxText.new()
combo.append_text(_("By Name"))
combo.append_text(_("By Top Rated"))
combo.append_text(_("By Newest First"))
combo.append_text(_("By Relevance"))
combo.set_active(self._SORT_BY_TOP_RATED)
return combo
def _get_combo_children(self):
return len(self.sort_methods_combobox.get_model())
def _use_combobox_with_sort_by_search_ranking(self):
if self._get_combo_children() == 4:
return
self.sort_methods_combobox.append_text(_("By Relevance"))
def _use_combobox_without_sort_by_search_ranking(self):
if self._get_combo_children() == 3:
return
self.sort_methods_combobox.remove(self._SORT_BY_SEARCH_RANKING)
self.set_sort_method_with_no_signal(self._SORT_BY_TOP_RATED)
def set_sort_method_with_no_signal(self, sort_method):
combo = self.sort_methods_combobox
combo.handler_block(self._handler)
combo.set_active(sort_method)
combo.handler_unblock(self._handler)
def set_allow_user_sorting(self, do_allow):
self.sort_methods_combobox.set_visible(do_allow)
def set_header_labels(self, first_line, second_line):
if second_line:
markup = '%s\n%s' % (first_line, second_line)
else:
markup = "%s" % first_line
return self.header_label.set_markup(markup)
def set_model(self, model):
self.tree_view.set_model(model)
def get_model(self):
return self.tree_view.appmodel
def display_matches(self, matches, is_search=False):
# FIXME: installedpane handles display of the trees intimately,
# so for the time being lets just return None in the case of our
# TreeView displaying an AppTreeStore ... ;(
# ... also we don't currently support user sorting in the
# installedview, so issue is somewhat moot for the time being...
if isinstance(self.get_model(), AppTreeStore):
LOG.debug("display_matches called on AppTreeStore, ignoring")
return
model = self.get_model()
# disconnect the model from the view before running
# set_from_matches to ensure that the _cell_data_func_cb is not
# run when the placeholder items are set, otherwise the purpose
# of the "load-on-demand" is gone and it leads to bugs like
# LP: #964433
self.set_model(None)
if model:
model.set_from_matches(matches)
self.set_model(model)
adj = self.tree_view_scroll.get_vadjustment()
if adj:
adj.set_lower(self.vadj)
adj.set_value(self.vadj)
def reset_default_sort_mode(self):
""" force the appview to reset to the default sort method without
doing a refresh or sending any signals
"""
self._force_default_sort_method = True
def configure_sort_method(self, is_search=False):
""" configures the sort method UI appropriately based on current
conditions, including whether a search is in progress.
Note that this will not change the users current sort method,
if that is the intention, call reset_default_sort_mode()
"""
# figure out what combobox we need
if is_search:
self._use_combobox_with_sort_by_search_ranking()
else:
self._use_combobox_without_sort_by_search_ranking()
# and what sorting
if self._force_default_sort_method:
# always reset this, its the job of the user of the appview
# to call reset_default_sort_mode() to reset this
self._force_default_sort_method = False
# and now set the default sort depending on if its a view or not
if is_search:
self.set_sort_method_with_no_signal(
self._SORT_BY_SEARCH_RANKING)
else:
self.set_sort_method_with_no_signal(
self._SORT_BY_TOP_RATED)
def clear_model(self):
return self.tree_view.clear_model()
def get_sort_mode(self):
active_index = self.sort_methods_combobox.get_active()
return self._SORT_METHOD_INDEX[active_index]
def get_app_icon_details(self):
""" helper for unity dbus support to provide details about the
application icon as it is displayed on-screen
"""
icon_size = self._get_app_icon_size_on_screen()
(icon_x, icon_y) = self._get_app_icon_xy_position_on_screen()
return (icon_size, icon_x, icon_y)
def _get_app_icon_size_on_screen(self):
""" helper for unity dbus support to get the size of the maximum side
for the application icon as it is displayed on-screen
"""
icon_size = 32
if (self.tree_view.selected_row_renderer and
self.tree_view.selected_row_renderer.icon):
pb = self.tree_view.selected_row_renderer.icon
if pb.get_width() > pb.get_height():
icon_size = pb.get_width()
else:
icon_size = pb.get_height()
return icon_size
def _get_app_icon_xy_position_on_screen(self):
""" helper for unity dbus support to get the x,y position of
the application icon as it is displayed on-screen
"""
# find top-level parent
parent = self
while parent.get_parent():
parent = parent.get_parent()
# get top-level window position
(px, py) = parent.get_position()
# and return the coordinate values
if self.tree_view.selected_row_renderer:
return (px + self.tree_view.selected_row_renderer.icon_x_offset,
py + self.tree_view.selected_row_renderer.icon_y_offset)
else:
return (px, py)