25
25
from gettext import gettext as _
27
from softwarecenter.enums import (Icons,
27
from softwarecenter.enums import (Icons,
31
from softwarecenter.utils import ExecutionTime, SimpleFileDownloader, split_icon_ext
31
from softwarecenter.utils import (
32
36
from softwarecenter.backend import get_install_backend
33
37
from softwarecenter.backend.reviews import get_review_loader
34
38
from softwarecenter.paths import SOFTWARE_CENTER_ICON_CACHE_DIR
44
48
LOG = logging.getLogger(__name__)
45
49
_FREE_AS_IN_BEER = ("0.00", "")
47
52
class CategoryRowReference:
48
""" A simple container for Category properties to be
53
""" A simple container for Category properties to be
49
54
displayed in a AppListStore or AppTreeStore
82
"needs-refresh" : (GObject.SignalFlags.RUN_LAST,
85
"needs-refresh": (GObject.SignalFlags.RUN_LAST,
88
def __init__(self, db, cache, icons, icon_size=48, global_icon_cache=False):
91
def __init__(self, db, cache, icons, icon_size=48,
92
global_icon_cache=False):
89
93
GObject.GObject.__init__(self)
102
106
self.icons = icons
103
107
self.icon_size = icon_size
105
# cache the 'missing icon' used in the treeview for apps without an icon
109
# cache the 'missing icon' used in the treeview for apps without an
106
111
self._missing_icon = icons.load_icon(Icons.MISSING_APP, icon_size, 0)
107
112
if global_icon_cache:
108
113
self.icon_cache = _app_icon_cache
110
115
self.icon_cache = {}
113
117
def _download_icon_and_show_when_ready(self, url, pkgname, icon_file_name):
114
LOG.debug("did not find the icon locally, must download %s" % icon_file_name)
118
LOG.debug("did not find the icon locally, must download %s" %
116
121
def on_image_download_complete(downloader, image_file_path, pkgname):
117
122
LOG.debug("download for '%s' complete" % image_file_path)
118
123
pb = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_file_path,
121
# replace the icon in the icon_cache now that we've got the real one
126
# replace the icon in the icon_cache now that we've got the real
122
128
icon_file = split_icon_ext(os.path.basename(image_file_path))
123
129
self.icon_cache[icon_file] = pb
124
130
self.emit("needs-refresh", pkgname)
126
132
if url is not None:
127
icon_file_path = os.path.join(SOFTWARE_CENTER_ICON_CACHE_DIR, icon_file_name)
133
icon_file_path = os.path.join(SOFTWARE_CENTER_ICON_CACHE_DIR,
128
135
image_downloader = SimpleFileDownloader()
129
image_downloader.connect('file-download-complete', on_image_download_complete, pkgname)
136
image_downloader.connect('file-download-complete',
137
on_image_download_complete, pkgname)
130
138
image_downloader.download_file(url, icon_file_path)
132
140
def update_availability(self, doc):
153
160
def is_purchasable(self, doc):
154
161
if doc.purchasable is None:
155
doc.purchasable = doc.get_value(XapianValues.PRICE) not in _FREE_AS_IN_BEER
162
doc.purchasable = (doc.get_value(XapianValues.PRICE) not in
156
164
return doc.purchasable
158
166
def get_pkgname(self, doc):
195
203
# icons.load_icon takes between 0.001 to 0.01s on my
196
204
# machine, this is a significant burden because get_value
197
205
# is called *a lot*. caching is the only option
199
207
# look for the icon on the iconpath
200
208
if self.icons.has_icon(icon_name):
201
209
icon = self.icons.load_icon(icon_name, self.icon_size, 0)
226
234
def _category_translate(self, catname):
227
""" helper that will look into the categories we got from the
235
""" helper that will look into the categories we got from the
228
236
parser and returns the translated name if it find it,
229
237
otherwise it resorts to plain gettext
231
# look into parsed categories that use .directory translation
239
# look into parsed categories that use .directory translation
232
240
for cat in self.all_categories:
233
241
if cat.untranslated_name == catname:
273
281
# the amount of items to initially lo
276
284
def __init__(self, db, cache, icons, icon_size, global_icon_cache):
277
AppPropertiesHelper.__init__(self, db, cache, icons, icon_size,
285
AppPropertiesHelper.__init__(self, db, cache, icons, icon_size,
278
286
global_icon_cache)
281
289
self.backend = get_install_backend()
282
self.backend.connect("transaction-progress-changed", self._on_transaction_progress_changed)
283
self.backend.connect("transaction-started", self._on_transaction_started)
284
self.backend.connect("transaction-finished", self._on_transaction_finished)
290
self.backend.connect("transaction-progress-changed",
291
self._on_transaction_progress_changed)
292
self.backend.connect("transaction-started",
293
self._on_transaction_started)
294
self.backend.connect("transaction-finished",
295
self._on_transaction_finished)
286
297
# keep track of paths for transactions in progress
287
298
self.transaction_path_map = {}
307
318
def notify_action_request(self, doc, path):
308
319
pkgname = str(self.get_pkgname(doc))
309
320
self.transaction_path_map[pkgname] = (path, self.get_iter(path))
312
322
def set_from_matches(self, matches):
314
324
raise NotImplementedError
316
326
# the following methods ensure that the contents data is refreshed
317
# whenever a transaction potentially changes it:
318
def _on_transaction_started(self, backend, pkgname, appname, trans_id, trans_type):
327
# whenever a transaction potentially changes it:
328
def _on_transaction_started(self, backend, pkgname, appname, trans_id,
319
330
#~ self._refresh_transaction_map()
323
334
if pkgname in self.transaction_path_map:
324
335
path, it = self.transaction_path_map[pkgname]
325
336
self.row_changed(path, it)
328
338
def _on_transaction_finished(self, backend, result):
329
339
pkgname = str(result.pkgname)
356
366
#~ print "Appstore buffered icons in %s seconds" % t_lapsed
357
367
#from softwarecenter.utils import get_nice_size
358
368
#~ cache_size = get_nice_size(sys.getsizeof(_app_icon_cache))
359
#~ print "Number of icons in cache: %s consuming: %sb" % (len(_app_icon_cache), cache_size)
369
#~ print "Number of icons in cache: %s consuming: %sb" % (
370
#~ len(_app_icon_cache), cache_size)
360
371
return False # remove from sources on completion
362
373
if self.current_matches is not None:
363
374
GObject.idle_add(buffer_icons)
366
376
def load_range(self, indices, step):
370
381
class AppListStore(Gtk.ListStore, AppGenericStore):
371
382
""" use for flat applist views. for large lists this appends rows approx
376
"appcount-changed" : (GObject.SignalFlags.RUN_LAST,
378
(GObject.TYPE_PYOBJECT, ),
387
"appcount-changed": (GObject.SignalFlags.RUN_LAST,
389
(GObject.TYPE_PYOBJECT, ),
380
391
# meh, this is a signal from AppPropertiesHelper
381
"needs-refresh" : (GObject.SignalFlags.RUN_LAST,
392
"needs-refresh": (GObject.SignalFlags.RUN_LAST,
387
def __init__(self, db, cache, icons, icon_size=AppGenericStore.ICON_SIZE,
398
def __init__(self, db, cache, icons, icon_size=AppGenericStore.ICON_SIZE,
388
399
global_icon_cache=True):
389
400
AppGenericStore.__init__(
390
401
self, db, cache, icons, icon_size, global_icon_cache)
392
403
self.set_column_types(self.COL_TYPES)
394
405
self.current_matches = None
398
407
def set_from_matches(self, matches):
399
408
""" set the content of the liststore based on a list of
440
448
except IndexError:
443
if row_content: continue
444
453
doc = db.get_document(matches[i].docid)
445
454
doc.available = doc.installed = doc.purchasable = None
446
455
self[(i,)][0] = doc
450
458
# reset the tranaction map because it will now be invalid
451
459
self.transaction_path_map = {}
452
460
self.current_matches = None
453
461
Gtk.ListStore.clear(self)
457
464
class AppTreeStore(Gtk.TreeStore, AppGenericStore):
458
465
""" A treestore based application model
461
def __init__(self, db, cache, icons, icon_size=AppGenericStore.ICON_SIZE,
468
def __init__(self, db, cache, icons, icon_size=AppGenericStore.ICON_SIZE,
462
469
global_icon_cache=True):
463
470
AppGenericStore.__init__(
464
471
self, db, cache, icons, icon_size, global_icon_cache)
465
472
Gtk.TreeStore.__init__(self)
466
473
self.set_column_types(self.COL_TYPES)
469
475
def set_documents(self, parent, documents):
470
476
for doc in documents:
471
doc.available = None; doc.installed = doc.purchasable = None
478
doc.installed = doc.purchasable = None
472
479
self.append(parent, (doc,))
474
481
self.transaction_path_map = {}
477
483
def set_category_documents(self, cat, documents):
478
484
category = CategoryRowReference(cat.untranslated_name,
484
490
self.set_documents(it, documents)
487
def set_nocategory_documents(self, documents, untranslated_name=None, display_name=None):
493
def set_nocategory_documents(self, documents, untranslated_name=None,
488
495
category = UncategorisedRowRef(untranslated_name,