From fa1d1835b6a9f600cd0dc8e3cf29690441e9456f Mon Sep 17 00:00:00 2001 From: Anish Mangal Date: Wed, 14 Dec 2011 13:01:30 +0000 Subject: Remove AC microformat updater files Dextrose now uses the out-of-tree microformat updater from OLPC. --- diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am index 2582c31..8e01626 100644 --- a/data/icons/Makefile.am +++ b/data/icons/Makefile.am @@ -11,7 +11,6 @@ sugar_DATA = \ module-modemconfiguration.svg \ module-network.svg \ module-power.svg \ - module-updater.svg \ module-proxy.svg EXTRA_DIST = $(sugar_DATA) diff --git a/data/icons/module-updater.svg b/data/icons/module-updater.svg deleted file mode 100644 index a521f61..0000000 --- a/data/icons/module-updater.svg +++ /dev/null @@ -1,16 +0,0 @@ - - -]> - - - - - - - - - - - - diff --git a/extensions/cpsection/Makefile.am b/extensions/cpsection/Makefile.am index 78d548b..2074d11 100644 --- a/extensions/cpsection/Makefile.am +++ b/extensions/cpsection/Makefile.am @@ -9,7 +9,6 @@ SUBDIRS = \ modemconfiguration \ network \ power \ - updater \ proxy \ # diff --git a/extensions/cpsection/updater/Makefile.am b/extensions/cpsection/updater/Makefile.am deleted file mode 100644 index 897dbf3..0000000 --- a/extensions/cpsection/updater/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -SUBDIRS = backends - -sugardir = $(pkgdatadir)/extensions/cpsection/updater - -sugar_PYTHON = \ - __init__.py \ - model.py \ - view.py diff --git a/extensions/cpsection/updater/__init__.py b/extensions/cpsection/updater/__init__.py deleted file mode 100644 index 6010615..0000000 --- a/extensions/cpsection/updater/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (C) 2008, OLPC -# -# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -from gettext import gettext as _ - -CLASS = 'ActivityUpdater' -ICON = 'module-updater' -TITLE = _('Software update') -KEYWORDS = ['software', 'activity', 'update'] diff --git a/extensions/cpsection/updater/backends/Makefile.am b/extensions/cpsection/updater/backends/Makefile.am deleted file mode 100644 index e9c1284..0000000 --- a/extensions/cpsection/updater/backends/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -sugardir = $(pkgdatadir)/extensions/cpsection/updater/backends - -sugar_PYTHON = \ - microformat.py \ - __init__.py diff --git a/extensions/cpsection/updater/backends/__init__.py b/extensions/cpsection/updater/backends/__init__.py deleted file mode 100644 index 0dd0174..0000000 --- a/extensions/cpsection/updater/backends/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/python -# Copyright (C) 2009, Sugar Labs -# -# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/extensions/cpsection/updater/backends/microformat.py b/extensions/cpsection/updater/backends/microformat.py deleted file mode 100644 index ca4f1d5..0000000 --- a/extensions/cpsection/updater/backends/microformat.py +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2011, Anish Mangal -# -# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -import logging -from HTMLParser import HTMLParser -import urllib -import re - -import gio -import gobject -import gconf - -from jarabe import config - -client = gconf.client_get_default() -_UPDATE_PATH = client.get_string('/desktop/sugar/updater_url') -_ACTIVITIES_LIST = {} -ACTION_CHECKING = 0 -ACTION_UPDATING = 1 -ACTION_DOWNLOADING = 2 - -class MicroformatParser(HTMLParser): - - def __init__(self, data, completion_cb): - HTMLParser.__init__(self) - self.reset() - self._data_to_parse = data - self._activity_id = '' - self._activity_url = '' - self._activity_version = '' - self._activity_size = 1 - self._activity_name = '' - self._inside_activity_block = False - self._inside_activity_version = False - self._inside_activity_id = False - self._inside_activity_url = False - self._inside_activity_size = False - self._inside_activity_name = False - self._activity_block_tag = '' - self._completion_cb = completion_cb - - def parse(self): - self.feed(self._data_to_parse) - - def handle_endtag(self, tag): - if tag == self._activity_block_tag and self._inside_activity_block: - self._inside_activity_block = False - - _ACTIVITIES_LIST[self._activity_id] = \ - {'version':self._activity_version, - 'url':self._activity_url, - 'size':self._activity_size, - 'name':self._activity_name} - - elif tag == 'a': - if self._inside_activity_url: - self._inside_activity_url = False - - elif tag == 'body': - num_bundles = len(_ACTIVITIES_LIST) - progress = num_bundles - for bundle, info in _ACTIVITIES_LIST.items(): - progress = progress + 1 - if _ACTIVITIES_LIST[bundle]['size'] == 1: - try: - _ACTIVITIES_LIST[bundle]['size'] = \ - gio.File(_ACTIVITIES_LIST[bundle]['url']).\ - query_info('*').get_size() - - except Exception, e: - logging.exception(e) - - if _ACTIVITIES_LIST[bundle]['size'] == 0: - logging.error('Size of activity %s reported as ' - '0 bytes. Excluding from update list' % bundle) - del _ACTIVITIES_LIST[bundle] - - elif _ACTIVITIES_LIST[bundle]['name'] == '': - # Do some regex magic to get the 'probable' - # activity name. - activity_name = re.split('\.', - bundle)[-1] - activity_name = re.sub('^[\s|\t]*', '', - activity_name) - activity_name = re.sub('[\s|\t]*$', '', - activity_name) - activity_name = re.sub('[A|a]ctivity$', '', - activity_name) - _ACTIVITIES_LIST[bundle]['name'] = \ - activity_name - - self._completion_cb(_ACTIVITIES_LIST, None) - - def handle_starttag(self, tag, attrs): - for attribute, value in attrs: - if value == 'olpc-activity-info': - self._inside_activity_block = True - self._activity_block_tag = tag - - if tag == 'span': - for attribute, value in attrs: - if value == 'olpc-activity-id': - self._inside_activity_id = True - elif value == 'olpc-activity-version': - self._inside_activity_version = True - elif value == 'olpc-activity-name': - self._inside_activity_name = True - elif value == 'olpc-activity-size': - self._inside_activity_size = True - elif value == 'olpc-activity-url': - self._inside_activity_url = True - - elif tag == 'a': - if self._inside_activity_url: - for attribute, value in attrs: - if attribute == 'href': - self._activity_url = value - - def handle_data(self, data): - if self._inside_activity_version: - self._activity_version = str(data) - self._inside_activity_version = False - - elif self._inside_activity_id: - self._activity_id = data - self._inside_activity_id = False - - elif self._inside_activity_name: - self._activity_name = data - self._inside_activity_name = False - - elif self._inside_activity_size: - self._activity_size = int(data) - self._inside_activity_size = False - -class _UpdateFetcher(gobject.GObject): - - __gsignals__ = { - 'progress': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - ([int, str, float, int])), - } - - def __init__(self, completion_cb): - gobject.GObject.__init__(self) - # ASLO knows only about stable SP releases - major, minor = config.version.split('.')[0:2] - sp_version = '%s.%s' % (major, int(minor) + int(minor) % 2) - self._data = '' - self._completion_cb = completion_cb - - def download_bundle_updates(self): - self.emit('progress', ACTION_CHECKING, 'Fetching update ' - 'information', 1, 3) - self._url = _UPDATE_PATH - self._file = gio.File(self._url) - logging.debug('Fetch %s', self._url) - self._file.read_async(self.__read_async_cb) - - def __read_async_cb(self, gfile, result): - try: - stream = gfile.read_finish(result) - except gio.Error, e: - self.stop() - logging.exception('Error while fetching content from %s' % - self._url) - return - stream.read_async(4096, self.__stream_read_cb) - - def __stream_read_cb(self, stream, result): - data = stream.read_finish(result) - if not data: - self._data_finished() - return - self._data_read(data) - stream.read_async(4096, self.__stream_read_cb) - - def _data_read(self, data): - self._data += data - - def read_finish(self): - pass - - def _data_finished(self): - self.emit('progress', ACTION_CHECKING, 'Fetching update ' - 'information', 2, 3) - parser = MicroformatParser(self._data, self._completion_cb) - gobject.idle_add(parser.parse) diff --git a/extensions/cpsection/updater/model.py b/extensions/cpsection/updater/model.py deleted file mode 100755 index 1a3941c..0000000 --- a/extensions/cpsection/updater/model.py +++ /dev/null @@ -1,383 +0,0 @@ -# Copyright (C) 2009, Sugar Labs -# Copyright (C) 2009, Tomeu Vizoso -# -# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -"""Sugar bundle updater: model. - -This module implements the non-GUI portions of the bundle updater, including -list of installed bundls, whether updates are needed, and the URL at which to -find the bundle updated. -""" - -import os -import logging -import tempfile -from urlparse import urlparse -import traceback - -import gobject -import gio - -from sugar import env -from sugar.datastore import datastore -from sugar.bundle.activitybundle import ActivityBundle -from sugar.bundle.bundleversion import NormalizedVersion - -from jarabe.model import bundleregistry - -from backends import microformat - -class MetaBundle(): - - def __init__(self, bundle_id, version, name): - self._bundle_id = bundle_id - self._version = version - self._name = name - - def get_name(self): - return self._name - - def get_bundle_id(self): - return self._bundle_id - - def get_icon(self): - pass - - def get_activity_version(self): - return self._version - -class UpdateModel(gobject.GObject): - __gtype_name__ = 'SugarUpdateModel' - - __gsignals__ = { - 'progress': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - ([int, str, float, int])), - } - - ACTION_CHECKING = 0 - ACTION_UPDATING = 1 - ACTION_DOWNLOADING = 2 - - def __init__(self): - gobject.GObject.__init__(self) - - self.updates = None - self._bundles_to_check = None - self._bundles_to_update = None - self._total_bundles_to_update = 0 - self._downloader = None - self._cancelling = False - - def __progress_cb(self, model, action, description, current, total): - self.emit('progress', action, description, current, total) - - def check_updates(self): - self.updates = [] - self._current_bundles = {} - for bundle in bundleregistry.get_registry(): - self._current_bundles[bundle.get_bundle_id()] =\ - {'version':bundle.get_activity_version(), - 'bundle': bundle} - self._bundles_to_check = list(bundleregistry.get_registry()) - self._fetcher = microformat._UpdateFetcher(self.__bundle_info_fetched_cb) - self._fetcher.connect('progress', self.__progress_cb) - gobject.idle_add(self._fetcher.download_bundle_updates) - - def __bundle_info_fetched_cb(self, new_bundles, error_message): - if error_message is not None: - logging.error('Error getting update information from server:\n' - '%s' % error_message) - - if self._cancelling: - self._cancel_checking() - else: - for bundle_id, info in new_bundles.items(): - if bundle_id in self._current_bundles: - old_version = self._current_bundles[bundle_id]['version'] - new_version = new_bundles[bundle_id]['version'] - if NormalizedVersion(old_version) < \ - NormalizedVersion(new_version): - self.updates.append(BundleUpdate( - self._current_bundles[bundle_id]['bundle'], - new_bundles[bundle_id]['version'], - new_bundles[bundle_id]['url'], - new_bundles[bundle_id]['size'], - 'update')) - else: - bundle = MetaBundle(bundle_id, - new_bundles[bundle_id]['version'], - new_bundles[bundle_id]['name']) - self.updates.append(BundleUpdate(bundle, - new_bundles[bundle_id]['version'], - new_bundles[bundle_id]['url'], - new_bundles[bundle_id]['size'], - 'new')) - - self.emit('progress', UpdateModel.ACTION_CHECKING, 'Fetching update ' - 'information', 3, 3) - - def update(self, bundle_ids): - self._bundles_to_update = [] - for bundle_update in self.updates: - if bundle_update.bundle.get_bundle_id() in bundle_ids: - self._bundles_to_update.append(bundle_update) - - self._total_bundles_to_update = len(self._bundles_to_update) - self._download_next_update() - - def _download_next_update(self): - if self._cancelling: - self._cancel_updating() - return - - bundle_update = self._bundles_to_update.pop() - - total = self._total_bundles_to_update * 2 - current = total - len(self._bundles_to_update) * 2 - 2 - - self.emit('progress', UpdateModel.ACTION_DOWNLOADING, - bundle_update.bundle.get_name(), current, total) - - self._downloader = _Downloader(bundle_update) - self._downloader.connect('progress', self.__downloader_progress_cb) - self._downloader.connect('error', self.__downloader_error_cb) - - def __downloader_progress_cb(self, downloader, progress): - logging.debug('__downloader_progress_cb %r', progress) - - if self._cancelling: - self._cancel_updating() - return - - total = self._total_bundles_to_update * 2 - current = total - len(self._bundles_to_update) * 2 - 2 + progress - - self.emit('progress', UpdateModel.ACTION_DOWNLOADING, - self._downloader.bundle_update.bundle.get_name(), - current, total) - - if progress == 1: - self._install_update(self._downloader.bundle_update, - self._downloader.get_local_file_path()) - self._downloader = None - - def __downloader_error_cb(self, downloader, error_message): - logging.error('Error downloading update:\n%s', error_message) - - if self._cancelling: - self._cancel_updating() - return - - total = self._total_bundles_to_update - current = total - len(self._bundles_to_update) - self.emit('progress', UpdateModel.ACTION_UPDATING, '', current, total) - - if self._bundles_to_update: - # do it in idle so the UI has a chance to refresh - gobject.idle_add(self._download_next_update) - - def _install_update(self, bundle_update, local_file_path): - - total = self._total_bundles_to_update - current = total - len(self._bundles_to_update) - 0.5 - - self.emit('progress', UpdateModel.ACTION_UPDATING, - bundle_update.bundle.get_name(), - current, total) - - # TODO: Should we first expand the zip async so we can provide progress - # and only then copy to the journal? - jobject = datastore.create() - try: - title = '%s-%s' % (bundle_update.bundle.get_name(), - bundle_update.version) - jobject.metadata['title'] = title - jobject.metadata['mime_type'] = ActivityBundle.MIME_TYPE - jobject.file_path = local_file_path - datastore.write(jobject, transfer_ownership=True) - finally: - jobject.destroy() - - self.emit('progress', UpdateModel.ACTION_UPDATING, - bundle_update.bundle.get_name(), - current + 0.5, total) - - if self._bundles_to_update: - # do it in idle so the UI has a chance to refresh - gobject.idle_add(self._download_next_update) - - def cancel(self): - self._cancelling = True - - def _cancel_checking(self): - logging.debug('UpdateModel._cancel_checking') - total = len(bundleregistry.get_registry()) - current = total - len(self._bundles_to_check) - self.emit('progress', UpdateModel.ACTION_CHECKING, '', current, - current) - self._bundles_to_check = None - self._cancelling = False - - def _cancel_updating(self): - logging.debug('UpdateModel._cancel_updating') - current = (self._total_bundles_to_update - - len(self._bundles_to_update) - 1) - self.emit('progress', UpdateModel.ACTION_UPDATING, '', current, - current) - - if self._downloader is not None: - self._downloader.cancel() - file_path = self._downloader.get_local_file_path() - if file_path is not None and os.path.exists(file_path): - os.unlink(file_path) - self._downloader = None - - self._total_bundles_to_update = 0 - self._bundles_to_update = None - self._cancelling = False - - -class BundleUpdate(object): - - def __init__(self, bundle, version, link, size, package_type = None): - self.bundle = bundle - self.version = version - self.link = link - self.size = size - - # Specify whether installing a new bundle or updating an - # existing one - self.package_type = package_type - -class _Downloader(gobject.GObject): - _CHUNK_SIZE = 10240 # 10K - __gsignals__ = { - 'progress': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - ([float])), - 'error': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - ([str])), - } - - def __init__(self, bundle_update): - gobject.GObject.__init__(self) - - self.bundle_update = bundle_update - self._input_stream = None - self._output_stream = None - self._pending_buffers = [] - self._input_file = gio.File(bundle_update.link) - self._output_file = None - self._downloaded_size = 0 - self._cancelling = False - - self._input_file.read_async(self.__file_read_async_cb) - - def cancel(self): - self._cancelling = True - - def __file_read_async_cb(self, gfile, result): - if self._cancelling: - return - - try: - self._input_stream = self._input_file.read_finish(result) - except: - self.emit('error', traceback.format_exc()) - return - - temp_file_path = self._get_temp_file_path(self.bundle_update.link) - self._output_file = gio.File(temp_file_path) - self._output_stream = self._output_file.create() - - self._input_stream.read_async(self._CHUNK_SIZE, self.__read_async_cb, - gobject.PRIORITY_LOW) - - def __read_async_cb(self, input_stream, result): - if self._cancelling: - return - - data = input_stream.read_finish(result) - - if data is None: - # TODO - pass - elif not data: - logging.debug('closing input stream') - self._input_stream.close() - self._check_if_finished_writing() - else: - self._pending_buffers.append(data) - self._input_stream.read_async(self._CHUNK_SIZE, - self.__read_async_cb, - gobject.PRIORITY_LOW) - - self._write_next_buffer() - - def __write_async_cb(self, output_stream, result, user_data): - if self._cancelling: - return - - count = output_stream.write_finish(result) - - self._downloaded_size += count - progress = self._downloaded_size / float(self.bundle_update.size) - self.emit('progress', progress) - - self._check_if_finished_writing() - - if self._pending_buffers: - self._write_next_buffer() - - def _write_next_buffer(self): - if self._pending_buffers and not self._output_stream.has_pending(): - data = self._pending_buffers.pop(0) - # TODO: we pass the buffer as user_data because of - # http://bugzilla.gnome.org/show_bug.cgi?id=564102 - self._output_stream.write_async(data, self.__write_async_cb, - gobject.PRIORITY_LOW, - user_data=data) - - def _get_temp_file_path(self, uri): - # TODO: Should we use the HTTP headers for the file name? - scheme_, netloc_, path, params_, query_, fragment_ = \ - urlparse(uri) - path = os.path.basename(path) - - if not os.path.exists(env.get_user_activities_path()): - os.makedirs(env.get_user_activities_path()) - - base_name, extension_ = os.path.splitext(path) - fd, file_path = tempfile.mkstemp(dir=env.get_user_activities_path(), - prefix=base_name, suffix='.xo') - os.close(fd) - os.unlink(file_path) - - return file_path - - def get_local_file_path(self): - return self._output_file.get_path() - - def _check_if_finished_writing(self): - if not self._pending_buffers and \ - not self._output_stream.has_pending() and \ - self._input_stream.is_closed(): - - logging.debug('closing output stream') - self._output_stream.close() - - self.emit('progress', 1.0) diff --git a/extensions/cpsection/updater/view.py b/extensions/cpsection/updater/view.py deleted file mode 100644 index 891f552..0000000 --- a/extensions/cpsection/updater/view.py +++ /dev/null @@ -1,398 +0,0 @@ -# Copyright (C) 2008, One Laptop Per Child -# Copyright (C) 2009, Tomeu Vizoso -# -# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -from gettext import gettext as _ -from gettext import ngettext -import locale -import logging - -import gobject -import gtk - -from sugar.graphics import style -from sugar.graphics.icon import Icon, CellRendererIcon - -from jarabe.controlpanel.sectionview import SectionView - -from model import UpdateModel - -_DEBUG_VIEW_ALL = True - - -class ActivityUpdater(SectionView): - - def __init__(self, model, alerts): - SectionView.__init__(self) - - self._model = UpdateModel() - self._model.connect('progress', self.__progress_cb) - - self.set_spacing(style.DEFAULT_SPACING) - self.set_border_width(style.DEFAULT_SPACING * 2) - - self._top_label = gtk.Label() - self._top_label.set_line_wrap(True) - self._top_label.set_justify(gtk.JUSTIFY_LEFT) - self._top_label.props.xalign = 0 - self.pack_start(self._top_label, expand=False) - self._top_label.show() - - separator = gtk.HSeparator() - self.pack_start(separator, expand=False) - separator.show() - - bottom_label = gtk.Label() - bottom_label.set_line_wrap(True) - bottom_label.set_justify(gtk.JUSTIFY_LEFT) - bottom_label.props.xalign = 0 - bottom_label.set_markup( - _('Software updates correct errors, eliminate security ' \ - 'vulnerabilities, and provide new features.')) - self.pack_start(bottom_label, expand=False) - bottom_label.show() - - self._update_box = None - self._progress_pane = None - - self._refresh() - - def _switch_to_update_box(self): - if self._update_box in self.get_children(): - return - - if self._progress_pane in self.get_children(): - self.remove(self._progress_pane) - self._progress_pane = None - - if self._update_box is None: - self._update_box = UpdateBox(self._model) - self._update_box.refresh_button.connect('clicked', - self.__refresh_button_clicked_cb) - self._update_box.install_button.connect('clicked', - self.__install_button_clicked_cb) - - self.pack_start(self._update_box, expand=True, fill=True) - self._update_box.show() - - def _switch_to_progress_pane(self): - if self._progress_pane in self.get_children(): - return - - if self._update_box in self.get_children(): - self.remove(self._update_box) - self._update_box = None - - if self._progress_pane is None: - self._progress_pane = ProgressPane() - self._progress_pane.cancel_button.connect('clicked', - self.__cancel_button_clicked_cb) - - self.pack_start(self._progress_pane, expand=True, fill=False) - self._progress_pane.show() - - def _clear_center(self): - if self._progress_pane in self.get_children(): - self.remove(self._progress_pane) - self._progress_pane = None - - if self._update_box in self.get_children(): - self.remove(self._update_box) - self._update_box = None - - def __progress_cb(self, model, action, bundle_name, current, total): - if current == total and action == UpdateModel.ACTION_CHECKING: - self._finished_checking() - return - elif current == total: - self._finished_updating(int(current)) - return - - if action == UpdateModel.ACTION_CHECKING: - message = _('%s...') % bundle_name - elif action == UpdateModel.ACTION_DOWNLOADING: - message = _('Downloading %s...') % bundle_name - elif action == UpdateModel.ACTION_UPDATING: - message = _('Updating %s...') % bundle_name - - self._switch_to_progress_pane() - self._progress_pane.set_message(message) - self._progress_pane.set_progress(current / float(total)) - - def _finished_checking(self): - logging.debug('ActivityUpdater._finished_checking') - available_updates = len(self._model.updates) - if not available_updates: - top_message = _('Your software is up-to-date') - else: - top_message = ngettext('You can install %s update', - 'You can install %s updates', - available_updates) - top_message = top_message % available_updates - top_message = gobject.markup_escape_text(top_message) - - self._top_label.set_markup('%s' % top_message) - - if not available_updates: - self._clear_center() - else: - self._switch_to_update_box() - self._update_box.refresh() - - def __refresh_button_clicked_cb(self, button): - self._refresh() - - def _refresh(self): - top_message = _('Checking for updates...') - self._top_label.set_markup('%s' % top_message) - self._model.check_updates() - - def __install_button_clicked_cb(self, button): - text = '%s' % _('Installing updates...') - self._top_label.set_markup(text) - self._model.update(self._update_box.get_bundles_to_update()) - - def __cancel_button_clicked_cb(self, button): - self._model.cancel() - - def _finished_updating(self, installed_updates): - logging.debug('ActivityUpdater._finished_updating') - top_message = ngettext('%s update was installed', - '%s updates were installed', installed_updates) - top_message = top_message % installed_updates - top_message = gobject.markup_escape_text(top_message) - self._top_label.set_markup('%s' % top_message) - self._clear_center() - - def undo(self): - self._model.cancel() - - -class ProgressPane(gtk.VBox): - """Container which replaces the `ActivityPane` during refresh or - install.""" - - def __init__(self): - gtk.VBox.__init__(self) - self.set_spacing(style.DEFAULT_PADDING) - self.set_border_width(style.DEFAULT_SPACING * 2) - - self._progress = gtk.ProgressBar() - self.pack_start(self._progress) - self._progress.show() - - self._label = gtk.Label() - self._label.set_line_wrap(True) - self._label.set_property('xalign', 0.5) - self._label.modify_fg(gtk.STATE_NORMAL, - style.COLOR_BUTTON_GREY.get_gdk_color()) - self.pack_start(self._label) - self._label.show() - - alignment_box = gtk.Alignment(xalign=0.5, yalign=0.5) - self.pack_start(alignment_box) - alignment_box.show() - - self.cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL) - alignment_box.add(self.cancel_button) - self.cancel_button.show() - - def set_message(self, message): - self._label.set_text(message) - - def set_progress(self, fraction): - self._progress.props.fraction = fraction - - -class UpdateBox(gtk.VBox): - - def __init__(self, model): - gtk.VBox.__init__(self) - - self._model = model - - self.set_spacing(style.DEFAULT_PADDING) - - scrolled_window = gtk.ScrolledWindow() - scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self.pack_start(scrolled_window) - scrolled_window.show() - - self._update_list = UpdateList(model) - self._update_list.props.model.connect('row-changed', - self.__row_changed_cb) - scrolled_window.add(self._update_list) - self._update_list.show() - - bottom_box = gtk.HBox() - bottom_box.set_spacing(style.DEFAULT_SPACING) - self.pack_start(bottom_box, expand=False) - bottom_box.show() - - self._size_label = gtk.Label() - self._size_label.props.xalign = 0 - self._size_label.set_justify(gtk.JUSTIFY_LEFT) - bottom_box.pack_start(self._size_label, expand=True) - self._size_label.show() - - self.refresh_button = gtk.Button(stock=gtk.STOCK_REFRESH) - bottom_box.pack_start(self.refresh_button, expand=False) - self.refresh_button.show() - - self.install_button = gtk.Button(_('Install selected')) - self.install_button.props.image = Icon(icon_name='emblem-downloads', - icon_size=gtk.ICON_SIZE_BUTTON) - bottom_box.pack_start(self.install_button, expand=False) - self.install_button.show() - - self._update_total_size_label() - - def refresh(self): - self._update_list.refresh() - - def __row_changed_cb(self, list_model, path, iterator): - self._update_total_size_label() - self._update_install_button() - - def _update_total_size_label(self): - total_size = 0 - for row in self._update_list.props.model: - if row[UpdateListModel.SELECTED]: - total_size += row[UpdateListModel.SIZE] - - markup = _('Download size: %s') % _format_size(total_size) - self._size_label.set_markup(markup) - - def _update_install_button(self): - for row in self._update_list.props.model: - if row[UpdateListModel.SELECTED]: - self.install_button.props.sensitive = True - return - self.install_button.props.sensitive = False - - def get_bundles_to_update(self): - bundles_to_update = [] - for row in self._update_list.props.model: - if row[UpdateListModel.SELECTED]: - bundles_to_update.append(row[UpdateListModel.BUNDLE_ID]) - return bundles_to_update - - -class UpdateList(gtk.TreeView): - - def __init__(self, model): - list_model = UpdateListModel(model) - gtk.TreeView.__init__(self, list_model) - - self.set_reorderable(False) - self.set_enable_search(False) - self.set_headers_visible(False) - - toggle_renderer = gtk.CellRendererToggle() - toggle_renderer.props.activatable = True - toggle_renderer.props.xpad = style.DEFAULT_PADDING - toggle_renderer.props.indicator_size = style.zoom(26) - toggle_renderer.connect('toggled', self.__toggled_cb) - - toggle_column = gtk.TreeViewColumn() - toggle_column.pack_start(toggle_renderer) - toggle_column.add_attribute(toggle_renderer, 'active', - UpdateListModel.SELECTED) - self.append_column(toggle_column) - - icon_renderer = CellRendererIcon(self) - icon_renderer.props.width = style.STANDARD_ICON_SIZE - icon_renderer.props.height = style.STANDARD_ICON_SIZE - icon_renderer.props.size = style.STANDARD_ICON_SIZE - icon_renderer.props.xpad = style.DEFAULT_PADDING - icon_renderer.props.ypad = style.DEFAULT_PADDING - icon_renderer.props.stroke_color = style.COLOR_TOOLBAR_GREY.get_svg() - icon_renderer.props.fill_color = style.COLOR_TRANSPARENT.get_svg() - - icon_column = gtk.TreeViewColumn() - icon_column.pack_start(icon_renderer) - icon_column.add_attribute(icon_renderer, 'file-name', - UpdateListModel.ICON_FILE_NAME) - self.append_column(icon_column) - - text_renderer = gtk.CellRendererText() - - description_column = gtk.TreeViewColumn() - description_column.pack_start(text_renderer) - description_column.add_attribute(text_renderer, 'markup', - UpdateListModel.DESCRIPTION) - self.append_column(description_column) - - def __toggled_cb(self, cell_renderer, path): - row = self.props.model[path] - row[UpdateListModel.SELECTED] = not row[UpdateListModel.SELECTED] - - def refresh(self): - pass - - -class UpdateListModel(gtk.ListStore): - - BUNDLE_ID = 0 - SELECTED = 1 - ICON_FILE_NAME = 2 - DESCRIPTION = 3 - SIZE = 4 - - def __init__(self, model): - gtk.ListStore.__init__(self, str, bool, str, str, int) - - for bundle_update in model.updates: - row = [None] * 5 - row[self.BUNDLE_ID] = bundle_update.bundle.get_bundle_id() - row[self.SELECTED] = False - row[self.ICON_FILE_NAME] = bundle_update.bundle.get_icon() - - if bundle_update.package_type == 'update': - row[self.SELECTED] = True - details = _('From version %(current)s to %(new)s (Size: %(size)s)') - details = details % \ - {'current': bundle_update.bundle.get_activity_version(), - 'new': bundle_update.version, - 'size': _format_size(bundle_update.size)} - elif bundle_update.package_type == 'new': - details = _('Install new activity version %(new)s (Size: %(size)s)') - details = details % \ - {'new': bundle_update.version, - 'size': _format_size(bundle_update.size)} - - row[self.DESCRIPTION] = '%s\n%s' % \ - (bundle_update.bundle.get_name(), details) - - row[self.SIZE] = bundle_update.size - - self.append(row) - - -def _format_size(size): - """Convert a given size in bytes to a nicer better readable unit""" - if size == 0: - # TRANS: download size is 0 - return _('None') - elif size < 1024: - # TRANS: download size of very small updates - return _('1 KB') - elif size < 1024 * 1024: - # TRANS: download size of small updates, e.g. '250 KB' - return locale.format_string(_('%.0f KB'), size / 1024.0) - else: - # TRANS: download size of updates, e.g. '2.3 MB' - return locale.format_string(_('%.1f MB'), size / 1024.0 / 1024) -- cgit v0.9.1