Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Schampijer <simon@schampijer.de>2009-08-12 07:13:21 (GMT)
committer Simon Schampijer <simon@schampijer.de>2009-08-12 07:13:21 (GMT)
commitc8016588eaefed5a55daf6e2387087a202cd5179 (patch)
tree5c723e24aa89c0c4ce6ebe54f6a93a7acf51ff8c
parentf0d5242a11e5a6f5430629420c5e64d74734ac92 (diff)
parent0224153536d2fbdbd318211173171074f38251cb (diff)
Merge branch 'master' of gitorious@git.sugarlabs.org:sugar/mainline
-rw-r--r--bin/sugar.in3
-rw-r--r--configure.ac4
-rw-r--r--extensions/cpsection/Makefile.am2
-rw-r--r--extensions/cpsection/updater/Makefile.am8
-rw-r--r--extensions/cpsection/updater/__init__.py22
-rw-r--r--extensions/cpsection/updater/backends/Makefile.am5
-rw-r--r--extensions/cpsection/updater/backends/__init__.py16
-rw-r--r--extensions/cpsection/updater/backends/aslo.py157
-rwxr-xr-xextensions/cpsection/updater/model.py292
-rw-r--r--extensions/cpsection/updater/view.py382
-rw-r--r--po/nl.po133
-rw-r--r--src/jarabe/desktop/activitieslist.py2
-rw-r--r--src/jarabe/desktop/keydialog.py2
-rw-r--r--src/jarabe/frame/activitiestray.py12
-rw-r--r--src/jarabe/journal/__init__.py15
-rw-r--r--src/jarabe/journal/listview.py2
-rw-r--r--src/jarabe/journal/objectchooser.py6
-rw-r--r--src/jarabe/model/buddy.py11
-rw-r--r--src/jarabe/model/bundleregistry.py8
-rw-r--r--src/jarabe/model/filetransfer.py1
-rw-r--r--src/jarabe/view/service.py19
21 files changed, 1009 insertions, 93 deletions
diff --git a/bin/sugar.in b/bin/sugar.in
index 7b5ca25..1c29a12 100644
--- a/bin/sugar.in
+++ b/bin/sugar.in
@@ -6,6 +6,9 @@ fi
export GTK2_RC_FILES="@prefix@/share/sugar/data/sugar-$SUGAR_SCALING.gtkrc"
+# Needed for executing wpa_passphrase
+export PATH="$PATH":/sbin:/usr/sbin
+
if ! test -f "$GTK2_RC_FILES"; then
echo "sugar: ERROR: Gtk theme for scaling $SUGAR_SCALING not available in path $GTK2_RC_FILES"
exit 1
diff --git a/configure.ac b/configure.ac
index 979c990..e7a6fb1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT([Sugar],[0.85.2],[],[sugar])
+AC_INIT([Sugar],[0.85.3],[],[sugar])
AC_PREREQ([2.59])
@@ -61,6 +61,8 @@ extensions/cpsection/frame/Makefile
extensions/cpsection/language/Makefile
extensions/cpsection/network/Makefile
extensions/cpsection/power/Makefile
+extensions/cpsection/updater/Makefile
+extensions/cpsection/updater/backends/Makefile
extensions/deviceicon/Makefile
extensions/globalkey/Makefile
src/Makefile
diff --git a/extensions/cpsection/Makefile.am b/extensions/cpsection/Makefile.am
index 73e5164..ffca6ca 100644
--- a/extensions/cpsection/Makefile.am
+++ b/extensions/cpsection/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = aboutme aboutcomputer datetime frame language network power
+SUBDIRS = aboutme aboutcomputer datetime frame language network power updater
sugardir = $(pkgdatadir)/extensions/cpsection
sugar_PYTHON = __init__.py
diff --git a/extensions/cpsection/updater/Makefile.am b/extensions/cpsection/updater/Makefile.am
new file mode 100644
index 0000000..897dbf3
--- /dev/null
+++ b/extensions/cpsection/updater/Makefile.am
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 0000000..6010615
--- /dev/null
+++ b/extensions/cpsection/updater/__init__.py
@@ -0,0 +1,22 @@
+# 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
new file mode 100644
index 0000000..e280a07
--- /dev/null
+++ b/extensions/cpsection/updater/backends/Makefile.am
@@ -0,0 +1,5 @@
+sugardir = $(pkgdatadir)/extensions/cpsection/updater/backends
+
+sugar_PYTHON = \
+ aslo.py \
+ __init__.py
diff --git a/extensions/cpsection/updater/backends/__init__.py b/extensions/cpsection/updater/backends/__init__.py
new file mode 100644
index 0000000..0dd0174
--- /dev/null
+++ b/extensions/cpsection/updater/backends/__init__.py
@@ -0,0 +1,16 @@
+#!/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/aslo.py b/extensions/cpsection/updater/backends/aslo.py
new file mode 100644
index 0000000..8c01ec2
--- /dev/null
+++ b/extensions/cpsection/updater/backends/aslo.py
@@ -0,0 +1,157 @@
+#!/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
+
+'''Activity information microformat parser.
+
+Activity information is embedded in HTML/XHTML/XML pages using a
+Resource Description Framework (RDF) http://www.w3.org/RDF/ .
+
+An example::
+
+<?xml version="1.0" encoding="UTF-8"?>
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+<RDF:Description about="urn:mozilla:extension:bounce">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li resource="urn:mozilla:extension:bounce:7"/>
+ </RDF:Seq>
+ </em:updates>
+</RDF:Description>
+
+<RDF:Description about="urn:mozilla:extension:bounce:7">
+ <em:version>7</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>{3ca105e0-2280-4897-99a0-c277d1b733d2}</em:id>
+ <em:minVersion>0.82</em:minVersion>
+ <em:maxVersion>0.84</em:maxVersion>
+ <em:updateLink>http://foo.xo</em:updateLink>
+ <em:updateSize>7</em:updateSize>
+ <em:updateHash>sha256:816a7c43b4f1ea4769c61c03ea4..</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+</RDF:Description></RDF:RDF>
+'''
+
+import logging
+from xml.etree.ElementTree import XML
+import traceback
+
+import gio
+
+from jarabe import config
+
+_FIND_DESCRIPTION = \
+ './/{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description'
+_FIND_VERSION = './/{http://www.mozilla.org/2004/em-rdf#}version'
+_FIND_LINK = './/{http://www.mozilla.org/2004/em-rdf#}updateLink'
+_FIND_SIZE = './/{http://www.mozilla.org/2004/em-rdf#}updateSize'
+
+_UPDATE_PATH = 'http://activities.sugarlabs.org/services/update.php'
+
+_fetcher = None
+
+
+class _UpdateFetcher(object):
+
+ _CHUNK_SIZE = 10240
+
+ def __init__(self, bundle, completion_cb):
+
+ url = '%s?id=%s&appVersion=%s' % \
+ (_UPDATE_PATH, bundle.get_bundle_id(), config.version)
+
+ self._completion_cb = completion_cb
+ self._file = gio.File(url)
+ self._stream = None
+ self._xml_data = ''
+ self._bundle = bundle
+
+ self._file.read_async(self.__file_read_async_cb)
+
+ def __file_read_async_cb(self, gfile, result):
+ try:
+ self._stream = self._file.read_finish(result)
+ except:
+ global _fetcher
+ _fetcher = None
+ self._completion_cb(None, None, None, None, traceback.format_exc())
+ return
+
+ self._stream.read_async(self._CHUNK_SIZE, self.__stream_read_async_cb)
+
+ def __stream_read_async_cb(self, stream, result):
+ xml_data = self._stream.read_finish(result)
+ if xml_data is None:
+ global _fetcher
+ _fetcher = None
+ self._completion_cb(self._bundle, None, None, None,
+ 'Error reading update information for %s from '
+ 'server.' % self._bundle.get_bundle_id())
+ return
+ elif not xml_data:
+ self._process_result()
+ else:
+ self._xml_data += xml_data
+ self._stream.read_async(self._CHUNK_SIZE,
+ self.__stream_read_async_cb)
+
+ def _process_result(self):
+ document = XML(self._xml_data)
+
+ if document.find(_FIND_DESCRIPTION) is None:
+ logging.debug('Bundle %s not available in the server for the '
+ 'version %s' % (self._bundle.get_bundle_id(),
+ config.version))
+ version = None
+ link = None
+ size = None
+ else:
+ try:
+ version = int(document.find(_FIND_VERSION).text)
+ except ValueError:
+ logging.error(traceback.format_exc())
+ version = 0
+
+ link = document.find(_FIND_LINK).text
+
+ try:
+ size = long(document.find(_FIND_SIZE).text) * 1024
+ except ValueError:
+ logging.error(traceback.format_exc())
+ size = 0
+
+ global _fetcher
+ _fetcher = None
+ self._completion_cb(self._bundle, version, link, size, None)
+
+
+def fetch_update_info(bundle, completion_cb):
+ '''Queries the server for a newer version of the ActivityBundle.
+
+ completion_cb receives bundle, version, link, size and possibly an error
+ message:
+
+ def completion_cb(bundle, version, link, size, error_message):
+ '''
+ global _fetcher
+
+ if _fetcher is not None:
+ raise RuntimeError('Multiple simultaneous requests are not supported')
+
+ _fetcher = _UpdateFetcher(bundle, completion_cb)
diff --git a/extensions/cpsection/updater/model.py b/extensions/cpsection/updater/model.py
new file mode 100755
index 0000000..489cfa5
--- /dev/null
+++ b/extensions/cpsection/updater/model.py
@@ -0,0 +1,292 @@
+# 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 jarabe.model import bundleregistry
+
+from backends import aslo
+
+
+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
+
+ def check_updates(self):
+ self.updates = []
+ self._bundles_to_check = \
+ [bundle for bundle in bundleregistry.get_registry()]
+ self._check_next_update()
+
+ def _check_next_update(self):
+ total = len(bundleregistry.get_registry())
+ current = total - len(self._bundles_to_check)
+
+ bundle = self._bundles_to_check.pop()
+ self.emit('progress', UpdateModel.ACTION_CHECKING, bundle.get_name(),
+ current, total)
+
+ aslo.fetch_update_info(bundle, self.__check_completed_cb)
+
+ def __check_completed_cb(self, bundle, version, link, size, error_message):
+ if error_message is not None:
+ logging.error('Error getting update information from server:\n'
+ '%s' % error_message)
+
+ if version is not None and version > bundle.get_activity_version():
+ self.updates.append(BundleUpdate(bundle, version, link, size))
+
+ if self._bundles_to_check:
+ gobject.idle_add(self._check_next_update)
+ else:
+ total = len(bundleregistry.get_registry())
+ if bundle is None:
+ name = ''
+ else:
+ name = bundle.get_name()
+ self.emit('progress', UpdateModel.ACTION_CHECKING, name, total,
+ total)
+
+ 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):
+ 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)
+ 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)
+
+ total = self._total_bundles_to_update * 2
+ current = total - len(self._bundles_to_update) * 2
+ 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 * 2
+ current = total - len(self._bundles_to_update) * 2 - 1
+
+ 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()
+
+ current += 1
+ self.emit('progress', UpdateModel.ACTION_UPDATING,
+ bundle_update.bundle.get_name(),
+ 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 get_total_bundles_to_update(self):
+ return self._total_bundles_to_update
+
+
+class BundleUpdate(object):
+
+ def __init__(self, bundle, version, link, size):
+ self.bundle = bundle
+ self.version = version
+ self.link = link
+ self.size = size
+
+
+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._input_file.read_async(self.__file_read_async_cb)
+
+ def __file_read_async_cb(self, gfile, result):
+ 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):
+ 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):
+ 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)
+
+ 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
new file mode 100644
index 0000000..9a77743
--- /dev/null
+++ b/extensions/cpsection/updater/view.py
@@ -0,0 +1,382 @@
+# 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 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.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()
+ return
+
+ if action == UpdateModel.ACTION_CHECKING:
+ message = _('Checking %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):
+ 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('<big>%s</big>' % 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('<big>%s</big>' % top_message)
+ self._model.check_updates()
+
+ def __install_button_clicked_cb(self, button):
+ self._top_label.set_markup('<big>%s</big>' % _('Installing updates...'))
+ self._model.update(self._update_box.get_bundles_to_update())
+
+ def _finished_updating(self):
+ installed_updates = self._model.get_total_bundles_to_update()
+ 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('<big>%s</big>' % top_message)
+ self._clear_center()
+
+
+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()
+
+ cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL)
+ alignment_box.add(cancel_button)
+ 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] = True
+ row[self.ICON_FILE_NAME] = bundle_update.bundle.get_icon()
+
+ details = _('From version %(current)d 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)}
+
+ row[self.DESCRIPTION] = '<b>%s</b>\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(_('%.0f KB'), size / 1024.0)
+ else:
+ # TRANS: download size of updates, e.g. '2.3 MB'
+ return locale.format(_('%.1f MB'), size / 1024.0 / 1024)
diff --git a/po/nl.po b/po/nl.po
index 7a101cc..d70783b 100644
--- a/po/nl.po
+++ b/po/nl.po
@@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-03-19 00:30-0400\n"
-"PO-Revision-Date: 2009-05-21 02:42-0400\n"
+"POT-Creation-Date: 2009-06-11 00:31-0400\n"
+"PO-Revision-Date: 2009-07-31 16:03-0400\n"
"Last-Translator: Myckel Habets <myckel@sdf.lonestar.org>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: nl\n"
@@ -65,50 +65,43 @@ msgstr "Klik om de kleur te veranderen:"
msgid "About my Computer"
msgstr "Over mijn computer"
-#: ../extensions/cpsection/aboutcomputer/model.py:26
+#: ../extensions/cpsection/aboutcomputer/model.py:28
msgid "Not available"
msgstr "Niet beschikbaar"
-#: ../extensions/cpsection/aboutcomputer/view.py:59
+#: ../extensions/cpsection/aboutcomputer/view.py:60
msgid "Identity"
msgstr "Identiteit"
-#: ../extensions/cpsection/aboutcomputer/view.py:68
+#: ../extensions/cpsection/aboutcomputer/view.py:69
msgid "Serial Number:"
msgstr "Serienummer:"
-#: ../extensions/cpsection/aboutcomputer/view.py:90
+#: ../extensions/cpsection/aboutcomputer/view.py:91
msgid "Software"
msgstr "Software"
-#: ../extensions/cpsection/aboutcomputer/view.py:99
+#: ../extensions/cpsection/aboutcomputer/view.py:100
msgid "Build:"
msgstr "Bouw:"
-#: ../extensions/cpsection/aboutcomputer/view.py:114
+#: ../extensions/cpsection/aboutcomputer/view.py:115
msgid "Sugar:"
msgstr "Sugar:"
-#: ../extensions/cpsection/aboutcomputer/view.py:130
+#: ../extensions/cpsection/aboutcomputer/view.py:131
msgid "Firmware:"
msgstr "Firmware:"
-#: ../extensions/cpsection/aboutcomputer/view.py:145
+#: ../extensions/cpsection/aboutcomputer/view.py:146
msgid "Wireless Firmware:"
msgstr "Firmware draadloos netwerk:"
-#: ../extensions/cpsection/aboutcomputer/view.py:168
+#: ../extensions/cpsection/aboutcomputer/view.py:169
msgid "Copyright and License"
msgstr "Copyright en licentie"
-#: ../extensions/cpsection/aboutcomputer/view.py:176
-msgid ""
-"© 2008 One Laptop per Child Association Inc; Red Hat Inc; and Contributors."
-msgstr ""
-"© 2008 One Laptop per Child Association Inc; Red Hat Inc; en anderen die "
-"bijgedragen hebben."
-
-#: ../extensions/cpsection/aboutcomputer/view.py:183
+#: ../extensions/cpsection/aboutcomputer/view.py:184
msgid ""
"Sugar is the graphical user interface that you are looking at. Sugar is free "
"software, covered by the GNU General Public License, and you are welcome to "
@@ -120,7 +113,7 @@ msgstr ""
"je mag het aanpassen en/of kopieën distribueren onder de condities zoals "
"vermeld in de licentie."
-#: ../extensions/cpsection/aboutcomputer/view.py:195
+#: ../extensions/cpsection/aboutcomputer/view.py:196
msgid "Full license:"
msgstr "Volledige licentie:"
@@ -298,55 +291,88 @@ msgstr "%(hour)d:%(min).2d over"
msgid "Charged"
msgstr "Opgeladen"
-#: ../extensions/deviceicon/network.py:40
+#: ../extensions/deviceicon/network.py:43
#, python-format
msgid "IP address: %s"
msgstr "IP adres: %s"
-#: ../extensions/deviceicon/network.py:104
+#: ../extensions/deviceicon/network.py:109
msgid "Disconnect..."
msgstr "Verbinding verbreken..."
-#: ../extensions/deviceicon/network.py:109
-#: ../src/jarabe/desktop/meshbox.py:248
+#: ../extensions/deviceicon/network.py:113
+msgid "Create new wireless network"
+msgstr "Nieuw draadloos netwerk aanmaken"
+
+#: ../extensions/deviceicon/network.py:119
+#: ../src/jarabe/desktop/meshbox.py:250
msgid "Connecting..."
msgstr "Verbinden..."
# TODO: show the channel number
-#: ../extensions/deviceicon/network.py:113
-#: ../extensions/deviceicon/network.py:166
-#: ../src/jarabe/desktop/meshbox.py:254
+#: ../extensions/deviceicon/network.py:123
+#: ../extensions/deviceicon/network.py:179
+#: ../src/jarabe/desktop/meshbox.py:256
msgid "Connected"
msgstr "Verbonden"
-#: ../extensions/deviceicon/network.py:126
+#: ../extensions/deviceicon/network.py:139
msgid "Channel"
msgstr "Kanaal"
-#: ../extensions/deviceicon/network.py:141
+#: ../extensions/deviceicon/network.py:154
msgid "Wired Network"
msgstr "Bedraad netwerk"
-#: ../extensions/deviceicon/network.py:169
+#: ../extensions/deviceicon/network.py:182
msgid "Speed"
msgstr "Snelheid"
+#: ../extensions/deviceicon/network.py:389
+#, python-format
+msgid "%s's network"
+msgstr "%s's netwerk"
+
#: ../extensions/deviceicon/speaker.py:59
msgid "My Speakers"
msgstr "Mijn Speakers"
-#: ../extensions/deviceicon/speaker.py:135
+#: ../extensions/deviceicon/speaker.py:133
msgid "Unmute"
msgstr "Ontdempen"
-#: ../extensions/deviceicon/speaker.py:138
+#: ../extensions/deviceicon/speaker.py:136
msgid "Mute"
msgstr "Dempen"
-#: ../extensions/globalkey/screenshot.py:51
+#: ../extensions/globalkey/screenshot.py:56
+msgid "Mesh"
+msgstr "Mesh"
+
+#: ../extensions/globalkey/screenshot.py:58
+#: ../src/jarabe/frame/zoomtoolbar.py:38
+msgid "Group"
+msgstr "Groep"
+
+#: ../extensions/globalkey/screenshot.py:60
+#: ../src/jarabe/frame/zoomtoolbar.py:40
+msgid "Home"
+msgstr "Thuis"
+
+#: ../extensions/globalkey/screenshot.py:66
+#: ../src/jarabe/frame/zoomtoolbar.py:42
+msgid "Activity"
+msgstr "Activiteit"
+
+#: ../extensions/globalkey/screenshot.py:69
msgid "Screenshot"
msgstr "Schermafdruk"
+#: ../extensions/globalkey/screenshot.py:71
+#, python-format
+msgid "Screenshot of \"%s\""
+msgstr "Schermafdruk van \"%s\""
+
#: ../data/sugar.schemas.in.h:1
msgid "Backup URL"
msgstr "Backup URL"
@@ -680,24 +706,24 @@ msgstr "WPA en WPA 2 Persoonlijk"
msgid "Wireless Security:"
msgstr "Draadloze netwerkbeveiliging:"
-#: ../src/jarabe/desktop/meshbox.py:132
+#: ../src/jarabe/desktop/meshbox.py:134
msgid "Connect"
msgstr "Verbinden"
-#: ../src/jarabe/desktop/meshbox.py:136
+#: ../src/jarabe/desktop/meshbox.py:138
msgid "Disconnect"
msgstr "Verbinding verbreken"
# TRANS: Action label for resuming an activity.
#. TRANS: Action label for resuming an activity.
-#: ../src/jarabe/desktop/meshbox.py:442
+#: ../src/jarabe/desktop/meshbox.py:449
#: ../src/jarabe/frame/activitiestray.py:761
#: ../src/jarabe/journal/journaltoolbox.py:425
#: ../src/jarabe/journal/palettes.py:72 ../src/jarabe/view/palettes.py:66
msgid "Resume"
msgstr "Hervatten"
-#: ../src/jarabe/desktop/meshbox.py:447
+#: ../src/jarabe/desktop/meshbox.py:454
#: ../src/jarabe/frame/activitiestray.py:235
msgid "Join"
msgstr "Bijvoegen"
@@ -787,18 +813,6 @@ msgstr "%s in klembord zetten"
msgid "Neighborhood"
msgstr "Omgeving"
-#: ../src/jarabe/frame/zoomtoolbar.py:38
-msgid "Group"
-msgstr "Groep"
-
-#: ../src/jarabe/frame/zoomtoolbar.py:40
-msgid "Home"
-msgstr "Thuis"
-
-#: ../src/jarabe/frame/zoomtoolbar.py:42
-msgid "Activity"
-msgstr "Activiteit"
-
#: ../src/jarabe/intro/window.py:124
msgid "Click to change color:"
msgstr "Klik om de kleur te veranderen:"
@@ -911,7 +925,7 @@ msgstr "Je dagboek is leeg"
msgid "No matching entries "
msgstr "Geen overeenkomende ingangen "
-#: ../src/jarabe/journal/listview.py:369
+#: ../src/jarabe/journal/listview.py:370
msgid "Clear search"
msgstr "Zoekopdracht wissen"
@@ -1014,24 +1028,24 @@ msgstr "Bron weergeven"
msgid "Stop"
msgstr "Stop"
-#: ../src/jarabe/view/palettes.py:171
+#: ../src/jarabe/view/palettes.py:174
msgid "Remove favorite"
msgstr "Verwijder favoriet"
-#: ../src/jarabe/view/palettes.py:175
+#: ../src/jarabe/view/palettes.py:178
msgid "Make favorite"
msgstr "Maak favoriet"
-#: ../src/jarabe/view/palettes.py:238
+#: ../src/jarabe/view/palettes.py:241
msgid "Show contents"
msgstr "Inhoud weergeven"
-#: ../src/jarabe/view/palettes.py:260 ../src/jarabe/view/palettes.py:309
+#: ../src/jarabe/view/palettes.py:263 ../src/jarabe/view/palettes.py:313
#, python-format
msgid "%(free_space)d MB Free"
msgstr "%(free_space)d MB vrij"
-#: ../src/jarabe/view/palettes.py:285
+#: ../src/jarabe/view/palettes.py:288
msgid "Unmount"
msgstr "Loskoppelen"
@@ -1052,6 +1066,12 @@ msgstr "Activiteitsbundel Bron"
msgid "View source: %r"
msgstr "Bron weergeven: %r"
+#~ msgid ""
+#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; and Contributors."
+#~ msgstr ""
+#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; en anderen die "
+#~ "bijgedragen hebben."
+
#~ msgid "Document"
#~ msgstr "Document"
@@ -1073,9 +1093,6 @@ msgstr "Bron weergeven: %r"
#~ msgid "About my XO"
#~ msgstr "Over mijn XO"
-#~ msgid "Mesh"
-#~ msgstr "Mesh"
-
#~ msgid "Connected to a School Mesh Portal"
#~ msgstr "Verbonden met een School Mesh Portaal"
diff --git a/src/jarabe/desktop/activitieslist.py b/src/jarabe/desktop/activitieslist.py
index 299df14..1284af4 100644
--- a/src/jarabe/desktop/activitieslist.py
+++ b/src/jarabe/desktop/activitieslist.py
@@ -203,7 +203,7 @@ class ListModel(gtk.TreeModelSort):
for row in self._model:
if row[ListModel.COLUMN_BUNDLE_ID] == bundle_id and \
row[ListModel.COLUMN_VERSION] == version:
- self.remove(row.iter)
+ self._model.remove(row.iter)
return
def _add_activity(self, activity_info):
diff --git a/src/jarabe/desktop/keydialog.py b/src/jarabe/desktop/keydialog.py
index 93f07c4..b9b229b 100644
--- a/src/jarabe/desktop/keydialog.py
+++ b/src/jarabe/desktop/keydialog.py
@@ -239,7 +239,7 @@ class WPAKeyDialog(KeyDialog):
elif len(key) >= 8 and len(key) <= 63:
# passphrase
from subprocess import Popen, PIPE
- p = Popen(['/usr/sbin/wpa_passphrase', ssid, key], stdout=PIPE)
+ p = Popen(['wpa_passphrase', ssid, key], stdout=PIPE)
for line in p.stdout:
if line.strip().startswith("psk="):
real_key = line.strip()[4:]
diff --git a/src/jarabe/frame/activitiestray.py b/src/jarabe/frame/activitiestray.py
index 1e2b8e8..0025403 100644
--- a/src/jarabe/frame/activitiestray.py
+++ b/src/jarabe/frame/activitiestray.py
@@ -815,8 +815,9 @@ class OutgoingTransferPalette(BaseTransferPalette):
self._update()
def _update(self):
- logging.debug('_update state: %r' % self.file_transfer.props.state)
- if self.file_transfer.props.state == filetransfer.FT_STATE_PENDING:
+ new_state = self.file_transfer.props.state
+ logging.debug('_update state: %r' % new_state)
+ if new_state == filetransfer.FT_STATE_PENDING:
menu_item = MenuItem(_('Cancel'), icon_name='dialog-cancel')
menu_item.connect('activate', self.__cancel_activate_cb)
@@ -840,8 +841,8 @@ class OutgoingTransferPalette(BaseTransferPalette):
vbox.add(label)
label.show()
- elif self.file_transfer.props.state in \
- [filetransfer.FT_STATE_ACCEPTED, filetransfer.FT_STATE_OPEN]:
+ elif new_state in [filetransfer.FT_STATE_ACCEPTED,
+ filetransfer.FT_STATE_OPEN]:
for item in self.menu.get_children():
self.menu.remove(item)
@@ -865,7 +866,8 @@ class OutgoingTransferPalette(BaseTransferPalette):
self.update_progress()
- elif self.file_transfer.props.state == filetransfer.FT_STATE_COMPLETED:
+ elif new_state in [filetransfer.FT_STATE_COMPLETED,
+ filetransfer.FT_STATE_CANCELLED]:
for item in self.menu.get_children():
self.menu.remove(item)
diff --git a/src/jarabe/journal/__init__.py b/src/jarabe/journal/__init__.py
index e69de29..6373228 100644
--- a/src/jarabe/journal/__init__.py
+++ b/src/jarabe/journal/__init__.py
@@ -0,0 +1,15 @@
+# Copyright (C) 2007, One Laptop Per Child
+#
+# 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/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py
index 4b325f9..5e20577 100644
--- a/src/jarabe/journal/listview.py
+++ b/src/jarabe/journal/listview.py
@@ -246,7 +246,7 @@ class BaseListView(gtk.Bin):
def __destroy_cb(self, widget):
if self._model is not None:
- self._model.destroy()
+ self._model.stop()
def __favorite_set_data_cb(self, column, cell, tree_model, tree_iter):
favorite = self._model[tree_iter][ListModel.COLUMN_FAVORITE]
diff --git a/src/jarabe/journal/objectchooser.py b/src/jarabe/journal/objectchooser.py
index 0e6de27..827f228 100644
--- a/src/jarabe/journal/objectchooser.py
+++ b/src/jarabe/journal/objectchooser.py
@@ -128,9 +128,9 @@ class ObjectChooser(gtk.Window):
def __query_changed_cb(self, toolbar, query):
self._list_view.update_with_query(query)
- def __volume_changed_cb(self, volume_toolbar, volume_id):
- logging.debug('Selected volume: %r.' % volume_id)
- self._toolbar.set_volume_id(volume_id)
+ def __volume_changed_cb(self, volume_toolbar, mount_point):
+ logging.debug('Selected volume: %r.' % mount_point)
+ self._toolbar.set_mount_point(mount_point)
def __visibility_notify_event_cb(self, window, event):
logging.debug('visibility_notify_event_cb %r' % self)
diff --git a/src/jarabe/model/buddy.py b/src/jarabe/model/buddy.py
index b51b808..1fa9e2c 100644
--- a/src/jarabe/model/buddy.py
+++ b/src/jarabe/model/buddy.py
@@ -35,6 +35,9 @@ class BuddyModel(gobject.GObject):
'icon-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([])),
+ 'tags-changed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
'current-activity-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
@@ -47,6 +50,7 @@ class BuddyModel(gobject.GObject):
gobject.GObject.__init__(self)
self._color = None
+ self._tags = None
self._ba_handler = None
self._pc_handler = None
self._dis_handler = None
@@ -99,6 +103,9 @@ class BuddyModel(gobject.GObject):
def get_color(self):
return self._color
+ def get_tags(self):
+ return self._tags
+
def get_buddy(self):
return self._buddy
@@ -124,6 +131,7 @@ class BuddyModel(gobject.GObject):
self._buddy = buddy
self._key = self._buddy.props.key
self._nick = self._buddy.props.nick
+ self._tags = self._buddy.props.tags
self._set_color_from_string(self._buddy.props.color)
self._pc_handler = self._buddy.connect('property-changed',
@@ -155,6 +163,9 @@ class BuddyModel(gobject.GObject):
if 'nick' in keys:
self._nick = self._buddy.props.nick
self.emit('nick-changed', self.get_nick())
+ if 'tags' in keys:
+ self._tags = self._buddy.props.tags
+ self.emit('tags-changed', self.get_tags())
def _buddy_disappeared_cb(self, buddy):
if buddy != self._buddy:
diff --git a/src/jarabe/model/bundleregistry.py b/src/jarabe/model/bundleregistry.py
index 2155208..9d7e957 100644
--- a/src/jarabe/model/bundleregistry.py
+++ b/src/jarabe/model/bundleregistry.py
@@ -169,6 +169,9 @@ class BundleRegistry(gobject.GObject):
def __iter__(self):
return self._bundles.__iter__()
+ def __len__(self):
+ return len(self._bundles)
+
def _scan_directory(self, path):
if not os.path.isdir(path):
return
@@ -330,12 +333,9 @@ class BundleRegistry(gobject.GObject):
def install(self, bundle):
activities_path = env.get_user_activities_path()
- if self.get_bundle(bundle.get_bundle_id()):
- raise AlreadyInstalledException
-
for installed_bundle in self._bundles:
if bundle.get_bundle_id() == installed_bundle.get_bundle_id() and \
- bundle.get_activity_version() == \
+ bundle.get_activity_version() <= \
installed_bundle.get_activity_version():
raise AlreadyInstalledException
elif bundle.get_bundle_id() == installed_bundle.get_bundle_id():
diff --git a/src/jarabe/model/filetransfer.py b/src/jarabe/model/filetransfer.py
index 6419f28..d0d28fa 100644
--- a/src/jarabe/model/filetransfer.py
+++ b/src/jarabe/model/filetransfer.py
@@ -51,6 +51,7 @@ FT_REASON_REMOTE_ERROR = 6
CHANNEL_TYPE_FILE_TRANSFER = \
'org.freedesktop.Telepathy.Channel.Type.FileTransfer'
+# TODO Move to use splice_async() in Sugar 0.88
class StreamSplicer(gobject.GObject):
_CHUNK_SIZE = 10240 # 10K
__gsignals__ = {
diff --git a/src/jarabe/view/service.py b/src/jarabe/view/service.py
index df00ba6..ef225bf 100644
--- a/src/jarabe/view/service.py
+++ b/src/jarabe/view/service.py
@@ -29,8 +29,6 @@ _DBUS_SHELL_IFACE = "org.laptop.Shell"
_DBUS_OWNER_IFACE = "org.laptop.Shell.Owner"
_DBUS_PATH = "/org/laptop/Shell"
-_DBUS_RAINBOW_IFACE = "org.laptop.security.Rainbow"
-
class UIService(dbus.service.Object):
"""Provides d-bus service to script the shell's operations
@@ -50,8 +48,6 @@ class UIService(dbus.service.Object):
anything other than add_bundle
"""
- _rainbow = None
-
def __init__(self):
bus = dbus.SessionBus()
bus_name = dbus.service.BusName(_DBUS_SERVICE, bus=bus)
@@ -123,22 +119,9 @@ class UIService(dbus.service.Object):
def _owner_icon_changed_cb(self, new_icon):
self.IconChanged(dbus.ByteArray(new_icon))
- def _get_rainbow_service(self):
- """Lazily initializes an interface to the Rainbow security daemon."""
- if self._rainbow is None:
- system_bus = dbus.SystemBus()
- obj = system_bus.get_object(_DBUS_RAINBOW_IFACE, '/',
- follow_name_owner_changes=True)
- self._rainbow = dbus.Interface(obj,
- dbus_interface=_DBUS_RAINBOW_IFACE)
- return self._rainbow
-
@dbus.service.signal(_DBUS_OWNER_IFACE, signature="s")
def CurrentActivityChanged(self, activity_id):
- if os.path.exists('/etc/olpc-security'):
- self._get_rainbow_service().ChangeActivity(
- activity_id,
- dbus_interface=_DBUS_RAINBOW_IFACE)
+ pass
def _cur_activity_changed_cb(self, shell_model, new_activity):
new_id = ""